[Scummvm-git-logs] scummvm master -> 5eefba1a2cd412016e83a89eaae31376a95c9367
neuromancer
noreply at scummvm.org
Thu Oct 15 11:23:16 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:
64e38120de SCUMM: MORTIMER: correctly implement pause
dc7fb5e71d SCUMM: MORTIMER: correctly implemented extra dialogs follow the original design
3ed49bed17 SCUMM: MORTIMER: implemented autofire in full game and demo
5eefba1a2c SCUMM: MORTIMER: fixed animation transition between ship gameplay and interview mode
Commit: 64e38120deefcfe0705a4190e9eda1277ebe5795
https://github.com/scummvm/scummvm/commit/64e38120deefcfe0705a4190e9eda1277ebe5795
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-15T11:20:55+02:00
Commit Message:
SCUMM: MORTIMER: correctly implement pause
Changed paths:
engines/scumm/insane/mortimer/mortimer.cpp
engines/scumm/insane/mortimer/mortimer.h
engines/scumm/insane/mortimer/text.cpp
engines/scumm/insane/mortimer/text.h
diff --git a/engines/scumm/insane/mortimer/mortimer.cpp b/engines/scumm/insane/mortimer/mortimer.cpp
index 2fac84cc856..208e5b6635f 100644
--- a/engines/scumm/insane/mortimer/mortimer.cpp
+++ b/engines/scumm/insane/mortimer/mortimer.cpp
@@ -34,8 +34,6 @@
#include "common/util.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
-#include "graphics/font.h"
-#include "graphics/fontman.h"
#include "graphics/paletteman.h"
#include "gui/message.h"
@@ -75,7 +73,7 @@ Common::KeymapArray MortimerEngine::initKeymaps() {
const Binding bindings[] = {
{"FIRE", _s("Fire"), kActionFire, {"MOUSE_LEFT", "MOUSE_RIGHT", "JOY_A", "JOY_B"}},
{"PAUSE", _s("Pause"), kActionPause, {"SPACE", "JOY_START", nullptr, nullptr}},
- {"SKIP", _s("Skip"), kActionSkip, {"ESCAPE", "JOY_Y", nullptr, nullptr}},
+ {"SKIP", _s("Skip cutscene / open menu"), kActionSkip, {"ESCAPE", "JOY_Y", nullptr, nullptr}},
{"RESTART", _s("Restart"), kActionRestart, {"C+r", "JOY_X", nullptr, nullptr}},
{"QUIT", _s("Quit"), kActionQuit, {"q", "A+q", nullptr, nullptr}},
{"MENU", _s("Game menu"), kActionMenu, {"F5", "JOY_LEFT_SHOULDER", nullptr, nullptr}},
@@ -1138,6 +1136,12 @@ void MortimerEngine::pauseEngineIntern(bool pause) {
_nextFrameTime += _system->getMillis() - _pauseTime;
}
+bool MortimerEngine::canPause() const {
+ // SHELL's input handler excludes mode 4 (the animal and castle riddles).
+ const int scene = _interviewActive ? _interview.sceneId() : _castleActive ? _castle.sceneId() : -1;
+ return !isPaused() && !(scene >= 8 && scene <= 11) && scene != 23;
+}
+
Common::Error MortimerEngine::run() {
const int boot = ConfMan.hasKey("boot_param") ? ConfMan.getInt("boot_param") : -1;
if (_isDemo && boot == kBootProgrammer)
@@ -1201,6 +1205,14 @@ Common::Error MortimerEngine::run() {
}
Common::Event event;
while (_eventMan->pollEvent(event)) {
+ if (event.kbdRepeat && (event.type == Common::EVENT_KEYDOWN ||
+ event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START))
+ continue;
+ // The event manager leaves global menu requests to an already paused engine.
+ if (event.type == Common::EVENT_MAINMENU && pause.isActive()) {
+ event.type = Common::EVENT_CUSTOM_ENGINE_ACTION_START;
+ event.customType = kActionMenu;
+ }
if (_highScoresActive && !isPaused()) {
if (_highScores.enteringName()) {
if (event.type == Common::EVENT_KEYDOWN && !event.kbdRepeat && _highScores.handleKey(event.kbd)) {
@@ -1219,8 +1231,6 @@ Common::Error MortimerEngine::run() {
continue;
}
if (event.type == Common::EVENT_KEYDOWN) {
- if (event.kbdRepeat)
- continue;
if (handleCheatKey(event.kbd)) {
if (!_loadError.empty()) {
exit = true;
@@ -1263,10 +1273,20 @@ Common::Error MortimerEngine::run() {
}
break;
case kActionSkip:
- if (pause.isActive())
+ if (!gameplay()) {
+ if (pause.isActive())
+ pause.clear();
+ else if (!isPaused())
+ exit = !nextScene(true);
+ break;
+ }
+ // fall through
+ case kActionMenu:
+ if (pause.isActive()) {
pause.clear();
- else if (!interactive() || _cheats)
- exit = !nextScene(true);
+ draw();
+ }
+ openMainMenuDialog();
break;
case kActionQuit:
exit = true;
@@ -1274,7 +1294,7 @@ Common::Error MortimerEngine::run() {
case kActionPause:
if (pause.isActive())
pause.clear();
- else
+ else if (canPause())
pause = pauseEngine();
break;
case kActionRestart:
@@ -1282,9 +1302,6 @@ Common::Error MortimerEngine::run() {
pause.clear();
exit = !restart();
break;
- case kActionMenu:
- openMainMenuDialog();
- break;
case kActionMouse:
_controllerMode = false;
setMouse(_eventMan->getMousePos());
@@ -1311,7 +1328,7 @@ Common::Error MortimerEngine::run() {
if (int32(_system->getMillis() - _nextFrameTime) > 250)
_nextFrameTime = _system->getMillis() + 1000 / currentMovie().frameRate();
}
- draw();
+ draw(pause.isActive());
if (_finished) {
if (!_viewingHighScores) {
GUI::MessageDialog dialog(_("Play again?"), _("Restart"), _("Quit"));
@@ -1432,7 +1449,7 @@ void MortimerEngine::drawDashboard() {
drawSprite(_cockpit[3], 306, 465, true);
}
-void MortimerEngine::draw() {
+void MortimerEngine::draw(bool paused) {
if (_interviewActive) {
_interview.draw(_screen, _intro, _soundState.variable(1));
if (_interview.showDashboard())
@@ -1473,11 +1490,8 @@ void MortimerEngine::draw() {
if (_interviewActive && _interview.payingBonus())
_subtitles.drawScore(_screen, _score);
}
- if (isPaused()) {
- const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont);
- _screen.fillRect(Common::Rect(90, 142, 550, 182), 10);
- font->drawString(&_screen, _("Paused"), 90, 155, 460, 20, Graphics::kTextAlignCenter);
- }
+ if (paused)
+ _subtitles.drawPause(_screen, _isDemo);
_system->copyRectToScreen(_screen.getPixels(), _screen.pitch, 0, 0, 640, 480);
_system->updateScreen();
}
diff --git a/engines/scumm/insane/mortimer/mortimer.h b/engines/scumm/insane/mortimer/mortimer.h
index 0e51e33d958..a0d22fecf60 100644
--- a/engines/scumm/insane/mortimer/mortimer.h
+++ b/engines/scumm/insane/mortimer/mortimer.h
@@ -90,6 +90,9 @@ private:
MortimerFlight ¤tFlight() { return _castleActive ? _castle : _flight; }
bool interactive() const { return _castleActive ? _castle.courseView() :
_crashStage == kNoCrash && !_interviewActive && _introIndex < 0 && _flight.interactive(); }
+ bool gameplay() const { return _interviewActive || _castleActive || _crashStage != kNoCrash ||
+ (_introIndex < 0 && (!_isDemo || _flight.interactive())); }
+ bool canPause() const;
void setMouse(const Common::Point &position);
void updateSteering(bool centerView = false);
void updateView(int x, int y);
@@ -97,7 +100,7 @@ private:
void fire();
Common::Point moviePoint(const Common::Point &point) const;
Common::Point screenPoint(const Common::Point &point) const;
- void draw();
+ void draw(bool paused = false);
void drawSprite(const MortimerSprite &sprite, int x, int y, bool centered = false, const Common::Rect *clip = nullptr);
void drawProjectile();
void drawTargets();
diff --git a/engines/scumm/insane/mortimer/text.cpp b/engines/scumm/insane/mortimer/text.cpp
index 2ce99a8265f..75b5f2acee9 100644
--- a/engines/scumm/insane/mortimer/text.cpp
+++ b/engines/scumm/insane/mortimer/text.cpp
@@ -23,6 +23,7 @@
#include "common/file.h"
#include "common/tokenizer.h"
#include "common/util.h"
+#include "graphics/fontman.h"
#include "graphics/surface.h"
namespace Scumm {
@@ -152,6 +153,25 @@ void MortimerSubtitles::drawScore(Graphics::Surface &screen, uint32 score) const
drawMortimerMessage(screen, _infoFont, Common::String::format(text(3).c_str(), (long)score), 231);
}
+void MortimerSubtitles::drawPause(Graphics::Surface &screen, bool isDemo) const {
+ // SHELL 404ae0 (retail), 4053cb (demo): a fixed, centered 400 x 100 box.
+ Common::Rect box(400, 100);
+ box.moveTo((screen.w - box.width()) / 2, (screen.h - box.height()) / 2);
+ screen.fillRect(box, isDemo ? 0 : 251);
+ for (int i = 0; i < (isDemo ? 1 : 3); ++i) {
+ screen.hLine(box.left, box.top, box.right - 1, isDemo ? 255 : 250);
+ screen.vLine(box.left, box.top, box.bottom - 1, isDemo ? 255 : 250);
+ screen.vLine(box.right - 1, box.top, box.bottom - 1, isDemo ? 255 : 252);
+ screen.hLine(box.left, box.bottom - 1, box.right - 1, isDemo ? 255 : 252);
+ box.grow(-1);
+ }
+ const Graphics::Font &font = isDemo ?
+ *FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont) : _infoFont;
+ font.drawString(&screen, "Game paused - press space to continue.", 0,
+ (screen.h - font.getFontHeight() - 1) / 2, screen.w, isDemo ? 255 : 231,
+ Graphics::kTextAlignCenter, 0, false);
+}
+
void MortimerSubtitles::draw(Graphics::Surface &screen, const Common::Array<MortimerMovie::Caption> &captions, bool enabled,
const MortimerMovieContext *context) const {
for (uint i = 0; i < captions.size(); ++i) {
diff --git a/engines/scumm/insane/mortimer/text.h b/engines/scumm/insane/mortimer/text.h
index fc59b923f13..afe7c4e009a 100644
--- a/engines/scumm/insane/mortimer/text.h
+++ b/engines/scumm/insane/mortimer/text.h
@@ -47,6 +47,7 @@ public:
bool loadText(Common::SeekableReadStream &stream);
Common::String text(uint16 id) const;
void drawScore(Graphics::Surface &screen, uint32 score) const;
+ void drawPause(Graphics::Surface &screen, bool isDemo) const;
void draw(Graphics::Surface &screen, const Common::Array<MortimerMovie::Caption> &captions, bool enabled,
const MortimerMovieContext *context = nullptr) const;
Commit: dc7fb5e71de59b389b31f18fd42f2d8e45b7b9e8
https://github.com/scummvm/scummvm/commit/dc7fb5e71de59b389b31f18fd42f2d8e45b7b9e8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-15T11:20:55+02:00
Commit Message:
SCUMM: MORTIMER: correctly implemented extra dialogs follow the original design
Changed paths:
engines/scumm/insane/mortimer/dialogs.cpp
engines/scumm/insane/mortimer/dialogs.h
diff --git a/engines/scumm/insane/mortimer/dialogs.cpp b/engines/scumm/insane/mortimer/dialogs.cpp
index 6eb4c104e15..aa2999aea9d 100644
--- a/engines/scumm/insane/mortimer/dialogs.cpp
+++ b/engines/scumm/insane/mortimer/dialogs.cpp
@@ -31,6 +31,7 @@
namespace Scumm {
const uint32 kMortimerHighScoresCmd = 'MHSC';
+const char *const kMortimerDifficultyLevels[] = {_s("Easy"), _s("Medium"), _s("Hard")};
MortimerDifficultyDialog::MortimerDifficultyDialog(bool isDemo, uint flightDifficulty, uint riddleDifficulty, bool girl, bool newPilot)
: GUI::Dialog("MortimerDifficultyDialog"), _character(nullptr), _pilotName(nullptr) {
@@ -41,42 +42,50 @@ MortimerDifficultyDialog::MortimerDifficultyDialog(bool isDemo, uint flightDiffi
}
if (!isDemo) {
new GUI::StaticTextWidget(this, _name + ".CharacterLabel", _("Character:"));
- _character = new GUI::PopUpWidget(this, _name + ".Character");
- _character->appendEntry(_("Sid"), 0);
- _character->appendEntry(_("Sally"), 1);
- _character->setSelectedTag(girl ? 1 : 0);
+ _character = new GUI::RadiobuttonGroup(this);
+ new GUI::RadiobuttonWidget(this, _name + ".Sally", _character, 1, _("Sally"));
+ new GUI::RadiobuttonWidget(this, _name + ".Sid", _character, 0, _("Sid"));
+ _character->setValue(girl ? 1 : 0);
}
new GUI::StaticTextWidget(this, _name + ".FlightLabel", _("Flight difficulty:"));
new GUI::StaticTextWidget(this, _name + ".RiddleLabel", _("Riddle difficulty:"));
- _flight = new GUI::PopUpWidget(this, _name + ".Flight", _("Choose how difficult flying and recovering from crashes should be."));
- _riddle = new GUI::PopUpWidget(this, _name + ".Riddle", isDemo ?
- _("The demo includes only one set of riddles.") : _("Choose the riddle difficulty and the cost and number of guesses."));
- const char *const levels[] = {_s("Easy"), _s("Medium"), _s("Hard")};
- for (uint i = 0; i < ARRAYSIZE(levels); ++i) {
- _flight->appendEntry(_(levels[i]), i);
+ _flight = new GUI::RadiobuttonGroup(this);
+ _riddle = new GUI::RadiobuttonGroup(this);
+ for (uint i = 0; i < ARRAYSIZE(kMortimerDifficultyLevels); ++i) {
+ new GUI::RadiobuttonWidget(this, Common::String::format("%s.Flight%u", _name.c_str(), i), _flight, i,
+ _(kMortimerDifficultyLevels[i]), _("Choose how difficult flying and recovering from crashes should be."));
if (!isDemo)
- _riddle->appendEntry(_(levels[i]), i);
+ new GUI::RadiobuttonWidget(this, Common::String::format("%s.Riddle%u", _name.c_str(), i), _riddle, i,
+ _(kMortimerDifficultyLevels[i]), _("Choose the riddle difficulty and the cost and number of guesses."));
}
if (isDemo) {
- _riddle->appendEntry(_("Fixed (demo)"), 0);
+ new GUI::RadiobuttonWidget(this, _name + ".Riddle0", _riddle, 0, _("Fixed (demo)"),
+ _("The demo includes only one set of riddles."));
_riddle->setEnabled(false);
}
- _flight->setSelectedTag(MIN(flightDifficulty, 2U));
- _riddle->setSelectedTag(isDemo ? 0 : MIN(riddleDifficulty, 2U));
- new GUI::ButtonWidget(this, _name + ".Cancel", _("~C~ancel"), Common::U32String(), GUI::kCloseCmd);
+ _flight->setValue(MIN(flightDifficulty, 2U));
+ _riddle->setValue(isDemo ? 0 : MIN(riddleDifficulty, 2U));
+ if (newPilot)
+ new GUI::ButtonWidget(this, _name + ".Back", _("Back"), Common::U32String(), GUI::kCloseCmd);
new GUI::ButtonWidget(this, _name + ".Start", _("Start"), Common::U32String(), GUI::kOKCmd, Common::ASCII_RETURN);
}
+MortimerDifficultyDialog::~MortimerDifficultyDialog() {
+ delete _flight;
+ delete _riddle;
+ delete _character;
+}
+
uint MortimerDifficultyDialog::flightDifficulty() const {
- return _flight->getSelectedTag();
+ return _flight->getValue();
}
uint MortimerDifficultyDialog::riddleDifficulty() const {
- return _riddle->getSelectedTag();
+ return _riddle->getValue();
}
bool MortimerDifficultyDialog::girl() const {
- return _character && _character->getSelectedTag() == 1;
+ return _character && _character->getValue() == 1;
}
Common::String MortimerDifficultyDialog::pilotName() const {
@@ -88,25 +97,41 @@ Common::String MortimerDifficultyDialog::pilotName() const {
void MortimerDifficultyDialog::reflowLayout() {
GUI::ThemeEval &layouts = *g_gui.xmlEval();
const int lineHeight = layouts.getVar("Globals.Line.Height");
- const int width = 240 * g_gui.getScaleFactor();
+ const int width = 280 * g_gui.getScaleFactor();
+ const int columnWidth = (width - 8) / 2;
layouts.addDialog(_name, "screen_center")
- .addLayout(GUI::ThemeLayout::kLayoutVertical, 8)
+ .addLayout(GUI::ThemeLayout::kLayoutVertical, 8, GUI::ThemeLayout::kItemAlignCenter)
.addPadding(8, 8, 8, 8)
.addWidget("Title", "", width, lineHeight, Graphics::kTextAlignCenter);
- if (_pilotName)
- layouts.addWidget("NameLabel", "", -1, lineHeight).addWidget("Name", "EditTextWidget");
- if (_character)
- layouts.addWidget("CharacterLabel", "", -1, lineHeight).addWidget("Character", "PopUp");
- layouts.addWidget("FlightLabel", "", -1, lineHeight)
- .addWidget("Flight", "PopUp")
- .addWidget("RiddleLabel", "", -1, lineHeight)
- .addWidget("Riddle", "PopUp")
- .addLayout(GUI::ThemeLayout::kLayoutHorizontal, 8, GUI::ThemeLayout::kItemAlignEnd)
+ if (_character) {
+ layouts.addWidget("CharacterLabel", "", -1, lineHeight)
+ .addLayout(GUI::ThemeLayout::kLayoutHorizontal, 8)
.addPadding(0, 0, 0, 0)
- .addSpace()
- .addWidget("Cancel", "Button")
- .addWidget("Start", "Button")
- .closeLayout()
+ .addWidget("Sally", "Radiobutton", columnWidth)
+ .addWidget("Sid", "Radiobutton", columnWidth)
+ .closeLayout();
+ }
+ layouts.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 8)
+ .addPadding(0, 0, 0, 0)
+ .addLayout(GUI::ThemeLayout::kLayoutVertical, 8)
+ .addPadding(0, 0, 0, 0)
+ .addWidget("FlightLabel", "", columnWidth, lineHeight);
+ for (uint i = 0; i < ARRAYSIZE(kMortimerDifficultyLevels); ++i)
+ layouts.addWidget(Common::String::format("Flight%u", i), "Radiobutton");
+ layouts.closeLayout()
+ .addLayout(GUI::ThemeLayout::kLayoutVertical, 8)
+ .addPadding(0, 0, 0, 0)
+ .addWidget("RiddleLabel", "", columnWidth, lineHeight);
+ for (uint i = 0; i < ARRAYSIZE(kMortimerDifficultyLevels); ++i)
+ layouts.addWidget(Common::String::format("Riddle%u", i), "Radiobutton");
+ layouts.closeLayout().closeLayout();
+ if (_pilotName)
+ layouts.addWidget("NameLabel", "", -1, lineHeight).addWidget("Name", "EditTextWidget", -1, lineHeight);
+ layouts.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 8, GUI::ThemeLayout::kItemAlignEnd)
+ .addPadding(0, 0, 0, 0)
+ .addWidget("Back", "Button")
+ .addWidget("Start", "Button")
+ .closeLayout()
.closeLayout()
.closeDialog();
GUI::Dialog::reflowLayout();
@@ -135,8 +160,7 @@ MortimerPilotDialog::MortimerPilotDialog(const SaveStateList &pilots) : GUI::Dia
_pilot->appendEntry(pilots[i].getDescription(), pilots[i].getSaveSlot());
_pilot->setSelected(pilots.empty() ? 0 : 1);
new GUI::ButtonWidget(this, _name + ".HighScores", _("High Scores"), Common::U32String(), kMortimerHighScoresCmd);
- new GUI::ButtonWidget(this, _name + ".Cancel", _("~C~ancel"), Common::U32String(), GUI::kCloseCmd);
- new GUI::ButtonWidget(this, _name + ".Start", _("Start"), Common::U32String(), GUI::kOKCmd, Common::ASCII_RETURN);
+ new GUI::ButtonWidget(this, _name + ".Start", _("Play Mortimer"), Common::U32String(), GUI::kOKCmd, Common::ASCII_RETURN);
}
int MortimerPilotDialog::selectedPilot() const {
@@ -148,18 +172,13 @@ void MortimerPilotDialog::reflowLayout() {
const int lineHeight = layouts.getVar("Globals.Line.Height");
const int width = 280 * g_gui.getScaleFactor();
layouts.addDialog(_name, "screen_center")
- .addLayout(GUI::ThemeLayout::kLayoutVertical, 8)
+ .addLayout(GUI::ThemeLayout::kLayoutVertical, 8, GUI::ThemeLayout::kItemAlignStretch)
.addPadding(8, 8, 8, 8)
.addWidget("Title", "", width, lineHeight, Graphics::kTextAlignCenter)
.addWidget("Pilot", "PopUp")
.addWidget("Hint", "", -1, lineHeight, Graphics::kTextAlignCenter)
+ .addWidget("Start", "Button")
.addWidget("HighScores", "Button")
- .addLayout(GUI::ThemeLayout::kLayoutHorizontal, 8, GUI::ThemeLayout::kItemAlignEnd)
- .addPadding(0, 0, 0, 0)
- .addSpace()
- .addWidget("Cancel", "Button")
- .addWidget("Start", "Button")
- .closeLayout()
.closeLayout()
.closeDialog();
GUI::Dialog::reflowLayout();
diff --git a/engines/scumm/insane/mortimer/dialogs.h b/engines/scumm/insane/mortimer/dialogs.h
index 29bcd5d2048..0d66bec8236 100644
--- a/engines/scumm/insane/mortimer/dialogs.h
+++ b/engines/scumm/insane/mortimer/dialogs.h
@@ -27,6 +27,7 @@
namespace GUI {
class PopUpWidget;
class EditTextWidget;
+class RadiobuttonGroup;
}
namespace Scumm {
@@ -34,6 +35,7 @@ namespace Scumm {
class MortimerDifficultyDialog : public GUI::Dialog {
public:
MortimerDifficultyDialog(bool isDemo, uint flightDifficulty, uint riddleDifficulty, bool girl = false, bool newPilot = false);
+ ~MortimerDifficultyDialog() override;
uint flightDifficulty() const;
uint riddleDifficulty() const;
bool girl() const;
@@ -44,8 +46,8 @@ protected:
void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
private:
- GUI::PopUpWidget *_flight, *_riddle;
- GUI::PopUpWidget *_character;
+ GUI::RadiobuttonGroup *_flight, *_riddle;
+ GUI::RadiobuttonGroup *_character;
GUI::EditTextWidget *_pilotName;
};
Commit: 3ed49bed17363a29b3d880f4d5e7a07b6ba2ad49
https://github.com/scummvm/scummvm/commit/3ed49bed17363a29b3d880f4d5e7a07b6ba2ad49
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-15T13:16:26+02:00
Commit Message:
SCUMM: MORTIMER: implemented autofire in full game and demo
Changed paths:
engines/scumm/insane/mortimer/mortimer.cpp
engines/scumm/insane/mortimer/mortimer.h
diff --git a/engines/scumm/insane/mortimer/mortimer.cpp b/engines/scumm/insane/mortimer/mortimer.cpp
index 208e5b6635f..fa2ffd2c237 100644
--- a/engines/scumm/insane/mortimer/mortimer.cpp
+++ b/engines/scumm/insane/mortimer/mortimer.cpp
@@ -120,7 +120,8 @@ MortimerEngine::MortimerEngine(OSystem *system, bool isDemo) : Engine(system), _
_viewport(24, 20, 616, 360), _mouse(320, 240), _view(44, 60), _aim(320, 190), _shotWeapon(MortimerFlight::kRevitascope),
_crashStage(kNoCrash), _nextFrameTime(0), _pauseTime(0), _introIndex(-1), _loadedRegion(-1), _creditedRescues(0),
_segment(0), _carriedStones(0), _completedSpecies(0), _shellShocks(0), _shockFrame(0), _crashCount(0),
- _uiFrame(0), _shotFrame(3), _glowFrame(10), _glowStones(0), _pendingGlowStones(0), _finished(false),
+ _uiFrame(0), _shotFrame(3), _glowFrame(10), _glowStones(0), _pendingGlowStones(0),
+ _fireButtons(0), _fireCountdown(0), _finished(false),
_controllerMode(false), _interviewActive(false), _castleActive(false), _resourcesLoaded(false), _playingCached(false), _needsRepair(false), _hitJolt(false),
_cheats(ConfMan.getBool("mortimer_cheats")), _programmer(!isDemo && ConfMan.getBool("mortimer_programmer_easter_egg")), _invulnerable(false) {
memset(_steering, 0, sizeof(_steering));
@@ -372,6 +373,7 @@ void MortimerEngine::updateMovieContext() {
bool MortimerEngine::enterCampaignScene() {
stopSounds();
+ _fireButtons = _fireCountdown = 0;
if (_highScoresActive) {
setScoreNameInput(false);
_highScoresActive = false;
@@ -603,6 +605,7 @@ void MortimerEngine::creditRescues() {
}
bool MortimerEngine::restart(bool all) {
+ _fireButtons = _fireCountdown = 0;
_crashStage = kNoCrash;
_needsRepair = _hitJolt = false;
_shockFrame = _crashCount = 0;
@@ -1076,6 +1079,8 @@ Common::Point MortimerEngine::screenPoint(const Common::Point &point) const {
}
void MortimerEngine::fire() {
+ // Retail SHELL 40e510 repeats held input every 1/7/7 ticks by flight difficulty.
+ _fireCountdown = _flightDifficulty ? 7 : 1;
MortimerFlight &flight = currentFlight();
if (_shotFrame >= 3) {
_shot = _aim;
@@ -1105,6 +1110,19 @@ void MortimerEngine::fire() {
}
}
+bool MortimerEngine::updateAutofire() {
+ if (_finished || !canFire()) {
+ _fireButtons = _fireCountdown = 0;
+ return true;
+ }
+ if (!_fireButtons)
+ return true;
+ if (_fireCountdown && --_fireCountdown)
+ return true;
+ fire();
+ return !_castleActive || processCastle();
+}
+
void MortimerEngine::updateInterface() {
++_uiFrame;
_shockFrame ^= 1;
@@ -1130,10 +1148,13 @@ bool MortimerEngine::speechPlaying() const {
void MortimerEngine::pauseEngineIntern(bool pause) {
Engine::pauseEngineIntern(pause);
- if (pause)
+ if (pause) {
_pauseTime = _system->getMillis();
- else
+ // Dialogs may consume the button release while the engine is paused.
+ _fireButtons = _fireCountdown = 0;
+ } else {
_nextFrameTime += _system->getMillis() - _pauseTime;
+ }
}
bool MortimerEngine::canPause() const {
@@ -1208,6 +1229,13 @@ Common::Error MortimerEngine::run() {
if (event.kbdRepeat && (event.type == Common::EVENT_KEYDOWN ||
event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START))
continue;
+ if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_END && event.customType == kActionFire) {
+ if (_fireButtons)
+ --_fireButtons;
+ continue;
+ }
+ if (event.type == Common::EVENT_FOCUS_LOST || event.type == Common::EVENT_INPUT_CHANGED)
+ _fireButtons = _fireCountdown = 0;
// The event manager leaves global menu requests to an already paused engine.
if (event.type == Common::EVENT_MAINMENU && pause.isActive()) {
event.type = Common::EVENT_CUSTOM_ENGINE_ACTION_START;
@@ -1255,11 +1283,13 @@ Common::Error MortimerEngine::run() {
switch (event.customType) {
case kActionFire:
if (!isPaused() && !_finished) {
- if (_castleActive) {
- if (_castle.courseView() && _castle.acceptsShots())
- fire();
- else
- _castle.click(_mouse);
+ if (canFire()) {
+ ++_fireButtons;
+ fire();
+ if (_castleActive)
+ exit = !processCastle();
+ } else if (_castleActive) {
+ _castle.click(_mouse);
exit = !processCastle();
} else if (_interviewActive) {
const bool playing = _interview.playingMovie();
@@ -1267,8 +1297,6 @@ Common::Error MortimerEngine::run() {
if (playing && !_interview.playingMovie())
stopSounds();
exit = !processInterview();
- } else if (interactive()) {
- fire();
}
}
break;
@@ -1322,7 +1350,7 @@ Common::Error MortimerEngine::run() {
continue;
if (!isPaused() && !_finished && int32(_system->getMillis() - _nextFrameTime) >= 0) {
_nextFrameTime += 1000 / currentMovie().frameRate();
- if (!updateScene())
+ if (!updateScene() || !updateAutofire())
break;
// Avoid racing through queued frames following a slow backend call.
if (int32(_system->getMillis() - _nextFrameTime) > 250)
diff --git a/engines/scumm/insane/mortimer/mortimer.h b/engines/scumm/insane/mortimer/mortimer.h
index a0d22fecf60..39e715ae7dd 100644
--- a/engines/scumm/insane/mortimer/mortimer.h
+++ b/engines/scumm/insane/mortimer/mortimer.h
@@ -90,6 +90,7 @@ private:
MortimerFlight ¤tFlight() { return _castleActive ? _castle : _flight; }
bool interactive() const { return _castleActive ? _castle.courseView() :
_crashStage == kNoCrash && !_interviewActive && _introIndex < 0 && _flight.interactive(); }
+ bool canFire() const { return interactive() && (!_castleActive || _castle.acceptsShots()); }
bool gameplay() const { return _interviewActive || _castleActive || _crashStage != kNoCrash ||
(_introIndex < 0 && (!_isDemo || _flight.interactive())); }
bool canPause() const;
@@ -98,6 +99,7 @@ private:
void updateView(int x, int y);
void updateInterface();
void fire();
+ bool updateAutofire();
Common::Point moviePoint(const Common::Point &point) const;
Common::Point screenPoint(const Common::Point &point) const;
void draw(bool paused = false);
@@ -154,6 +156,7 @@ private:
uint _creditedRescues;
uint _segment, _carriedStones, _completedSpecies, _shellShocks, _shockFrame, _crashCount;
uint _uiFrame, _shotFrame, _glowFrame, _glowStones, _pendingGlowStones;
+ uint _fireButtons, _fireCountdown;
uint _iconFlash[7];
bool _finished, _controllerMode, _interviewActive, _castleActive;
bool _resourcesLoaded, _playingCached;
Commit: 5eefba1a2cd412016e83a89eaae31376a95c9367
https://github.com/scummvm/scummvm/commit/5eefba1a2cd412016e83a89eaae31376a95c9367
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-15T13:22:47+02:00
Commit Message:
SCUMM: MORTIMER: fixed animation transition between ship gameplay and interview mode
Changed paths:
engines/scumm/insane/mortimer/interview.cpp
diff --git a/engines/scumm/insane/mortimer/interview.cpp b/engines/scumm/insane/mortimer/interview.cpp
index c3b6fe40c9a..716b21ec693 100644
--- a/engines/scumm/insane/mortimer/interview.cpp
+++ b/engines/scumm/insane/mortimer/interview.cpp
@@ -305,7 +305,8 @@ bool MortimerInterview::fullScreen() const {
}
bool MortimerInterview::movieDoubled() const {
- return _stage == kGate || _stage == kSong || _stage == kOpenGate;
+ // Retail popups start at 320x240, then switch to native size with IACT 6/7.
+ return _stage == kGate || _stage == kSong || _stage == kOpenGate || (!_isDemo && _stage == kPopup);
}
const byte *MortimerInterview::palette(const MortimerMovie &movie) const {
More information about the Scummvm-git-logs
mailing list