[Scummvm-git-logs] scummvm master -> 091b14801bae7f9bf159b54b788c87b743cf4c29
neuromancer
noreply at scummvm.org
Wed Jul 15 13:57:22 UTC 2026
This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
6c361c3549 HYPNO: completed KO localization for wetlands
394e18ae9c HYPNO: added missing sounds in boyz
581bdc81ed SCUMM: RA: improved rapid fire support for both games
daf0923d3a SCUMM: RA2: properly show difficulty from every pilot
091b14801b SCUMM: RA2: scale crossair for some levels
Commit: 6c361c35495e1905678040f27e359255b027924c
https://github.com/scummvm/scummvm/commit/6c361c35495e1905678040f27e359255b027924c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-15T15:55:56+02:00
Commit Message:
HYPNO: completed KO localization for wetlands
Changed paths:
engines/hypno/hypno.h
engines/hypno/wet/arcade.cpp
engines/hypno/wet/hard.cpp
engines/hypno/wet/wet.cpp
diff --git a/engines/hypno/hypno.h b/engines/hypno/hypno.h
index ac7de039d2d..c7479e06a8e 100644
--- a/engines/hypno/hypno.h
+++ b/engines/hypno/hypno.h
@@ -527,6 +527,7 @@ private:
uint16 getNextChar(const Common::String &str, uint32 &c);
void drawGlyph(const Common::BitArray &font, int x, int y, int bitoffset, int width, int height, int pitch, uint32 color, bool invert);
void drawKoreanChar(uint16 chr, int &curx, int y, uint32 color);
+ void drawStatLine(const Common::String &name, const Common::String &value, int y, uint32 color);
void runMainMenu(Code *code);
void runLevelMenu(Code *code);
void runCheckLives(Code *code);
diff --git a/engines/hypno/wet/arcade.cpp b/engines/hypno/wet/arcade.cpp
index 74be3622e4e..bd4dd370119 100644
--- a/engines/hypno/wet/arcade.cpp
+++ b/engines/hypno/wet/arcade.cpp
@@ -400,6 +400,18 @@ bool WetEngine::checkTransition(ArcadeTransitions &transitions, ArcadeShooting *
}
+// Fixed columns instead of the original "%-20s = ...": that padding counts
+// bytes, so a Korean label (2 bytes, 9px per glyph) drifts the '=' column.
+// Korean labels use the 9px G9A font, spaces included; values stay scifi08.
+void WetEngine::drawStatLine(const Common::String &name, const Common::String &value, int y, uint32 color) {
+ const int labelX = 60;
+ const int valueX = labelX + 21 * 7;
+ const bool korean = (_language == Common::KO_KOR);
+
+ drawString(korean ? "g9a.syf" : "scifi08.fgx", getLocalizedString(name), labelX, korean ? y - 1 : y, 0, color);
+ drawString("scifi08.fgx", value, valueX, y, 0, color);
+}
+
void WetEngine::runAfterArcade(ArcadeShooting *arc) {
_checkpoint = _currentLevel;
@@ -436,15 +448,22 @@ void WetEngine::runAfterArcade(ArcadeShooting *arc) {
Common::Event event;
while (!shouldQuit() && !skip) {
+ const Common::String pts = getLocalizedString("points");
+
drawImage(*frame, 0, 0, false);
- drawString("scifi08.fgx", Common::String::format("Lives : %d", _lives), 36, 2, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %7d", "SHOTS FIRED", _stats.shootsFired), 60, 46, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %7d", "ENEMY TARGETS", _stats.enemyTargets), 60, 56, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %7d", "TARGETS DESTROYED", _stats.targetsDestroyed), 60, 66, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %7d", "TARGETS MISSED", _stats.targetsMissed), 60, 76, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %5d %%", "KILL RATIO", killRatio()), 60, 86, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %5d %%", "ACCURACY", accuracyRatio()), 60, 96, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %5d %%", "ENERGY", _health), 60, 106, 0, c);
+ if (_language == Common::KO_KOR) {
+ drawString("g9a.syf", getLocalizedString("lives"), 36, 1, 0, c);
+ drawString("scifi08.fgx", Common::String::format(": %d", _lives), 36 + 3 * 9, 2, 0, c);
+ } else {
+ drawString("scifi08.fgx", Common::String::format("%s : %d", getLocalizedString("lives").c_str(), _lives), 36, 2, 0, c);
+ }
+ drawStatLine("shotsFired", Common::String::format("= %7d", _stats.shootsFired), 46, c);
+ drawStatLine("enemyTargets", Common::String::format("= %7d", _stats.enemyTargets), 56, c);
+ drawStatLine("targetsDestroyed", Common::String::format("= %7d", _stats.targetsDestroyed), 66, c);
+ drawStatLine("targetsMissed", Common::String::format("= %7d", _stats.targetsMissed), 76, c);
+ drawStatLine("killRatio", Common::String::format("= %5d %%", killRatio()), 86, c);
+ drawStatLine("accuracy", Common::String::format("= %5d %%", accuracyRatio()), 96, c);
+ drawStatLine("health", Common::String::format("= %5d %%", _health), 106, c);
while (g_system->getEventManager()->pollEvent(event)) {
// Events
@@ -457,8 +476,8 @@ void WetEngine::runAfterArcade(ArcadeShooting *arc) {
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_KEYDOWN:
bonusCounter = _bonus;
- drawString("scifi08.fgx", Common::String::format("%-20s = %3d pts", "BONUS", _bonus), 60, 116, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %3d pts", "SCORE", _score), 60, 126, 0, c);
+ drawStatLine("bonus", Common::String::format("= %3d %s", _bonus, pts.c_str()), 116, c);
+ drawStatLine("score", Common::String::format("= %3d %s", _score, pts.c_str()), 126, c);
skip = true;
break;
default:
@@ -469,13 +488,13 @@ void WetEngine::runAfterArcade(ArcadeShooting *arc) {
if (bonusCounter < _bonus) {
bonusCounter++;
scoreCounter++;
- drawString("scifi08.fgx", Common::String::format("%-20s = %3d pts", "BONUS", bonusCounter), 60, 116, 0, c);
- drawString("scifi08.fgx", Common::String::format("%-20s = %3d pts", "SCORE", scoreCounter), 60, 126, 0, c);
+ drawStatLine("bonus", Common::String::format("= %3d %s", bonusCounter, pts.c_str()), 116, c);
+ drawStatLine("score", Common::String::format("= %3d %s", scoreCounter, pts.c_str()), 126, c);
}
extraLife |= checkScoreMilestones(scoreCounter); // This increase the number of lives, if necessary
if (extraLife) {
- drawString("scifi08.fgx", "EXTRA LIFE", 164, 140, 0, kHypnoColorRed);
+ drawString("scifi08.fgx", getLocalizedString("extraLife"), 164, 140, 0, kHypnoColorRed);
}
drawScreen();
diff --git a/engines/hypno/wet/hard.cpp b/engines/hypno/wet/hard.cpp
index 9ff212713a9..2f131d4a434 100644
--- a/engines/hypno/wet/hard.cpp
+++ b/engines/hypno/wet/hard.cpp
@@ -378,7 +378,7 @@ Common::String WetEngine::getLocalizedString(const Common::String &name) {
case Common::ES_ESP:
return "ENERGIA";
case Common::KO_KOR:
- return "\xb5\x41\x90\xe1\xbb\xa1"; // ì²´ë ¥ (health)
+ return "\xb5\x41\x90\xe1\xbb\xa1"; // ìëì§ (energy)
default:
return "ENERGY";
}
@@ -409,7 +409,7 @@ Common::String WetEngine::getLocalizedString(const Common::String &name) {
case Common::ES_ESP:
return "BLANCO FIJADO";
case Common::KO_KOR:
- return "\xa1\xa2\xce\x61\x20\xbb\xe1\xaf\x81"; // 목í물í¬ì°© (target acquired)
+ return "\xa1\xa2\xce\x61\xa2\x89\x20\xcd\xa1\xc0\x62"; // 목í물 í¬ì°© (target acquired)
default:
return "TARGET ACQUIRED";
}
@@ -420,10 +420,75 @@ Common::String WetEngine::getLocalizedString(const Common::String &name) {
case Common::ES_ESP:
return "ELIGE DIRECCION";
case Common::KO_KOR:
- return "\xa4\x77\xd0\xb7\xac\xe5\x00\x00\xc8\x82"; // ì í¥ì í (choose direction)
+ return "\xa4\x77\xd0\xb7\x20\xac\xe5\xc8\x82"; // ë°©í¥ ì í (choose direction)
default:
return "CHOOSE DIRECTION";
}
+ } else if (name == "shotsFired") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\xa2\x81\x8b\xa1\x20\xa4\x69\xac\x61"; // 무기 ë°ì¬
+ default:
+ return "SHOTS FIRED";
+ }
+ } else if (name == "enemyTargets") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\xb8\xe2\xb7\x81\x20\xa1\xa2\xce\x61\xa2\x89"; // ì ì 목í물
+ default:
+ return "ENEMY TARGETS";
+ }
+ } else if (name == "targetsDestroyed") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\xa1\xa2\xce\x61\xa2\x89\x20\xb9\x41\x88\xe1"; // 목í물 ì ê±°
+ default:
+ return "TARGETS DESTROYED";
+ }
+ } else if (name == "targetsMissed") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\x91\xbd\xc3\xa5\x20\xa1\xa2\xce\x61\xa2\x89"; // ëì¹ ëª©í물
+ default:
+ return "TARGETS MISSED";
+ }
+ } else if (name == "killRatio") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\xa1\x77\xba\x97\xb7\x49"; // ëª
ì¤ì¨
+ default:
+ return "KILL RATIO";
+ }
+ } else if (name == "accuracy") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\xb8\xf7\xd1\xc2\x95\xa1"; // ì íë
+ default:
+ return "ACCURACY";
+ }
+ } else if (name == "bonus") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\xc2\x81\x88\x61\xb8\xf1\xae\x81"; // ì¶ê°ì ì
+ default:
+ return "BONUS";
+ }
+ } else if (name == "points") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\xb8\xf1"; // ì
+ default:
+ return "pts";
+ }
+ } else if (name == "extraLife") {
+ return "EXTRA LIFE";
+ } else if (name == "lives") {
+ switch (_language) {
+ case Common::KO_KOR:
+ return "\xac\x97\x20\xa1\x77"; // ì ëª
+ default:
+ return "Lives";
+ }
} else
error("Invalid string name to localize: %s", name.c_str());
}
diff --git a/engines/hypno/wet/wet.cpp b/engines/hypno/wet/wet.cpp
index f951e8b3ce2..c15411d576a 100644
--- a/engines/hypno/wet/wet.cpp
+++ b/engines/hypno/wet/wet.cpp
@@ -684,7 +684,8 @@ void WetEngine::drawKoreanChar(uint16 chr, int &curx, int y, uint32 color) {
int mididx = mididxlut[mid];
int finidx = fin >= 0x12 ? fin - 2 : fin - 1;
- if (initidx < 0 || initidx > 19 || mididx < 0 || mididx > 21 || finidx < 0 || finidx >= 27)
+ // finidx 0 is "no final", 1..27 the final consonants: ã
is 27, so 28 is the bound
+ if (initidx < 0 || initidx > 19 || mididx < 0 || mididx > 21 || finidx < 0 || finidx >= 28)
return;
const int mid_to_init_lut[32] = {
Commit: 394e18ae9c09031a53aaed488d18bb2a2d62937c
https://github.com/scummvm/scummvm/commit/394e18ae9c09031a53aaed488d18bb2a2d62937c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-15T15:55:56+02:00
Commit Message:
HYPNO: added missing sounds in boyz
Changed paths:
engines/hypno/boyz/boyz.cpp
engines/hypno/boyz/hard.cpp
engines/hypno/boyz/scene.cpp
engines/hypno/hypno.h
diff --git a/engines/hypno/boyz/boyz.cpp b/engines/hypno/boyz/boyz.cpp
index 6b7b7c2a16f..c522f629651 100644
--- a/engines/hypno/boyz/boyz.cpp
+++ b/engines/hypno/boyz/boyz.cpp
@@ -215,6 +215,9 @@ void BoyzEngine::loadAssets() {
Transition *territory_1 = new Transition("c19.mi_");
territory_1->intros.push_back("warnings/w17s.smk");
territory_1->intros.push_back("c1/c1t01.smk");
+ territory_1->music = kTransitionMusic;
+ territory_1->musicStereo = true;
+ territory_1->playMusicDuringIntro = true;
_levels["<territory_1>"] = territory_1;
Transition *warning_1 = new Transition("c14.mi_");
@@ -235,6 +238,9 @@ void BoyzEngine::loadAssets() {
Transition *territory_2 = new Transition("c21.mi_");
territory_2->intros.push_back("warnings/w13s.smk");
territory_2->intros.push_back("c2/c2t01.smk");
+ territory_2->music = kTransitionMusic;
+ territory_2->musicStereo = true;
+ territory_2->playMusicDuringIntro = true;
_levels["<territory_2>"] = territory_2;
loadArcadeLevel("c21.mi_", "c22.mi_", "<retry_menu>", "");
@@ -243,6 +249,9 @@ void BoyzEngine::loadAssets() {
Transition *territory_3 = new Transition("<select_c3>");
territory_3->intros.push_back("warnings/w14s.smk");
territory_3->intros.push_back("c3/c3t01.smk");
+ territory_3->music = kTransitionMusic;
+ territory_3->musicStereo = true;
+ territory_3->playMusicDuringIntro = true;
_levels["<territory_3>"] = territory_3;
loadArcadeLevel("c31.mi_", "<check_c3>", "<retry_menu>", "");
@@ -275,6 +284,9 @@ void BoyzEngine::loadAssets() {
Transition *territory_4 = new Transition("c41.mi_");
territory_4->intros.push_back("warnings/w15s.smk");
territory_4->intros.push_back("c4/c4t01.smk");
+ territory_4->music = kTransitionMusic;
+ territory_4->musicStereo = true;
+ territory_4->playMusicDuringIntro = true;
_levels["<territory_4>"] = territory_4;
loadArcadeLevel("c41.mi_", "c42.mi_", "<retry_menu>", "");
@@ -289,6 +301,9 @@ void BoyzEngine::loadAssets() {
territory_5->intros.push_back("warnings/w16s.smk");
territory_5->intros.push_back("c5/c5t01.smk");
territory_5->intros.push_back("c5/c5intras.smk");
+ territory_5->music = kTransitionMusic;
+ territory_5->musicStereo = true;
+ territory_5->playMusicDuringIntro = true;
_levels["<territory_5>"] = territory_5;
loadArcadeLevel("c51.mi_", "<check_c5>", "<retry_menu>", "");
diff --git a/engines/hypno/boyz/hard.cpp b/engines/hypno/boyz/hard.cpp
index f50aa4f9914..216e935a5e7 100644
--- a/engines/hypno/boyz/hard.cpp
+++ b/engines/hypno/boyz/hard.cpp
@@ -32,6 +32,9 @@
namespace Hypno {
void BoyzEngine::runCode(Code *code) {
+ if (!isMusicActive())
+ playMusic(kMenuMusic, 22050, true);
+
if (code->name == "<main_menu>")
runMainMenu(code);
else if (code->name == "<difficulty_menu>")
@@ -92,6 +95,7 @@ void BoyzEngine::runMainMenu(Code *code) {
if (event.kbd.keycode == Common::KEYCODE_BACKSPACE)
_name.deleteLastChar();
else if (event.kbd.keycode == Common::KEYCODE_RETURN && !_name.empty()) {
+ playSound(kMenuChoiceSound, 1);
cont = false;
} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
if (runExitMenu())
@@ -100,7 +104,7 @@ void BoyzEngine::runMainMenu(Code *code) {
}
else if (Common::isAlpha(event.kbd.keycode)) {
- playSound("sound/m_choice.raw", 1);
+ playSound(kMenuChoiceSound, 1);
_name = _name + char(event.kbd.keycode - 32);
}
@@ -171,10 +175,12 @@ bool BoyzEngine::runExitMenu() {
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionYes) {
+ playSound(kMenuChoiceSound, 1);
quit = true;
cont = false;
} else if (event.customType == kActionNo) {
+ playSound(kMenuChoiceSound, 1);
quit = false;
cont = false;
break;
@@ -183,10 +189,12 @@ bool BoyzEngine::runExitMenu() {
case Common::EVENT_LBUTTONDOWN:
if (yesBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
quit = true;
cont = false;
break;
} else if (noBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
quit = false;
cont = false;
break;
@@ -239,30 +247,38 @@ void BoyzEngine::runDifficultyMenu(Code *code) {
case Common::EVENT_LBUTTONDOWN:
if (chumpBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
_difficulty = "chump";
cont = false;
} else if (punkBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
_difficulty = "punk";
cont = false;
} else if (badAssBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
_difficulty = "bad ass";
cont = false;
} else if (cancelBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
cont = false;
}
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionDifficultyChump) {
+ playSound(kMenuChoiceSound, 1);
_difficulty = "chump";
cont = false;
} else if (event.customType == kActionDifficultyPunk) {
+ playSound(kMenuChoiceSound, 1);
_difficulty = "punk";
cont = false;
} else if (event.customType == kActionDifficultyBadass) {
+ playSound(kMenuChoiceSound, 1);
_difficulty = "bad ass";
cont = false;
} else if (event.customType == kActionDifficultExit) {
+ playSound(kMenuChoiceSound, 1);
cont = false;
}
break;
@@ -332,36 +348,46 @@ void BoyzEngine::runRetryMenu(Code *code) {
case Common::EVENT_LBUTTONDOWN:
if (retryMissionBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
_nextLevel = _checkpoint;
cont = false;
} else if (restartTerritoryBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
// Restore initial health for the team
_health = _maxHealth;
_stats = _globalStats;
_nextLevel = firstLevelTerritory(_checkpoint);
cont = false;
} else if (restartMissionBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
_nextLevel = "<main_menu>";
cont = false;
- } else if (quitBox.contains(mousePos))
+ } else if (quitBox.contains(mousePos)) {
+ playSound(kMenuChoiceSound, 1);
quitGame();
+ }
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionRetry) {
+ playSound(kMenuChoiceSound, 1);
_nextLevel = _checkpoint;
cont = false;
} else if (event.customType == kActionNewMission) {
+ playSound(kMenuChoiceSound, 1);
_nextLevel = "<main_menu>";
cont = false;
} else if (event.customType == kActionRestart) {
+ playSound(kMenuChoiceSound, 1);
// Restore initial health for the team
_health = _maxHealth;
_stats = _globalStats;
_nextLevel = firstLevelTerritory(_checkpoint);
cont = false;
- } else if (event.customType == kActionQuit)
+ } else if (event.customType == kActionQuit) {
+ playSound(kMenuChoiceSound, 1);
quitGame();
+ }
break;
default:
@@ -461,10 +487,13 @@ void BoyzEngine::endCredits(Code *code) {
}
void BoyzEngine::showCredits() {
+ stopMusic();
+ playMusic(kCreditsMusic, 22050, true);
MVideo c1("intro/sbcred1.smk", Common::Point(0, 0), false, true, false);
runIntro(c1);
MVideo c2("intro/sbcred2.smk", Common::Point(0, 0), false, true, false);
runIntro(c2);
+ stopMusic();
}
int BoyzEngine::getTerritory(const Common::String &level) {
diff --git a/engines/hypno/boyz/scene.cpp b/engines/hypno/boyz/scene.cpp
index ad9b0970a1d..f6a865e27ac 100644
--- a/engines/hypno/boyz/scene.cpp
+++ b/engines/hypno/boyz/scene.cpp
@@ -194,6 +194,10 @@ bool BoyzEngine::hoverHotspot(Common::Point mousePos) {
if (found) {
Hotspot *menu = hots->begin();
if (menu->type == MakeMenu && !menu->background.empty()) { // Hihghlight
+ if (selected.rect != _lastHighlighted) {
+ _lastHighlighted = selected.rect;
+ playSound(kMenuHiliteSound, 1);
+ }
Graphics::Surface sub = menu->backgroundFrames[2]->getSubArea(selected.rect);
drawImage(*menu->backgroundFrames[1], 0, 0, false);
renderHighlights(hots);
@@ -202,6 +206,7 @@ bool BoyzEngine::hoverHotspot(Common::Point mousePos) {
}
return true;
}
+ _lastHighlighted = Common::Rect();
return false;
}
diff --git a/engines/hypno/hypno.h b/engines/hypno/hypno.h
index c7479e06a8e..ee0c69b40bd 100644
--- a/engines/hypno/hypno.h
+++ b/engines/hypno/hypno.h
@@ -635,6 +635,15 @@ private:
const Graphics::Font *_font;
};
+// SOUND.LIB ships four music tracks that no level script references: the menus
+// and the territory approach videos use them. m_choice/m_hilite are the menu
+// click and highlight sounds (as in Wetlands).
+static const char *const kMenuChoiceSound = "sound/m_choice.raw";
+static const char *const kMenuHiliteSound = "sound/m_hilite.raw";
+static const char *const kMenuMusic = "sound/46-g.raw";
+static const char *const kTransitionMusic = "sound/45.raw";
+static const char *const kCreditsMusic = "sound/42.raw";
+
class BoyzEngine : public HypnoEngine {
public:
BoyzEngine(OSystem *syst, const ADGameDescription *gd);
@@ -687,6 +696,7 @@ public:
void saveProfile(const Common::String &name, int levelId);
private:
+ Common::Rect _lastHighlighted;
void renderHighlights(Hotspots *hs);
void waitForUserClick(uint32 timeout);
int pickABox();
Commit: 581bdc81ed52ff748b4639efc1d4277ddb27b5ba
https://github.com/scummvm/scummvm/commit/581bdc81ed52ff748b4639efc1d4277ddb27b5ba
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-15T15:55:56+02:00
Commit Message:
SCUMM: RA: improved rapid fire support for both games
Changed paths:
engines/scumm/insane/rebel1/iact.cpp
engines/scumm/insane/rebel1/menu.cpp
engines/scumm/insane/rebel1/rebel.cpp
engines/scumm/insane/rebel1/rebel.h
engines/scumm/insane/rebel2/menu.cpp
engines/scumm/insane/rebel2/rebel.cpp
engines/scumm/insane/rebel2/rebel.h
diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index 913bcd56006..d874fca97a2 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -2005,7 +2005,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
_playerFired = false;
_playerSecondaryHeld = false;
_fireCooldown = 0;
- _rapidFirePhase = 0;
+ _rapidFireCounter = 0;
memset(_shotSlots, 0, sizeof(_shotSlots));
_shotAlternator = 0;
_shotSideToggle = false;
@@ -2344,20 +2344,23 @@ void InsaneRebel1::handleGameChunk(int32 subSize, Common::SeekableReadStream &b,
// Called once per frame during interactive rendering.
void InsaneRebel1::processShot() {
+ // Rapid fire (not in the DOS original, ported from RA2) repeats held
+ // shots every 5th frame from the press.
+ const bool freshPress = _playerFired && _fireCooldown == 0;
+ if (freshPress)
+ _rapidFireCounter = 0;
+
+ bool rapidFireShot = false;
if (_optRapidFire) {
- // fires immediately, while held repeats are gated to phase 0.
- _rapidFirePhase++;
- if (_rapidFirePhase > 2)
- _rapidFirePhase = 0;
+ rapidFireShot = (_rapidFireCounter % 5 == 0) && _playerFired;
+ _rapidFireCounter++;
}
if (!_playerFired)
return;
- if (_fireCooldown != 0) {
- if (!_optRapidFire || _rapidFirePhase != 0)
- return;
- }
+ if (_fireCooldown != 0 && !rapidFireShot)
+ return;
// On-foot mode: only spawn when in aiming stance (dirIndex 11-19) or flags force it.
const uint16 effectiveOpcode = getEffectiveGameOpcode();
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 0ec0c19f47c..6947fb4f0a9 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -1359,6 +1359,8 @@ void InsaneRebel1::runOptionsMenu() {
break;
case 6:
_optRapidFire = !_optRapidFire;
+ ConfMan.setBool("rebel1_rapid_fire", _optRapidFire);
+ ConfMan.flushToDisk();
break;
case 7:
break;
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 276660a4b9b..5ec62f2f5cb 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -351,7 +351,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
!_vm->_mixer->isSoundTypeMuted(Audio::Mixer::kSpeechSoundType);
_optTextEnabled = ConfMan.getBool("subtitles");
_optControlsYFlip = false;
- _optRapidFire = true;
+ _optRapidFire = ConfMan.hasKey("rebel1_rapid_fire") ? ConfMan.getBool("rebel1_rapid_fire") : true;
_optVolume = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) * 127 / Audio::Mixer::kMaxChannelVolume;
const struct { const char *name; int32 score; byte difficulty; } kDefaultScores[kHighScoreCount] = {
@@ -380,7 +380,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
_playerFired = false;
_playerSecondaryHeld = false;
_fireCooldown = 0;
- _rapidFirePhase = 0;
+ _rapidFireCounter = 0;
_gameplayFlags75fe = 0;
_gameplayFlags75ff = 0;
memset(_shotSlots, 0, sizeof(_shotSlots));
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index b2e5508d197..bf7f6969681 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -587,7 +587,8 @@ private:
// Secondary held button used by L9 on-foot controls.
bool _playerSecondaryHeld;
int16 _fireCooldown;
- int16 _rapidFirePhase;
+ // Rapid-fire cadence counter, re-anchored on each press.
+ int16 _rapidFireCounter;
uint16 _gameplayFlags75fe;
uint16 _gameplayFlags75ff;
diff --git a/engines/scumm/insane/rebel2/menu.cpp b/engines/scumm/insane/rebel2/menu.cpp
index cc1ef27966b..87531c97332 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -1588,6 +1588,8 @@ int InsaneRebel2::processOptionsInput() {
break;
case 5:
_optRapidFire = !_optRapidFire;
+ ConfMan.setBool("rebel2_rapid_fire", _optRapidFire);
+ ConfMan.flushToDisk();
break;
case 6:
break;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index acc4da6582d..81fd27d02d2 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -428,6 +428,7 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
_flyControlMode = 0;
_shipFiring = false;
_prevMouseButtons = 0;
+ _rapidFireCounter = 0;
_shipDirectionH = 2;
_shipDirectionV = 3;
_shipDirectionIndex = 2 * 7 + 3;
@@ -536,7 +537,8 @@ InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
// "subtitles" key, which the text-render paths gate on.
_optTextEnabled = ConfMan.getBool("subtitles");
_optControlsFlipped = false;
- _optRapidFire = false;
+ // Unlike the original, this defaults to on.
+ _optRapidFire = ConfMan.hasKey("rebel2_rapid_fire") ? ConfMan.getBool("rebel2_rapid_fire") : true;
_optVolumeLevel = _vm->_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 2;
_menuInputActive = false;
@@ -1511,6 +1513,7 @@ int32 InsaneRebel2::processMouse() {
bool leftPressed = (currentButtons & 1) != 0;
bool leftWasPressed = (_prevMouseButtons & 1) != 0;
+ bool leftEdge = leftPressed && !leftWasPressed;
bool rightPressed = (currentButtons & 2) != 0;
bool rightWasPressed = (_prevMouseButtons & 2) != 0;
@@ -1520,7 +1523,7 @@ int32 InsaneRebel2::processMouse() {
if (rightPressed && !rightWasPressed) {
_rebelControlMode |= 2;
}
- if (leftPressed && !leftWasPressed) {
+ if (leftEdge) {
_rebelControlMode |= 1;
}
} else {
@@ -1542,7 +1545,16 @@ int32 InsaneRebel2::processMouse() {
if (autoFire)
_rebelControlMode |= 1;
- bool triggerShot = ((_rebelHandler == 25) ? (leftPressed && !leftWasPressed) : leftPressed) || autoFire;
+ // Rapid fire injects a held-button shot every 5th frame from the press.
+ if (leftEdge)
+ _rapidFireCounter = 0;
+ bool rapidFireShot = false;
+ if (!_rebelAutoPlay && _optRapidFire) {
+ rapidFireShot = (_rapidFireCounter % 5 == 0) && leftPressed;
+ _rapidFireCounter++;
+ }
+
+ bool triggerShot = leftEdge || rapidFireShot || autoFire;
if (_rebelHandler == 8) {
_shipFiring = triggerShot && canShoot;
}
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 580cc74101f..8022adf1376 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -810,6 +810,9 @@ public:
bool _shipFiring;
uint32 _prevMouseButtons;
+ // Rapid-fire cadence counter, re-anchored on each press.
+ int16 _rapidFireCounter;
+
int16 _shipDirectionIndex;
int16 _shipDirectionH;
int16 _shipDirectionV;
Commit: daf0923d3ac7efdb3f1176d43e2ab2809cb35261
https://github.com/scummvm/scummvm/commit/daf0923d3ac7efdb3f1176d43e2ab2809cb35261
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-15T15:55:56+02:00
Commit Message:
SCUMM: RA2: properly show difficulty from every pilot
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 87531c97332..de13ab5a26d 100644
--- a/engines/scumm/insane/rebel2/menu.cpp
+++ b/engines/scumm/insane/rebel2/menu.cpp
@@ -178,8 +178,7 @@ int InsaneRebel2::processMenuInput() {
int mouseY = event.mouse.y;
for (int i = 0; i < _menuItemCount; i++) {
int itemY = baseY + i * itemSpacing;
- if (mouseY >= itemY - itemHitTop - 3 * (highRes ? 2 : 1) &&
- mouseY < itemY - itemHitTop + itemHitHeight - 3 * (highRes ? 2 : 1)) {
+ if (mouseY >= itemY - itemHitTop && mouseY < itemY - itemHitTop + itemHitHeight) {
if (i != _menuSelection) {
_menuSelection = i;
debugC(DEBUG_INSANE, "Menu: Hover selection changed to %d (mouseY=%d)", _menuSelection, mouseY);
@@ -771,8 +770,7 @@ int InsaneRebel2::processChapterSelectInput() {
for (int i = 0; i < _chapterItemCount; i++) {
int itemY = baseY + i * itemSpacing;
- if (mouseY >= itemY - itemHitTop - 3 * (highRes ? 2 : 1) &&
- mouseY < itemY - itemHitTop + itemHitHeight - 3 * (highRes ? 2 : 1)) {
+ if (mouseY >= itemY - itemHitTop && mouseY < itemY - itemHitTop + itemHitHeight) {
if (i != _chapterSelection) {
_chapterSelection = i;
_previewOffsetY = _chapterSelection * -50 + 75;
@@ -1246,8 +1244,7 @@ int InsaneRebel2::processLevelSelectInput() {
_vm->_mouse.y = event.mouse.y;
for (int i = 0; i < itemCount; i++) {
int itemY = itemBaseY + i * itemSpacing;
- if (event.mouse.y >= itemY - itemHitTop - 3 * (highRes ? 2 : 1) &&
- event.mouse.y < itemY - itemHitTop + itemHitHeight - 3 * (highRes ? 2 : 1)) {
+ if (event.mouse.y >= itemY - itemHitTop && event.mouse.y < itemY - itemHitTop + itemHitHeight) {
selection = i;
break;
}
@@ -1350,6 +1347,40 @@ void InsaneRebel2::drawLevelSelectOverlay(byte *renderBitmap, int pitch, int wid
}
drawMenuItems(renderBitmap, pitch, width, height, pilotItems, _numPilots + 4, _levelSelection);
+
+ drawPilotInfoLines(renderBitmap);
+}
+
+// Difficulty and current chapter of the highlighted pilot, at the bottom left.
+void InsaneRebel2::drawPilotInfoLines(byte *renderBitmap) {
+ if (_pilotMenuMode != kPilotModeSelect || _levelSelection < 0 || _levelSelection >= _numPilots)
+ return;
+
+ SmushPlayer *splayer = ((ScummEngine_v7 *)_vm)->_splayer;
+ if (!splayer)
+ return;
+
+ const PilotData &pilot = _pilots[_levelSelection];
+ const int scale = isHiRes() ? 2 : 1;
+
+ const char *diffFmt = splayer->getString(25);
+ if (!diffFmt || !diffFmt[0])
+ diffFmt = "^f01^c248Difficulty: %s";
+ const char *diffName = splayer->getString(111 + CLIP<int>(pilot.difficulty, 0, 5));
+ if (diffName && strlen(diffName) > 9) {
+ // Skip the ^f01^c005 prefix so the name inherits the line's color.
+ Common::String line = Common::String::format(diffFmt, diffName + 9);
+ drawMenuString(renderBitmap, line.c_str(), 30 * scale, 180 * scale);
+ }
+
+ int chapterIdx = 15;
+ while (chapterIdx > 0 && pilot.damage[chapterIdx] >= 0xFF)
+ chapterIdx--;
+ const char *chapFmt = splayer->getString(26);
+ if (!chapFmt || !chapFmt[0])
+ chapFmt = "^f01^c248Chapter: %hd";
+ Common::String line = Common::String::format(chapFmt, (short)(chapterIdx + 1));
+ drawMenuString(renderBitmap, line.c_str(), 30 * scale, 190 * scale);
}
void InsaneRebel2::initDefaultRankings() {
diff --git a/engines/scumm/insane/rebel2/rebel.h b/engines/scumm/insane/rebel2/rebel.h
index 8022adf1376..d3af0ee0c96 100644
--- a/engines/scumm/insane/rebel2/rebel.h
+++ b/engines/scumm/insane/rebel2/rebel.h
@@ -137,6 +137,7 @@ public:
int16 _previewOffsetY;
void drawChapterInfoLine(byte *renderBitmap, int pitch, int width, int height);
+ void drawPilotInfoLines(byte *renderBitmap);
Common::String getRankString(int rating);
Common::String getChapterPassword(int level, int difficulty);
Commit: 091b14801bae7f9bf159b54b788c87b743cf4c29
https://github.com/scummvm/scummvm/commit/091b14801bae7f9bf159b54b788c87b743cf4c29
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-07-15T15:55:56+02:00
Commit Message:
SCUMM: RA2: scale crossair for some levels
Changed paths:
engines/scumm/insane/rebel2/render.cpp
diff --git a/engines/scumm/insane/rebel2/render.cpp b/engines/scumm/insane/rebel2/render.cpp
index 21ab71f798b..76e006884d0 100644
--- a/engines/scumm/insane/rebel2/render.cpp
+++ b/engines/scumm/insane/rebel2/render.cpp
@@ -4304,6 +4304,9 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
int cw = _smush_iconsNut->getCharWidth(reticleIndex);
int ch = _smush_iconsNut->getCharHeight(reticleIndex);
+ // Enlarge the tiny third-person/cover reticles; the turret crosshair is already large.
+ const int spriteScale = (_rebelHandler == 0x26) ? 1 : 2;
+
const int reticleScale = isHiRes() ? 2 : getRebel2IndicatorScale(width, height);
int crosshairX = aimPos.x * reticleScale;
int crosshairY = aimPos.y * reticleScale;
@@ -4317,12 +4320,14 @@ void InsaneRebel2::renderCrosshair(byte *renderBitmap, int pitch, int width, int
crosshairY += _rebelViewOffsetY * reticleScale;
}
- crosshairX += _smush_iconsNut->getCharXOffset(reticleIndex) - cw / 2;
- crosshairY += _smush_iconsNut->getCharYOffset(reticleIndex) - ch / 2;
+ crosshairX += (_smush_iconsNut->getCharXOffset(reticleIndex) - cw / 2) * spriteScale;
+ crosshairY += (_smush_iconsNut->getCharYOffset(reticleIndex) - ch / 2) * spriteScale;
- renderNutSprite(renderBitmap, pitch, width, height,
+ // reticle glyphs key only on color 0, so transparent231 is false.
+ renderNutSpriteScaledClipped(renderBitmap, pitch, width, height,
+ 0, 0, width, height,
crosshairX, crosshairY,
- _smush_iconsNut, reticleIndex);
+ _smush_iconsNut, reticleIndex, false, spriteScale, false);
}
}
More information about the Scummvm-git-logs
mailing list