[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 &currentFlight() { 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 &currentFlight() { 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