[Scummvm-git-logs] scummvm master -> 2f734f9dcd76e2d289d80483ddb36697be7e5d09

dreammaster noreply at scummvm.org
Sat Oct 24 06:26:01 UTC 2026


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

Summary:
f7b6742267 MM: MM1: New characters now start at age 18
1574da9d7f MM: MM1: Fix spell level progression for spellcasting classes
a1c9b990b7 MM: MM1: Fix restoring SP whilst resting
ee98226029 MM: MM1: Shorten long condition names in the Quick Reference
0f52819596 MM: MM1: Fix Lord Hacker quest handling in the enhanced interface
10388f8f50 MM: MM1: Fix Lord Ironfist's "find Lord Kilburn" quest completion
5ed2fe7b12 MM: MM1: Fix enhanced template costs not properly updating
16d847424b MM: MM1: Fix Temple healing availability and HP cap restoration
13200ca6bc MM: MM1: Fix truncation of monster names using the full name field
613fa80e36 MM: MM1: Fix memory leak loading monster images
92b432c91e MM: MM1: Fix crash loading a save while a location is open
8b28e61dd3 MM: MM1: Fix backward movement being blocked by passable walls
aebb7b323a MM: MM1: Refuse to start an encounter while one is already active
937ad9dc60 MM: MM1: Fix encounter monsters using the wrong monster record
ecc7558dc2 MM: MM1: Fix monster touch effect lookup using the wrong table index
1ab6f6d822 MM: MM1: Fix missing monster touch action messages
4eb5c91a0d MM: MM1: Fix missing separator between monster name and spell message
ccf34c2240 MM: MM1: Fix monster spells using the wrong monster's data
f0c3e1cb7c MM: MM1: Fix combat overwriting party members with copies of others
9c6957dd78 MM: MM1: Base monster status recovery on monster level
e2550b0b54 MM: MM1: Fix Turn Undead using monster hit points instead of level
f473d87a26 MM: MM1: Fix Lightning Bolt inheriting damage from earlier spells
7565912056 MM: MM1: Fix Fireball damaging the party and targeting monsters in melee
ac52d86b4c MM: MM1: Fix Web silently failing due to a stale target check
4b5f38ad63 MM: MM1: Apply the Leather Skin AC bonus to displayed AC and enemy attacks
f364b3711a MM: MM1: Fix combat Exchange party order and can-attack handling
6a8b4b5d8f MM: MM1: Always open ranged target selection when shooting in combat
55afbcea1b MM: MM1: Fix Dispel changing the active monster while clearing statuses
3dea45b621 MM: MM1: Fix party-wide monster effects changing the selected character
f2b3ae6d71 MM: MM1: Fix missing monster spell messages
c3f5ff3b92 MM: MM1: Fix monster spell condition effects not applying or showing correctly
ed113f1449 MM: MM1: Fix Power and Quickness applying to the caster instead of the target
c3bdeed9d6 MM: MM1: Reset spell state before casting party spells
a90c1938f7 MM: MM1: Fix Crazed Wizard cave challenges and save progress
3fc7d9848a MM: MM1: Close gold/gem-only loot in the Search window with one dismissal
3d7d58c773 MM: MM1: Fix blank Detect Magic/Trap result in the enhanced Search window
edf3719f90 MM: MM1: Keep treasure items when every party backpack is full
a2588f0499 MM: MM1: Fix Gems button in the enhanced Trade screen
06da555639 MM: MM1: Let the desert message finish before random encounters start
026bee5fac MM: MM1: Fix enhanced Teleport window not closing on Escape
0dfcb8581a MM: MM1: Fix keyboard controls after returning to the launcher
54040cf153 MM: MM1: Use F1-F6 for party member selection
f042034d91 MM: MM1: Start with the enhanced minimap enabled and toggle it with F9
ba032c4c7a MM: MM1: Add player-defined favorite shortcuts to the enhanced spellbook
26167a02f5 MM: MM1: Fix the number of spells listed in the enhanced spellbook
a516dd651a MM: MM1: Add enemy selection for single-target combat spells
c4ce76c271 MM: MM1: Show enhanced spell results inside the combat view
2932506bcc MM: MM1: Keep spell errors inside the combat view
307e82e1b0 MM: MM1: Rebuild the full combat screen for monster spell messages
91a0b02f19 MM: MM1: Use the combat bottom-message area for monster notices
e1b5d82683 MM: MM1: Fix the combat victory message resizing later combat screens
48aed06312 MM: MM1: Fix the first draw of the enhanced combat screen
72d1f2a351 MM: MM1: Fix combat messages being skipped before they are drawn
9a4781359f MM: MM1: Make the static combat commands clickable in the enhanced interface
05a96ef32e MM: MM1: Make combat command labels clickable as well as their icons
e354ab62dc MM: MM1: Fix Time Distortion bypassing the normal combat cleanup
622b699802 MM: MM1: Explain active effects in the Protect window
3729dbd44e MM: MM1: Make the combat Delay setting control message duration
e8c0d49f82 MM: MM1: Add a combat log window to the enhanced combat view
125838a04a MM: MM1: Keep claimed rewards claimed across save/load in the same map visit
ffe69ad5e0 MM: MM1: Keep finite treasure claimed after saving and reloading
7ddf01838d MM: MM1: Add an enhanced Quest Log
70c5c7792a MM: MM1: Record completed ruler quests for the Quest Log
51e46d5a1e MM: MM1: Add the Crazed Wizard to the Quest Log
e81eeac3a6 MM: MM1: Track the Clerics of the South gong tones and show them in the Quest Log
cac041ed6b MM: MM1: Add the Color Bridge to the Quest Log
2321316476 MM: MM1: End the game when the whole party dies crossing the desert
5d01918420 MM: MM1: Fix Divine Intervention setting age from level instead of adding five years
8cabedf7a4 MM: MM1: Allow column E when choosing a Fly destination
d76797dfd9 MM: MM1: Cap gem rewards at the 16-bit limit instead of wrapping around
dd55214453 MM: MM1: Reset the Great Beast flags after spinning the Wheel
f57c2b5bbd MM: MM1: Give extra melee attacks to Knights, Paladins and Archers
68f3265fc8 MM: MM1: Fix argument handling in the encounter debug command
a5950db336 MM: MM1: Require enough gems to cast spells that cost gems
b5a3dbbb1f MM: MM1: Fix Resurrection only working on characters who are not eradicated
a31340bc9a MM: MM1: Fix Raise Dead only working on characters who are not dead
2a2d466f83 MM: MM1: Fix Rejuvenate reporting failure on success and underflowing the age
55dba78dc7 MM: MM1: Fix Town Portal using the wrong byte for the destination map
df0b2c513a MM: MM1: Show the Town Portal destination prompt
4a9b420daf MM: MM1: Fix the Accuracy and Speed shrines changing Might instead
4dc64ee02e MM: MM1: Show the Psychic Blast result and fix its victim index
5be96b01b9 MM: MM1: Fix Acid Rain hitting monsters in melee range
2c225de848 MM: MM1: Stop dead monsters recovering or regenerating before cleanup
7da8242558 MM: MM1: Time the Turn Undead and Holy Word results like other combat spells
3a4c762954 MM: MM1: Don't damage monsters that resist a damage spell
da62c2b7c2 MM: MM1: Give item drops after combat and fix the treasure tier offset
6e82ba8ee2 MM: MM1: Fix Prismatic Light depending on state left by earlier spells
0af2f3cc45 MM: MM1: Make Etherealize move one square in the facing direction
6fd16e662e MM: MM1: Make Gather collect gems and food and stop gold overflowing
d2c09603aa MM: MM1: Fix large gold and gem transfers between characters
f529776da1 MM: MM1: Add Gather and Share controls to the enhanced inventory screen
095a8119e7 MM: MM1: Fix the experience needed to train for the next level
444fbc9575 MM: MM1: Update the enhanced Training Hall when switching characters
ddc42b8c24 MM: MM1: Stop the encounter dialog redrawing over the combat screen
ce10a29b9e MM: MM1: Send the party to the Sorpigal Inn after the enhanced Keeper dialog
71928aa620 MM: MM1: Stop re-running King Alamar's event after the imposter is defeated
d704cd7471 MM: MM1: Fix the Hooded Figure color quiz reading the wrong sign flag
b96993cde7 MM: MM1: Let the Dog Statue give the Gold Key again and award its XP once
58c193cf62 MM: MM1: Fix the Statue of Judgement worthiness check and result text
59c72a2c77 MM: MM1: Mark the Statue of Judgement reward as claimed for the Quest Log
1dd23b63c5 MM: MM1: Record Lord Ironfist's quest discoveries where they happen
e71ef50075 MM: MM1: Knock out characters due to heat
44c79282a8 MM: MM1: Keep offering the White Queen Idol until the party has it
32859f6736 MM: MM1: Only offer the shipwreck search on actual shipwreck squares
a0f271067c MM: MM1: Show Okrim's ghost after winning the fight and award his ring
870ed9690e MM: MM1: Add vertical scrolling for long messages in the enhanced interface
3b51434c3f MM: MM1: Stop the Hooded Figure quizzing party members who have no sign
22df624852 MM: MM1: Fix the Volcano stabilizer dials and keep them across save and load
44919fbe4d MM: MM1: Show the current and selected Volcano dial letter
c4c4d7d875 MM: MM1: Keep conveyor belts switched off across save and load
5b1709feaa MM: MM1: Stop the enhanced conveyor message replaying its dismiss key
435b6c438c MM: MM1: Save Arenko's tree-climbing progress and pay his reward only once
3ce1e2d068 MM: MM1: Add Arenko's Challenge to the Quest Log
b36880c10a MM: MM1: Add Og's chess challenge to the Quest Log
8f9559aa66 MM: MM1: Mark arrival squares as exits on the enhanced minimap
2d4934fce9 MM: MM1: Discover teleport landing squares and repaint exit markers immediately
77cf3b9000 MM: MM1: Use proper map names in the enhanced interface
0db82a0af2 MM: MM1: Refresh the character displays after the Statue of Judgement
996b68c3b5 MM: MM1: Restart Lord Inspectron's quest cycle at his first quest
8fc0b76844 MM: MM1: Name the cleric fire protection spell correctly
469cff3a21 MM: MM1: Let the numpad PgUp and PgDn keys page the enhanced spellbook
284a5a2d1d MM: MM1: Minor bugfixes
a46d864450 MM: MM1: Keep fire protection active when a fight starts in the hot zone
8f71fbdd53 MM: MM1: Pay Og's reward immediately, take the Queen Idols and show his clue
240c5e1721 MM: MM1: Fix the swamp encounter check testing the wrong wall byte
9680b442f0 MM: MM1: Show an arrow beside the experience when a character can level up
e795e40efd MM: MM1: Move the combat Go back and Delay currently text clear of the Escape button
c406bdf04b MM: MM1: Record which monsters the party has encountered
67c86938d5 MM: MM1: Add the enhanced Bestiary window
f1cbc37dd5 MM: MM1: Open the Bestiary from the exploration screen with N
5864bb85a9 MM: MM1: Add a skull button to the game frame that opens the Bestiary
8a3e2bfeac MM: MM1: Open the Bestiary from combat with N
a7f23d6f00 MM: MM1: Call removeCharge through a pointer to the chosen inventory
60f27065f4 MM: MM1: Tidy comments, braces and whitespace
1bbea61874 MM: MM1: Keep the Y and N keys working in scrolling Yes/No messages
bd3a42a557 MM: MM1: Add mapText helper for text that is laid out differently when enhanced
bb1118d8ad MM: MM1: Replace STRING lookups in game logic with literal text
a4f2c29576 MM: MM1: Replace STRING lookups in maps with literal text
faf5923a50 MM: MM1: Replace STRING lookups in classic views with literal text
d36a16fa97 MM: MM1: Replace STRING lookups in enhanced views with literal text
bf187c5f0f MM: MM1: Remove STRING lookup and StringsData
8f14098c87 MM: MM1: Fix tavern rumors list
43c163c15c MM: MM1: Fix incorrect hex escape in tavern text
c2e2176295 MM: MM1: Remove extra space in breathes string constants
a54101d98c MM: MM1: Fix indexing for tavern tips
2f734f9dcd NEWS: Added dragoo84's Might & Magic 1 bugfix/improvements patch


Commit: f7b674226707bd858188827f94a1e9bd159c9a7f
    https://github.com/scummvm/scummvm/commit/f7b674226707bd858188827f94a1e9bd159c9a7f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:32+11:00

Commit Message:
MM: MM1: New characters now start at age 18

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/character.cpp
    engines/mm/mm1/data/character.h


diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index f672ab2a25e..41b029bc81b 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -260,7 +260,8 @@ void Character::clear() {
 	_intelligence = _might = _personality = _endurance = 0;
 	_speed = _accuracy = _luck = 0;
 	_level = 1;
-	_age = _ageDayCtr = 0;
+	_age = 18;
+	_ageDayCtr = 0;
 	_exp = 0;
 	_sp = 0;
 	_spellLevel = 0;
diff --git a/engines/mm/mm1/data/character.h b/engines/mm/mm1/data/character.h
index a6a1702a934..341a99dbfe7 100644
--- a/engines/mm/mm1/data/character.h
+++ b/engines/mm/mm1/data/character.h
@@ -437,7 +437,7 @@ struct Character : public PrimaryAttributes {
 	Race _race = HUMAN;
 	CharacterClass _class = NONE;
 
-	byte _age = 0;
+	byte _age = 18;
 	int _ageDayCtr = 0;
 	AttributePair16 _sp;
 	AttributePair _spellLevel;


Commit: 1574da9d7f29b8f6a6f349dbcf2434b4bd9cedba
    https://github.com/scummvm/scummvm/commit/1574da9d7f29b8f6a6f349dbcf2434b4bd9cedba
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:32+11:00

Commit Message:
MM: MM1: Fix spell level progression for spellcasting classes

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/character.cpp


diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index 41b029bc81b..d8e5acbd2a2 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -357,30 +357,25 @@ Character::LevelIncrease Character::increaseLevel() {
 	_hp = _hpMax = _hpCurrent;
 
 	int gainedSpells = 0;
-	if (classNum < ARCHER) {
-		if (_level._base < 7)
-			gainedSpells = 0;
-		else if (_level._base == 7)
-			gainedSpells = 1;
-		else if (_level._base == 9)
-			gainedSpells = 2;
-		else if (_level._base == 11)
-			gainedSpells = 3;
-		else if (_level._base == 13)
-			gainedSpells = 4;
-	} else if (classNum < SORCERER) {
-		if (_level._base == 3)
-			gainedSpells = 2;
-		else if (_level._base == 5)
-			gainedSpells = 3;
-		else if (_level._base == 7)
-			gainedSpells = 4;
-		else if (_level._base == 9)
-			gainedSpells = 5;
-		else if (_level._base == 11)
-			gainedSpells = 6;
-		else if (_level._base == 13)
-			gainedSpells = 7;
+
+	// Clerics and Sorcerers gain a spell level at character levels
+	// 3, 5, 7, 9, 11 and 13, up to spell level 7.
+	if (_class == CLERIC || _class == SORCERER) {
+		if (_level._base >= 3 && _level._base <= 13 &&
+				(_level._base & 1))
+			gainedSpells = (_level._base + 1) / 2;
+
+	// Paladins and Archers begin casting at character level 7 and
+	// gain another spell level at 9, 11 and 13, up to spell level 4.
+	} else if (_class == PALADIN || _class == ARCHER) {
+		if (_level._base >= 7 && _level._base <= 13 &&
+				(_level._base & 1))
+			gainedSpells = (_level._base - 5) / 2;
+	}
+
+	if (gainedSpells) {
+		_spellLevel = gainedSpells;
+		updateSP();
 	}
 
 	LevelIncrease result;
@@ -460,7 +455,7 @@ void Character::updateSP() {
 	int personality = _personality._current;
 	int level = _level._current;
 	int index = 3;
-	AttributePair newSP;
+	AttributePair16 newSP;
 
 	// Spell points only relevant for spell casters
 	if (_spellLevel._current) {


Commit: a1c9b990b739797c667dd7f18545257cee0726c3
    https://github.com/scummvm/scummvm/commit/a1c9b990b739797c667dd7f18545257cee0726c3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:32+11:00

Commit Message:
MM: MM1: Fix restoring SP whilst resting

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/character.cpp


diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index d8e5acbd2a2..6aa348ca113 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -557,8 +557,13 @@ void Character::rest() {
 	if (_condition & BAD_CONDITION)
 		return;
 
-	updateSP();
+	// Restore temporary attribute and level drains before recalculating
+	// maximum SP. Food and disease still determine recovery below.
+	const int currentSP = _sp._current;
 	updateAttributes();
+	updateSP();
+	_sp._current = MIN(currentSP, (int)_sp._base);
+
 	updateAC();
 	updateResistances();
 


Commit: ee98226029e9ee443a37944168f9960a49d8e35e
    https://github.com/scummvm/scummvm/commit/ee98226029e9ee443a37944168f9960a49d8e35e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:32+11:00

Commit Message:
MM: MM1: Shorten long condition names in the Quick Reference

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/quick_ref.cpp


diff --git a/engines/mm/mm1/views_enh/quick_ref.cpp b/engines/mm/mm1/views_enh/quick_ref.cpp
index a2f40202fce..a3e3a67579b 100644
--- a/engines/mm/mm1/views_enh/quick_ref.cpp
+++ b/engines/mm/mm1/views_enh/quick_ref.cpp
@@ -105,6 +105,12 @@ void QuickRef::writeCharacterLine(int charNum) {
 
 	setTextColor(c.conditionColor());
 	Common::String condStr = c.getConditionString(c.worstCondition());
+
+	// Keep long condition names within their column
+	if (condStr == "Stone")
+		condStr = "Ston";
+	else if (condStr.size() > 5)
+		condStr = Common::String(condStr.c_str(), 5);
 	writeString(COLUMN_CONDITION, yp, condStr);
 
 	setTextColor(0);


Commit: 0f528195969d4d06833c307e1ef8a0aa002e5a66
    https://github.com/scummvm/scummvm/commit/0f528195969d4d06833c307e1ef8a0aa002e5a66
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:32+11:00

Commit Message:
MM: MM1: Fix Lord Hacker quest handling in the enhanced interface

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map36.cpp
    engines/mm/mm1/views_enh/interactions/hacker.cpp


diff --git a/engines/mm/mm1/maps/map36.cpp b/engines/mm/mm1/maps/map36.cpp
index b8813deb2a4..091a35de9d8 100644
--- a/engines/mm/mm1/maps/map36.cpp
+++ b/engines/mm/mm1/maps/map36.cpp
@@ -158,7 +158,7 @@ void Map36::acceptQuest() {
 			Character &c = g_globals->_party[i];
 			c._flags[9] = CHARFLAG9_80;
 			c._flags[6] = CHARFLAG6_80;
-			c._backpack.empty();
+			c._backpack.clear();
 		}
 
 		send(SoundMessage(
@@ -196,7 +196,7 @@ Common::String Map36::checkQuestComplete() {
 		}
 
 		return Common::String::format(
-			STRING["maps.map36.inspectron6"].c_str(),
+			STRING["maps.map36.hacker6"].c_str(),
 			QUEST_EXPERIENCE[qIndex]);
 	} else {
 		// The quest isn't yet complete
diff --git a/engines/mm/mm1/views_enh/interactions/hacker.cpp b/engines/mm/mm1/views_enh/interactions/hacker.cpp
index 583985251e9..6dbf8dde3e5 100644
--- a/engines/mm/mm1/views_enh/interactions/hacker.cpp
+++ b/engines/mm/mm1/views_enh/interactions/hacker.cpp
@@ -61,12 +61,12 @@ bool Hacker::msgGame(const GameMessage &msg) {
 		int questNum = g_globals->_party[0]._quest;
 		Common::String line;
 
-		if (questNum >= 8 && questNum <= 14)
+		if (questNum >= 15 && questNum <= 21)
 			line = map.checkQuestComplete();
 		else
 			line = STRING["maps.map36.hacker4"];
 
-		g_maps->_mapPos.y++;
+		g_maps->_mapPos.x--;
 		map.redrawGame();
 
 		addText(line);
@@ -81,8 +81,15 @@ bool Hacker::msgKeypress(const KeypressMessage &msg) {
 	if (_mode == CAN_ACCEPT) {
 		if (msg.keycode == Common::KEYCODE_y) {
 			map.acceptQuest();
-			_mode = ACCEPTED_QUEST;
 
+			// Asking for an eighth quest is a punishment, which
+			// leaves the quest at zero
+			if (!g_globals->_party[0]._quest) {
+				close();
+				return true;
+			}
+
+			_mode = ACCEPTED_QUEST;
 			clearButtons();
 			Common::String line = Common::String::format("%s %s",
 				STRING["maps.map36.hacker3"].c_str(),


Commit: 10388f8f50e3dfddf60f5b0ecc50b791e8ad91c3
    https://github.com/scummvm/scummvm/commit/10388f8f50e3dfddf60f5b0ecc50b791e8ad91c3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:32+11:00

Commit Message:
MM: MM1: Fix Lord Ironfist's "find Lord Kilburn" quest completion

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map24.cpp


diff --git a/engines/mm/mm1/maps/map24.cpp b/engines/mm/mm1/maps/map24.cpp
index 12afaf6efc0..c564195114f 100644
--- a/engines/mm/mm1/maps/map24.cpp
+++ b/engines/mm/mm1/maps/map24.cpp
@@ -59,6 +59,9 @@ void Map24::special01() {
 		STRING["maps.map24.kilburn"],
 		[]() {
 			Map24 &map = *static_cast<Map24 *>(g_maps->_currentMap);
+			// Meeting Kilburn satisfies Lord Ironfist's second quest
+			for (uint i = 0; i < g_globals->_party.size(); ++i)
+				g_globals->_party[i]._flags[7] |= CHARFLAG7_2;
 			if (!map.addItem(MAP_OF_DESERT_ID)) {
 				g_maps->clearSpecial();
 				map.none160();


Commit: 5ed2fe7b121caaf0b27ec44fe646b4bea7d30e2b
    https://github.com/scummvm/scummvm/commit/5ed2fe7b121caaf0b27ec44fe646b4bea7d30e2b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:32+11:00

Commit Message:
MM: MM1: Fix enhanced template costs not properly updating

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/locations/temple.cpp
    engines/mm/mm1/views_enh/locations/temple.h


diff --git a/engines/mm/mm1/views_enh/locations/temple.cpp b/engines/mm/mm1/views_enh/locations/temple.cpp
index 03ec80e4497..d76b80a964e 100644
--- a/engines/mm/mm1/views_enh/locations/temple.cpp
+++ b/engines/mm/mm1/views_enh/locations/temple.cpp
@@ -154,12 +154,18 @@ bool Temple::msgAction(const ActionMessage &msg) {
 }
 
 bool Temple::msgGame(const GameMessage &msg) {
-	Location::msgGame(msg);
 	if (msg._name == "UPDATE")
 		updateCosts();
+
+	Location::msgGame(msg);
 	return true;
 }
 
+void Temple::charSwitched(Character *priorChar) {
+	updateCosts();
+	PartyView::charSwitched(priorChar);
+}
+
 void Temple::updateCosts() {
 	Maps::Map &map = *g_maps->_currentMap;
 	int i;
@@ -210,6 +216,7 @@ void Temple::restoreHealth() {
 			--c._endurance;
 		}
 
+		updateCosts();
 		redraw();
 	}
 }
@@ -227,6 +234,7 @@ void Temple::uncurseItems() {
 			}
 		}
 
+		updateCosts();
 		redraw();
 	}
 }
@@ -235,8 +243,9 @@ void Temple::restoreAlignment() {
 	if (subtractGold(_alignmentCost)) {
 		Character &c = *g_globals->_currCharacter;
 		c._alignment = c._alignmentInitial;
-		c._alignmentCtr = ALIGNMENT_VALS[c._alignment];
+		c._alignmentCtr = ALIGNMENT_VALS[c._alignment - 1];
 
+		updateCosts();
 		redraw();
 	}
 }
diff --git a/engines/mm/mm1/views_enh/locations/temple.h b/engines/mm/mm1/views_enh/locations/temple.h
index 3f1e9bf60ee..fdc4579be07 100644
--- a/engines/mm/mm1/views_enh/locations/temple.h
+++ b/engines/mm/mm1/views_enh/locations/temple.h
@@ -38,6 +38,7 @@ private:
 	int _alignmentCost = 0, _donateCost = 0;
 private:
 	void updateCosts();
+	void charSwitched(Character *priorChar) override;
 	void writeOption(int row, char c, const Common::String &text, const Common::String &cost);
 	void restoreHealth();
 	void uncurseItems();


Commit: 16d847424b1ade9bbddddd46e1bb94ee001d3641
    https://github.com/scummvm/scummvm/commit/16d847424b1ade9bbddddd46e1bb94ee001d3641
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:32+11:00

Commit Message:
MM: MM1: Fix Temple healing availability and HP cap restoration

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views/locations/temple.cpp
    engines/mm/mm1/views_enh/locations/temple.cpp


diff --git a/engines/mm/mm1/views/locations/temple.cpp b/engines/mm/mm1/views/locations/temple.cpp
index 3fd274bf61d..256999008ba 100644
--- a/engines/mm/mm1/views/locations/temple.cpp
+++ b/engines/mm/mm1/views/locations/temple.cpp
@@ -112,7 +112,7 @@ void Temple::changeCharacter(uint index) {
 		_isEradicated = true;
 	} else if (c._condition & BAD_CONDITION) {
 		_healCost = HEAL_COST2[townNum];
-	} else if (c._condition || c._hp < c._hpMax) {
+	} else if (c._condition || c._hpCurrent < c._hpMax) {
 		_healCost = HEAL_COST3[townNum];
 	}
 
@@ -168,7 +168,8 @@ void Temple::restoreHealth() {
 	if (subtractGold(_healCost)) {
 		Character &c = *g_globals->_currCharacter;
 		c._condition = FINE;
-		c._hpCurrent = c._hp;
+		c._hpMax = c._hp;
+		c._hpCurrent = c._hpMax;
 
 		if (_isEradicated) {
 			c._age += 10;
diff --git a/engines/mm/mm1/views_enh/locations/temple.cpp b/engines/mm/mm1/views_enh/locations/temple.cpp
index d76b80a964e..db0694d232c 100644
--- a/engines/mm/mm1/views_enh/locations/temple.cpp
+++ b/engines/mm/mm1/views_enh/locations/temple.cpp
@@ -184,7 +184,7 @@ void Temple::updateCosts() {
 		_isEradicated = true;
 	} else if (c._condition & BAD_CONDITION) {
 		_healCost = HEAL_COST2[townNum];
-	} else if (c._condition || c._hp < c._hpMax) {
+	} else if (c._condition || c._hpCurrent < c._hpMax) {
 		_healCost = HEAL_COST3[townNum];
 	}
 
@@ -209,7 +209,8 @@ void Temple::restoreHealth() {
 	if (subtractGold(_healCost)) {
 		Character &c = *g_globals->_currCharacter;
 		c._condition = FINE;
-		c._hpCurrent = c._hp;
+		c._hpMax = c._hp;
+		c._hpCurrent = c._hpMax;
 
 		if (_isEradicated) {
 			c._age += 10;


Commit: 13200ca6bc3c2f0857e3603aa11c1d9e8236f5c1
    https://github.com/scummvm/scummvm/commit/13200ca6bc3c2f0857e3603aa11c1d9e8236f5c1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:35+11:00

Commit Message:
MM: MM1: Fix truncation of monster names using the full name field

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/monsters.cpp


diff --git a/engines/mm/mm1/data/monsters.cpp b/engines/mm/mm1/data/monsters.cpp
index 91745ba470d..fc69ea72dda 100644
--- a/engines/mm/mm1/data/monsters.cpp
+++ b/engines/mm/mm1/data/monsters.cpp
@@ -54,7 +54,7 @@ bool Monsters::load() {
 		Common::String line = f.readLine();
 		assert(line.size() > 20 && line[0] == '"' && line[16] == '"');
 
-		mon._name = Common::String(line.c_str() + 1, line.c_str() + 15);
+		mon._name = Common::String(line.c_str() + 1, line.c_str() + 16);
 		while (mon._name.hasSuffix(" "))
 			mon._name.deleteLastChar();
 


Commit: 613fa80e36ff3107a46af95de5f1b09ead5fdac1
    https://github.com/scummvm/scummvm/commit/613fa80e36ff3107a46af95de5f1b09ead5fdac1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:36+11:00

Commit Message:
MM: MM1: Fix memory leak loading monster images

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/monsters.cpp


diff --git a/engines/mm/mm1/data/monsters.cpp b/engines/mm/mm1/data/monsters.cpp
index fc69ea72dda..6591527f36b 100644
--- a/engines/mm/mm1/data/monsters.cpp
+++ b/engines/mm/mm1/data/monsters.cpp
@@ -97,6 +97,7 @@ Graphics::ManagedSurface Monsters::getMonsterImage(int imgNum) {
 	if (!decoder.loadStream(*entry, 104, 96))
 		error("Failed decoding monster image");
 
+	delete entry;
 	img.copyFrom(*decoder.getSurface());
 
 	return img;


Commit: 92b432c91efb56ff85c478337d3eb84e316e1be0
    https://github.com/scummvm/scummvm/commit/92b432c91efb56ff85c478337d3eb84e316e1be0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:36+11:00

Commit Message:
MM: MM1: Fix crash loading a save while a location is open

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/mm1.cpp
    engines/mm/mm1/views_enh/game_view.cpp


diff --git a/engines/mm/mm1/mm1.cpp b/engines/mm/mm1/mm1.cpp
index e0f9794cce6..efe62566216 100644
--- a/engines/mm/mm1/mm1.cpp
+++ b/engines/mm/mm1/mm1.cpp
@@ -170,6 +170,10 @@ Common::Error MM1Engine::synchronizeSave(Common::Serializer &s) {
 
 	// If we're loading a savegame, switch to the game view
 	if (s.isLoading()) {
+		// Discard any location animation retained by the enhanced
+		// GameView before restoring the saved game state
+		if (g_engine->isEnhanced())
+			g_events->send("View", GameMessage("LOCATION", -1));
 		g_events->replaceView("Game", true);
 	}
 
diff --git a/engines/mm/mm1/views_enh/game_view.cpp b/engines/mm/mm1/views_enh/game_view.cpp
index a42b6e25a65..cbd0e88aadd 100644
--- a/engines/mm/mm1/views_enh/game_view.cpp
+++ b/engines/mm/mm1/views_enh/game_view.cpp
@@ -144,36 +144,39 @@ bool GameView::msgGame(const GameMessage &msg) {
 }
 
 void GameView::showLocation(int locationId) {
-	if (locationId == -1) {
+	// Loading a game can leave the previous location animation active.
+	// Safely close it before leaving or starting another location.
+	if (_anim) {
 		_anim->leave();
 		delete _anim;
 		_anim = nullptr;
-
-	} else {
-		assert(!_anim);
-		switch (locationId) {
-		case LOC_TRAINING:
-			_anim = new Animations::Training();
-			break;
-		case LOC_MARKET:
-			_anim = new Animations::Market();
-			break;
-		case LOC_TEMPLE:
-			_anim = new Animations::Temple();
-			break;
-		case LOC_BLACKSMITH:
-			_anim = new Animations::Blacksmith();
-			break;
-		case LOC_TAVERN:
-			_anim = new Animations::Tavern();
-			break;
-		default:
-			error("Unknown location type");
-			break;
-		}
-
-		_anim->enter();
 	}
+
+	if (locationId == -1)
+		return;
+
+	switch (locationId) {
+	case LOC_TRAINING:
+		_anim = new Animations::Training();
+		break;
+	case LOC_MARKET:
+		_anim = new Animations::Market();
+		break;
+	case LOC_TEMPLE:
+		_anim = new Animations::Temple();
+		break;
+	case LOC_BLACKSMITH:
+		_anim = new Animations::Blacksmith();
+		break;
+	case LOC_TAVERN:
+		_anim = new Animations::Tavern();
+		break;
+	default:
+		error("Unknown location type");
+		break;
+	}
+
+	_anim->enter();
 }
 
 void GameView::draw() {


Commit: 8b28e61dd3518ff0e8aefe9324d0b3b57445b38e
    https://github.com/scummvm/scummvm/commit/8b28e61dd3518ff0e8aefe9324d0b3b57445b38e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Fix backward movement being blocked by passable walls

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/view_base.cpp


diff --git a/engines/mm/mm1/game/view_base.cpp b/engines/mm/mm1/game/view_base.cpp
index 985e3efab80..120af0b4f6f 100644
--- a/engines/mm/mm1/game/view_base.cpp
+++ b/engines/mm/mm1/game/view_base.cpp
@@ -145,7 +145,9 @@ void ViewBase::forward(KeybindingAction action) {
 
 	//  Figure out direction mask
 	byte mask = maps._forwardMask;
-	if (action == KEYBIND_STRAFE_LEFT) {
+	if (action == KEYBIND_BACKWARDS) {
+		mask = maps._backwardsMask;
+	} else if (action == KEYBIND_STRAFE_LEFT) {
 		mask = maps._leftMask;
 	} else if (action == KEYBIND_STRAFE_RIGHT) {
 		mask = maps._rightMask;
@@ -198,31 +200,7 @@ void ViewBase::forward(KeybindingAction action) {
 }
 
 void ViewBase::backwards() {
-	Maps::Maps &maps = g_globals->_maps;
-	Maps::Map &map = *g_globals->_maps._currentMap;
-	Common::Point delta = g_maps->getMoveDelta(maps._backwardsMask);
-
-	if (!g_globals->_intangible) {
-		if (maps._currentWalls & maps._backwardsMask) {
-			Sound::sound(SOUND_1);
-			g_globals->_party.checkPartyDead();
-			return;
-		}
-		if (maps._currentState & 0x55 & maps._backwardsMask) {
-			Sound::sound(SOUND_1);
-			g_globals->_party.checkPartyDead();
-			return;
-		}
-	}
-
-	g_globals->_treasure.clear();
-
-	int maxVal = map[Maps::MAP_29];
-	if (g_engine->getRandomNumber(maxVal) == maxVal)
-		_stepRandom = true;
-
-	g_globals->_maps.step(delta);
-	update();
+	forward(KEYBIND_BACKWARDS);
 }
 
 void ViewBase::obstructed(byte mask) {


Commit: aebb7b323a74dbd955a89cdf2288d260c6a75789
    https://github.com/scummvm/scummvm/commit/aebb7b323a74dbd955a89cdf2288d260c6a75789
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Refuse to start an encounter while one is already active

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/encounter.cpp


diff --git a/engines/mm/mm1/game/encounter.cpp b/engines/mm/mm1/game/encounter.cpp
index 91520da80c0..ed499c70b0b 100644
--- a/engines/mm/mm1/game/encounter.cpp
+++ b/engines/mm/mm1/game/encounter.cpp
@@ -19,6 +19,8 @@
  *
  */
 
+#include "common/textconsole.h"
+
 #include "mm/mm1/game/encounter.h"
 #include "mm/mm1/data/monsters.h"
 #include "mm/mm1/maps/maps.h"
@@ -32,6 +34,12 @@ namespace MM1 {
 namespace Game {
 
 void Encounter::execute() {
+	if (g_events->isPresent("Combat") ||
+			g_events->isPresent("Encounter")) {
+		warning("MM1: Ignoring nested encounter request");
+		return;
+	}
+
 	if (!g_globals->_encountersOn)
 		return;
 


Commit: 937ad9dc60af4901211101955b82be2e04353da6
    https://github.com/scummvm/scummvm/commit/937ad9dc60af4901211101955b82be2e04353da6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Fix encounter monsters using the wrong monster record

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/encounter.cpp


diff --git a/engines/mm/mm1/game/encounter.cpp b/engines/mm/mm1/game/encounter.cpp
index ed499c70b0b..e1cfa2c5883 100644
--- a/engines/mm/mm1/game/encounter.cpp
+++ b/engines/mm/mm1/game/encounter.cpp
@@ -138,7 +138,9 @@ exit_loop:
 
 		// Add monster details to list
 		_monsterLevel = _monsterSummaries[i]._level;
-		const Monster &srcMons = (*g_globals->_monsters)[_monsterSummaries[i]._num];
+		const Monster &srcMons = (*g_globals->_monsters)[
+			(_monsterSummaries[i]._level - 1) * 16 +
+			_monsterSummaries[i]._num];
 		_monsterList.push_back(srcMons);
 		Monster &mons = _monsterList.back();
 		mons._level = _monsterSummaries[i]._level;


Commit: ecc7558dc2feea03de2bdc9081858a2bdaacbfe4
    https://github.com/scummvm/scummvm/commit/ecc7558dc2feea03de2bdc9081858a2bdaacbfe4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Fix monster touch effect lookup using the wrong table index

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/monster_touch.cpp


diff --git a/engines/mm/mm1/game/monster_touch.cpp b/engines/mm/mm1/game/monster_touch.cpp
index aca4460fa0b..e49029d7bd1 100644
--- a/engines/mm/mm1/game/monster_touch.cpp
+++ b/engines/mm/mm1/game/monster_touch.cpp
@@ -61,7 +61,12 @@ const MonsterTouchAction MonsterTouch::ACTIONS[25] = {
 
 bool MonsterTouch::monsterTouch(uint index, Common::String &line) {
 	line = "";
-	return (this->*ACTIONS[index])(line);
+
+	// Touch effect IDs are 1-based
+	if (index == 0 || index > ARRAYSIZE(ACTIONS))
+		return false;
+
+	return (this->*ACTIONS[index - 1])(line);
 }
 
 bool MonsterTouch::canPerform(int level) const {


Commit: 1ab6f6d8221c8c8ae0ffa277d32e7e4b6a71e9be
    https://github.com/scummvm/scummvm/commit/1ab6f6d8221c8c8ae0ffa277d32e7e4b6a71e9be
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Fix missing monster touch action messages

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/monster_touch.cpp


diff --git a/engines/mm/mm1/game/monster_touch.cpp b/engines/mm/mm1/game/monster_touch.cpp
index e49029d7bd1..396448aaa9d 100644
--- a/engines/mm/mm1/game/monster_touch.cpp
+++ b/engines/mm/mm1/game/monster_touch.cpp
@@ -56,8 +56,16 @@ const MonsterTouchAction MonsterTouch::ACTIONS[25] = {
 	&MonsterTouch::action24
 };
 
-#define LINE(ACTION) line = Common::String::format("    %s%s", \
-	STRING["monster_actions.and"].c_str(), STRING[ACTION].c_str())
+static Common::String formatMonsterAction(const Common::String &action) {
+	Common::String conjunction = STRING["monster_actions.and"];
+	if (!conjunction.empty() && conjunction.firstChar() == '!')
+		conjunction.deleteChar(0);
+
+	return Common::String::format("    %s%s!",
+		conjunction.c_str(), action.c_str());
+}
+
+#define LINE(ACTION) line = formatMonsterAction(STRING[ACTION])
 
 bool MonsterTouch::monsterTouch(uint index, Common::String &line) {
 	line = "";
@@ -149,7 +157,7 @@ bool MonsterTouch::action06(Common::String &line) {
 bool MonsterTouch::action07(Common::String &line) {
 	if (damageType5()) {
 		setCondition(3);
-		LINE("monster_actions.cures_paralysis");
+		LINE("monster_actions.causes_paralysis");
 		return true;
 	}
 
@@ -336,7 +344,7 @@ bool MonsterTouch::action20(Common::String &line) {
 		}
 	}
 
-	LINE("monster_actions.drains_abilities");
+	LINE("monster_actions.drains_ability");
 	return true;
 }
 
@@ -345,7 +353,7 @@ bool MonsterTouch::action21(Common::String &line) {
 
 	if (canPerform(2)) {
 		c._backpack.clear();
-		LINE("monster_actions.steals_backpack");
+		line = formatMonsterAction("steals backpack");
 		return true;
 
 	} else {


Commit: 4eb5c91a0d51cfd50eb612eeb7a0a2676c20c06c
    https://github.com/scummvm/scummvm/commit/4eb5c91a0d51cfd50eb612eeb7a0a2676c20c06c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Fix missing separator between monster name and spell message

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_monsters.cpp


diff --git a/engines/mm/mm1/game/spells_monsters.cpp b/engines/mm/mm1/game/spells_monsters.cpp
index 3c5e6b31a06..d99c35079ac 100644
--- a/engines/mm/mm1/game/spells_monsters.cpp
+++ b/engines/mm/mm1/game/spells_monsters.cpp
@@ -75,6 +75,7 @@ void SpellsMonsters::castMonsterSpell(const Common::String &monsterName, int spe
 	// All spell messages start with the monster who casts it
 	_lines.clear();
 	_lines.push_back(Line(monsterName));
+	add(' ');
 
 	(this->*SPELLS[spellNum - 1])();
 }


Commit: ccf34c22402c4623730cb723ca3ddcf1094e6fcc
    https://github.com/scummvm/scummvm/commit/ccf34c22402c4623730cb723ca3ddcf1094e6fcc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Fix monster spells using the wrong monster's data

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_monsters.cpp


diff --git a/engines/mm/mm1/game/spells_monsters.cpp b/engines/mm/mm1/game/spells_monsters.cpp
index d99c35079ac..dc5ea05ce47 100644
--- a/engines/mm/mm1/game/spells_monsters.cpp
+++ b/engines/mm/mm1/game/spells_monsters.cpp
@@ -104,7 +104,7 @@ void SpellsMonsters::spell03_fire() {
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
 
-	int count = _remainingMonsters[getMonsterIndex()]->_level;
+	int count = g_globals->_encounters._monsterList[getMonsterIndex()]._level;
 	g_globals->_spellsState._damage += count * 6;
 
 	damageRandomChar();
@@ -158,7 +158,7 @@ void SpellsMonsters::spell08_paralyze() {
 		g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FEAR;
 		g_globals->_spellsState._damage = PARALYZED;
 
-		if (_remainingMonsters[getMonsterIndex()]->_level >= 5) {
+		if (g_globals->_encounters._monsterList[getMonsterIndex()]._level >= 5) {
 			handlePartyEffects();
 		} else {
 			chooseCharacter();
@@ -201,9 +201,9 @@ void SpellsMonsters::spell12_explode() {
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
 	g_globals->_spellsState._damage = getRandomNumber(
-		_remainingMonsters[getMonsterIndex()]->_level);
-	_remainingMonsters[getMonsterIndex()]->_level = 0;
-	_remainingMonsters[getMonsterIndex()]->_status = MONFLAG_DEAD;
+		g_globals->_encounters._monsterList[getMonsterIndex()]._level);
+	g_globals->_encounters._monsterList[getMonsterIndex()]._level = 0;
+	g_globals->_encounters._monsterList[getMonsterIndex()]._status = MONFLAG_DEAD;
 	removeMonster();
 
 	add(':');
@@ -219,7 +219,7 @@ void SpellsMonsters::spell13_fireball() {
 
 		// This whole condition choice makes no sense
 		g_globals->_spellsState._damage += 6 *
-			_remainingMonsters[getMonsterIndex()]->_level;
+			g_globals->_encounters._monsterList[getMonsterIndex()]._level;
 		g_globals->_spellsState._damage = getRandomNumber(g_globals->_spellsState._damage) + 4;
 
 		add(':');
@@ -236,7 +236,7 @@ void SpellsMonsters::spell14_fireBreath() {
 
 	// This whole condition choice makes no sense
 	g_globals->_spellsState._damage += 8 *
-		_remainingMonsters[getMonsterIndex()]->_level;
+		g_globals->_encounters._monsterList[getMonsterIndex()]._level;
 	g_globals->_spellsState._damage = getRandomNumber(g_globals->_spellsState._damage);
 
 	add(':');
@@ -351,7 +351,7 @@ void SpellsMonsters::spell25_poison() {
 		STRING["monster_spells.breathes"].c_str(),
 		STRING["monster_spells.poison"].c_str()));
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
-	g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
+	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
 
 	add(':');
@@ -363,7 +363,7 @@ void SpellsMonsters::spell26_lightning() {
 		STRING["monster_spells.breathes"].c_str(),
 		STRING["monster_spells.lightning"].c_str()));
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
-	g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
+	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
 
 	add(':');
@@ -375,7 +375,7 @@ void SpellsMonsters::spell27_frost() {
 		STRING["monster_spells.breathes"].c_str(),
 		STRING["monster_spells.frost"].c_str()));
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_COLD;
-	g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
+	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
 
 	add(':');
@@ -387,7 +387,7 @@ void SpellsMonsters::spell28_spikes() {
 		STRING["monster_spells.breathes"].c_str(),
 		STRING["monster_spells.spikes"].c_str()));
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
-	g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
+	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
 
 	add(':');
@@ -399,7 +399,7 @@ void SpellsMonsters::spell29_acid() {
 		STRING["monster_spells.breathes"].c_str(),
 		STRING["monster_spells.acid"].c_str()));
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ACID;
-	g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
+	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
 
 	add(':');
@@ -411,7 +411,7 @@ void SpellsMonsters::spell30_fire() {
 		STRING["monster_spells.breathes"].c_str(),
 		STRING["monster_spells.fire"].c_str()));
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
-	g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
+	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
 
 	add(':');
@@ -420,7 +420,7 @@ void SpellsMonsters::spell30_fire() {
 
 void SpellsMonsters::spell31_energy() {
 	add(STRING["monster_spells.energy"]);
-	g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
+	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
 
 	add(':');


Commit: f0c3e1cb7c665fbda144b76eec65616418ac0e0b
    https://github.com/scummvm/scummvm/commit/f0c3e1cb7c665fbda144b76eec65616418ac0e0b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Fix combat overwriting party members with copies of others

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index cb04d480564..c832d0bc6af 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -684,10 +684,10 @@ void Combat::checkMonsterActions() {
 
 	// Chosen character was incapitated, so scan through party
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
-		c = g_globals->_party[i];
+		Character &candidate = g_globals->_party[i];
 
-		if (!(c._condition & (BAD_CONDITION | UNCONSCIOUS))) {
-			g_globals->_currCharacter = &c;
+		if (!(candidate._condition & (BAD_CONDITION | UNCONSCIOUS))) {
+			g_globals->_currCharacter = &candidate;
 			monsterAttackShooting();
 			return;
 		}
@@ -1234,12 +1234,12 @@ bool Combat::divineIntervention() {
 	c._age = age;
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
-		c = g_globals->_party[i];
+		Character &partyMember = g_globals->_party[i];
 
-		if (c._condition != ERADICATED) {
-			c._condition = 0;
-			c._hpCurrent = c._hp;
-			c._hpMax = c._hpCurrent;
+		if (partyMember._condition != ERADICATED) {
+			partyMember._condition = 0;
+			partyMember._hpCurrent = partyMember._hp;
+			partyMember._hpMax = partyMember._hpCurrent;
 		}
 	}
 


Commit: 9c6957dd789417b439ec4c2270d4d44905b9b15b
    https://github.com/scummvm/scummvm/commit/9c6957dd789417b439ec4c2270d4d44905b9b15b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Base monster status recovery on monster level

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index c832d0bc6af..9b3254d1f27 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -572,7 +572,7 @@ bool Combat::monsterChanges() {
 }
 
 void Combat::proc2() {
-	int threshold = getMonsterIndex() * 8 + 20;
+	int threshold = MAX((int)_monsterP->_level - 1, 0) * 8 + 20;
 	int val = getRandomNumber(100);
 
 	_val9 = (val != 100 && val <= threshold) ? 1 : 0;


Commit: e2550b0b54d77e4e7231758edf1f14c135769e28
    https://github.com/scummvm/scummvm/commit/e2550b0b54d77e4e7231758edf1f14c135769e28
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:37+11:00

Commit Message:
MM: MM1: Fix Turn Undead using monster hit points instead of level

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 9b3254d1f27..32f2f900f82 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1135,7 +1135,7 @@ void Combat::turnUndead() {
 
 			if ((monster->_resistUndead & IS_UNDEAD) &&
 					(getRandomNumber(20) + g_globals->_currCharacter->_level) >=
-					(_remainingMonsters[i]->_hp * 2 + 10)) {
+					(monster->_level * 2 + 10)) {
 				destroyMonster();
 				++_monstersDestroyedCtr;
 			}


Commit: f473d87a2660428bfb170f6c754ba176a9cccd59
    https://github.com/scummvm/scummvm/commit/f473d87a2660428bfb170f6c754ba176a9cccd59
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:38+11:00

Commit Message:
MM: MM1: Fix Lightning Bolt inheriting damage from earlier spells

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 32f2f900f82..91dc223738c 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1344,6 +1344,7 @@ void Combat::lightningBolt() {
 	ss._mmVal1++;
 	ss._resistanceTypeOrTargetCount = 3;
 	ss._resistanceIndex = 2;
+	ss._damage = 0;
 
 	levelAdjust();
 }


Commit: 75659120560f2544c4bca12ea59e59bcfb75c99f
    https://github.com/scummvm/scummvm/commit/75659120560f2544c4bca12ea59e59bcfb75c99f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:38+11:00

Commit Message:
MM: MM1: Fix Fireball damaging the party and targeting monsters in melee

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/game/combat.h
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 91dc223738c..0d2625e42b9 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1193,12 +1193,21 @@ void Combat::summonLightning2() {
 
 void Combat::fireball2() {
 	SpellsState &ss = g_globals->_spellsState;
+
 	ss._mmVal1 = 1;
 	ss._resistanceIndex = 1;
-	ss._resistanceTypeOrTargetCount = 5;
 	ss._damage = 0;
 
-	levelAdjust();
+	// Fireball affects up to five monsters outside hand-to-hand range
+	_destMonsterNum = _attackersCount;
+	_spellMonsterCount = _remainingMonsters.size();
+	ss._resistanceTypeOrTargetCount = MIN(
+		5, (int)_remainingMonsters.size() - _destMonsterNum);
+
+	for (uint i = 0; i < g_globals->_currCharacter->_level._current; ++i)
+		ss._damage += getRandomNumber(6);
+
+	iterateMonsters2Inner();
 }
 
 void Combat::levelAdjust() {
@@ -1314,29 +1323,13 @@ void Combat::identifyMonster() {
 }
 #undef COL2
 
-void Combat::fireball() {
-	SpellsState &ss = g_globals->_spellsState;
-
-	if (_destMonsterNum < _attackersCount) {
-		Common::String line1 = Common::String::format("%s %s",
-			g_globals->_currCharacter->_name,
-			STRING["spells.casts_spell"].c_str());
-
-		ss._damage = g_globals->_currCharacter->_level * 2 + 4;
-		ss._mmVal1++;
-		ss._resistanceIndex++;
-		ss._resistanceTypeOrTargetCount++;
-		handlePartyDamage();
+bool Combat::fireball() {
+	// Fireball cannot target monsters engaged in hand-to-hand combat
+	if (_attackersCount >= (int)_remainingMonsters.size())
+		return false;
 
-		InfoMessage msg(0, 0, line1);
-		msg._delaySeconds = 3;
-		msg._callback = []() {
-			g_globals->_combat->fireball2();
-		};
-		displaySpellResult(msg);
-	} else {
-		fireball2();
-	}
+	fireball2();
+	return true;
 }
 
 void Combat::lightningBolt() {
diff --git a/engines/mm/mm1/game/combat.h b/engines/mm/mm1/game/combat.h
index 1f9a76bc3d0..28df1cc426f 100644
--- a/engines/mm/mm1/game/combat.h
+++ b/engines/mm/mm1/game/combat.h
@@ -333,7 +333,7 @@ public:
 
 	// Wizard spells that need access to internal fields
 	void identifyMonster();
-	void fireball();
+	bool fireball();
 	void lightningBolt();
 	void makeRoom();
 	void slow();
diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index c1130122b9f..b649bcd4122 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -754,8 +754,8 @@ SpellResult SpellsParty::wizard28_scare() {
 }
 
 SpellResult SpellsParty::wizard31_fireball() {
-	g_globals->_combat->fireball();
-	return SR_SUCCESS_SILENT;
+	return g_globals->_combat->fireball() ?
+		SR_SUCCESS_SILENT : SR_FAILED;
 }
 
 SpellResult SpellsParty::wizard32_fly() {


Commit: ac52d86b4cb719ae011fb0374d736963fdbda43a
    https://github.com/scummvm/scummvm/commit/ac52d86b4cb719ae011fb0374d736963fdbda43a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:38+11:00

Commit Message:
MM: MM1: Fix Web silently failing due to a stale target check

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 0d2625e42b9..f6c1210614a 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1370,9 +1370,6 @@ void Combat::weaken() {
 
 bool Combat::web() {
 	SpellsState &ss = g_globals->_spellsState;
-	if (_destMonsterNum < _attackersCount)
-		return false;
-
 	ss._mmVal1++;
 	ss._resistanceIndex = 0;
 	ss._resistanceTypeOrTargetCount = 5;


Commit: 4b5f38ad633798633526eefa87cab03b16dd9119
    https://github.com/scummvm/scummvm/commit/4b5f38ad633798633526eefa87cab03b16dd9119
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:38+11:00

Commit Message:
MM: MM1: Apply the Leather Skin AC bonus to displayed AC and enemy attacks

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/character.cpp
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index 6aa348ca113..ef420b459cc 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -418,7 +418,10 @@ void Character::updateAttributes() {
 }
 
 void Character::updateAC() {
-	int ac = _ac._base;
+	// Leather Skin stores the caster's level as a party-wide AC bonus.
+	// Rebuild from base so repeated updates don't stack it.
+	int ac = _ac._base +
+		g_globals->_activeSpells._s.leather_skin;
 
 	if (_speed >= 40)
 		ac += 9;
diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index f6c1210614a..46d18853183 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1485,7 +1485,9 @@ void Combat::monsterAttackInner() {
 	Encounter &enc = g_globals->_encounters;
 	Character &c = *g_globals->_currCharacter;
 
-	_destAC = c._ac._base;
+	// Include the Leather Skin bonus in the enemy hit roll
+	_destAC = c._ac._base +
+		g_globals->_activeSpells._s.leather_skin;
 	int monsterIndex = getMonsterIndex();
 	_attackerLevel = enc._monsterList[monsterIndex]._level * 2 + 4;
 


Commit: f364b3711a2eb961c1fa14941e5b6323754aee47
    https://github.com/scummvm/scummvm/commit/f364b3711a2eb961c1fa14941e5b6323754aee47
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:38+11:00

Commit Message:
MM: MM1: Fix combat Exchange party order and can-attack handling

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/views_enh/which_character.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 46d18853183..1ffc237e789 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1545,7 +1545,7 @@ void Combat::selectMonsterTarget() {
 			}
 		}
 		_partyIndex = idx;
-		Character &c = g_globals->_party[_partyIndex];
+		Character &c = *g_globals->_combatParty[_partyIndex];
 		g_globals->_currCharacter = &c;
 
 		if (!(c._condition & BAD_CONDITION) && !(c._condition & UNCONSCIOUS)) {
@@ -1584,6 +1584,7 @@ void Combat::exchangeWith(int charNum) {
 	g_globals->_combatParty[_currentChar]->_checked = true;
 	Character *c1 = g_globals->_combatParty[_currentChar];
 	Character *c2 = g_globals->_combatParty[charNum];
+	SWAP(c1->_canAttack, c2->_canAttack);
 	g_globals->_combatParty[_currentChar] = c2;
 	g_globals->_combatParty[charNum] = c1;
 
diff --git a/engines/mm/mm1/views_enh/which_character.cpp b/engines/mm/mm1/views_enh/which_character.cpp
index 88bdb6d5896..9527104a6d9 100644
--- a/engines/mm/mm1/views_enh/which_character.cpp
+++ b/engines/mm/mm1/views_enh/which_character.cpp
@@ -64,8 +64,15 @@ bool WhichCharacter::msgAction(const ActionMessage &msg) {
 	case KEYBIND_VIEW_PARTY6: {
 		uint charNum = msg._action - KEYBIND_VIEW_PARTY1;
 		if (charNum < g_globals->_party.size()) {
-			close();
-			selectCharacter(charNum);
+			if (dynamic_cast<Combat *>(g_events->priorView()) != nullptr) {
+				// Apply the exchange before closing so Combat
+				// redraws the updated party order immediately
+				send("Combat", GameMessage("EXCHANGE", charNum));
+				close();
+			} else {
+				close();
+				selectCharacter(charNum);
+			}
 		}
 		return true;
 	}


Commit: 6a8b4b5d8f264cb28eca038a346e864b56231952
    https://github.com/scummvm/scummvm/commit/6a8b4b5d8f264cb28eca038a346e864b56231952
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:38+11:00

Commit Message:
MM: MM1: Always open ranged target selection when shooting in combat

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index c877e5b1970..9a13dc134ce 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -878,13 +878,8 @@ void Combat::fight() {
 }
 
 void Combat::shoot() {
-	if (_allowShoot) {
-		if (_remainingMonsters.size() < 2) {
-			attackMonsterPhysical();
-		} else {
-			setOption(OPTION_SHOOT);
-		}
-	}
+	if (_allowShoot)
+		setOption(OPTION_SHOOT);
 }
 
 void Combat::writeMessage() {


Commit: 55afbcea1b3300b61c4913599494b13ed175b6ea
    https://github.com/scummvm/scummvm/commit/55afbcea1b3300b61c4913599494b13ed175b6ea
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:38+11:00

Commit Message:
MM: MM1: Fix Dispel changing the active monster while clearing statuses

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 1ffc237e789..12d81896e28 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -240,7 +240,6 @@ bool Combat::canMonsterCast() const {
 void Combat::dispelParty() {
 	g_globals->_activeSpells.clear();
 	Character *tmpC = g_globals->_currCharacter;
-	Monster *tmpM = _monsterP;
 
 	for (uint i = 0; i < g_globals->_combatParty.size(); ++i) {
 		Character &c = *g_globals->_combatParty[i];
@@ -255,12 +254,8 @@ void Combat::dispelParty() {
 
 	g_globals->_currCharacter = tmpC;
 
-	for (uint i = 0; i < _remainingMonsters.size(); ++i) {
-		monsterSetPtr(i);
+	for (uint i = 0; i < _remainingMonsters.size(); ++i)
 		_remainingMonsters[i]->_status = 0;
-	}
-
-	_monsterP = tmpM;
 }
 
 void Combat::combatLoop(bool checkMonstersFirst) {


Commit: 3dea45b62126cc31911c35901d988510ded3d208
    https://github.com/scummvm/scummvm/commit/3dea45b62126cc31911c35901d988510ded3d208
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:38+11:00

Commit Message:
MM: MM1: Fix party-wide monster effects changing the selected character

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_monsters.cpp


diff --git a/engines/mm/mm1/game/spells_monsters.cpp b/engines/mm/mm1/game/spells_monsters.cpp
index dc5ea05ce47..a9afb62c83e 100644
--- a/engines/mm/mm1/game/spells_monsters.cpp
+++ b/engines/mm/mm1/game/spells_monsters.cpp
@@ -676,12 +676,16 @@ void SpellsMonsters::setCondition(byte newCondition) {
 }
 
 void SpellsMonsters::handlePartyEffects() {
+	Character *priorCharacter = g_globals->_currCharacter;
+
 	add(':');
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
 		g_globals->_currCharacter = &g_globals->_party[i];
 		writeConditionEffect();
 	}
+
+	g_globals->_currCharacter = priorCharacter;
 }
 
 void SpellsMonsters::handlePartyDamage() {


Commit: f2b3ae6d71fc1e6aa6d0d9b55893803ad6a516a6
    https://github.com/scummvm/scummvm/commit/f2b3ae6d71fc1e6aa6d0d9b55893803ad6a516a6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:39+11:00

Commit Message:
MM: MM1: Fix missing monster spell messages

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_monsters.cpp


diff --git a/engines/mm/mm1/game/spells_monsters.cpp b/engines/mm/mm1/game/spells_monsters.cpp
index a9afb62c83e..cef322261ee 100644
--- a/engines/mm/mm1/game/spells_monsters.cpp
+++ b/engines/mm/mm1/game/spells_monsters.cpp
@@ -112,7 +112,7 @@ void SpellsMonsters::spell03_fire() {
 
 void SpellsMonsters::spell04_blindness() {
 	if (casts()) {
-		add(STRING["monster_spells.blindness"]);
+		add("blindness");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._damage = 2;
@@ -186,9 +186,9 @@ void SpellsMonsters::spell10_lightningBolt() {
 }
 
 void SpellsMonsters::spell11_strangeGas() {
-	add(Common::String::format("%s %s",
+	add(Common::String::format("%s%s",
 		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.strange_gas"].c_str()));
+		"strange gas"));
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
 	g_globals->_spellsState._damage = BAD_CONDITION | STONE;
@@ -263,7 +263,7 @@ void SpellsMonsters::spell16_acidArrow() {
 }
 
 void SpellsMonsters::spell17_elements() {
-	add(STRING["monster_spells.call_elements"]);
+	add("calls upon the elements");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._damage = getRandomNumber(21) + 39;
 
@@ -566,13 +566,13 @@ bool SpellsMonsters::testElementalResistance() {
 void SpellsMonsters::writeDamage() {
 	add(STRING["monster_spells.takes"]);
 	add(Common::String::format("%d ", _damage));
-	add(STRING[_damage > 1 ? "monster_spells.points" : "monster_spells.point"]);
+	add(STRING[_damage > 1 ? "dialogs.combat.points" : "dialogs.combat.point"]);
 	add(' ');
 
 	if (_lines.back()._text.size() >= 30)
 		add('!');
 	else
-		add(STRING["monster_spells.of_damage"]);
+		add(STRING["dialogs.combat.of_damage"]);
 }
 
 void SpellsMonsters::proc9() {


Commit: c3f5ff3b926b69bb2c282368dfacbaf565b15fea
    https://github.com/scummvm/scummvm/commit/c3f5ff3b926b69bb2c282368dfacbaf565b15fea
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:39+11:00

Commit Message:
MM: MM1: Fix monster spell condition effects not applying or showing correctly

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_monsters.cpp


diff --git a/engines/mm/mm1/game/spells_monsters.cpp b/engines/mm/mm1/game/spells_monsters.cpp
index cef322261ee..071c1ca3a6b 100644
--- a/engines/mm/mm1/game/spells_monsters.cpp
+++ b/engines/mm/mm1/game/spells_monsters.cpp
@@ -641,22 +641,23 @@ void SpellsMonsters::writeConditionEffect() {
 	if (!charAffected() || !isEffective() || !testElementalResistance())
 		return;
 
-	if (g_globals->_spellsState._damage == 0) {
+	const byte condition = g_globals->_spellsState._damage;
+	setCondition(condition);
+
+	if (condition == 0) {
 		effectNum = 10;
-	} else if (g_globals->_spellsState._damage & BAD_CONDITION) {
-		if (!(g_globals->_spellsState._damage & (BAD_CONDITION | DEAD)))
-			effectNum = 7;
-		else if (!(g_globals->_spellsState._damage & (BAD_CONDITION | STONE)))
-			effectNum = 8;
-		else if (g_globals->_spellsState._damage == ERADICATED)
-			effectNum = 9;
-		else
-			effectNum = 10;
+	} else if (condition == ERADICATED) {
+		effectNum = 9;
+	} else if (condition == (BAD_CONDITION | DEAD)) {
+		effectNum = 7;
+	} else if (condition == (BAD_CONDITION | STONE)) {
+		effectNum = 8;
 	} else {
+		// Lowest set condition bit selects the message
 		effectNum = 0;
 
-		for (byte bitset = g_globals->_spellsState._damage; bitset & 1;
-			++effectNum, bitset >>= 1) {
+		for (byte bitset = condition; !(bitset & 1);
+				++effectNum, bitset >>= 1) {
 		}
 	}
 


Commit: ed113f1449c37a61ed284b17d43c4f1b548fb077
    https://github.com/scummvm/scummvm/commit/ed113f1449c37a61ed284b17d43c4f1b548fb077
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:39+11:00

Commit Message:
MM: MM1: Fix Power and Quickness applying to the caster instead of the target

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index b649bcd4122..a8b3b1b8a78 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -733,12 +733,12 @@ SpellResult SpellsParty::wizard25_levitate() {
 }
 
 SpellResult SpellsParty::wizard26_power() {
-	g_globals->_currCharacter->_might._current += getRandomNumber(4);
+	_destChar->_might._current += getRandomNumber(4);
 	return SR_SUCCESS_DONE;
 }
 
 SpellResult SpellsParty::wizard27_quickness() {
-	g_globals->_currCharacter->_speed._current += getRandomNumber(4);
+	_destChar->_speed._current += getRandomNumber(4);
 	return SR_SUCCESS_DONE;
 }
 


Commit: c3bdeed9d60eabb77982017c07b631ee5995cb0f
    https://github.com/scummvm/scummvm/commit/c3bdeed9d60eabb77982017c07b631ee5995cb0f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:39+11:00

Commit Message:
MM: MM1: Reset spell state before casting party spells

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index a8b3b1b8a78..bc97d4e7f37 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -139,6 +139,13 @@ SpellResult SpellsParty::cast(uint spell, Character *destChar) {
 	assert(spell < SPELLS_COUNT);
 	_destChar = destChar;
 
+	// Don't carry damage or resistance data between spells
+	SpellsState &ss = g_globals->_spellsState;
+	ss._mmVal1 = 0;
+	ss._resistanceIndex = 0;
+	ss._resistanceTypeOrTargetCount = RESISTANCE_MAGIC;
+	ss._damage = 0;
+
 	return SPELLS[spell]();
 }
 


Commit: a90c1938f706f555d3fd8ee0fc57e7ba6b02e0ca
    https://github.com/scummvm/scummvm/commit/a90c1938f706f555d3fd8ee0fc57e7ba6b02e0ca
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:39+11:00

Commit Message:
MM: MM1: Fix Crazed Wizard cave challenges and save progress

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map.cpp
    engines/mm/mm1/maps/map06.cpp
    engines/mm/mm1/maps/map06.h
    engines/mm/mm1/maps/maps.cpp
    engines/mm/mm1/views_enh/map.cpp


diff --git a/engines/mm/mm1/maps/map.cpp b/engines/mm/mm1/maps/map.cpp
index 9ec4db90b9d..9cbccc1087f 100644
--- a/engines/mm/mm1/maps/map.cpp
+++ b/engines/mm/mm1/maps/map.cpp
@@ -131,16 +131,19 @@ void Map::unlockDoor() {
 }
 
 void Map::visitedSpecial() {
-	if (!_visited[g_maps->_mapOffset])
-		_visited[g_maps->_mapOffset] = VISITED_SPECIAL;
+	byte &visited = _visited[g_maps->_mapOffset];
+	if (!(visited & 0x7f))
+		visited = (visited & 0x80) | VISITED_SPECIAL;
 }
 
 void Map::visitedExit() {
-	_visited[g_maps->_mapOffset] = VISITED_EXIT;
+	byte &visited = _visited[g_maps->_mapOffset];
+	visited = (visited & 0x80) | VISITED_EXIT;
 }
 
 void Map::visitedBusiness() {
-	_visited[g_maps->_mapOffset] = VISITED_BUSINESS;
+	byte &visited = _visited[g_maps->_mapOffset];
+	visited = (visited & 0x80) | VISITED_BUSINESS;
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map06.cpp b/engines/mm/mm1/maps/map06.cpp
index 735eeb59972..accb9f31f0d 100644
--- a/engines/mm/mm1/maps/map06.cpp
+++ b/engines/mm/mm1/maps/map06.cpp
@@ -31,7 +31,7 @@ namespace Maps {
 
 #define VAL1 446
 #define VAL2 447
-#define VAL3 329
+#define ENCOUNTER_COMPLETE 0x80
 
 static const byte MONSTER_ID1[13] = {
 	2, 1, 8, 9, 5, 9, 2, 7, 15, 4, 11, 1, 5
@@ -177,7 +177,15 @@ void Map06::special10() {
 void Map06::special13() {
 	Game::Encounter &enc = g_globals->_encounters;
 	g_maps->clearSpecial();
-	_data[VAL3]++;
+
+	// Record completion in the map visitation data, which is serialized
+	// in savegames, so progress survives saving and loading
+	byte &visited = _visited[g_maps->_mapOffset];
+	if (visited & ENCOUNTER_COMPLETE) {
+		checkPartyDead();
+		return;
+	}
+	visited |= ENCOUNTER_COMPLETE;
 
 	int monsterCount = getRandomNumber(5);
 
@@ -189,20 +197,45 @@ void Map06::special13() {
 	for (int i = 0; i < monsterCount; ++i)
 		enc.addMonster(id1, id2);
 
+	// Without manual mode, execute() discards the scripted monsters
+	// and generates an unrelated random encounter
+	enc._manual = true;
 	enc.execute();
 }
 
 void Map06::special26() {
-	if (_data[VAL3]) {
+	if (completedEncounters() < 13) {
 		send(SoundMessage(STRING["maps.map06.wizard"]));
 	} else {
 		g_globals->_treasure.setGold(12000);
 		g_globals->_treasure._items[1] = BRONZE_KEY_ID;
 		g_globals->_treasure._items[2] = getRandomNumber(12) + 182;
+		clearEncounterProgress();
 		g_events->addAction(KEYBIND_SEARCH);
 	}
 }
 
+int Map06::completedEncounters() const {
+	int result = 0;
+
+	// The thirteen challenges occupy map rows 0 through 12
+	for (int y = 0; y < 13; ++y) {
+		for (int x = 0; x < MAP_W; ++x) {
+			if (_visited[y * MAP_W + x] & ENCOUNTER_COMPLETE)
+				++result;
+		}
+	}
+
+	return result;
+}
+
+void Map06::clearEncounterProgress() {
+	for (int y = 0; y < 13; ++y) {
+		for (int x = 0; x < MAP_W; ++x)
+			_visited[y * MAP_W + x] &= ~ENCOUNTER_COMPLETE;
+	}
+}
+
 void Map06::slide() {
 	_data[VAL2]++;
 
diff --git a/engines/mm/mm1/maps/map06.h b/engines/mm/mm1/maps/map06.h
index 4967e48cb27..0b108fcfca7 100644
--- a/engines/mm/mm1/maps/map06.h
+++ b/engines/mm/mm1/maps/map06.h
@@ -43,6 +43,7 @@ private:
 	void special13();
 	void special26();
 	void slide();
+	void clearEncounterProgress();
 
 	const SpecialFn SPECIAL_FN[27] = {
 		&Map06::special00,
@@ -76,6 +77,8 @@ private:
 public:
 	Map06() : Map(6, "cave2", 0x1, 1) {}
 
+	int completedEncounters() const;
+
 	/**
 	 * Handles all special stuff that happens on the map
 	 */
diff --git a/engines/mm/mm1/maps/maps.cpp b/engines/mm/mm1/maps/maps.cpp
index 67feeee443a..71179c234dd 100644
--- a/engines/mm/mm1/maps/maps.cpp
+++ b/engines/mm/mm1/maps/maps.cpp
@@ -274,8 +274,8 @@ void Maps::loadTown(TownId townId) {
 	}
 
 	byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x];
-	if (!visited)
-		visited = VISITED_NORMAL;
+	if (!(visited & 0x7f))
+		visited = (visited & 0x80) | VISITED_NORMAL;
 }
 
 void Maps::town15setup() {
@@ -384,8 +384,8 @@ void Maps::turnAround() {
 void Maps::step(const Common::Point &delta) {
 	_mapPos += delta;
 	byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x];
-	if (!visited) {
-		visited = VISITED_NORMAL;
+	if (!(visited & 0x7f)) {
+		visited = (visited & 0x80) | VISITED_NORMAL;
 	}
 
 	int section = 0, id = 0;
@@ -424,8 +424,8 @@ void Maps::changeMap(uint16 id, byte section) {
 
 void Maps::visitedTile() {
 	byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x];
-	if (!visited)
-		visited = VISITED_NORMAL;
+	if (!(visited & 0x7f))
+		visited = (visited & 0x80) | VISITED_NORMAL;
 }
 
 void Maps::clearSpecial() {
diff --git a/engines/mm/mm1/views_enh/map.cpp b/engines/mm/mm1/views_enh/map.cpp
index c116f25ae02..88a9533155c 100644
--- a/engines/mm/mm1/views_enh/map.cpp
+++ b/engines/mm/mm1/views_enh/map.cpp
@@ -54,7 +54,8 @@ void Map::draw() {
 			mazeY >= yStart; yp += MAP_TILE_H, --mazeY) {
 		for (int xp = MAP_BORDER_SIZE, mazeX = xStart;
 				mazeX < (xStart + totalX); xp += MAP_TILE_W, ++mazeX) {
-			byte visited = map._visited[mazeY * MAP_W + mazeX];
+			// The high bit may contain map-specific persistent state
+			byte visited = map._visited[mazeY * MAP_W + mazeX] & 0x0f;
 
 			if (!visited) {
 				g_globals->_tileSprites.draw(&s, 1,


Commit: 3fc7d9848a341b75c981d60acc99ecb5a09859f3
    https://github.com/scummvm/scummvm/commit/3fc7d9848a341b75c981d60acc99ecb5a09859f3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:39+11:00

Commit Message:
MM: MM1: Close gold/gem-only loot in the Search window with one dismissal

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/search.cpp


diff --git a/engines/mm/mm1/views_enh/search.cpp b/engines/mm/mm1/views_enh/search.cpp
index 587429a50c2..8f52139737d 100644
--- a/engines/mm/mm1/views_enh/search.cpp
+++ b/engines/mm/mm1/views_enh/search.cpp
@@ -201,8 +201,15 @@ void Search::timeout() {
 		break;
 
 	case GET_TREASURE:
-		setMode(GET_ITEMS);
-		draw();
+		// Gold and gems are already awarded by drawTreasure(). If there are
+		// no item slots to show, close now rather than entering
+		// GET_ITEMS_DONE and requiring a second dismissal.
+		if (g_globals->_treasure.hasItems()) {
+			setMode(GET_ITEMS);
+			draw();
+		} else {
+			close();
+		}
 		break;
 
 	case GET_ITEMS:
@@ -387,7 +394,10 @@ void Search::drawTreasure() {
 	}
 
 	Sound::sound2(SOUND_5);
-	delaySeconds(2);
+
+	// Gold/gem-only loot stays visible for longer, unless the player
+	// dismisses it early with a keypress or mouse click
+	delaySeconds(g_globals->_treasure.hasItems() ? 2 : 7);
 }
 
 void Search::drawItem() {


Commit: 3d7d58c7736eff74161ce28d2a6f22f1a697edb9
    https://github.com/scummvm/scummvm/commit/3d7d58c7736eff74161ce28d2a6f22f1a697edb9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:40+11:00

Commit Message:
MM: MM1: Fix blank Detect Magic/Trap result in the enhanced Search window

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/search.cpp
    engines/mm/mm1/views_enh/search.h


diff --git a/engines/mm/mm1/views_enh/search.cpp b/engines/mm/mm1/views_enh/search.cpp
index 8f52139737d..7c7ac007b2f 100644
--- a/engines/mm/mm1/views_enh/search.cpp
+++ b/engines/mm/mm1/views_enh/search.cpp
@@ -95,6 +95,10 @@ void Search::draw() {
 		writeString(96, 32, STRING["enhdialogs.misc.go_back"]);
 		break;
 
+	case RESPONSE:
+		writeLine(_responseLine, _responseText, ALIGN_MIDDLE);
+		break;
+
 	case GET_TREASURE:
 		drawTreasure();
 		break;
@@ -280,14 +284,14 @@ void Search::detectMagicTrap2() {
 
 	if (c._class == PALADIN || c._class == CLERIC) {
 		Sound::sound(SOUND_2);
-		ScrollView::draw();
-		writeLine(2, STRING["dialogs.search.bad_class"], ALIGN_MIDDLE);
+		_responseLine = 2;
+		_responseText = STRING["dialogs.search.bad_class"];
 		delaySeconds(4);
 
 	} else if (c._sp == 0) {
 		Sound::sound(SOUND_2);
-		ScrollView::draw();
-		writeLine(2, STRING["dialogs.search.no_sp"], ALIGN_MIDDLE);
+		_responseLine = 2;
+		_responseText = STRING["dialogs.search.no_sp"];
 		delaySeconds(4);
 
 	} else {
@@ -296,12 +300,14 @@ void Search::detectMagicTrap2() {
 			g_globals->_treasure.getGems() ? 'Y' : 'N';
 		char trapped = g_globals->_treasure._trapType == 1 ? 'Y' : 'N';
 
-		ScrollView::draw();
-		writeLine(1, Common::String::format(
+		_responseLine = 1;
+		_responseText = Common::String::format(
 			STRING["dialogs.search.magic_trap"].c_str(),
-			magic, trapped), ALIGN_MIDDLE);
+			magic, trapped);
 		delaySeconds(8);
 	}
+
+	redraw();
 }
 
 bool Search::whoWillTry() {
diff --git a/engines/mm/mm1/views_enh/search.h b/engines/mm/mm1/views_enh/search.h
index a691209fcdb..c4e69a57728 100644
--- a/engines/mm/mm1/views_enh/search.h
+++ b/engines/mm/mm1/views_enh/search.h
@@ -41,6 +41,8 @@ private:
 	OptionMode _optionMode = OMODE_OPEN;
 	byte _val1 = 0;
 	int _lineNum = 0;
+	Common::String _responseText;
+	int _responseLine = 1;
 	Shared::Xeen::SpriteResource _escSprite;
 
 	void setMode(Mode mode);


Commit: edf3719f90c359a166cfdb8ca79e911fbe46b420
    https://github.com/scummvm/scummvm/commit/edf3719f90c359a166cfdb8ca79e911fbe46b420
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:40+11:00

Commit Message:
MM: MM1: Keep treasure items when every party backpack is full

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/search.cpp


diff --git a/engines/mm/mm1/views_enh/search.cpp b/engines/mm/mm1/views_enh/search.cpp
index 7c7ac007b2f..8c95df5dedd 100644
--- a/engines/mm/mm1/views_enh/search.cpp
+++ b/engines/mm/mm1/views_enh/search.cpp
@@ -408,42 +408,49 @@ void Search::drawTreasure() {
 
 void Search::drawItem() {
 	Treasure &treasure = g_globals->_treasure;
-	int itemId = treasure.removeItem();
 	_lineNum = 0;
 
-	// Iterate through any treasure items
-	if (itemId != 0) {
-		// Find a person with free backpack space for the item
+	// Only remove the treasure item once somebody can carry it,
+	// otherwise it's lost when every backpack is full
+	if (treasure.hasItems()) {
+		Character *recipient = nullptr;
+
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
 			Character &c = g_globals->_party[i];
+			if (!c._backpack.full()) {
+				recipient = &c;
+				break;
+			}
+		}
 
-			// Check if character has backpack space
-			if (c._backpack.full())
-				continue;
+		if (!recipient) {
+			// Leave the item so it can be collected by Searching again
+			// once a backpack slot has been freed
+			writeLine(_lineNum++, "Party inventory is full!", ALIGN_MIDDLE);
+			setMode(GET_ITEMS_DONE);
+			delaySeconds(7);
+			return;
+		}
 
+		const int itemId = treasure.removeItem();
+		if (itemId != 0) {
 			Item *item = g_globals->_items.getItem(itemId);
-			c._backpack.add(itemId, item->_maxCharges);
+			recipient->_backpack.add(itemId, item->_maxCharges);
 
-			// Add line for found item
 			writeLine(_lineNum++, Common::String::format(
 				STRING["dialogs.search.found_item"].c_str(),
-				c._name,
+				recipient->_name,
 				item->_name.c_str()
 			));
 
 			if (treasure.hasItems()) {
 				delaySeconds(2);
 				return;
-			} else {
-				// Finished final item, so go down below to switch to items done
-				break;
 			}
 		}
 	}
 
-	// At this point we've either displayed the up to 3 item
-	// lines (in addition to gold and/or gems), or the party's backpacks
-	// were completely full up. Wait for 7 seconds and close the view
+	// Finished the final item
 	setMode(GET_ITEMS_DONE);
 	delaySeconds(7);
 }


Commit: a2588f0499cdb043b5b9c636bf526fcc3b81476b
    https://github.com/scummvm/scummvm/commit/a2588f0499cdb043b5b9c636bf526fcc3b81476b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:40+11:00

Commit Message:
MM: MM1: Fix Gems button in the enhanced Trade screen

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/trade.cpp


diff --git a/engines/mm/mm1/views_enh/trade.cpp b/engines/mm/mm1/views_enh/trade.cpp
index 3edd0df3666..3200924d408 100644
--- a/engines/mm/mm1/views_enh/trade.cpp
+++ b/engines/mm/mm1/views_enh/trade.cpp
@@ -112,7 +112,7 @@ void Trade::setMode(TradeMode mode) {
 	switch (_mode) {
 	case TRADE_OPTIONS:
 		addButton(&_btnIcons, Common::Point(80, 0), 32, Common::KEYCODE_g);
-		addButton(&_btnIcons, Common::Point(158, 0), 34, Common::KEYCODE_d);
+		addButton(&_btnIcons, Common::Point(158, 0), 34, Common::KEYCODE_m);
 		addButton(&_btnIcons, Common::Point(80, 20), 36, Common::KEYCODE_f);
 		addButton(&_btnIcons, Common::Point(158, 20), 26, Common::KEYCODE_i);
 		addButton(&g_globals->_escSprites, Common::Point(0, 20), 0, KEYBIND_ESCAPE);


Commit: 06da55563922ccd8811c307d496ab030b09a13b1
    https://github.com/scummvm/scummvm/commit/06da55563922ccd8811c307d496ab030b09a13b1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:40+11:00

Commit Message:
MM: MM1: Let the desert message finish before random encounters start

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map_desert.cpp


diff --git a/engines/mm/mm1/maps/map_desert.cpp b/engines/mm/mm1/maps/map_desert.cpp
index 503b6865f3a..d15f3fba68d 100644
--- a/engines/mm/mm1/maps/map_desert.cpp
+++ b/engines/mm/mm1/maps/map_desert.cpp
@@ -96,7 +96,10 @@ void MapDesert::desert() {
 
 		case 199:
 		case 200:
-			g_globals->_encounters.execute();
+			msg._callback = []() {
+				g_globals->_encounters.execute();
+			};
+			msg._delaySeconds = 3;
 			break;
 
 		default:


Commit: 026bee5facba626af32d10937c6a8072b9f84660
    https://github.com/scummvm/scummvm/commit/026bee5facba626af32d10937c6a8072b9f84660
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:41+11:00

Commit Message:
MM: MM1: Fix enhanced Teleport window not closing on Escape

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/spells/teleport.cpp


diff --git a/engines/mm/mm1/views_enh/spells/teleport.cpp b/engines/mm/mm1/views_enh/spells/teleport.cpp
index 28423249d9f..83c1602e761 100644
--- a/engines/mm/mm1/views_enh/spells/teleport.cpp
+++ b/engines/mm/mm1/views_enh/spells/teleport.cpp
@@ -79,13 +79,13 @@ bool Teleport::msgKeypress(const KeypressMessage &msg) {
 		redraw();
 	}
 
-	return true;
+	return false;
 }
 
 bool Teleport::msgAction(const ActionMessage &msg) {
 	switch (msg._action) {
 	case KEYBIND_ESCAPE:
-		close();
+		g_events->replaceView("Game", true);
 		return true;
 
 	case KEYBIND_SELECT:
@@ -104,7 +104,7 @@ void Teleport::teleport() {
 	Maps::Maps &maps = *g_maps;
 	Maps::Map &map = *maps._currentMap;
 
-	close();
+	g_events->replaceView("Game", true);
 	if (map[Maps::MAP_FLAGS] & 2) {
 		g_events->send(SoundMessage(STRING["spells.failed"]));
 


Commit: 0dfcb8581a4afe258c1ed9eb0b641ecb8884253f
    https://github.com/scummvm/scummvm/commit/0dfcb8581a4afe258c1ed9eb0b641ecb8884253f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:41+11:00

Commit Message:
MM: MM1: Fix keyboard controls after returning to the launcher

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/metaengine.cpp


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index b16b0399a9b..966c7d18fcf 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -165,18 +165,13 @@ static const KeysRecord ALL_RECORDS[] = {
 static void initKeymapPointers() {
 	Common::Keymapper *const mapper = g_engine->getEventManager()->getKeymapper();
 
-	if (!g_menuKeymap)
-		g_menuKeymap = mapper->getKeymap(kMenuKeymapId);
-	if (!g_minimalKeymap)
-		g_minimalKeymap = mapper->getKeymap(kMinimalKeymapId);
-	if (!g_partyKeymap)
-		g_partyKeymap = mapper->getKeymap(kPartyKeymapId);
-	if (!g_normalKeymap)
-		g_normalKeymap = mapper->getKeymap(kNormalKeymapId);
-	if (!g_combatKeymap)
-		g_combatKeymap = mapper->getKeymap(kCombatKeymapId);
-	if (!g_cheatsKeymap)
-		g_cheatsKeymap = mapper->getKeymap(kCheatsKeymapId);
+	// Always refetch, since keymaps are recreated each time the engine starts
+	g_menuKeymap = mapper->getKeymap(kMenuKeymapId);
+	g_minimalKeymap = mapper->getKeymap(kMinimalKeymapId);
+	g_partyKeymap = mapper->getKeymap(kPartyKeymapId);
+	g_normalKeymap = mapper->getKeymap(kNormalKeymapId);
+	g_combatKeymap = mapper->getKeymap(kCombatKeymapId);
+	g_cheatsKeymap = mapper->getKeymap(kCheatsKeymapId);
 }
 
 Common::KeymapArray MetaEngine::initKeymaps() {


Commit: 54040cf153646e33ce4b61b568720804af9913f7
    https://github.com/scummvm/scummvm/commit/54040cf153646e33ce4b61b568720804af9913f7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:41+11:00

Commit Message:
MM: MM1: Use F1-F6 for party member selection

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/metaengine.cpp
    engines/mm/mm1/views/character_info.cpp
    engines/mm/mm1/views/character_view_combat.cpp
    engines/mm/mm1/views/combat.cpp
    engines/mm/mm1/views/interactions/giant.cpp
    engines/mm/mm1/views/interactions/gypsy.cpp
    engines/mm/mm1/views/interactions/resistances.cpp
    engines/mm/mm1/views/quick_ref.cpp
    engines/mm/mm1/views/spells/cast_spell.cpp
    engines/mm/mm1/views_enh/combat.cpp
    engines/mm/mm1/views_enh/interactions/giant.cpp
    engines/mm/mm1/views_enh/interactions/gypsy.cpp
    engines/mm/mm1/views_enh/interactions/resistances.cpp
    engines/mm/mm1/views_enh/items_view.cpp
    engines/mm/mm1/views_enh/who_will_try.cpp


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index 966c7d18fcf..799f19205a9 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -56,12 +56,12 @@ static const KeybindingRecord MENU_KEYS[] = {
 };
 
 static const KeybindingRecord PARTY_KEYS[] = {
-	{ KEYBIND_VIEW_PARTY1, "PARTY1", _s("View party member 1"), "1", nullptr },
-	{ KEYBIND_VIEW_PARTY2, "PARTY2", _s("View party member 2"), "2", nullptr },
-	{ KEYBIND_VIEW_PARTY3, "PARTY3", _s("View party member 3"), "3", nullptr },
-	{ KEYBIND_VIEW_PARTY4, "PARTY4", _s("View party member 4"), "4", nullptr },
-	{ KEYBIND_VIEW_PARTY5, "PARTY5", _s("View party member 5"), "5", nullptr },
-	{ KEYBIND_VIEW_PARTY6, "PARTY6", _s("View party member 6"), "6", nullptr },
+	{ KEYBIND_VIEW_PARTY1, "PARTY1", _s("View party member 1"), "F1", nullptr },
+	{ KEYBIND_VIEW_PARTY2, "PARTY2", _s("View party member 2"), "F2", nullptr },
+	{ KEYBIND_VIEW_PARTY3, "PARTY3", _s("View party member 3"), "F3", nullptr },
+	{ KEYBIND_VIEW_PARTY4, "PARTY4", _s("View party member 4"), "F4", nullptr },
+	{ KEYBIND_VIEW_PARTY5, "PARTY5", _s("View party member 5"), "F5", nullptr },
+	{ KEYBIND_VIEW_PARTY6, "PARTY6", _s("View party member 6"), "F6", nullptr },
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
 };
 
diff --git a/engines/mm/mm1/views/character_info.cpp b/engines/mm/mm1/views/character_info.cpp
index 9d6e196799a..7fed07f0e6a 100644
--- a/engines/mm/mm1/views/character_info.cpp
+++ b/engines/mm/mm1/views/character_info.cpp
@@ -75,15 +75,13 @@ void CharacterInfo::draw() {
 			_textPos.x = (i % 2) == 0 ? 1 : 22;
 			_textPos.y = 15 + (i / 2);
 			writeChar(c._condition ? '*' : ' ');
-			writeChar('1' + i);
-			writeString(") ");
+			writeString(Common::String::format("F%d) ", (int)i + 1));
 			writeString(c._name);
 		}
 
 		writeString(10, 20, Common::String::format(
-			STRING["dialogs.character.trade_with"].c_str(),
-			(int)g_globals->_party.size()
-		));
+			"Trade with character (F1-F%d)",
+			(int)g_globals->_party.size()));
 		break;
 
 	case TRADE_KIND:
diff --git a/engines/mm/mm1/views/character_view_combat.cpp b/engines/mm/mm1/views/character_view_combat.cpp
index 9d4bcae3d8a..108b318dcd9 100644
--- a/engines/mm/mm1/views/character_view_combat.cpp
+++ b/engines/mm/mm1/views/character_view_combat.cpp
@@ -40,8 +40,8 @@ bool CharacterViewCombat::msgUnfocus(const UnfocusMessage &msg) {
 void CharacterViewCombat::draw() {
 	CharacterBase::draw();
 
-	writeString(12, 22, Common::String::format("'1'-'%c' %s",
-		'0' + g_globals->_combatParty.size(), STRING["dialogs.quick_ref.to_view"].c_str()));
+	writeString(12, 22, Common::String::format("F1-F%d %s",
+		(int)g_globals->_combatParty.size(), STRING["dialogs.quick_ref.to_view"].c_str()));
 	escToGoBack();
 }
 
diff --git a/engines/mm/mm1/views/combat.cpp b/engines/mm/mm1/views/combat.cpp
index 1240991a755..73f4ca29ee8 100644
--- a/engines/mm/mm1/views/combat.cpp
+++ b/engines/mm/mm1/views/combat.cpp
@@ -425,8 +425,8 @@ void Combat::writeDelaySelect() {
 void Combat::writeExchangeSelect() {
 	resetBottom();
 	writeString(7, 20, Common::String::format(
-		STRING["dialogs.combat.exchange_places"].c_str(),
-		'0' + g_globals->_combatParty.size()));
+		"Exchange places with (F1-F%d)",
+		(int)g_globals->_combatParty.size()));
 	escToGoBack(12, 23);
 }
 
diff --git a/engines/mm/mm1/views/interactions/giant.cpp b/engines/mm/mm1/views/interactions/giant.cpp
index 2ae907ce8e2..a1817b07a8b 100644
--- a/engines/mm/mm1/views/interactions/giant.cpp
+++ b/engines/mm/mm1/views/interactions/giant.cpp
@@ -45,7 +45,8 @@ bool Giant::msgUnfocus(const UnfocusMessage &msg) {
 
 void Giant::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map30.giant"]);
+	writeString(0, 1, "Statue of a giant holding the Scale of\n"
+		"Judgement. Who will climb on (F1-F6)?");
 }
 
 bool Giant::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/interactions/gypsy.cpp b/engines/mm/mm1/views/interactions/gypsy.cpp
index 61e03aa6303..8ee6a43bdbf 100644
--- a/engines/mm/mm1/views/interactions/gypsy.cpp
+++ b/engines/mm/mm1/views/interactions/gypsy.cpp
@@ -70,7 +70,7 @@ void Gypsy::modeChanged(bool allowSelection) {
 void Gypsy::draw() {
 	clearSurface();
 	if (!_character) {
-		writeString(0, 1, STRING["maps.map23.gypsy"]);
+		writeString(0, 1, "A toothless Gypsy seer asks:\nWho would like to hear their sign (F1-F6)?");
 
 	} else {
 		Common::String line = Common::String::format(
diff --git a/engines/mm/mm1/views/interactions/resistances.cpp b/engines/mm/mm1/views/interactions/resistances.cpp
index 4e90b480bc6..8d3d43f3220 100644
--- a/engines/mm/mm1/views/interactions/resistances.cpp
+++ b/engines/mm/mm1/views/interactions/resistances.cpp
@@ -48,7 +48,7 @@ void Resistances::draw() {
 	Sound::sound(SOUND_2);
 
 	if (!_charSelected) {
-		writeString(0, 1, STRING["maps.map02.morango"]);
+		writeString(0, 1, "Morango the Mystic asks:\nWho shall I measure (F1-F6)?");
 
 	} else {
 		Character &c = *g_globals->_currCharacter;
diff --git a/engines/mm/mm1/views/quick_ref.cpp b/engines/mm/mm1/views/quick_ref.cpp
index 031e157490e..2cbcae87076 100644
--- a/engines/mm/mm1/views/quick_ref.cpp
+++ b/engines/mm/mm1/views/quick_ref.cpp
@@ -86,9 +86,8 @@ void QuickRef::draw() {
 		printCondition();
 	}
 
-	writeString(12, 21, "'1'-'");
-	writeNumber(g_globals->_party.size());
-	writeString("' ");
+	writeString(12, 21, Common::String::format("F1-F%d ",
+		(int)g_globals->_party.size()));
 	writeString(STRING["dialogs.quick_ref.to_view"]);
 
 	escToGoBack();
diff --git a/engines/mm/mm1/views/spells/cast_spell.cpp b/engines/mm/mm1/views/spells/cast_spell.cpp
index 7b76203e934..f019d6e2a73 100644
--- a/engines/mm/mm1/views/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views/spells/cast_spell.cpp
@@ -125,9 +125,8 @@ void CastSpell::draw() {
 
 	case SELECT_CHAR:
 		writeString(22, 3, Common::String::format(
-			STRING["spells.cast_on_char"].c_str(),
-			(int)g_globals->_party.size()
-		));
+			"Cast on character (F1-F%d)",
+			(int)g_globals->_party.size()));
 		break;
 
 	case PRESS_ENTER:
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 9a13dc134ce..6afa934b5d0 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -493,8 +493,8 @@ void Combat::writeDelaySelect() {
 
 void Combat::writeExchangeSelect() {
 	writeBottomText(0, 1, Common::String::format(
-		STRING["dialogs.combat.exchange_places"].c_str(),
-		'0' + g_globals->_combatParty.size()),
+		"Exchange places with (F1-F%d)",
+		(int)g_globals->_combatParty.size()),
 		ALIGN_MIDDLE);
 }
 
diff --git a/engines/mm/mm1/views_enh/interactions/giant.cpp b/engines/mm/mm1/views_enh/interactions/giant.cpp
index 53d899a739e..756adbed44f 100644
--- a/engines/mm/mm1/views_enh/interactions/giant.cpp
+++ b/engines/mm/mm1/views_enh/interactions/giant.cpp
@@ -39,7 +39,8 @@ bool Giant::msgFocus(const FocusMessage &msg) {
 	PartyView::msgFocus(msg);
 
 	_charSelected = false;
-	addText(STRING["maps.map30.giant"]);
+	addText("Statue of a giant holding the Scale of Judgement. "
+		"Who will climb on (F1-F6)?");
 	return true;
 }
 
diff --git a/engines/mm/mm1/views_enh/interactions/gypsy.cpp b/engines/mm/mm1/views_enh/interactions/gypsy.cpp
index 7ad16fa2b85..f011a9051ee 100644
--- a/engines/mm/mm1/views_enh/interactions/gypsy.cpp
+++ b/engines/mm/mm1/views_enh/interactions/gypsy.cpp
@@ -36,7 +36,7 @@ bool Gypsy::msgFocus(const FocusMessage &msg) {
 	PartyView::msgFocus(msg);
 
 	_charSelected = false;
-	addText(STRING["maps.map23.gypsy"]);
+	addText("A toothless Gypsy seer asks:\nWho would like to hear their sign (F1-F6)?");
 	return true;
 }
 
diff --git a/engines/mm/mm1/views_enh/interactions/resistances.cpp b/engines/mm/mm1/views_enh/interactions/resistances.cpp
index 8176d719d82..b2e15f75875 100644
--- a/engines/mm/mm1/views_enh/interactions/resistances.cpp
+++ b/engines/mm/mm1/views_enh/interactions/resistances.cpp
@@ -35,7 +35,7 @@ Resistances::Resistances() : Interaction("Resistances", 4) {
 bool Resistances::msgFocus(const FocusMessage &msg) {
 	PartyView::msgFocus(msg);
 
-	addText(STRING["maps.map02.morango"]);
+	addText("Morango the Mystic asks:\nWho shall I measure (F1-F6)?");
 	return true;
 }
 
diff --git a/engines/mm/mm1/views_enh/items_view.cpp b/engines/mm/mm1/views_enh/items_view.cpp
index a3e9e56ab5c..2d0d70ac4b6 100644
--- a/engines/mm/mm1/views_enh/items_view.cpp
+++ b/engines/mm/mm1/views_enh/items_view.cpp
@@ -51,9 +51,7 @@ void ItemsView::addButton(int frame, const Common::String &text,
 bool ItemsView::msgFocus(const FocusMessage &msg) {
 	PartyView::msgFocus(msg);
 
-	// Disable the normal '1' to '6' character selection keybindings,
-	// since we're using them in this dialog for item selection
-	MetaEngine::setKeybindingMode(KeybindingMode::KBMODE_MENUS);
+	// F1-F6 switch party members while 1-6 remain available for items
 
 	_selectedItem = -1;
 	return true;
diff --git a/engines/mm/mm1/views_enh/who_will_try.cpp b/engines/mm/mm1/views_enh/who_will_try.cpp
index 9c988b58cb9..7d33c5fa161 100644
--- a/engines/mm/mm1/views_enh/who_will_try.cpp
+++ b/engines/mm/mm1/views_enh/who_will_try.cpp
@@ -62,8 +62,7 @@ void WhoWillTry::draw() {
 	PartyView::draw();
 
 	writeString(0, 0, Common::String::format(
-		STRING["dialogs.misc.who_will_try"].c_str(),
-		'0' + g_globals->_party.size()));
+		"Who will try?\nF1-F%d", (int)g_globals->_party.size()));
 }
 
 bool WhoWillTry::msgAction(const ActionMessage &msg) {


Commit: f042034d91457095076d9ac062d55c9a488f90b6
    https://github.com/scummvm/scummvm/commit/f042034d91457095076d9ac062d55c9a488f90b6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:41+11:00

Commit Message:
MM: MM1: Start with the enhanced minimap enabled and toggle it with F9

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/globals.h
    engines/mm/mm1/metaengine.cpp


diff --git a/engines/mm/mm1/globals.h b/engines/mm/mm1/globals.h
index 9c47d491c98..706f587f76b 100644
--- a/engines/mm/mm1/globals.h
+++ b/engines/mm/mm1/globals.h
@@ -59,7 +59,7 @@ public:
 	Treasure _treasure;
 	byte _delay = 5;
 	int _nonCombatEffectCtr = 0, _combatEffectCtr = 0;
-	bool _minimapOn = false;
+	bool _minimapOn = true;
 	Common::Array<SuggestedName> *_suggestedNames = nullptr;
 
 	// Console flags
diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index 799f19205a9..d8ed272fe74 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -106,7 +106,7 @@ static const KeybindingRecord NORMAL_KEYS[] = {
 
 	{ KEYBIND_SPELL, "CAST", _s("Cast spell"), "c", nullptr },
 	{ KEYBIND_MAP, "MAP", _s("Show map"), "m", nullptr },
-	{ KEYBIND_MINIMAP, "MINIMAP", _s("Toggle minimap"), "=", nullptr },
+	{ KEYBIND_MINIMAP, "MINIMAP", _s("Toggle minimap"), "F9", nullptr },
 	{ KEYBIND_ORDER, "ORDER", _s("Reorder party"), "o", nullptr },
 	// I18N: Action of hero party in Might & Magic 1
 	{ KEYBIND_PROTECT, "PROTECT", _s("Protect"), "p", nullptr },


Commit: ba032c4c7ad9d1342e9c02b77da2edf521f9adaa
    https://github.com/scummvm/scummvm/commit/ba032c4c7ad9d1342e9c02b77da2edf521f9adaa
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:42+11:00

Commit Message:
MM: MM1: Add player-defined favorite shortcuts to the enhanced spellbook

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/spells/spellbook.cpp
    engines/mm/mm1/views_enh/spells/spellbook.h


diff --git a/engines/mm/mm1/views_enh/spells/spellbook.cpp b/engines/mm/mm1/views_enh/spells/spellbook.cpp
index 84e518272eb..5bafa4f4be4 100644
--- a/engines/mm/mm1/views_enh/spells/spellbook.cpp
+++ b/engines/mm/mm1/views_enh/spells/spellbook.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/spells/spellbook.h"
+#include "common/config-manager.h"
 #include "mm/mm1/game/spells_party.h"
 #include "mm/mm1/globals.h"
 
@@ -112,21 +113,24 @@ void Spellbook::draw() {
 		const int yp = 15 + 9 * i;
 		writeString(0, yp, Common::String::format("%c", (i == 9) ? '0' : '1' + i));
 
-		const int spellIndex = _topIndex + i;
+		const int displayIndex = _topIndex + i;
 
 		if (_count == 0) {
 			if (i == 0) {
 				setTextColor(37);
 				writeString(12, yp, STRING["enhdialogs.spellbook.non_caster"]);
 			}
-		} else if (spellIndex < _count) {
+		} else if (displayIndex < _count) {
+			const int spellIndex = _spellOrder[displayIndex];
+
 			// Spell requirements
 			int lvl, num;
 			getSpellLevelNum(CATEGORY_SPELLS_COUNT * (_isWizard ? 1 : 0) + spellIndex, lvl, num);
 			setSpell(g_globals->_currCharacter, lvl, num);
 
-			setTextColor((spellIndex == _selectedIndex) ? 15 :
-				(canCast() ? 37 : 1));
+			const int textColor = (displayIndex == _selectedIndex) ? 15 :
+				(canCast() ? 37 : 1);
+			setTextColor(textColor);
 
 			// Spell name and requirements
 			Common::String spellName = STRING[Common::String::format(
@@ -134,7 +138,19 @@ void Spellbook::draw() {
 				_isWizard ? "wizard" : "cleric",
 				spellIndex
 			)];
-			writeString(12, yp, spellName);
+
+			// Mark the spell being assigned, and existing favorites
+			if (_favoriteAssignMode && spellIndex == _favoriteAssignSpell) {
+				setTextColor(15);
+				writeString(7, yp, "X");
+				setTextColor(textColor);
+			} else if (isFavorite(spellIndex)) {
+				setTextColor(15);
+				writeString(7, yp, "*");
+				setTextColor(textColor);
+			}
+
+			writeString(14, yp, spellName);
 			writeString(152, yp, Common::String::format("%d/%d",
 				_requiredSp, _requiredGems), ALIGN_RIGHT);
 		}
@@ -143,12 +159,24 @@ void Spellbook::draw() {
 
 bool Spellbook::msgKeypress(const KeypressMessage &msg) {
 	if (msg.keycode >= Common::KEYCODE_0 && msg.keycode <= Common::KEYCODE_9) {
-		int newIndex = _topIndex + (msg.keycode == Common::KEYCODE_0 ?
-			9 : msg.keycode - Common::KEYCODE_1);
-		if (newIndex < _count) {
-			_selectedIndex = newIndex;
-			redraw();
+		const int shortcut = (msg.keycode == Common::KEYCODE_0) ?
+			9 : msg.keycode - Common::KEYCODE_1;
+
+		if (_favoriteAssignMode) {
+			assignFavoriteSlot(shortcut);
+		} else {
+			const int newIndex = _topIndex + shortcut;
+			if (newIndex < _count) {
+				_selectedIndex = newIndex;
+				redraw();
+			}
 		}
+	} else if (msg.keycode == Common::KEYCODE_x) {
+		if (_favoriteAssignMode)
+			removeFavorite(_favoriteAssignSpell);
+		else
+			beginFavoriteAssignment();
+
 	} else if (msg.keycode == Common::KEYCODE_PAGEUP) {
 		if (_topIndex > 0) {
 			_topIndex = MAX(_topIndex - 10, 0);
@@ -210,6 +238,169 @@ bool Spellbook::msgGame(const GameMessage &msg) {
 	}
 }
 
+Common::String Spellbook::favoritesKey(int slot) const {
+	// Favorites are stored per character, identified by name and class
+	const Common::String name = g_globals->_currCharacter->_name;
+	uint32 hash = 2166136261U;
+
+	for (uint i = 0; i < name.size(); ++i) {
+		hash ^= (byte)name[i];
+		hash *= 16777619U;
+	}
+
+	return Common::String::format(
+		"mm1_spell_favorite_%08x_%d_slot_%d",
+		hash, (int)g_globals->_currCharacter->_class, slot);
+}
+
+void Spellbook::loadFavorites() {
+	for (int slot = 0; slot < 10; ++slot) {
+		const Common::String key = favoritesKey(slot);
+
+		// Stored value is spell index + 1; zero means unused slot
+		const int value = ConfMan.hasKey(key) ? ConfMan.getInt(key) : 0;
+		_favoriteSlots[slot] = value - 1;
+
+		if (_favoriteSlots[slot] < -1 ||
+				_favoriteSlots[slot] >= CATEGORY_SPELLS_COUNT)
+			_favoriteSlots[slot] = -1;
+	}
+}
+
+void Spellbook::saveFavorites() const {
+	for (int slot = 0; slot < 10; ++slot)
+		ConfMan.setInt(favoritesKey(slot), _favoriteSlots[slot] + 1);
+
+	ConfMan.flushToDisk();
+}
+
+int Spellbook::favoriteSlot(int spellIndex) const {
+	for (int slot = 0; slot < 10; ++slot) {
+		if (_favoriteSlots[slot] == spellIndex)
+			return slot;
+	}
+
+	return -1;
+}
+
+bool Spellbook::isFavorite(int spellIndex) const {
+	return favoriteSlot(spellIndex) != -1;
+}
+
+void Spellbook::beginFavoriteAssignment() {
+	if (_selectedIndex < 0 || _selectedIndex >= _count)
+		return;
+
+	_favoriteAssignSpell = _spellOrder[_selectedIndex];
+	_favoriteAssignMode = true;
+	redraw();
+}
+
+void Spellbook::assignFavoriteSlot(int slot) {
+	if (!_favoriteAssignMode || slot < 0 || slot >= 10 ||
+			_favoriteAssignSpell < 0)
+		return;
+
+	const int spellIndex = _favoriteAssignSpell;
+	const int knownCount = _count;
+
+	// Moving an existing favorite removes its previous assignment
+	for (int i = 0; i < 10; ++i) {
+		if (_favoriteSlots[i] == spellIndex)
+			_favoriteSlots[i] = -1;
+	}
+
+	_favoriteSlots[slot] = spellIndex;
+	saveFavorites();
+
+	_favoriteAssignMode = false;
+	_favoriteAssignSpell = -1;
+	buildSpellOrder(knownCount);
+
+	// Keep the newly assigned spell highlighted
+	_selectedIndex = 0;
+	for (int i = 0; i < _count; ++i) {
+		if (_spellOrder[i] == spellIndex) {
+			_selectedIndex = i;
+			break;
+		}
+	}
+
+	_topIndex = (_selectedIndex / 10) * 10;
+	redraw();
+}
+
+void Spellbook::removeFavorite(int spellIndex) {
+	const int knownCount = _count;
+	bool changed = false;
+
+	for (int slot = 0; slot < 10; ++slot) {
+		if (_favoriteSlots[slot] == spellIndex) {
+			_favoriteSlots[slot] = -1;
+			changed = true;
+		}
+	}
+
+	_favoriteAssignMode = false;
+	_favoriteAssignSpell = -1;
+
+	if (changed)
+		saveFavorites();
+
+	buildSpellOrder(knownCount);
+
+	_selectedIndex = 0;
+	for (int i = 0; i < _count; ++i) {
+		if (_spellOrder[i] == spellIndex) {
+			_selectedIndex = i;
+			break;
+		}
+	}
+
+	_topIndex = (_selectedIndex / 10) * 10;
+	redraw();
+}
+
+void Spellbook::buildSpellOrder(int knownCount) {
+	_spellOrder.clear();
+
+	bool used[CATEGORY_SPELLS_COUNT] = { false };
+	int nextNormal = 0;
+
+	// The first ten visible entries correspond directly to shortcuts
+	// 1-0. A favorite assigned to a slot is fixed to that exact row
+	for (int slot = 0; slot < 10 &&
+			(int)_spellOrder.size() < knownCount; ++slot) {
+		const int favorite = _favoriteSlots[slot];
+
+		if (favorite >= 0 && favorite < knownCount && !used[favorite]) {
+			_spellOrder.push_back(favorite);
+			used[favorite] = true;
+			continue;
+		}
+
+		// Fill an unused slot with the next ordinary spell, keeping
+		// assigned favorites reserved for their own shortcut rows
+		while (nextNormal < knownCount &&
+				(used[nextNormal] || isFavorite(nextNormal)))
+			++nextNormal;
+
+		if (nextNormal < knownCount) {
+			_spellOrder.push_back(nextNormal);
+			used[nextNormal] = true;
+			++nextNormal;
+		}
+	}
+
+	// Append every remaining spell in normal order
+	for (int spellIndex = 0; spellIndex < knownCount; ++spellIndex) {
+		if (!used[spellIndex])
+			_spellOrder.push_back(spellIndex);
+	}
+
+	_count = (int)_spellOrder.size();
+}
+
 void Spellbook::updateChar() {
 	// Refresh the cast spell side dialog for new character
 	send("CastSpell", GameMessage("UPDATE"));
@@ -221,25 +412,44 @@ void Spellbook::updateChar() {
 	const Character &c = *g_globals->_currCharacter;
 	_isWizard = c._class == SORCERER || c._class == ARCHER;
 
-	_selectedIndex = (g_events->isInCombat() ? c._combatSpell : c._nonCombatSpell) % CATEGORY_SPELLS_COUNT;
-	if (_selectedIndex == -1)
-		_selectedIndex = 0;
-	_topIndex = (_selectedIndex / 10) * 10;
-
+	int knownCount;
 	if (c._spellLevel._current == 0) {
-		_count = 0;
+		knownCount = 0;
 	} else {
-		_count = (c._spellLevel._current < 5) ?
+		knownCount = (c._spellLevel._current < 5) ?
 			c._spellLevel * 8 - 1 : 31 + (c._spellLevel - 4) * 5;
 	}
 
-	// And finally, update the display
+	_favoriteAssignMode = false;
+	_favoriteAssignSpell = -1;
+	loadFavorites();
+	buildSpellOrder(knownCount);
+
+	// Reopen on the page containing the previously selected spell
+	const int selectedSpell = (g_events->isInCombat() ?
+		c._combatSpell : c._nonCombatSpell) % CATEGORY_SPELLS_COUNT;
+
+	_selectedIndex = 0;
+	if (selectedSpell >= 0) {
+		for (int i = 0; i < _count; ++i) {
+			if (_spellOrder[i] == selectedSpell) {
+				_selectedIndex = i;
+				break;
+			}
+		}
+	}
+
+	_topIndex = (_selectedIndex / 10) * 10;
 	redraw();
 }
 
 void Spellbook::spellSelected() {
 	Character &c = *g_globals->_currCharacter;
-	int spellIndex = (_isWizard ? CATEGORY_SPELLS_COUNT : 0) + _selectedIndex;
+	if (_selectedIndex < 0 || _selectedIndex >= _count)
+		return;
+
+	int spellIndex = (_isWizard ? CATEGORY_SPELLS_COUNT : 0) +
+		_spellOrder[_selectedIndex];
 
 	// Set the selected spell for the character
 	c.setSpellNumber(spellIndex);
diff --git a/engines/mm/mm1/views_enh/spells/spellbook.h b/engines/mm/mm1/views_enh/spells/spellbook.h
index 3bdbd54b74b..fb28ac004d2 100644
--- a/engines/mm/mm1/views_enh/spells/spellbook.h
+++ b/engines/mm/mm1/views_enh/spells/spellbook.h
@@ -40,6 +40,22 @@ private:
 	bool _isWizard = false;
 	int _topIndex = 0, _count = 0;
 	int _selectedIndex = -1;
+	Common::Array<int> _spellOrder;
+	int _favoriteSlots[10] = {
+		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+	};
+	bool _favoriteAssignMode = false;
+	int _favoriteAssignSpell = -1;
+
+	void buildSpellOrder(int knownCount);
+	Common::String favoritesKey(int slot) const;
+	void loadFavorites();
+	void saveFavorites() const;
+	int favoriteSlot(int spellIndex) const;
+	bool isFavorite(int spellIndex) const;
+	void beginFavoriteAssignment();
+	void assignFavoriteSlot(int slot);
+	void removeFavorite(int spellIndex);
 
 	/**
 	 * Loads buttons for the dialog


Commit: 26167a02f56ad5ea697993634bf5bc215d899b7d
    https://github.com/scummvm/scummvm/commit/26167a02f56ad5ea697993634bf5bc215d899b7d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:42+11:00

Commit Message:
MM: MM1: Fix the number of spells listed in the enhanced spellbook

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/spells/spellbook.cpp


diff --git a/engines/mm/mm1/views_enh/spells/spellbook.cpp b/engines/mm/mm1/views_enh/spells/spellbook.cpp
index 5bafa4f4be4..cf936a49870 100644
--- a/engines/mm/mm1/views_enh/spells/spellbook.cpp
+++ b/engines/mm/mm1/views_enh/spells/spellbook.cpp
@@ -417,7 +417,8 @@ void Spellbook::updateChar() {
 		knownCount = 0;
 	} else {
 		knownCount = (c._spellLevel._current < 5) ?
-			c._spellLevel * 8 - 1 : 31 + (c._spellLevel - 4) * 5;
+			c._spellLevel._current * 8 :
+			32 + (c._spellLevel._current - 4) * 5;
 	}
 
 	_favoriteAssignMode = false;


Commit: a516dd651a5eaba68398faaa7ae415dbf344809d
    https://github.com/scummvm/scummvm/commit/a516dd651a5eaba68398faaa7ae415dbf344809d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:42+11:00

Commit Message:
MM: MM1: Add enemy selection for single-target combat spells

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/game/combat.h
    engines/mm/mm1/game/spells_party.cpp
    engines/mm/mm1/views_enh/combat.cpp
    engines/mm/mm1/views_enh/combat.h
    engines/mm/mm1/views_enh/spells/cast_spell.cpp
    engines/mm/mm1/views_enh/spells/cast_spell.h


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 12d81896e28..570c3755aec 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -73,6 +73,7 @@ void Combat::clear() {
 	_timesHit = 0;
 	_isShooting = false;
 
+	_selectedSpellMonster = -1;
 	_turnUndeadUsed = false;
 	_divineInterventionUsed = false;
 	_monstersDestroyedCtr = 0;
@@ -920,11 +921,23 @@ bool Combat::monsterTouch(Common::String &line) {
 }
 
 void Combat::iterateMonsters1() {
+	_selectedSpellMonster = -1;
 	_destMonsterNum = 0;
 	_spellMonsterCount = _remainingMonsters.size();
 	iterateMonsters1Inner();
 }
 
+void Combat::iterateSelectedMonster1() {
+	if (!setSpellTarget(_selectedSpellMonster)) {
+		spellFailed();
+		return;
+	}
+
+	_spellMonsterCount = _remainingMonsters.size();
+	g_globals->_spellsState._resistanceTypeOrTargetCount = 1;
+	iterateMonsters1Inner();
+}
+
 void Combat::iterateMonsters1Inner() {
 	Common::String line1 = Common::String::format("%s %s",
 		g_globals->_currCharacter->_name,
@@ -997,11 +1010,23 @@ void Combat::iterateMonsters1Inner() {
 }
 
 void Combat::iterateMonsters2() {
+	_selectedSpellMonster = -1;
 	_destMonsterNum = 0;
 	_spellMonsterCount = _remainingMonsters.size();
 	iterateMonsters2Inner();
 }
 
+void Combat::iterateSelectedMonster2() {
+	if (!setSpellTarget(_selectedSpellMonster)) {
+		spellFailed();
+		return;
+	}
+
+	_spellMonsterCount = _remainingMonsters.size();
+	g_globals->_spellsState._resistanceTypeOrTargetCount = 1;
+	iterateMonsters2Inner();
+}
+
 void Combat::iterateMonsters2Inner() {
 	Common::String line1 = Common::String::format("%s %s",
 		g_globals->_currCharacter->_name,
@@ -1090,17 +1115,33 @@ void Combat::iterateMonsters2Inner() {
 }
 
 void Combat::characterDone() {
+	// Spell damage marks defeated monsters dead but does not remove them
+	removeDeadMonsters();
+	_selectedSpellMonster = -1;
 	g_globals->_combatParty[_currentChar]->_checked = true;
 	combatLoop();
 }
 
 void Combat::resetDestMonster() {
+	_selectedSpellMonster = -1;
 	_destMonsterNum = 0;
 	_monsterP = _remainingMonsters[0];
 	monsterIndexOf();
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_15;
 }
 
+bool Combat::setSpellTarget(int monsterNum) {
+	if (monsterNum < 0 ||
+			monsterNum >= (int)_remainingMonsters.size())
+		return false;
+
+	_selectedSpellMonster = monsterNum;
+	_destMonsterNum = monsterNum;
+	monsterSetPtr(monsterNum);
+	monsterIndexOf();
+	return true;
+}
+
 void Combat::spellFailed() {
 	g_globals->_combatParty[_currentChar]->_checked = true;
 	Sound::sound(SOUND_2);
@@ -1194,6 +1235,7 @@ void Combat::fireball2() {
 	ss._damage = 0;
 
 	// Fireball affects up to five monsters outside hand-to-hand range
+	_selectedSpellMonster = -1;
 	_destMonsterNum = _attackersCount;
 	_spellMonsterCount = _remainingMonsters.size();
 	ss._resistanceTypeOrTargetCount = MIN(
diff --git a/engines/mm/mm1/game/combat.h b/engines/mm/mm1/game/combat.h
index 28df1cc426f..b8ba8eccb10 100644
--- a/engines/mm/mm1/game/combat.h
+++ b/engines/mm/mm1/game/combat.h
@@ -76,6 +76,7 @@ protected:
 	Mode _mode = SELECT_OPTION;
 
 	int _destMonsterNum = 0;
+	int _selectedSpellMonster = -1;
 	int _monstersDestroyedCtr = 0;
 	bool _turnUndeadUsed = false;
 	bool _divineInterventionUsed = false;
@@ -322,6 +323,9 @@ public:
 
 	void iterateMonsters1();
 	void iterateMonsters2();
+	void iterateSelectedMonster1();
+	void iterateSelectedMonster2();
+	bool setSpellTarget(int monsterNum);
 	void resetDestMonster();
 
 	// Cleric spells that need access to internal fields
diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index bc97d4e7f37..d44c8f52c22 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -173,9 +173,9 @@ SpellResult SpellsParty::cleric13_blind() {
 	s._mmVal1++;
 	s._resistanceIndex = 7;
 	s._damage = BLINDED;
-	s._resistanceTypeOrTargetCount = static_cast<Resistance>((int)s._resistanceTypeOrTargetCount + 1);
+	s._resistanceTypeOrTargetCount = 1;
 
-	g_globals->_combat->iterateMonsters1();
+	g_globals->_combat->iterateSelectedMonster1();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -234,10 +234,10 @@ SpellResult SpellsParty::cleric23_pain() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._damage = getRandomNumber(6) + getRandomNumber(6);
 	ss._mmVal1++;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 	ss._resistanceIndex = 6;
 
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -264,9 +264,9 @@ SpellResult SpellsParty::cleric27_silence() {
 	ss._mmVal1++;
 	ss._resistanceIndex = 7;
 	ss._damage = SILENCED;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 
-	g_globals->_combat->iterateMonsters1();
+	g_globals->_combat->iterateSelectedMonster1();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -275,9 +275,9 @@ SpellResult SpellsParty::cleric28_suggestion() {
 	ss._mmVal1++;
 	ss._resistanceIndex = 6;
 	ss._damage = PARALYZED;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 
-	g_globals->_combat->iterateMonsters1();
+	g_globals->_combat->iterateSelectedMonster1();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -316,21 +316,22 @@ SpellResult SpellsParty::cleric34_lastingLight() {
 SpellResult SpellsParty::cleric35_produceFlame() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._mmVal1++;
-	ss._resistanceTypeOrTargetCount++;
-	ss._resistanceIndex = 4;
+	ss._resistanceTypeOrTargetCount = 1;
+	ss._resistanceIndex = 1;
 	ss._damage = getRandomNumber(6) +
 		getRandomNumber(6) + getRandomNumber(6);
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
 SpellResult SpellsParty::cleric36_produceFrost() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._mmVal1++;
-	ss._resistanceIndex++;
+	ss._resistanceTypeOrTargetCount = 1;
+	ss._resistanceIndex = 4;
 	ss._damage = getRandomNumber(6) +
 		getRandomNumber(6) + getRandomNumber(6);
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -626,10 +627,10 @@ SpellResult SpellsParty::cleric75_sunRay() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._damage = getRandomNumber(51) + 49;
 	ss._mmVal1++;
-	ss._resistanceIndex++;
+	ss._resistanceIndex = 1;
 	ss._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
 
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -646,9 +647,9 @@ SpellResult SpellsParty::wizard13_energyBlast() {
 
 	ss._damage = MIN(damage, 255);
 	ss._resistanceIndex = 5;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -657,9 +658,9 @@ SpellResult SpellsParty::wizard14_flameArrow() {
 	ss._damage = getRandomNumber(6);
 	ss._mmVal1++;
 	ss._resistanceIndex = 1;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -688,10 +689,10 @@ SpellResult SpellsParty::wizard21_electricArrow() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._damage = getRandomNumber(6) + getRandomNumber(6);
 	ss._mmVal1++;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 	ss._resistanceIndex = 2;
 
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -753,10 +754,10 @@ SpellResult SpellsParty::wizard28_scare() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._mmVal1++;
 	ss._resistanceIndex = 7;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 	ss._damage = 1;
 
-	g_globals->_combat->iterateMonsters1();
+	g_globals->_combat->iterateSelectedMonster1();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -802,25 +803,25 @@ SpellResult SpellsParty::wizard38_web() {
 SpellResult SpellsParty::wizard41_acidArrow() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._mmVal1++;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 	ss._resistanceIndex = 3;
-	ss._damage += getRandomNumber(10) +
+	ss._damage = getRandomNumber(10) +
 		getRandomNumber(10) + getRandomNumber(10);
 
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
 SpellResult SpellsParty::wizard42_coldBeam() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._mmVal1++;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 	ss._resistanceIndex = 4;
 
 	ss._damage = getRandomNumber(10) + getRandomNumber(10) +
 		getRandomNumber(10) + getRandomNumber(10);
 
-	g_globals->_combat->iterateMonsters2();
+	g_globals->_combat->iterateSelectedMonster2();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -828,10 +829,10 @@ SpellResult SpellsParty::wizard43_feebleMind() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._mmVal1++;
 	ss._resistanceIndex = 0;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 	ss._damage = 8;
 
-	g_globals->_combat->iterateMonsters1();
+	g_globals->_combat->iterateSelectedMonster1();
 	return SR_SUCCESS_SILENT;
 }
 
@@ -839,10 +840,10 @@ SpellResult SpellsParty::wizard44_freeze() {
 	SpellsState &ss = g_globals->_spellsState;
 	ss._mmVal1 = 0;
 	ss._resistanceIndex = 6;
-	ss._resistanceTypeOrTargetCount++;
+	ss._resistanceTypeOrTargetCount = 1;
 	ss._damage = 128;
 
-	g_globals->_combat->iterateMonsters1();
+	g_globals->_combat->iterateSelectedMonster1();
 	return SR_SUCCESS_SILENT;
 }
 
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 6afa934b5d0..ee5ed478d8b 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -108,6 +108,10 @@ bool Combat::msgGame(const GameMessage &msg) {
 			exchangeWith(charNum);
 		return true;
 
+	} else if (msg._name == "SELECT_SPELL_TARGET") {
+		setOption(OPTION_SPELL_TARGET);
+		return true;
+
 	} else if (msg._name == "DISABLE_ATTACKS") {
 		disableAttacks();
 		return true;
@@ -271,6 +275,20 @@ bool Combat::msgKeypress(const KeypressMessage &msg) {
 			}
 			break;
 
+		case OPTION_SPELL_TARGET:
+			if (msg.keycode >= Common::KEYCODE_a &&
+					msg.keycode < (int)(Common::KEYCODE_a +
+					_remainingMonsters.size())) {
+				int monsterNum = msg.keycode - Common::KEYCODE_a;
+
+				if (setSpellTarget(monsterNum)) {
+					_option = OPTION_NONE;
+					send("CastSpell",
+						GameMessage("MONSTER_SELECTED", monsterNum));
+				}
+			}
+			break;
+
 		case OPTION_DELAY:
 			if (msg.keycode >= Common::KEYCODE_0 &&
 				msg.keycode <= Common::KEYCODE_9) {
@@ -404,7 +422,8 @@ bool Combat::msgMouseUp(const MouseUpMessage &msg) {
 	}
 
 	if (_mode == SELECT_OPTION && (_option == OPTION_SHOOT ||
-			_option == OPTION_FIGHT)) {
+			_option == OPTION_FIGHT ||
+			_option == OPTION_SPELL_TARGET)) {
 		// Check for entries in the monster list being pressed
 		if (localPos.x >= MONSTERS_X && localPos.x < 310
 				&& localPos.y >= 0 && localPos.y < 100) {
@@ -442,6 +461,9 @@ void Combat::writeOptions() {
 	case OPTION_SHOOT:
 		writeShootSelect();
 		break;
+	case OPTION_SPELL_TARGET:
+		writeSpellTargetSelect();
+		break;
 	default:
 		break;
 	}
@@ -514,6 +536,14 @@ void Combat::writeShootSelect() {
 		ALIGN_MIDDLE);
 }
 
+void Combat::writeSpellTargetSelect() {
+	_attackableCount = _remainingMonsters.size();
+
+	writeBottomText(0, 1, Common::String::format(
+		"Target which (A-%c)?", 'A' + _attackableCount - 1),
+		ALIGN_MIDDLE);
+}
+
 void Combat::writeAttackOptions() {
 	writeOption(0, 0, 'A', STRING["dialogs.combat.attack"]);
 	writeOption(0, 1, 'F', STRING["dialogs.combat.fight"]);
diff --git a/engines/mm/mm1/views_enh/combat.h b/engines/mm/mm1/views_enh/combat.h
index 6db46669e6f..7dcc664d39f 100644
--- a/engines/mm/mm1/views_enh/combat.h
+++ b/engines/mm/mm1/views_enh/combat.h
@@ -38,7 +38,7 @@ private:
 	// Combat options that have sub-option selection
 	enum SelectedOption {
 		OPTION_NONE, OPTION_DELAY, OPTION_EXCHANGE,
-		OPTION_FIGHT, OPTION_SHOOT
+		OPTION_FIGHT, OPTION_SHOOT, OPTION_SPELL_TARGET
 	};
 	SelectedOption _option = OPTION_NONE;
 
@@ -52,6 +52,7 @@ private:
 	void writeAttackOptions();
 	void writeCastOption();
 	void writeShootOption();
+	void writeSpellTargetSelect();
 
 	void clearSurface() override;
 	void clearBottom();
diff --git a/engines/mm/mm1/views_enh/spells/cast_spell.cpp b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
index a99e8ccee2c..8d3507780a0 100644
--- a/engines/mm/mm1/views_enh/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
@@ -102,6 +102,9 @@ bool CastSpell::msgKeypress(const KeypressMessage &msg) {
 				spellError();
 			} else if (hasCharTarget()) {
 				addView("CharacterSelect");
+			} else if (hasMonsterTarget()) {
+				close();
+				send("Combat", GameMessage("SELECT_SPELL_TARGET"));
 			} else {
 				close();
 				castSpell();
@@ -141,6 +144,9 @@ bool CastSpell::msgGame(const GameMessage &msg) {
 	} else if (msg._name == "CHAR_SELECTED" && msg._value != -1) {
 		close();
 		castSpell(&g_globals->_party[msg._value]);
+
+	} else if (msg._name == "MONSTER_SELECTED" && msg._value != -1) {
+		castSpell();
 	}
 
 	return true;
@@ -168,6 +174,41 @@ void CastSpell::charSwitched(Character *priorChar) {
 	updateSelectedSpell();
 }
 
+bool CastSpell::hasMonsterTarget() {
+	if (!isInCombat())
+		return false;
+
+	// Spells that affect a single chosen monster
+	switch (_spellIndex) {
+	// Cleric
+	case 2:		// Blind
+	case 10:	// Pain
+	case 14:	// Silence
+	case 15:	// Suggestion
+	case 20:	// Produce Flame
+	case 21:	// Produce Frost
+	case 46:	// Sun Ray
+
+	// Wizard
+	case 49:	// Energy Blast
+	case 50:	// Flame Arrow
+	case 55:	// Electric Arrow
+	case 56:	// Hypnotize
+	case 57:	// Identify Monster
+	case 62:	// Scare
+	case 71:	// Acid Arrow
+	case 72:	// Cold Beam
+	case 73:	// Feeble Mind
+	case 74:	// Freeze
+	case 81:	// Finger of Death
+	case 85:	// Disintegration
+		return true;
+
+	default:
+		return false;
+	}
+}
+
 void CastSpell::castSpell(Character *target) {
 	if (_spellIndex == -1)
 		return;
diff --git a/engines/mm/mm1/views_enh/spells/cast_spell.h b/engines/mm/mm1/views_enh/spells/cast_spell.h
index 44aea1fd025..240c5b30ff5 100644
--- a/engines/mm/mm1/views_enh/spells/cast_spell.h
+++ b/engines/mm/mm1/views_enh/spells/cast_spell.h
@@ -47,6 +47,7 @@ private:
 	 * Casts the selected spell
 	 */
 	void castSpell(Character *target = nullptr);
+	bool hasMonsterTarget();
 
 	/**
 	 * Handles spell errors


Commit: c4ce76c271aeadd7ce421c53e55a7d40ca1d97be
    https://github.com/scummvm/scummvm/commit/c4ce76c271aeadd7ce421c53e55a7d40ca1d97be
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:42+11:00

Commit Message:
MM: MM1: Show enhanced spell results inside the combat view

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp
    engines/mm/mm1/views_enh/spells/cast_spell.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index ee5ed478d8b..96b7237fca0 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -96,6 +96,7 @@ bool Combat::msgGame(const GameMessage &msg) {
 	} else if (msg._name == "SPELL_RESULT") {
 		assert(msg._value >= 0 && msg._value < 40);
 		_spellResult._lines.clear();
+		_spellResult._callback = nullptr;
 		_spellResult._lines.push_back(Line(msg._value, 1, msg._stringValue));
 		_spellResult._delaySeconds = 3;
 
@@ -301,9 +302,13 @@ bool Combat::msgKeypress(const KeypressMessage &msg) {
 			break;
 		}
 	} else if (_mode == SPELL_RESULT && !isDelayActive()) {
-		// Displaying a spell result that required waiting for keypress
-		assert(_spellResult._callback);
-		_spellResult._callback();
+		// Displaying a spell result that required waiting for keypress.
+		// Some results, such as Holy Word and the cast spell Done/Failed
+		// messages, have no callback and simply finish the character's turn
+		if (_spellResult._callback)
+			_spellResult._callback();
+		else
+			block();
 
 	} else if (isDelayActive()) {
 		// In all other modes, if a delay is active, any keypress
diff --git a/engines/mm/mm1/views_enh/spells/cast_spell.cpp b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
index 8d3507780a0..8e6218b76eb 100644
--- a/engines/mm/mm1/views_enh/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
@@ -213,12 +213,25 @@ void CastSpell::castSpell(Character *target) {
 	if (_spellIndex == -1)
 		return;
 
+	// During combat, results are shown by the Combat view. An exploration
+	// InfoMessage would draw the dungeon or outdoor scene over the battle
+	auto showResult = [this](const Common::String &msg) {
+		if (isInCombat()) {
+			Common::String result = spellResultMessage(msg);
+			GameMessage gameMsg("SPELL_RESULT", result);
+			gameMsg._value = 20 - (result.size() / 2);
+			g_events->send("Combat", gameMsg);
+		} else {
+			g_events->send(InfoMessage(msg));
+		}
+	};
+
 	Character &c = *g_globals->_currCharacter;
 	c._sp._current = MAX((int)c._sp._current - _requiredSp, 0);
 	c._gems = MAX((int)c._gems - _requiredGems, 0);
 
 	if (!isMagicAllowed()) {
-		g_events->send(InfoMessage(STRING["spells.magic_doesnt_work"]));
+		showResult(STRING["spells.magic_doesnt_work"]);
 
 	} else {
 		// Cast the spell
@@ -226,11 +239,11 @@ void CastSpell::castSpell(Character *target) {
 
 		switch (result) {
 		case Game::SR_FAILED:
-			g_events->send(InfoMessage(STRING["spells.failed"]));
+			showResult(STRING["spells.failed"]);
 			break;
 
 		case Game::SR_SUCCESS_DONE:
-			g_events->send(InfoMessage(STRING["spells.done"]));
+			showResult(STRING["spells.done"]);
 			break;
 
 		default:


Commit: 2932506bcc08284b4922b67b9c9d7c4a4d66231f
    https://github.com/scummvm/scummvm/commit/2932506bcc08284b4922b67b9c9d7c4a4d66231f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:43+11:00

Commit Message:
MM: MM1: Keep spell errors inside the combat view

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/spells/cast_spell.cpp


diff --git a/engines/mm/mm1/views_enh/spells/cast_spell.cpp b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
index 8e6218b76eb..556211e6f9e 100644
--- a/engines/mm/mm1/views_enh/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
@@ -254,10 +254,16 @@ void CastSpell::castSpell(Character *target) {
 }
 
 void CastSpell::spellError() {
-	g_events->drawElements();
-
 	Common::String msg = getSpellError();
-	send(SoundMessage(msg, ALIGN_MIDDLE));
+
+	if (isInCombat()) {
+		// A global drawElements() here can redraw the underlying
+		// dungeon/outdoor scene over the battle screen
+		send("Combat", GameMessage("SPELL_RESULT", msg, 0));
+	} else {
+		g_events->drawElements();
+		send(SoundMessage(msg, ALIGN_MIDDLE));
+	}
 }
 
 } // namespace Spells


Commit: 307e82e1b002788b711773db683810a0747b9c60
    https://github.com/scummvm/scummvm/commit/307e82e1b002788b711773db683810a0747b9c60
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:43+11:00

Commit Message:
MM: MM1: Rebuild the full combat screen for monster spell messages

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 96b7237fca0..5afb0440982 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -152,6 +152,16 @@ void Combat::draw() {
 		delaySeconds(2);
 		return;
 	case MONSTER_SPELL:
+		// Monster spell effects can cycle several messages through the same
+		// row. Rebuild the complete combat frame so the previous message (or
+		// an underlying dungeon/outdoor redraw) cannot remain on screen
+		clearSurface();
+		writeStaticContent();
+		writeHandicap();
+		writeRound();
+		writePartyNumbers();
+		writeMonsters();
+		writeParty();
 		writeMonsterSpell();
 		delaySeconds(2);
 		return;


Commit: 91a0b02f19b8c21ecf4ccb2e5b0093b828a2fce5
    https://github.com/scummvm/scummvm/commit/91a0b02f19b8c21ecf4ccb2e5b0093b828a2fce5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:43+11:00

Commit Message:
MM: MM1: Use the combat bottom-message area for monster notices

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 5afb0440982..dbc9cf58ee2 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -790,44 +790,48 @@ void Combat::highlightNextRound() {
 }
 
 void Combat::writeMonsterEffects() {
+	resetBottom();
+
+	int line = 0;
 	if (_monstersRegenerate)
-		writeString(0, 21, STRING["dialogs.combat.regenerate"]);
+		writeBottomText(0, line++,
+			STRING["dialogs.combat.regenerate"]);
 
-	if (_monstersResistSpells) {
-		if (_textPos.y != 21)
-			_textPos.y = 20;
-
-		writeString(0, _textPos.y + 1, STRING["dialogs.combat.overcome"]);
-	}
+	if (_monstersResistSpells)
+		writeBottomText(0, line,
+			STRING["dialogs.combat.overcome"]);
 
 	writeMonsters();
 }
 
 void Combat::writeMonsterAction(bool flees) {
 	resetBottom();
-	writeString(0, 20, _monsterName);
-	writeChar(' ');
-	writeString(STRING[flees ?
-		"dialogs.combat.monster_flees" : "dialogs.combat.monster_wanders"
-	]);
+
+	const Common::String message = Common::String::format(
+		"%s %s", _monsterName.c_str(),
+		STRING[flees ?
+			"dialogs.combat.monster_flees" :
+			"dialogs.combat.monster_wanders"
+		].c_str());
+
+	writeBottomText(0, 0, message);
 }
 
 void Combat::writeMonsterSpell() {
 	resetBottom();
 
-	for (int i = 0, y = 0; i < (int)_monsterSpellLines.size() &&
-		_monsterSpellLines[i].y > y;
-		y = _monsterSpellLines[i].y, ++i) {
-		Common::String text = _monsterSpellLines[i]._text;
-		size_t idx;
-		while ((idx = text.findFirstOf(' ')) != Common::String::npos)
-			text.deleteChar(idx);
-
-		writeString(0, _monsterSpellLines[i].y, text);
+	for (int i = 0, y = -2; i < (int)_monsterSpellLines.size() &&
+			_monsterSpellLines[i].y > y;
+			y = _monsterSpellLines[i].y, ++i) {
+		const Line &line = _monsterSpellLines[i];
+		writeBottomText(MAX<int>(line.x, 0), MAX<int>(line.y, 0),
+			line._text, line._align);
 	}
 }
 
 void Combat::writeMonsterAttack() {
+	resetBottom();
+
 	Common::String monsterName = _monsterP->_name;
 	Common::String attackStyle = STRING[Common::String::format(
 		"dialogs.combat.attack_types.%d", _monsterAttackStyle)];


Commit: e1b5d8268310e6c40c589c03cc2955b120ee4499
    https://github.com/scummvm/scummvm/commit/e1b5d8268310e6c40c589c03cc2955b120ee4499
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:43+11:00

Commit Message:
MM: MM1: Fix the combat victory message resizing later combat screens

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index dbc9cf58ee2..0ae060b69b9 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -763,6 +763,7 @@ void Combat::clearPartyArea() {
 }
 
 void Combat::writeDefeat() {
+	const Common::Rect oldBounds = _bounds;
 	Common::String line1 = STRING["dialogs.combat.defeating1"];
 	Common::String line2 = STRING["dialogs.combat.defeating2"];
 	line1 = Common::String(line1.c_str() + 1, line1.c_str() + line1.size() - 2);
@@ -775,6 +776,9 @@ void Combat::writeDefeat() {
 	writeLine(0, line1, ALIGN_MIDDLE);
 	writeLine(2, line2, ALIGN_MIDDLE);
 	writeLine(4, line3, ALIGN_MIDDLE);
+
+	// Restore the combat view's size for any later combat screens
+	setBounds(oldBounds);
 }
 
 void Combat::highlightNextRound() {


Commit: 48aed06312051778c5c34a6a71c55ba36112fee3
    https://github.com/scummvm/scummvm/commit/48aed06312051778c5c34a6a71c55ba36112fee3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:43+11:00

Commit Message:
MM: MM1: Fix the first draw of the enhanced combat screen

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 0ae060b69b9..56676b67861 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -85,6 +85,10 @@ bool Combat::msgGame(const GameMessage &msg) {
 		// Clear combat data
 		clear();
 
+		// The combat loop may request a partial redraw before this view
+		// receives focus, so ensure the first draw is a complete one
+		_firstDraw = true;
+
 		loadMonsters();
 		setupCanAttacks();
 		setupHandicap();


Commit: 72d1f2a351877658db3cb1b7640b10243f8ad649
    https://github.com/scummvm/scummvm/commit/72d1f2a351877658db3cb1b7640b10243f8ad649
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:44+11:00

Commit Message:
MM: MM1: Fix combat messages being skipped before they are drawn

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp
    engines/mm/mm1/views_enh/combat.h


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 56676b67861..71824b9990e 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -40,6 +40,7 @@ Combat::Combat() : ScrollView("Combat") {
 
 void Combat::setMode(Mode newMode) {
 	_mode = newMode;
+	_modeDrawn = false;
 	clearButtons();
 
 	if (newMode == SELECT_OPTION) {
@@ -126,6 +127,8 @@ bool Combat::msgGame(const GameMessage &msg) {
 }
 
 void Combat::draw() {
+	_modeDrawn = true;
+
 	if (_firstDraw) {
 		_firstDraw = false;
 		if (_mode != SELECT_OPTION) {
@@ -274,6 +277,11 @@ void Combat::timeout() {
 }
 
 bool Combat::msgKeypress(const KeypressMessage &msg) {
+	// Ignore input until the current mode has been displayed, otherwise
+	// a quick keypress can skip a message before it's ever been shown
+	if (!_modeDrawn)
+		return true;
+
 	if (endDelay())
 		return true;
 
@@ -334,6 +342,9 @@ bool Combat::msgKeypress(const KeypressMessage &msg) {
 }
 
 bool Combat::msgAction(const ActionMessage &msg) {
+	if (!_modeDrawn)
+		return true;
+
 	if (endDelay())
 		return true;
 
diff --git a/engines/mm/mm1/views_enh/combat.h b/engines/mm/mm1/views_enh/combat.h
index 7dcc664d39f..4f95be8c46e 100644
--- a/engines/mm/mm1/views_enh/combat.h
+++ b/engines/mm/mm1/views_enh/combat.h
@@ -35,6 +35,7 @@ private:
 	uint _attackableCount = 0;
 	InfoMessage _spellResult;
 	bool _firstDraw = false;
+	bool _modeDrawn = false;
 	// Combat options that have sub-option selection
 	enum SelectedOption {
 		OPTION_NONE, OPTION_DELAY, OPTION_EXCHANGE,


Commit: 9a4781359fee0b65ed36e3cbec813f7e2cb3b71d
    https://github.com/scummvm/scummvm/commit/9a4781359fee0b65ed36e3cbec813f7e2cb3b71d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:44+11:00

Commit Message:
MM: MM1: Make the static combat commands clickable in the enhanced interface

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 71824b9990e..e6e83ca7519 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -451,6 +451,25 @@ bool Combat::msgMouseUp(const MouseUpMessage &msg) {
 		}
 	}
 
+	if (_mode == SELECT_OPTION && _option == OPTION_NONE) {
+		const KeybindingAction STATIC_ACTIONS[4] = {
+			KEYBIND_DELAY, KEYBIND_PROTECT, KEYBIND_QUICKREF,
+			(KeybindingAction)(KEYBIND_VIEW_PARTY1 + _currentChar)
+		};
+
+		// Delay, Protect, Quick Reference and View Character occupy
+		// the four static command rows on the left side of combat
+		for (int row = 0; row < 4; ++row) {
+			Common::Rect r(0, (7 + row) * LINE_H,
+				80, (8 + row) * LINE_H);
+
+			if (r.contains(localPos)) {
+				msgAction(ActionMessage(STATIC_ACTIONS[row]));
+				return true;
+			}
+		}
+	}
+
 	if (_mode == SELECT_OPTION && (_option == OPTION_SHOOT ||
 			_option == OPTION_FIGHT ||
 			_option == OPTION_SPELL_TARGET)) {


Commit: 05a96ef32ecadffb37cf41a05caa12c206a755ef
    https://github.com/scummvm/scummvm/commit/05a96ef32ecadffb37cf41a05caa12c206a755ef
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:44+11:00

Commit Message:
MM: MM1: Make combat command labels clickable as well as their icons

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index e6e83ca7519..f1fd750e092 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -652,7 +652,7 @@ Common::Rect Combat::getOptionButtonRect(uint col, uint row) {
 
 	const int x = 80 + col * 80;
 	const int y = (19 * LINE_H) + row * BTN_SIZE;
-	return Common::Rect(x, y, x + BTN_SIZE, y + BTN_SIZE);
+	return Common::Rect(x, y, x + 80, y + BTN_SIZE);
 }
 
 void Combat::writeOption(uint col, uint row, char c, const Common::String &msg) {


Commit: e354ab62dc31b013ed6439ad443b5f575152f475
    https://github.com/scummvm/scummvm/commit/e354ab62dc31b013ed6439ad443b5f575152f475
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:44+11:00

Commit Message:
MM: MM1: Fix Time Distortion bypassing the normal combat cleanup

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/game/combat.h
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 570c3755aec..6138e0b8a67 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1657,6 +1657,10 @@ void Combat::use() {
 	g_events->send("CharacterInfo", GameMessage("USE"));
 }
 
+void Combat::endCombat() {
+	combatDone();
+}
+
 void Combat::combatDone() {
 	// Reset several active spells
 	g_globals->_activeSpells._s.bless = 0;
diff --git a/engines/mm/mm1/game/combat.h b/engines/mm/mm1/game/combat.h
index b8ba8eccb10..49977310cce 100644
--- a/engines/mm/mm1/game/combat.h
+++ b/engines/mm/mm1/game/combat.h
@@ -346,6 +346,11 @@ public:
 	bool acidRain();
 	void fingerOfDeath();
 	void disintegration();
+
+	/**
+	 * End combat through the interface-specific cleanup path
+	 */
+	void endCombat();
 };
 
 } // namespace Game
diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index d44c8f52c22..63d3c2f2aa9 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -870,8 +870,8 @@ SpellResult SpellsParty::wizard48_timeDistortion() {
 	if (getRandomNumber(100) < map[Maps::MAP_DISPEL_THRESHOLD])
 		return SR_FAILED;
 
-	// End combat by closing the combat view
-	g_events->focusedView()->close();
+	// End combat through the normal cleanup and view-transition path
+	g_globals->_combat->endCombat();
 	return SR_SUCCESS_SILENT;
 }
 


Commit: 622b69980250cbea5aca11961ff99648577e224e
    https://github.com/scummvm/scummvm/commit/622b69980250cbea5aca11961ff99648577e224e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:45+11:00

Commit Message:
MM: MM1: Explain active effects in the Protect window

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/game/combat.h
    engines/mm/mm1/game/spells_party.cpp
    engines/mm/mm1/views_enh/protect.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 6138e0b8a67..eee097846cf 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -77,6 +77,12 @@ void Combat::clear() {
 	_turnUndeadUsed = false;
 	_divineInterventionUsed = false;
 	_monstersDestroyedCtr = 0;
+	_slowCasts = 0;
+	_weakenCasts = 0;
+	Common::fill(&_heroismBonus[0], &_heroismBonus[MAX_PARTY_SIZE], 0);
+	Common::fill(&_heroismIsSuper[0], &_heroismIsSuper[MAX_PARTY_SIZE], false);
+	Common::fill(&_powerBonus[0], &_powerBonus[MAX_PARTY_SIZE], -1);
+	Common::fill(&_quicknessBonus[0], &_quicknessBonus[MAX_PARTY_SIZE], -1);
 
 	// TODO: clear everything
 
@@ -1384,15 +1390,67 @@ void Combat::makeRoom() {
 		g_globals->_combatParty[i]->_canAttack = true;
 }
 
+int Combat::getHeroismBonus(uint index) const {
+	return index < MAX_PARTY_SIZE ? _heroismBonus[index] : 0;
+}
+
+bool Combat::isSuperHeroism(uint index) const {
+	return index < MAX_PARTY_SIZE && _heroismIsSuper[index];
+}
+
+int Combat::getPowerBonus(uint index) const {
+	return index < MAX_PARTY_SIZE ? _powerBonus[index] : -1;
+}
+
+int Combat::getQuicknessBonus(uint index) const {
+	return index < MAX_PARTY_SIZE ? _quicknessBonus[index] : -1;
+}
+
+void Combat::recordHeroism(Character *character, int bonus, bool isSuper) {
+	for (uint i = 0; i < g_globals->_party.size(); ++i) {
+		if (&g_globals->_party[i] == character) {
+			_heroismBonus[i] = bonus;
+			_heroismIsSuper[i] = isSuper;
+			return;
+		}
+	}
+}
+
+void Combat::recordPower(Character *character, int bonus) {
+	for (uint i = 0; i < g_globals->_party.size(); ++i) {
+		if (&g_globals->_party[i] == character) {
+			if (_powerBonus[i] < 0)
+				_powerBonus[i] = 0;
+			_powerBonus[i] += bonus;
+			return;
+		}
+	}
+}
+
+void Combat::recordQuickness(Character *character, int bonus) {
+	for (uint i = 0; i < g_globals->_party.size(); ++i) {
+		if (&g_globals->_party[i] == character) {
+			if (_quicknessBonus[i] < 0)
+				_quicknessBonus[i] = 0;
+			_quicknessBonus[i] += bonus;
+			return;
+		}
+	}
+}
+
 void Combat::slow() {
 	for (uint i = 0; i < _remainingMonsters.size(); ++i) {
 		monsterSetPtr(i);
 		_monsterP->_speed = MAX(_monsterP->_speed / 2, 1);
 	}
+
+	// Slow can be cast repeatedly; record this only for the current battle
+	++_slowCasts;
 }
 
 void Combat::weaken() {
 	SpellsState &ss = g_globals->_spellsState;
+	++_weakenCasts;
 
 	for (uint i = 0; i < _remainingMonsters.size(); ++i) {
 		monsterSetPtr(i);
@@ -1662,6 +1720,13 @@ void Combat::endCombat() {
 }
 
 void Combat::combatDone() {
+	_slowCasts = 0;
+	_weakenCasts = 0;
+	Common::fill(&_heroismBonus[0], &_heroismBonus[MAX_PARTY_SIZE], 0);
+	Common::fill(&_heroismIsSuper[0], &_heroismIsSuper[MAX_PARTY_SIZE], false);
+	Common::fill(&_powerBonus[0], &_powerBonus[MAX_PARTY_SIZE], -1);
+	Common::fill(&_quicknessBonus[0], &_quicknessBonus[MAX_PARTY_SIZE], -1);
+
 	// Reset several active spells
 	g_globals->_activeSpells._s.bless = 0;
 	g_globals->_activeSpells._s.invisbility = 0;
diff --git a/engines/mm/mm1/game/combat.h b/engines/mm/mm1/game/combat.h
index 49977310cce..80057589299 100644
--- a/engines/mm/mm1/game/combat.h
+++ b/engines/mm/mm1/game/combat.h
@@ -81,6 +81,14 @@ protected:
 	bool _turnUndeadUsed = false;
 	bool _divineInterventionUsed = false;
 	size_t _spellMonsterCount = 0;
+	// Transient combat-only display state; never serialized
+	int _slowCasts = 0;
+	int _weakenCasts = 0;
+	int _heroismBonus[MAX_PARTY_SIZE] = {};
+	bool _heroismIsSuper[MAX_PARTY_SIZE] = {};
+	// -1 means the spell has not been cast; a real roll may be zero
+	int _powerBonus[MAX_PARTY_SIZE] = {};
+	int _quicknessBonus[MAX_PARTY_SIZE] = {};
 
 	int _monsterAttackStyle = -1;
 
@@ -335,6 +343,16 @@ public:
 	bool divineIntervention();
 	void holyWord();
 
+	int getSlowCasts() const { return _slowCasts; }
+	int getWeakenCasts() const { return _weakenCasts; }
+	int getHeroismBonus(uint index) const;
+	bool isSuperHeroism(uint index) const;
+	int getPowerBonus(uint index) const;
+	int getQuicknessBonus(uint index) const;
+	void recordHeroism(Character *character, int bonus, bool isSuper);
+	void recordPower(Character *character, int bonus);
+	void recordQuickness(Character *character, int bonus);
+
 	// Wizard spells that need access to internal fields
 	void identifyMonster();
 	bool fireball();
diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index 63d3c2f2aa9..d948ef7373b 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -224,8 +224,11 @@ SpellResult SpellsParty::cleric22_heroism() {
 		_destChar->_level._current != _destChar->_level._base)
 		return SR_FAILED;
 
+	const int priorLevel = _destChar->_level._current;
 	_destChar->_level._current = MIN(
 		(int)_destChar->_level._current + 2, 255);
+	g_globals->_combat->recordHeroism(_destChar,
+		_destChar->_level._current - priorLevel, false);
 	restoreHp(6);
 	return SR_SUCCESS_DONE;
 }
@@ -391,8 +394,11 @@ SpellResult SpellsParty::cleric47_superHeroism() {
 	if (_destChar->_level._current != _destChar->_level._base)
 		return SR_FAILED;
 
+	const int priorLevel = _destChar->_level._current;
 	_destChar->_level._current = MIN(
 		(int)_destChar->_level._current + 3, 255);
+	g_globals->_combat->recordHeroism(_destChar,
+		_destChar->_level._current - priorLevel, true);
 	restoreHp(10);
 	return SR_SUCCESS_DONE;
 }
@@ -741,12 +747,16 @@ SpellResult SpellsParty::wizard25_levitate() {
 }
 
 SpellResult SpellsParty::wizard26_power() {
-	_destChar->_might._current += getRandomNumber(4);
+	const int bonus = getRandomNumber(4);
+	_destChar->_might._current += bonus;
+	g_globals->_combat->recordPower(_destChar, bonus);
 	return SR_SUCCESS_DONE;
 }
 
 SpellResult SpellsParty::wizard27_quickness() {
-	_destChar->_speed._current += getRandomNumber(4);
+	const int bonus = getRandomNumber(4);
+	_destChar->_speed._current += bonus;
+	g_globals->_combat->recordQuickness(_destChar, bonus);
 	return SR_SUCCESS_DONE;
 }
 
diff --git a/engines/mm/mm1/views_enh/protect.cpp b/engines/mm/mm1/views_enh/protect.cpp
index 71cebbadca1..8cdc99eb240 100644
--- a/engines/mm/mm1/views_enh/protect.cpp
+++ b/engines/mm/mm1/views_enh/protect.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/protect.h"
+#include "mm/mm1/game/combat.h"
 #include "mm/mm1/globals.h"
 
 namespace MM {
@@ -60,14 +61,45 @@ void Protect::draw() {
 	}
 
 	// The rest
+	const int slowCasts = g_globals->_combat ?
+		g_globals->_combat->getSlowCasts() : 0;
 	for (int i = 8; i < 18; ++i) {
-		if (i == 13)
-			yp += 8;
+		if (i == 13) {
+			if (slowCasts == 1) {
+				writeString(0, yp += 8,
+					"Slow - enemy Speed halved", ALIGN_MIDDLE);
+			} else if (slowCasts > 1) {
+				writeString(0, yp += 8, Common::String::format(
+					"Slow x%d - Speed halved per cast", slowCasts),
+					ALIGN_MIDDLE);
+			} else {
+				yp += 8;
+			}
+		}
 
 		if (g_globals->_activeSpells._arr[i]) {
 			Common::String line = STRING[Common::String::format(
 				"dialogs.protect.%d", i + 1)];
-			if (i == 17) {
+			if (i == 8) {
+				line += Common::String::format(" +%d AC",
+					g_globals->_activeSpells._arr[i]);
+			} else if (i == 9) {
+				line += " - prevents falls";
+			} else if (i == 10) {
+				line += " - allows water travel";
+			} else if (i == 11) {
+				line += " - prevents surprise";
+			} else if (i == 12) {
+				line += " 100%";
+			} else if (i == 13) {
+				line += " +1 hit/damage";
+			} else if (i == 14) {
+				line += " +3 AC vs attacks";
+			} else if (i == 15) {
+				line += " -8 missile damage";
+			} else if (i == 16) {
+				line += " -50% damage";
+			} else if (i == 17) {
 				line += Common::String::format("%d %s",
 					g_globals->_activeSpells._arr[17],
 					STRING["dialogs.protect.to_attacks"].c_str()
@@ -77,6 +109,51 @@ void Protect::draw() {
 			writeString(0, yp += 8, line, ALIGN_MIDDLE);
 		}
 	}
+
+	const int weakenCasts = g_globals->_combat ?
+		g_globals->_combat->getWeakenCasts() : 0;
+	if (weakenCasts == 1) {
+		writeString(0, yp += 8, "Weaken - enemy AC -1", ALIGN_MIDDLE);
+	} else if (weakenCasts > 1) {
+		writeString(0, yp += 8, Common::String::format(
+			"Weaken x%d - enemy AC -%d", weakenCasts, weakenCasts),
+			ALIGN_MIDDLE);
+	}
+
+	// Character-specific combat boosts. Multiple boosts on one character are
+	// combined into one compact row to preserve room in the Protect window.
+	if (g_globals->_combat) {
+		for (uint i = 0; i < g_globals->_party.size(); ++i) {
+			Common::String effects;
+			const int heroism = g_globals->_combat->getHeroismBonus(i);
+			const int power = g_globals->_combat->getPowerBonus(i);
+			const int quickness = g_globals->_combat->getQuicknessBonus(i);
+
+			if (heroism) {
+				effects = Common::String::format(
+					g_globals->_combat->isSuperHeroism(i) ?
+					"Super Heroism +%d Level" : "Heroism +%d Level",
+					heroism);
+			}
+			if (power >= 0) {
+				if (!effects.empty())
+					effects += ", ";
+				effects += Common::String::format("Power +%d Might", power);
+			}
+			if (quickness >= 0) {
+				if (!effects.empty())
+					effects += ", ";
+				effects += Common::String::format(
+					"Quickness +%d Speed", quickness);
+			}
+
+			if (!effects.empty()) {
+				writeString(0, yp += 8, Common::String::format("%s: %s",
+					g_globals->_party[i]._name, effects.c_str()),
+					ALIGN_MIDDLE);
+			}
+		}
+	}
 }
 
 bool Protect::msgAction(const ActionMessage &msg) {


Commit: 3729dbd44e3ab292a93b8a29f04343bd64f8cdcf
    https://github.com/scummvm/scummvm/commit/3729dbd44e3ab292a93b8a29f04343bd64f8cdcf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:45+11:00

Commit Message:
MM: MM1: Make the combat Delay setting control message duration

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views/combat.cpp
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views/combat.cpp b/engines/mm/mm1/views/combat.cpp
index 73f4ca29ee8..c9dbca9f58c 100644
--- a/engines/mm/mm1/views/combat.cpp
+++ b/engines/mm/mm1/views/combat.cpp
@@ -30,6 +30,17 @@ namespace MM {
 namespace MM1 {
 namespace Views {
 
+static uint combatMessageDelayFrames() {
+	// MM1's DELAY command is an ordinal 0-9 message-duration control:
+	// 0 is shortest, 5 is the default and 9 is longest. The original
+	// manual does not define those values as literal seconds.
+	//
+	// Preserve ScummVM's existing default action-result pace by mapping
+	// setting 5 to 3.0 seconds, then scale in half-second steps.
+	// This gives 0 -> 0.5 sec ... 5 -> 3.0 sec ... 9 -> 5.0 sec.
+	return ((uint)g_globals->_delay + 1) * FRAME_RATE / 2;
+}
+
 Combat::Combat() : TextView("Combat") {
 }
 
@@ -110,7 +121,7 @@ void Combat::draw() {
 		writeMonsters();
 		resetBottom();
 		highlightNextRound();
-		delaySeconds(1);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTER_ADVANCES:
 		writeString(0, 20, _monsterName);
@@ -118,37 +129,37 @@ void Combat::draw() {
 		writeSpaces(30);
 		writeRound();
 		writeMonsters();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTERS_AFFECTED:
 		writeMonsterEffects();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTER_SPELL:
 		writeMonsterSpell();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case INFILTRATION:
 		writeInfiltration();
-		delaySeconds(3);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case WAITS_FOR_OPENING:
 		writeWaitsForOpening();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case CHAR_ATTACKS:
 		writeMonsters();
 		writeCharAttackDamage();
-		delaySeconds(3);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case NO_EFFECT:
 		writeCharAttackNoEffect();
-		delaySeconds(3);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case DEFEATED_MONSTERS:
 		writeDefeat();
 		Sound::sound2(SOUND_3);
-		delaySeconds(3);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	default:
 		break;
@@ -170,18 +181,18 @@ void Combat::draw() {
 	case SPELL_RESULT:
 		writeSpellResult();
 		if (_spellResult._delaySeconds)
-			delaySeconds(_spellResult._delaySeconds);
+			delayFrames(combatMessageDelayFrames());
 		break;
 
 	case MONSTER_ATTACK:
 		writeMonsterAttack();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		break;
 
 	case MONSTER_FLEES:
 	case MONSTER_WANDERS:
 		writeMonsterAction(_mode == MONSTER_FLEES);
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		break;
 
 	default:
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index f1fd750e092..25e8be6f54a 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -30,6 +30,17 @@ namespace MM {
 namespace MM1 {
 namespace ViewsEnh {
 
+static uint combatMessageDelayFrames() {
+	// MM1's DELAY command is an ordinal 0-9 message-duration control:
+	// 0 is shortest, 5 is the default and 9 is longest. The original
+	// manual does not define those values as literal seconds.
+	//
+	// Preserve ScummVM's existing default action-result pace by mapping
+	// setting 5 to 3.0 seconds, then scale in half-second steps.
+	// This gives 0 -> 0.5 sec ... 5 -> 3.0 sec ... 9 -> 5.0 sec.
+	return ((uint)g_globals->_delay + 1) * FRAME_RATE / 2;
+}
+
 #define MONSTERS_X 120
 #define BOTTOM_Y 120
 #define LINE_H 8
@@ -145,18 +156,18 @@ void Combat::draw() {
 		writeMonsters();
 		resetBottom();
 		highlightNextRound();
-		delaySeconds(1);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTER_ADVANCES:
 		writeBottomText(0, 0, _monsterName);
 		writeString(STRING["dialogs.combat.advances"]);
 		writeRound();
 		writeMonsters();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTERS_AFFECTED:
 		writeMonsterEffects();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTER_SPELL:
 		// Monster spell effects can cycle several messages through the same
@@ -170,29 +181,29 @@ void Combat::draw() {
 		writeMonsters();
 		writeParty();
 		writeMonsterSpell();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case INFILTRATION:
 		writeInfiltration();
-		delaySeconds(3);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case WAITS_FOR_OPENING:
 		writeWaitsForOpening();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case CHAR_ATTACKS:
 		writeMonsters();
 		writeCharAttackDamage();
-		delaySeconds(3);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case NO_EFFECT:
 		writeCharAttackNoEffect();
-		delaySeconds(3);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	case DEFEATED_MONSTERS:
 		writeDefeat();
 		Sound::sound2(SOUND_3);
-		delaySeconds(5);
+		delayFrames(combatMessageDelayFrames());
 		return;
 	default:
 		break;
@@ -214,18 +225,18 @@ void Combat::draw() {
 	case SPELL_RESULT:
 		writeSpellResult();
 		if (_spellResult._delaySeconds)
-			delaySeconds(_spellResult._delaySeconds);
+			delayFrames(combatMessageDelayFrames());
 		break;
 
 	case MONSTER_ATTACK:
 		writeMonsterAttack();
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		break;
 
 	case MONSTER_FLEES:
 	case MONSTER_WANDERS:
 		writeMonsterAction(_mode == MONSTER_FLEES);
-		delaySeconds(2);
+		delayFrames(combatMessageDelayFrames());
 		break;
 
 	default:


Commit: e8c0d49f82c0c69360366f0f1adc8d889a11ff6b
    https://github.com/scummvm/scummvm/commit/e8c0d49f82c0c69360366f0f1adc8d889a11ff6b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:45+11:00

Commit Message:
MM: MM1: Add a combat log window to the enhanced combat view

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/metaengine.cpp
    engines/mm/mm1/metaengine.h
    engines/mm/mm1/views_enh/combat.cpp
    engines/mm/mm1/views_enh/combat.h
    engines/mm/mm1/views_enh/spells/cast_spell.cpp


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index d8ed272fe74..2e07a25541a 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -85,6 +85,11 @@ static const KeybindingRecord COMBAT_KEYS[] = {
 	// I18N: Combat command in Might & Magic 1
 	{ KEYBIND_COMBAT_USE, "COMBAT_USE", _s("Use"), "u", nullptr },
 
+	{ KEYBIND_COMBAT_LOG, "COMBAT_LOG", _s("Combat log"), "l", nullptr },
+	{ KEYBIND_COMBAT_LOG_UP, "COMBAT_LOG_UP", _s("Combat log up"), "UP", nullptr },
+	{ KEYBIND_COMBAT_LOG_DOWN, "COMBAT_LOG_DOWN", _s("Combat log down"), "DOWN", nullptr },
+	{ KEYBIND_ESCAPE, "COMBAT_LOG_CLOSE", _s("Close combat log"), "ESCAPE", nullptr },
+
 	// I18N: Combat command in Might & Magic 1
 	{ KEYBIND_DELAY, "DELAY", _s("Delay"), "d", nullptr },
 	// I18N: Combat command in Might & Magic 1
diff --git a/engines/mm/mm1/metaengine.h b/engines/mm/mm1/metaengine.h
index d5c758f152d..46eaec03cff 100644
--- a/engines/mm/mm1/metaengine.h
+++ b/engines/mm/mm1/metaengine.h
@@ -74,6 +74,9 @@ enum KeybindingAction {
 	KEYBIND_COMBAT_RETREAT,
 	KEYBIND_COMBAT_SHOOT,
 	KEYBIND_COMBAT_USE,
+	KEYBIND_COMBAT_LOG,
+	KEYBIND_COMBAT_LOG_UP,
+	KEYBIND_COMBAT_LOG_DOWN,
 
 	// Cheats
 	KEYBIND_CHEAT_COLLISIONS, KEYBIND_CHEAT_DESTROY,
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 25e8be6f54a..bdf9568b169 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -45,12 +45,15 @@ static uint combatMessageDelayFrames() {
 #define BOTTOM_Y 120
 #define LINE_H 8
 #define MONSTER_H 7
+#define COMBAT_LOG_VISIBLE_LINES 16
+#define COMBAT_LOG_MAX_LINES 240
 
 Combat::Combat() : ScrollView("Combat") {
 }
 
 void Combat::setMode(Mode newMode) {
 	_mode = newMode;
+	_combatModeLogged = false;
 	_modeDrawn = false;
 	clearButtons();
 
@@ -96,12 +99,14 @@ bool Combat::msgGame(const GameMessage &msg) {
 	if (msg._name == "COMBAT") {
 		// Clear combat data
 		clear();
+		clearCombatLog();
 
 		// The combat loop may request a partial redraw before this view
 		// receives focus, so ensure the first draw is a complete one
 		_firstDraw = true;
 
 		loadMonsters();
+		addEncounterToCombatLog();
 		setupCanAttacks();
 		setupHandicap();
 
@@ -121,14 +126,22 @@ bool Combat::msgGame(const GameMessage &msg) {
 
 	} else if (msg._name == "EXCHANGE" && msg._value != -1) {
 		int charNum = msg._value;
-		if (g_globals->_combatParty[charNum] != g_globals->_currCharacter)
+		if (g_globals->_combatParty[charNum] != g_globals->_currCharacter) {
+			addCombatLog(Common::String::format("%s exchanges places with %s.",
+				g_globals->_currCharacter->_name,
+				g_globals->_combatParty[charNum]->_name));
 			exchangeWith(charNum);
+		}
 		return true;
 
 	} else if (msg._name == "SELECT_SPELL_TARGET") {
 		setOption(OPTION_SPELL_TARGET);
 		return true;
 
+	} else if (msg._name == "COMBAT_LOG") {
+		addCombatLog(msg._stringValue);
+		return true;
+
 	} else if (msg._name == "DISABLE_ATTACKS") {
 		disableAttacks();
 		return true;
@@ -139,6 +152,10 @@ bool Combat::msgGame(const GameMessage &msg) {
 
 void Combat::draw() {
 	_modeDrawn = true;
+	if (_combatLogVisible) {
+		drawCombatLog();
+		return;
+	}
 
 	if (_firstDraw) {
 		_firstDraw = false;
@@ -156,6 +173,8 @@ void Combat::draw() {
 		writeMonsters();
 		resetBottom();
 		highlightNextRound();
+		if (beginCombatLogEvent())
+			addCombatLog(Common::String::format("-- Round %d --", _roundNum));
 		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTER_ADVANCES:
@@ -163,6 +182,10 @@ void Combat::draw() {
 		writeString(STRING["dialogs.combat.advances"]);
 		writeRound();
 		writeMonsters();
+		if (beginCombatLogEvent())
+			addCombatLog(Common::String::format("%s %s",
+				_monsterName.c_str(),
+				STRING["dialogs.combat.advances"].c_str()));
 		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTERS_AFFECTED:
@@ -288,11 +311,46 @@ void Combat::timeout() {
 }
 
 bool Combat::msgKeypress(const KeypressMessage &msg) {
-	// Ignore input until the current mode has been displayed, otherwise
-	// a quick keypress can skip a message before it's ever been shown
 	if (!_modeDrawn)
 		return true;
 
+
+	if (_combatLogVisible) {
+		switch (msg.keycode) {
+		case Common::KEYCODE_l:
+		case Common::KEYCODE_ESCAPE:
+			closeCombatLog();
+			break;
+		case Common::KEYCODE_UP:
+		case Common::KEYCODE_PAGEUP:
+			scrollCombatLog(msg.keycode == Common::KEYCODE_UP ? -1 :
+				-COMBAT_LOG_VISIBLE_LINES);
+			break;
+		case Common::KEYCODE_DOWN:
+		case Common::KEYCODE_PAGEDOWN:
+			scrollCombatLog(msg.keycode == Common::KEYCODE_DOWN ? 1 :
+				COMBAT_LOG_VISIBLE_LINES);
+			break;
+		case Common::KEYCODE_HOME:
+			_combatLogTop = 0;
+			redraw();
+			break;
+		case Common::KEYCODE_END:
+			_combatLogTop = MAX(0,
+				(int)_combatLogLines.size() - COMBAT_LOG_VISIBLE_LINES);
+			redraw();
+			break;
+		default:
+			break;
+		}
+		return true;
+	}
+
+	if (_mode == SELECT_OPTION && _option == OPTION_NONE &&
+			msg.keycode == Common::KEYCODE_l) {
+		openCombatLog();
+		return true;
+	}
 	if (endDelay())
 		return true;
 
@@ -356,6 +414,30 @@ bool Combat::msgAction(const ActionMessage &msg) {
 	if (!_modeDrawn)
 		return true;
 
+
+	if (_combatLogVisible) {
+		switch (msg._action) {
+		case KEYBIND_COMBAT_LOG:
+		case KEYBIND_ESCAPE:
+			closeCombatLog();
+			break;
+		case KEYBIND_COMBAT_LOG_UP:
+			scrollCombatLog(-1);
+			break;
+		case KEYBIND_COMBAT_LOG_DOWN:
+			scrollCombatLog(1);
+			break;
+		default:
+			break;
+		}
+		return true;
+	}
+
+	if (_mode == SELECT_OPTION && _option == OPTION_NONE &&
+			msg._action == KEYBIND_COMBAT_LOG) {
+		openCombatLog();
+		return true;
+	}
 	if (endDelay())
 		return true;
 
@@ -396,6 +478,8 @@ bool Combat::msgAction(const ActionMessage &msg) {
 		attack();
 		break;
 	case KEYBIND_COMBAT_BLOCK:
+		addCombatLog(Common::String::format("%s blocks.",
+			g_globals->_currCharacter->_name));
 		block();
 		break;
 	case KEYBIND_COMBAT_CAST:
@@ -417,12 +501,17 @@ bool Combat::msgAction(const ActionMessage &msg) {
 		addView("QuickRef");
 		break;
 	case KEYBIND_COMBAT_RETREAT:
+		addCombatLog(Common::String::format("%s attempts to retreat.",
+			g_globals->_currCharacter->_name));
 		retreat();
 		break;
 	case KEYBIND_COMBAT_SHOOT:
 		shoot();
 		break;
 	case KEYBIND_COMBAT_USE:
+		addCombatLog(Common::String::format(
+			"%s prepares to use an item.",
+			g_globals->_currCharacter->_name));
 		use();
 		break;
 	default:
@@ -433,6 +522,12 @@ bool Combat::msgAction(const ActionMessage &msg) {
 }
 
 bool Combat::msgMouseUp(const MouseUpMessage &msg) {
+	if (_combatLogVisible) {
+		if (msg._button == MouseMessage::MB_RIGHT)
+			closeCombatLog();
+		return true;
+	}
+
 	if (msg._button != MouseMessage::MB_LEFT)
 		return ScrollView::msgMouseUp(msg);
 
@@ -449,6 +544,12 @@ bool Combat::msgMouseUp(const MouseUpMessage &msg) {
 
 	if (_mode == SELECT_OPTION && _option == OPTION_NONE) {
 		// Check for option buttons being pressed
+		Common::Rect logButton = getOptionButtonRect(2, 2);
+		if (logButton.contains(localPos)) {
+			msgAction(ActionMessage(KEYBIND_COMBAT_LOG));
+			return true;
+		}
+
 		for (int col = 0; col < 3; ++col) {
 			for (int row = 0; row < 3; ++row) {
 				if (col != 2 || row != 2) {
@@ -564,8 +665,121 @@ void Combat::writeAllOptions() {
 	writeOption(1, 0, 'E', STRING["enhdialogs.combat.exchange"]);
 	writeOption(1, 1, 'U', STRING["enhdialogs.combat.use"]);
 	writeOption(1, 2, 'B', STRING["enhdialogs.combat.block"]);
+
+	writeOption(2, 2, 'L', "\x01""37Log");
+}
+
+void Combat::drawCombatLog() {
+	ScrollView::draw();
+	setReduced(false);
+	setTextColor(0);
+
+	writeString(0, 0, "Combat Log", ALIGN_MIDDLE);
+
+	if (_combatLogLines.empty()) {
+		writeString(0, 36, "No combat actions have been recorded yet.",
+			ALIGN_MIDDLE);
+	} else {
+		const int end = MIN((int)_combatLogLines.size(),
+			_combatLogTop + COMBAT_LOG_VISIBLE_LINES);
+		for (int i = _combatLogTop; i < end; ++i)
+			writeString(4, 18 + (i - _combatLogTop) * 9,
+				_combatLogLines[i]);
+
+		writeString(300, 164, Common::String::format("%d-%d / %d",
+			_combatLogTop + 1, end, (int)_combatLogLines.size()),
+			ALIGN_RIGHT);
+	}
+
+	writeString(0, 174, "Up/Down: Scroll    L/Esc: Return", ALIGN_MIDDLE);
+}
+
+void Combat::openCombatLog() {
+	_combatLogVisible = true;
+	_combatLogTop = MAX(0,
+		(int)_combatLogLines.size() - COMBAT_LOG_VISIBLE_LINES);
+	redraw();
 }
 
+void Combat::closeCombatLog() {
+	_combatLogVisible = false;
+	redraw();
+}
+
+void Combat::scrollCombatLog(int delta) {
+	const int maxTop = MAX(0,
+		(int)_combatLogLines.size() - COMBAT_LOG_VISIBLE_LINES);
+	_combatLogTop = CLIP(_combatLogTop + delta, 0, maxTop);
+	redraw();
+}
+
+void Combat::clearCombatLog() {
+	_combatLogLines.clear();
+	_combatLogTop = 0;
+	_combatLogVisible = false;
+	_combatModeLogged = false;
+}
+
+void Combat::addCombatLog(const Common::String &text) {
+	if (text.empty())
+		return;
+
+	const bool wasReduced = _fontReduced;
+	setReduced(false);
+	Common::StringArray wrapped = splitLines(text, 292);
+	setReduced(wasReduced);
+
+	for (const Common::String &line : wrapped)
+		_combatLogLines.push_back(line);
+
+	while (_combatLogLines.size() > COMBAT_LOG_MAX_LINES)
+		_combatLogLines.remove_at(0);
+}
+
+void Combat::addCombatLog(const LineArray &lines) {
+	for (const Line &line : lines) {
+		if (!line._text.empty())
+			addCombatLog(line._text);
+	}
+}
+
+void Combat::addEncounterToCombatLog() {
+	addCombatLog("Combat begins.");
+	addCombatLog("Enemies:");
+
+	for (uint i = 0; i < _remainingMonsters.size(); ++i) {
+		bool alreadyListed = false;
+		for (uint j = 0; j < i; ++j) {
+			if (_remainingMonsters[j]->_name == _remainingMonsters[i]->_name) {
+				alreadyListed = true;
+				break;
+			}
+		}
+		if (alreadyListed)
+			continue;
+
+		uint count = 0;
+		for (uint j = i; j < _remainingMonsters.size(); ++j) {
+			if (_remainingMonsters[j]->_name == _remainingMonsters[i]->_name)
+				++count;
+		}
+
+		addCombatLog(Common::String::format("  %u x %s", count,
+			_remainingMonsters[i]->_name.c_str()));
+	}
+
+	addCombatLog(Common::String::format("-- Round %d --", _roundNum));
+}
+
+bool Combat::beginCombatLogEvent() {
+	if (_combatModeLogged)
+		return false;
+
+	_combatModeLogged = true;
+	return true;
+}
+
+
 void Combat::writeDelaySelect() {
 	writeBottomText(0, 0, STRING["dialogs.combat.set_delay"]);
 	writeBottomText(0, 3, Common::String::format(
@@ -851,6 +1065,13 @@ void Combat::writeMonsterEffects() {
 			STRING["dialogs.combat.overcome"]);
 
 	writeMonsters();
+
+	if (beginCombatLogEvent()) {
+		if (_monstersRegenerate)
+			addCombatLog("The monsters regenerate.");
+		if (_monstersResistSpells)
+			addCombatLog("The monsters overcome spell effects.");
+	}
 }
 
 void Combat::writeMonsterAction(bool flees) {
@@ -864,6 +1085,9 @@ void Combat::writeMonsterAction(bool flees) {
 		].c_str());
 
 	writeBottomText(0, 0, message);
+
+	if (beginCombatLogEvent())
+		addCombatLog(message);
 }
 
 void Combat::writeMonsterSpell() {
@@ -876,6 +1100,9 @@ void Combat::writeMonsterSpell() {
 		writeBottomText(MAX<int>(line.x, 0), MAX<int>(line.y, 0),
 			line._text, line._align);
 	}
+
+	if (beginCombatLogEvent())
+		addCombatLog(_monsterSpellLines);
 }
 
 void Combat::writeMonsterAttack() {
@@ -892,7 +1119,12 @@ void Combat::writeMonsterAttack() {
 		c._name
 	);
 	writeBottomText(0, 0, line);
-	writeBottomText(0, 1, getAttackString());
+	const Common::String attackResult = getAttackString();
+	writeBottomText(0, 1, attackResult);
+
+	Common::StringArray combatLogEvent;
+	combatLogEvent.push_back(line);
+	combatLogEvent.push_back(attackResult);
 
 	if (_damage) {
 		// Attacks wake up sleeping characters
@@ -904,12 +1136,21 @@ void Combat::writeMonsterAttack() {
 		// adjust the damage amount. Another reason why we
 		// can't actually apply damage until here
 		int yp = 2;
-		if (monsterTouch(line))
+		if (monsterTouch(line)) {
 			writeBottomText(0, yp++, line);
+			combatLogEvent.push_back(line);
+		}
 
 		Common::String damageStr = subtractDamageFromChar();
-		if (!damageStr.empty())
+		if (!damageStr.empty()) {
 			writeBottomText(0, yp, damageStr);
+			combatLogEvent.push_back(damageStr);
+		}
+	}
+
+	if (beginCombatLogEvent()) {
+		for (const Common::String &logLine : combatLogEvent)
+			addCombatLog(logLine);
 	}
 }
 
@@ -922,6 +1163,9 @@ void Combat::writeInfiltration() {
 	writeBottomText(0, 0, line);
 	Sound::sound(SOUND_2);
 	Sound::sound(SOUND_2);
+
+	if (beginCombatLogEvent())
+		addCombatLog(line);
 }
 
 void Combat::writeWaitsForOpening() {
@@ -932,6 +1176,9 @@ void Combat::writeWaitsForOpening() {
 
 	resetBottom();
 	writeBottomText(0, 0, line);
+
+	if (beginCombatLogEvent())
+		addCombatLog(line);
 }
 
 void Combat::writeSpellResult() {
@@ -939,6 +1186,9 @@ void Combat::writeSpellResult() {
 		const Line &l = _spellResult._lines[i];
 		writeBottomText(l.x, l.y, l._text);
 	}
+
+	if (beginCombatLogEvent())
+		addCombatLog(_spellResult._lines);
 }
 
 void Combat::checkMonsterSpellDone() {
@@ -988,6 +1238,7 @@ void Combat::writeMessage() {
 
 void Combat::writeCharAttackDamage() {
 	resetBottom();
+	const bool wasShooting = _isShooting;
 
 	writeBottomText(0, 0, Common::String::format("%s %s %s",
 		g_globals->_currCharacter->_name,
@@ -1004,10 +1255,24 @@ void Combat::writeCharAttackDamage() {
 			_monsterP->_name.c_str(),
 			STRING["dialogs.combat.goes_down"].c_str()));
 	}
+
+	if (beginCombatLogEvent()) {
+		addCombatLog(Common::String::format("%s %s %s",
+			g_globals->_currCharacter->_name,
+			STRING[wasShooting ? "dialogs.combat.shoots" :
+				"dialogs.combat.attacks"].c_str(),
+			_monsterP->_name.c_str()));
+		addCombatLog(getAttackString());
+		if (_monsterP->_status == MONFLAG_DEAD)
+			addCombatLog(Common::String::format("%s %s",
+				_monsterP->_name.c_str(),
+				STRING["dialogs.combat.goes_down"].c_str()));
+	}
 }
 
 void Combat::writeCharAttackNoEffect() {
 	resetBottom();
+	const bool wasShooting = _isShooting;
 
 	writeBottomText(0, 0, Common::String::format("%s %s %s",
 		g_globals->_currCharacter->_name,
@@ -1018,6 +1283,15 @@ void Combat::writeCharAttackNoEffect() {
 	_isShooting = false;
 
 	writeBottomText(0, 1, STRING["dialogs.combat.weapon_no_effect"]);
+
+	if (beginCombatLogEvent()) {
+		addCombatLog(Common::String::format("%s %s %s",
+			g_globals->_currCharacter->_name,
+			STRING[wasShooting ? "dialogs.combat.shoots" :
+				"dialogs.combat.attacks"].c_str(),
+			_monsterP->_name.c_str()));
+		addCombatLog(STRING["dialogs.combat.weapon_no_effect"]);
+	}
 }
 
 Common::String Combat::getAttackString() {
diff --git a/engines/mm/mm1/views_enh/combat.h b/engines/mm/mm1/views_enh/combat.h
index 4f95be8c46e..a102da1fd16 100644
--- a/engines/mm/mm1/views_enh/combat.h
+++ b/engines/mm/mm1/views_enh/combat.h
@@ -36,6 +36,10 @@ private:
 	InfoMessage _spellResult;
 	bool _firstDraw = false;
 	bool _modeDrawn = false;
+	Common::StringArray _combatLogLines;
+	int _combatLogTop = 0;
+	bool _combatLogVisible = false;
+	bool _combatModeLogged = false;
 	// Combat options that have sub-option selection
 	enum SelectedOption {
 		OPTION_NONE, OPTION_DELAY, OPTION_EXCHANGE,
@@ -50,6 +54,15 @@ private:
 
 	void writeOptions();
 	void writeAllOptions();
+	void drawCombatLog();
+	void openCombatLog();
+	void closeCombatLog();
+	void scrollCombatLog(int delta);
+	void clearCombatLog();
+	void addCombatLog(const Common::String &text);
+	void addCombatLog(const LineArray &lines);
+	void addEncounterToCombatLog();
+	bool beginCombatLogEvent();
 	void writeAttackOptions();
 	void writeCastOption();
 	void writeShootOption();
diff --git a/engines/mm/mm1/views_enh/spells/cast_spell.cpp b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
index 556211e6f9e..eeb7c31e19a 100644
--- a/engines/mm/mm1/views_enh/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
@@ -227,6 +227,18 @@ void CastSpell::castSpell(Character *target) {
 	};
 
 	Character &c = *g_globals->_currCharacter;
+	if (isInCombat()) {
+		const Common::String spellName = _spellIndex < 47 ?
+			STRING[Common::String::format("spells.cleric.%d", _spellIndex)] :
+			STRING[Common::String::format("spells.wizard.%d", _spellIndex - 47)];
+		const Common::String logLine = target ?
+			Common::String::format("%s casts %s on %s.", c._name,
+				spellName.c_str(), target->_name) :
+			Common::String::format("%s casts %s.", c._name,
+				spellName.c_str());
+		send("Combat", GameMessage("COMBAT_LOG", logLine));
+	}
+
 	c._sp._current = MAX((int)c._sp._current - _requiredSp, 0);
 	c._gems = MAX((int)c._gems - _requiredGems, 0);
 


Commit: 125838a04a16b5a8fb413cb5b5aaceb0f7e7f3ca
    https://github.com/scummvm/scummvm/commit/125838a04a16b5a8fb413cb5b5aaceb0f7e7f3ca
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:45+11:00

Commit Message:
MM: MM1: Keep claimed rewards claimed across save/load in the same map visit

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map.h
    engines/mm/mm1/maps/map36.cpp
    engines/mm/mm1/maps/maps.cpp
    engines/mm/mm1/maps/maps.h


diff --git a/engines/mm/mm1/maps/map.h b/engines/mm/mm1/maps/map.h
index 2020e1ad056..060f0c458a1 100644
--- a/engines/mm/mm1/maps/map.h
+++ b/engines/mm/mm1/maps/map.h
@@ -150,6 +150,13 @@ public:
 	 */
 	virtual void special() = 0;
 
+	/**
+	 * Restores visit-local scripted state after loading a save. Overlay data
+	 * itself is reloaded from disk, so maps with mutable switches can rebuild
+	 * those values from already-serialized visitation metadata here.
+	 */
+	virtual void restoreVisitState() {}
+
 	/**
 	 * Returns true if mapping is allowed in enhanced mode.
 	 * This is to prevent places like the desert where the
diff --git a/engines/mm/mm1/maps/map36.cpp b/engines/mm/mm1/maps/map36.cpp
index 091a35de9d8..aab63ef70b0 100644
--- a/engines/mm/mm1/maps/map36.cpp
+++ b/engines/mm/mm1/maps/map36.cpp
@@ -119,6 +119,7 @@ void Map36::special07() {
 }
 
 void Map36::special11() {
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure.setGems(30);
 	special07();
 }
diff --git a/engines/mm/mm1/maps/maps.cpp b/engines/mm/mm1/maps/maps.cpp
index 71179c234dd..716a38fa143 100644
--- a/engines/mm/mm1/maps/maps.cpp
+++ b/engines/mm/mm1/maps/maps.cpp
@@ -220,7 +220,7 @@ void Maps::synchronizeCurrent(Common::Serializer &s) {
 
 	if (s.isLoading()) {
 		updateMasksOffsets();
-		changeMap(_id, _section);
+		changeMap(_id, _section, true);
 	}
 }
 
@@ -234,6 +234,8 @@ void Maps::select(uint16 id, byte section) {
 
 void Maps::display(uint16 id, byte section) {
 	select(id, section);
+	for (uint i = 0; i < MAP_SIZE; ++i)
+		_currentMap->_visited[i] &= ~(VISITED_TRANSIENT_STATE | VISITED_REWARD_CLAIMED);
 	loadTiles();
 	g_events->send("Game", GameMessage("UPDATE"));
 }
@@ -412,9 +414,21 @@ void Maps::step(const Common::Point &delta) {
 	changeMap(id, section);
 }
 
-void Maps::changeMap(uint16 id, byte section) {
-	// At this point, a new map is being entered
+void Maps::changeMap(uint16 id, byte section, bool loadingSave) {
+	// A genuine transition starts a fresh visit, so rewards can be claimed
+	// again. Loading a save retains claimed rewards and other visit-local
+	// scripted state
 	select(id, section);
+
+	for (uint i = 0; i < MAP_SIZE; ++i) {
+		if (loadingSave && (_currentMap->_visited[i] & VISITED_REWARD_CLAIMED))
+			_currentMap->_states[i] &= ~CELL_SPECIAL;
+		else if (!loadingSave)
+			_currentMap->_visited[i] &= ~(VISITED_TRANSIENT_STATE | VISITED_REWARD_CLAIMED);
+	}
+	if (loadingSave)
+		_currentMap->restoreVisitState();
+
 	loadTiles();
 
 	visitedTile();
@@ -433,6 +447,11 @@ void Maps::clearSpecial() {
 	_currentMap->_states[_mapOffset] &= ~CELL_SPECIAL;
 }
 
+void Maps::clearSpecialAndRememberReward() {
+	_currentMap->_visited[_mapOffset] |= VISITED_REWARD_CLAIMED;
+	clearSpecial();
+}
+
 Common::Point Maps::getMoveDelta(byte mask) {
 	switch (mask) {
 	case DIRMASK_E:
diff --git a/engines/mm/mm1/maps/maps.h b/engines/mm/mm1/maps/maps.h
index dca979e2d35..9f7c1fe776f 100644
--- a/engines/mm/mm1/maps/maps.h
+++ b/engines/mm/mm1/maps/maps.h
@@ -47,7 +47,11 @@ enum StateFlag {
 
 enum Visited {
 	VISITED_NONE = 0, VISITED_NORMAL = 1, VISITED_SPECIAL = 2,
-	VISITED_EXIT = 3, VISITED_BUSINESS = 4	
+	VISITED_EXIT = 3, VISITED_BUSINESS = 4,
+	// Visit-local scripted state. Serialized so save/load can preserve the
+	// current map visit, but cleared when the map is entered normally again
+	VISITED_TRANSIENT_STATE = 0x20,
+	VISITED_REWARD_CLAIMED = 0x40
 };
 
 /**
@@ -174,7 +178,7 @@ public:
 	 * Called when a map is left to load a new one
 	 * and trigger the 3d view to update
 	 */
-	void changeMap(uint16 id, byte section);
+	void changeMap(uint16 id, byte section, bool loadingSave = false);
 
 	/**
 	 * Mark a tile as visited
@@ -188,6 +192,11 @@ public:
 	 */
 	void clearSpecial();
 
+	/**
+	 * Disables a claimed reward for save/load during this map visit
+	 */
+	void clearSpecialAndRememberReward();
+
 	/**
 	 * Gets the moveement delta given a direction mask
 	 */


Commit: ffe69ad5e08429f0f21932ff680d6a36f2b79166
    https://github.com/scummvm/scummvm/commit/ffe69ad5e08429f0f21932ff680d6a36f2b79166
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:45+11:00

Commit Message:
MM: MM1: Keep finite treasure claimed after saving and reloading

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map04.cpp
    engines/mm/mm1/maps/map08.cpp
    engines/mm/mm1/maps/map10.cpp
    engines/mm/mm1/maps/map18.cpp
    engines/mm/mm1/maps/map22.cpp
    engines/mm/mm1/maps/map23.cpp
    engines/mm/mm1/maps/map25.cpp
    engines/mm/mm1/maps/map33.cpp
    engines/mm/mm1/maps/map35.cpp
    engines/mm/mm1/maps/map37.cpp
    engines/mm/mm1/maps/map42.cpp
    engines/mm/mm1/maps/map43.cpp
    engines/mm/mm1/maps/map49.cpp
    engines/mm/mm1/maps/map51.cpp
    engines/mm/mm1/maps/maps.cpp
    engines/mm/mm1/views/interactions/trivia.cpp
    engines/mm/mm1/views_enh/interactions/trivia.cpp


diff --git a/engines/mm/mm1/maps/map04.cpp b/engines/mm/mm1/maps/map04.cpp
index 604ca2b7951..e7c3f424136 100644
--- a/engines/mm/mm1/maps/map04.cpp
+++ b/engines/mm/mm1/maps/map04.cpp
@@ -48,7 +48,7 @@ void Map04::special() {
 	send(SoundMessage(
 		STRING["maps.map04.treasure"],
 		[]() {
-			g_maps->clearSpecial();
+			g_maps->clearSpecialAndRememberReward();
 			if ((*g_maps->_currentMap)[MAP04_TREASURE_STOLEN] < 255)
 				(*g_maps->_currentMap)[MAP04_TREASURE_STOLEN]++;
 			g_globals->_treasure.setGold(600);
diff --git a/engines/mm/mm1/maps/map08.cpp b/engines/mm/mm1/maps/map08.cpp
index 8d3d18e5811..6364384d4c0 100644
--- a/engines/mm/mm1/maps/map08.cpp
+++ b/engines/mm/mm1/maps/map08.cpp
@@ -52,11 +52,11 @@ void Map08::special() {
 		}
 	}
 
-	g_maps->clearSpecial();
-
 	if (g_maps->_mapPos.x == 0 || g_maps->_mapPos.x == 15) {
+		g_maps->clearSpecial();
 		g_globals->_encounters.execute();
 	} else {
+		g_maps->clearSpecialAndRememberReward();
 		addTreasure();
 	}
 }
@@ -122,7 +122,7 @@ void Map08::special08() {
 }
 
 void Map08::special20() {
-	g_maps->clearSpecial();
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure._items[2] = getRandomNumber(48);
 	g_globals->_treasure._trapType = 1;
 	g_globals->_treasure._container = SILVER_BOX;
diff --git a/engines/mm/mm1/maps/map10.cpp b/engines/mm/mm1/maps/map10.cpp
index 87310b31b1b..215750197a2 100644
--- a/engines/mm/mm1/maps/map10.cpp
+++ b/engines/mm/mm1/maps/map10.cpp
@@ -117,7 +117,7 @@ void Map10::special19() {
 }
 
 void Map10::special20() {
-	g_maps->clearSpecial();
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure._items[2] = 252;
 	g_globals->_treasure.setGems(20);
 	g_globals->_treasure._container = SILVER_CHEST;
@@ -160,6 +160,7 @@ void Map10::special29() {
 }
 
 void Map10::special30() {
+	g_maps->clearSpecial();
 	Game::Encounter &enc = g_globals->_encounters;
 	int monsterCount = getRandomNumber(4) + 3;
 	g_globals->_treasure._items[2] = THUNDRANIUM_ID;
@@ -178,6 +179,7 @@ void Map10::special31() {
 }
 
 void Map10::special32() {
+	g_maps->clearSpecial();
 	Game::Encounter &enc = g_globals->_encounters;
 	int monsterCount = getRandomNumber(4) + 3;
 	g_globals->_treasure._items[2] = LASER_BLASTER_ID;
diff --git a/engines/mm/mm1/maps/map18.cpp b/engines/mm/mm1/maps/map18.cpp
index b674e086259..dad8fd83ef3 100644
--- a/engines/mm/mm1/maps/map18.cpp
+++ b/engines/mm/mm1/maps/map18.cpp
@@ -164,7 +164,7 @@ void Map18::special08() {
 }
 
 void Map18::special09() {
-	g_maps->clearSpecial();
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure._items[2] = SILVER_KEY_ID;
 	g_globals->_treasure._trapType = 4;
 	g_globals->_treasure._container = IRON_BOX;
diff --git a/engines/mm/mm1/maps/map22.cpp b/engines/mm/mm1/maps/map22.cpp
index e0922d77d16..34665dd63b2 100644
--- a/engines/mm/mm1/maps/map22.cpp
+++ b/engines/mm/mm1/maps/map22.cpp
@@ -107,12 +107,13 @@ void Map22::special02() {
 	send(SoundMessage(
 		STRING["maps.map22.wagons"],
 		[]() {
-			g_maps->clearSpecial();
 			if (g_maps->_mapPos.x == 5) {
+				g_maps->clearSpecialAndRememberReward();
 				g_globals->_treasure._items[2] = MERCHANTS_PASS_ID;
 				g_events->addAction(KEYBIND_SEARCH);
 
 			} else {
+				g_maps->clearSpecial();
 				InfoMessage msg1(
 					16, 2, STRING["maps.map22.ambush"],
 					[]() {
diff --git a/engines/mm/mm1/maps/map23.cpp b/engines/mm/mm1/maps/map23.cpp
index 1428b7266ab..2c942565c74 100644
--- a/engines/mm/mm1/maps/map23.cpp
+++ b/engines/mm/mm1/maps/map23.cpp
@@ -183,7 +183,7 @@ void Map23::special12() {
 }
 
 void Map23::special13() {
-	g_maps->clearSpecial();
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure._container = IRON_BOX;
 	g_globals->_treasure._items[0] = 171;
 	g_globals->_treasure._items[1] = 183;
diff --git a/engines/mm/mm1/maps/map25.cpp b/engines/mm/mm1/maps/map25.cpp
index fdb746da6d2..0de74988bc6 100644
--- a/engines/mm/mm1/maps/map25.cpp
+++ b/engines/mm/mm1/maps/map25.cpp
@@ -120,8 +120,8 @@ void Map25::special() {
 		send(SoundMessage(
 			STRING["maps.map25.ship"],
 			[]() {
-				g_maps->clearSpecial();
 				if (g_maps->_mapOffset == 216) {
+					g_maps->clearSpecialAndRememberReward();
 					g_events->send(SoundMessage(STRING["maps.map25.jolly_raven"]));
 
 					for (uint i = 0; i < g_globals->_party.size(); ++i) {
@@ -131,6 +131,7 @@ void Map25::special() {
 
 					g_globals->_treasure.setGems(20);
 				} else {
+					g_maps->clearSpecial();
 					g_globals->_activeSpells._s.cursed = 10;
 					g_globals->_encounters.execute();
 				}
diff --git a/engines/mm/mm1/maps/map33.cpp b/engines/mm/mm1/maps/map33.cpp
index e63c2f3877b..786909d2db2 100644
--- a/engines/mm/mm1/maps/map33.cpp
+++ b/engines/mm/mm1/maps/map33.cpp
@@ -170,7 +170,7 @@ void Map33::special03() {
 }
 
 void Map33::special04() {
-	g_maps->clearSpecial();
+	g_maps->clearSpecialAndRememberReward();
 
 	send(SoundMessage(
 		STRING["maps.map33.corpse"],
@@ -196,6 +196,10 @@ void Map33::search(const Common::String &msg) {
 			Game::Encounter &enc = g_globals->_encounters;
 			int monsterCount = g_events->getRandomNumber(6) + 7;
 
+			// Consume the special for this map visit before combat,
+			// so winning cannot immediately retrigger it
+			g_maps->clearSpecial();
+
 			enc.clearMonsters();
 			enc.addMonster(map[VAL1], map[VAL2]);
 
diff --git a/engines/mm/mm1/maps/map35.cpp b/engines/mm/mm1/maps/map35.cpp
index e7488435db5..fbb25aa63ff 100644
--- a/engines/mm/mm1/maps/map35.cpp
+++ b/engines/mm/mm1/maps/map35.cpp
@@ -106,7 +106,7 @@ void Map35::special05() {
 }
 
 void Map35::special06() {
-	g_maps->clearSpecial();
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure.setGems(30);
 	g_events->addAction(KEYBIND_SEARCH);
 }
@@ -118,7 +118,7 @@ void Map35::special07() {
 }
 
 void Map35::special09() {
-	g_maps->clearSpecial();
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure.setGems(50);
 	special07();
 }
diff --git a/engines/mm/mm1/maps/map37.cpp b/engines/mm/mm1/maps/map37.cpp
index c4aa1f04a6c..62933fdc3d9 100644
--- a/engines/mm/mm1/maps/map37.cpp
+++ b/engines/mm/mm1/maps/map37.cpp
@@ -167,7 +167,7 @@ void Map37::special15() {
 }
 
 void Map37::special16() {
-	g_maps->clearSpecial();
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure._container = GOLD_BOX;
 	g_globals->_treasure.setGems(100);
 	g_events->addAction(KEYBIND_SEARCH);
diff --git a/engines/mm/mm1/maps/map42.cpp b/engines/mm/mm1/maps/map42.cpp
index 931abafd864..1e63372ba46 100644
--- a/engines/mm/mm1/maps/map42.cpp
+++ b/engines/mm/mm1/maps/map42.cpp
@@ -145,6 +145,7 @@ void Map42::special15() {
 }
 
 void Map42::special17() {
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure.setGold(7500);
 	g_globals->_treasure._container = GOLD_BOX;
 	g_events->addAction(KEYBIND_SEARCH);
diff --git a/engines/mm/mm1/maps/map43.cpp b/engines/mm/mm1/maps/map43.cpp
index 37c11a05054..14c8d37d709 100644
--- a/engines/mm/mm1/maps/map43.cpp
+++ b/engines/mm/mm1/maps/map43.cpp
@@ -119,7 +119,7 @@ void Map43::special05() {
 void Map43::special06() {
 	if (_data[TREASURE_STATE]) {
 		g_globals->_treasure.setGold(18000);
-		g_maps->clearSpecial();
+		g_maps->clearSpecialAndRememberReward();
 		g_events->addAction(KEYBIND_SEARCH);
 	} else {
 		Game::Encounter &enc = g_globals->_encounters;
diff --git a/engines/mm/mm1/maps/map49.cpp b/engines/mm/mm1/maps/map49.cpp
index 7b114e971f3..23041f7ae3e 100644
--- a/engines/mm/mm1/maps/map49.cpp
+++ b/engines/mm/mm1/maps/map49.cpp
@@ -152,6 +152,7 @@ void Map49::special16() {
 }
 
 void Map49::special20() {
+	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure.setGold(12000);
 	g_events->addAction(KEYBIND_SEARCH);
 }
diff --git a/engines/mm/mm1/maps/map51.cpp b/engines/mm/mm1/maps/map51.cpp
index 1d9f2c2dd90..f0462f07248 100644
--- a/engines/mm/mm1/maps/map51.cpp
+++ b/engines/mm/mm1/maps/map51.cpp
@@ -125,6 +125,10 @@ void Map51::special14() {
 }
 
 void Map51::special17() {
+	// The endpoint is both a hard-coded encounter and the Thundranium reward.
+	// Preload the item before combat, as with Ranalou's Thundranium cache
+	g_maps->clearSpecialAndRememberReward();
+	g_globals->_treasure._items[2] = THUNDRANIUM_ID;
 	encounter(&_data[000], &_data[000]);
 }
 
diff --git a/engines/mm/mm1/maps/maps.cpp b/engines/mm/mm1/maps/maps.cpp
index 716a38fa143..3178dc7d2e9 100644
--- a/engines/mm/mm1/maps/maps.cpp
+++ b/engines/mm/mm1/maps/maps.cpp
@@ -428,6 +428,16 @@ void Maps::changeMap(uint16 id, byte section, bool loadingSave) {
 	}
 	if (loadingSave)
 		_currentMap->restoreVisitState();
+	if (loadingSave && _mapId == 4) {
+		// The theft count lives in overlay data that is reloaded from disk,
+		// so rebuild it from the claimed treasure squares
+		byte stolenCount = 0;
+		for (uint i = 0; i < MAP_SIZE && stolenCount < 255; ++i) {
+			if (_currentMap->_visited[i] & VISITED_REWARD_CLAIMED)
+				++stolenCount;
+		}
+		(*_currentMap)[MAP04_TREASURE_STOLEN] = stolenCount;
+	}
 
 	loadTiles();
 
diff --git a/engines/mm/mm1/views/interactions/trivia.cpp b/engines/mm/mm1/views/interactions/trivia.cpp
index 2d1423e275b..18196b7bc16 100644
--- a/engines/mm/mm1/views/interactions/trivia.cpp
+++ b/engines/mm/mm1/views/interactions/trivia.cpp
@@ -59,6 +59,7 @@ void Trivia::answerEntered() {
 	close();
 
 	if (_answer.equalsIgnoreCase(_correctAnswer)) {
+		g_maps->clearSpecialAndRememberReward();
 		send(InfoMessage(STRING["maps.map21.correct"]));
 		g_globals->_party[0]._gems += 50;
 		Sound::sound(SOUND_3);
diff --git a/engines/mm/mm1/views_enh/interactions/trivia.cpp b/engines/mm/mm1/views_enh/interactions/trivia.cpp
index 43bb642ef7d..36d57b316bb 100644
--- a/engines/mm/mm1/views_enh/interactions/trivia.cpp
+++ b/engines/mm/mm1/views_enh/interactions/trivia.cpp
@@ -50,6 +50,7 @@ bool Trivia::msgGame(const GameMessage &msg) {
 
 void Trivia::answerEntered() {
 	if (_answer.equalsIgnoreCase(_correctAnswer)) {
+		g_maps->clearSpecialAndRememberReward();
 		send(InfoMessage(STRING["maps.map21.correct"]));
 		g_globals->_party[0]._gems += 50;
 		Sound::sound(SOUND_3);


Commit: 7ddf01838d8d5141125e167fb8ff1e5922411766
    https://github.com/scummvm/scummvm/commit/7ddf01838d8d5141125e167fb8ff1e5922411766
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:46+11:00

Commit Message:
MM: MM1: Add an enhanced Quest Log

Thanks to dragoo84

Changed paths:
  A engines/mm/mm1/views_enh/active_quest.cpp
  A engines/mm/mm1/views_enh/active_quest.h
    engines/mm/mm1/metaengine.cpp
    engines/mm/mm1/metaengine.h
    engines/mm/mm1/views_enh/dialogs.h
    engines/mm/mm1/views_enh/game.cpp
    engines/mm/mm1/views_enh/game_commands.cpp
    engines/mm/module.mk


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index 2e07a25541a..98d2151e53f 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -65,6 +65,16 @@ static const KeybindingRecord PARTY_KEYS[] = {
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
 };
 
+static const KeybindingRecord QUEST_LOG_KEYS[] = {
+	// I18N: ESC key
+	{ KEYBIND_ESCAPE, "ESCAPE", _s("Escape"), "ESCAPE", nullptr },
+	// I18N: Return key
+	{ KEYBIND_SELECT, "SELECT", _s("Select"), "RETURN", nullptr },
+	{ KEYBIND_QUEST_PREVIOUS, "QUEST_PREVIOUS", _s("Previous quest"), "UP", nullptr },
+	{ KEYBIND_QUEST_NEXT, "QUEST_NEXT", _s("Next quest"), "DOWN", nullptr },
+	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
+};
+
 static const KeybindingRecord COMBAT_KEYS[] = {
 	// I18N: Combat command in Might & Magic 1
 	{ KEYBIND_COMBAT_ATTACK, "COMBAT_ATTACK", _s("Attack"), "a", nullptr },
@@ -124,6 +134,7 @@ static const KeybindingRecord NORMAL_KEYS[] = {
 	// I18N: Action of hero party in Might & Magic 1
 	{ KEYBIND_UNLOCK, "UNLOCK", _s("Unlock"), "u", nullptr },
 	{ KEYBIND_QUICKREF, "QUICKREF", _s("Quick reference"), "q", nullptr },
+	{ KEYBIND_ACTIVE_QUEST, "ACTIVE_QUEST", _s("Show active quest"), "k", nullptr },
 
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
 };
@@ -140,6 +151,7 @@ static const char *const kMinimalKeymapId = "mm1_minimal";
 static const char *const kPartyKeymapId = "mm1_party";
 static const char *const kNormalKeymapId = "mm1_normal";
 static const char *const kCombatKeymapId = "mm1_combat";
+static const char *const kQuestLogKeymapId = "mm1_quest_log";
 static const char *const kCheatsKeymapId = "mm1_cheats";
 
 static Common::Keymap *g_menuKeymap = nullptr;
@@ -147,6 +159,7 @@ static Common::Keymap *g_minimalKeymap = nullptr;
 static Common::Keymap *g_partyKeymap = nullptr;
 static Common::Keymap *g_normalKeymap = nullptr;
 static Common::Keymap *g_combatKeymap = nullptr;
+static Common::Keymap *g_questLogKeymap = nullptr;
 static Common::Keymap *g_cheatsKeymap = nullptr;
 
 struct KeysRecord {
@@ -161,6 +174,7 @@ static const KeysRecord ALL_RECORDS[] = {
 	{ kNormalKeymapId,  _s("Might and Magic 1 - Main"), NORMAL_KEYS },
 	{ kPartyKeymapId,   _s("Might and Magic 1 - Party"), PARTY_KEYS },
 	{ kCombatKeymapId,  _s("Might and Magic 1 - Combat"), COMBAT_KEYS },
+	{ kQuestLogKeymapId, _s("Might and Magic 1 - Quest Log"), QUEST_LOG_KEYS },
 #if 0
 	{ kCheatsKeymapId,  _s("Might and Magic 1 - Cheats"), CHEAT_KEYS },
 #endif
@@ -176,6 +190,7 @@ static void initKeymapPointers() {
 	g_partyKeymap = mapper->getKeymap(kPartyKeymapId);
 	g_normalKeymap = mapper->getKeymap(kNormalKeymapId);
 	g_combatKeymap = mapper->getKeymap(kCombatKeymapId);
+	g_questLogKeymap = mapper->getKeymap(kQuestLogKeymapId);
 	g_cheatsKeymap = mapper->getKeymap(kCheatsKeymapId);
 }
 
@@ -224,6 +239,8 @@ static bool isKeymapEnabledForMode(const Common::Keymap *keymap, KeybindingMode
 		return mode == KBMODE_NORMAL;
 	if (keymap == g_combatKeymap)
 		return mode == KBMODE_COMBAT;
+	if (keymap == g_questLogKeymap)
+		return mode == KBMODE_QUEST_LOG;
 	if (keymap == g_cheatsKeymap)
 		return mode == KBMODE_NORMAL;
 
@@ -239,6 +256,7 @@ void MetaEngine::setKeybindingMode(KeybindingMode mode) {
 		g_partyKeymap,
 		g_normalKeymap,
 		g_combatKeymap,
+		g_questLogKeymap,
 		g_cheatsKeymap
 	};
 
diff --git a/engines/mm/mm1/metaengine.h b/engines/mm/mm1/metaengine.h
index 46eaec03cff..d3e32357dbb 100644
--- a/engines/mm/mm1/metaengine.h
+++ b/engines/mm/mm1/metaengine.h
@@ -49,6 +49,9 @@ enum KeybindingAction {
 	KEYBIND_SPELL,
 	KEYBIND_UNLOCK,
 	KEYBIND_QUICKREF,
+	KEYBIND_ACTIVE_QUEST,
+	KEYBIND_QUEST_PREVIOUS,
+	KEYBIND_QUEST_NEXT,
 	KEYBIND_VERSION,
 	KEYBIND_MENU,
 	KEYBIND_MINIMAP,
@@ -96,7 +99,8 @@ enum KeybindingMode {
 	KBMODE_MINIMAL,     ///< Keys for when doing text entry
 	KBMODE_PARTY_MENUS, ///< Keys for menus that allow switching party members
 	KBMODE_NORMAL,      ///< Keys available when normal in-game
-	KBMODE_COMBAT       ///< Keys when in combat mode
+	KBMODE_COMBAT,      ///< Keys when in combat mode
+	KBMODE_QUEST_LOG    ///< Keys when viewing the quest log
 };
 
 class MetaEngine {
diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
new file mode 100644
index 00000000000..a01c9634307
--- /dev/null
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -0,0 +1,368 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mm/mm1/views_enh/active_quest.h"
+#include "mm/mm1/globals.h"
+#include "mm/mm1/maps/maps.h"
+#include "mm/mm1/metaengine.h"
+
+namespace MM {
+namespace MM1 {
+namespace ViewsEnh {
+
+namespace {
+
+struct QuestLogEntry {
+	bool _completed = false;
+	Common::String _summary;
+	Common::String _title;
+	Common::String _details;
+};
+
+byte partyFlags(int index) {
+	byte result = 0;
+	for (uint i = 0; i < g_globals->_party.size(); ++i)
+		result |= g_globals->_party[i]._flags[index];
+	return result;
+}
+
+bool everyPartyMemberHas(int index, byte flags) {
+	if (g_globals->_party.empty())
+		return false;
+	for (uint i = 0; i < g_globals->_party.size(); ++i) {
+		if ((g_globals->_party[i]._flags[index] & flags) != flags)
+			return false;
+	}
+	return true;
+}
+
+void addEntry(Common::Array<QuestLogEntry> &entries, bool done,
+		const Common::String &title, const Common::String &details) {
+	QuestLogEntry entry;
+	entry._completed = done;
+	entry._title = title;
+	entry._summary = Common::String::format("[%s] %s",
+		done ? "Done" : "ACTIVE", title.c_str());
+	entry._details = details;
+	entries.push_back(entry);
+}
+
+void setFormalQuestText(Common::String &title, Common::String &details,
+		byte questNum) {
+	if (questNum >= 1 && questNum <= 7) {
+		title = "Lord Ironfist";
+		details = STRING[Common::String::format(
+			"maps.map43.quests.%d", questNum)];
+	} else if (questNum >= 8 && questNum <= 14) {
+		title = "Lord Inspectron";
+		details = STRING[Common::String::format(
+			"maps.map35.quests.%d", questNum - 8)];
+	} else if (questNum >= 15 && questNum <= 21) {
+		title = "Lord Hacker";
+		details = Common::String::format("%s %s",
+			STRING["maps.map36.hacker3"].c_str(),
+			STRING[Common::String::format("maps.map36.ingredients.%d",
+				questNum - 15)].c_str());
+	}
+}
+
+void addFormalQuest(Common::Array<QuestLogEntry> &entries) {
+	const byte questNum = g_globals->_party.empty() ?
+		0 : g_globals->_party[0]._quest;
+	if (!questNum || questNum == 255)
+		return;
+
+	Common::String title, details;
+	setFormalQuestText(title, details, questNum);
+	if (!title.empty())
+		addEntry(entries, false, title, details);
+}
+
+void addCourier(Common::Array<QuestLogEntry> &entries, bool done) {
+	const byte flags = partyFlags(0);
+	const bool dogStatueDone = (flags & CHARFLAG0_DOG_STATUE) != 0;
+	if (done != dogStatueDone)
+		return;
+	if (!done && !(flags & (CHARFLAG0_COURIER1 | CHARFLAG0_COURIER2 |
+			CHARFLAG0_COURIER3 | CHARFLAG0_ZOM_CLUE | CHARFLAG0_ZAM_CLUE |
+			CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)))
+		return;
+
+	// Later courier stages replace or clear some earlier flags, so infer
+	// completed steps from the prerequisites of the furthest known stage.
+	const bool strongholdDone = dogStatueDone || (flags & CHARFLAG0_40);
+	const bool chestDone = strongholdDone || (flags & CHARFLAG0_FOUND_CHEST);
+	const bool zamDone = chestDone || (flags & CHARFLAG0_ZAM_CLUE);
+	const bool zomDone = chestDone || (flags & CHARFLAG0_ZOM_CLUE);
+	const bool telgoranDone = zamDone || zomDone;
+	const bool agarDone = telgoranDone ||
+			(flags & (CHARFLAG0_COURIER2 | CHARFLAG0_COURIER3));
+
+	const int completed = (agarDone ? 1 : 0) + (telgoranDone ? 1 : 0) +
+			(zamDone ? 1 : 0) + (zomDone ? 1 : 0) +
+			(chestDone ? 1 : 0) + (strongholdDone ? 1 : 0) +
+			(dogStatueDone ? 1 : 0);
+
+	Common::String details = Common::String::format(
+			"Courier trail: %d/7 completed.", completed);
+	if (agarDone)
+		details += "\nAgar - delivered the courier's scroll.";
+	if (telgoranDone)
+		details += "\nTelgoran - completed the scroll delivery.";
+	if (zamDone && zomDone) {
+		details += "\nZam and Zom - received both clues.";
+	} else {
+		if (zamDone)
+			details += "\nZam - received his clue.";
+		if (zomDone)
+			details += "\nZom - received his clue.";
+	}
+	if (chestDone)
+		details += "\nHidden chest - recovered the Ruby Whistle.";
+	if (strongholdDone)
+		details += "\nAncient temple - entered the hidden stronghold.";
+	if (dogStatueDone)
+		details += "\nDog Statue - solved; reward claimed.";
+	else
+		details += "\nFollow the latest clue to its destination.";
+
+	addEntry(entries, done, "The Courier Mission", details);
+}
+
+void addAlamarStory(Common::Array<QuestLogEntry> &entries, bool done) {
+	const byte flags = partyFlags(13);
+	const bool won = (flags & CHARFLAG13_80) != 0;
+
+	if (done) {
+		if (won) {
+			addEntry(entries, true, "The Inner Sanctum",
+				"Completed the final quest within the Inner Sanctum.");
+		}
+		return;
+	}
+
+	if (won)
+		return;
+
+	const byte questNum = g_globals->_party.empty() ?
+		0 : g_globals->_party[0]._quest;
+	const byte projectorFlags[5] = {
+		CHARFLAG13_1, CHARFLAG13_2, CHARFLAG13_4,
+		CHARFLAG13_8, CHARFLAG13_10
+	};
+	const byte projectors = CHARFLAG13_1 | CHARFLAG13_2 | CHARFLAG13_4 |
+		CHARFLAG13_8 | CHARFLAG13_10;
+	int projectorCount = 0;
+	for (uint i = 0; i < 5; ++i) {
+		if (everyPartyMemberHas(13, projectorFlags[i]))
+			++projectorCount;
+	}
+	const bool defeatedImposter = (flags & CHARFLAG13_ALAMAR) != 0;
+	const bool inSoulMaze = g_maps && g_maps->_currentMap &&
+		g_maps->_currentMap->getMapIndex() == 48;
+	const bool hasEye = g_globals->_party.hasItem(EYE_OF_GOROS_ID);
+
+	// Later story evidence takes precedence over the stale quest byte 255.
+	// The original game does not clear 255 automatically after the fake
+	// Crypt of Carmenca assignment.
+	if (defeatedImposter || (flags & projectors)) {
+		if (projectorCount < 5) {
+			addEntry(entries, false, "The Inner Sanctum",
+				Common::String::format(
+					"Find and activate all five magical projectors.\nProgress: %d/5.",
+					projectorCount));
+		} else {
+			addEntry(entries, false, "The Inner Sanctum",
+				"All five projectors are active.\nProgress: 5/5.\nContinue into the Inner Sanctum.");
+		}
+		return;
+	}
+
+	if (inSoulMaze) {
+		addEntry(entries, false, "Soul Maze",
+			"Find your captor's name hidden within the Soul Maze, then report it to Orango.");
+		return;
+	}
+
+	if (hasEye) {
+		addEntry(entries, false, "The Eye of Goros",
+			"Confront King Alamar with the Eye of Goros.");
+		return;
+	}
+
+	if (questNum == 255) {
+		addEntry(entries, false, "King Alamar",
+			"Find the Crypt of Carmenca.");
+	}
+}
+
+void addRanalou(Common::Array<QuestLogEntry> &entries, bool done) {
+	struct PrisonerJudgmentRecord {
+		byte _flag;
+		const char *_location;
+		const char *_name;
+	};
+	static const PrisonerJudgmentRecord PRISONERS[] = {
+		{ CHARFLAG1_2,  "Dragadune Ruins",         "Mutated prisoner" },
+		{ CHARFLAG1_4,  "Castle Doom",             "Child prisoner" },
+		{ CHARFLAG1_8,  "Castle Alamar",           "Maiden prisoner" },
+		{ CHARFLAG1_10, "Castle White Wolf",       "Demon prisoner" },
+		{ CHARFLAG1_20, "Castle Blackridge North", "Man in shackles" },
+		{ CHARFLAG1_40, "Castle Blackridge South", "Cloaked prisoner" }
+	};
+
+	const byte flags = partyFlags(1);
+	const bool started = (flags & CHARFLAG1_1) != 0;
+	const bool finished = everyPartyMemberHas(1, CHARFLAG1_WORTHY);
+	int judged = 0;
+
+	for (uint i = 0; i < ARRAYSIZE(PRISONERS); ++i) {
+		if (flags & PRISONERS[i]._flag)
+			++judged;
+	}
+
+	// A prisoner may be encountered before the party meets Wizard Ranalou.
+	// Existing character flags let the log show that visit without adding any
+	// new save data. The exact judgment choice is not stored by MM1.
+	if (done != finished || (!done && !started && !judged))
+		return;
+
+	Common::String details = done ?
+		"Reward received. Prisoners judged:" :
+		Common::String::format("Prisoners judged: %d/6.", judged);
+	for (uint i = 0; i < ARRAYSIZE(PRISONERS); ++i) {
+		if (flags & PRISONERS[i]._flag) {
+			details += Common::String::format("\n%s - %s",
+				PRISONERS[i]._location, PRISONERS[i]._name);
+		}
+	}
+
+	addEntry(entries, done,
+		started || done ? "Wizard Ranalou's Prisoners" : "Prisoner Judgments",
+		details);
+}
+
+void addOtherReliableCompletions(Common::Array<QuestLogEntry> &entries) {
+	if (g_globals->_party.hasItem(KINGS_PASS_ID))
+		addEntry(entries, true, "The King's Pass", "Obtained the King's Pass from Percella.");
+	if (g_globals->_party.hasItem(KEY_CARD_ID))
+		addEntry(entries, true, "The Volcano God's Riddle", "Solved the Volcano God's riddle and obtained the Key Card.");
+}
+
+void buildQuestList(Common::Array<QuestLogEntry> &entries) {
+	// Active objectives always appear first
+	addFormalQuest(entries);
+	addCourier(entries, false);
+	addAlamarStory(entries, false);
+	addRanalou(entries, false);
+
+	// Completed history follows and remains selectable with Up/Down
+	addCourier(entries, true);
+	addAlamarStory(entries, true);
+	addRanalou(entries, true);
+	addOtherReliableCompletions(entries);
+}
+
+} // namespace
+
+ActiveQuest::ActiveQuest() : ScrollView("ActiveQuest") {
+	setBounds(Common::Rect(0, 0, 320, 200));
+	addButton(&g_globals->_escSprites, Common::Point(134, 170),
+		0, KEYBIND_ESCAPE, true);
+}
+
+bool ActiveQuest::msgFocus(const FocusMessage &msg) {
+	MetaEngine::setKeybindingMode(KeybindingMode::KBMODE_QUEST_LOG);
+	_selectedQuest = 0;
+	return ScrollView::msgFocus(msg);
+}
+
+void ActiveQuest::draw() {
+	ScrollView::draw();
+	setReduced(false);
+	writeLine(0, "Quest Log", ALIGN_MIDDLE);
+	writeString(152, 172, STRING["enhdialogs.misc.go_back"]);
+
+	Common::Array<QuestLogEntry> entries;
+	buildQuestList(entries);
+	if (entries.empty()) {
+		setReduced(true);
+		writeLine(8, "No tracked quests.", ALIGN_MIDDLE);
+		return;
+	}
+
+	if (_selectedQuest >= (int)entries.size())
+		_selectedQuest = entries.size() - 1;
+	if (_selectedQuest < 0)
+		_selectedQuest = 0;
+
+	const int first = (_selectedQuest / 5) * 5;
+	setReduced(true);
+	for (int row = 0; row < 5 && first + row < (int)entries.size(); ++row) {
+		const int index = first + row;
+		writeString(8, 28 + row * 8, Common::String::format("%s%s",
+			index == _selectedQuest ? "> " : "  ", entries[index]._summary.c_str()));
+	}
+
+	const QuestLogEntry &selected = entries[_selectedQuest];
+	setReduced(false);
+	writeString(0, 76, selected._title, ALIGN_MIDDLE);
+	setReduced(true);
+	Common::StringArray lines = splitLines(selected._details);
+	for (uint i = 0, y = 100; i < lines.size() && y <= 148; ++i, y += 8)
+		writeString(0, y, lines[i], ALIGN_MIDDLE);
+	writeString(0, 158, "Up/Down: select", ALIGN_MIDDLE);
+}
+
+void ActiveQuest::moveSelection(int delta) {
+	Common::Array<QuestLogEntry> entries;
+	buildQuestList(entries);
+	if (entries.empty())
+		return;
+	_selectedQuest += delta;
+	if (_selectedQuest < 0)
+		_selectedQuest = entries.size() - 1;
+	else if (_selectedQuest >= (int)entries.size())
+		_selectedQuest = 0;
+	redraw();
+}
+
+bool ActiveQuest::msgAction(const ActionMessage &msg) {
+	switch (msg._action) {
+	case KEYBIND_QUEST_PREVIOUS:
+		moveSelection(-1);
+		return true;
+	case KEYBIND_QUEST_NEXT:
+		moveSelection(1);
+		return true;
+	case KEYBIND_ESCAPE:
+	case KEYBIND_SELECT:
+		close();
+		return true;
+	default:
+		break;
+	}
+	return false;
+}
+
+} // namespace ViewsEnh
+} // namespace MM1
+} // namespace MM
diff --git a/engines/mm/mm1/views_enh/active_quest.h b/engines/mm/mm1/views_enh/active_quest.h
new file mode 100644
index 00000000000..23958d752ea
--- /dev/null
+++ b/engines/mm/mm1/views_enh/active_quest.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MM1_VIEWS_ENH_ACTIVE_QUEST_H
+#define MM1_VIEWS_ENH_ACTIVE_QUEST_H
+
+#include "mm/mm1/views_enh/scroll_view.h"
+
+namespace MM {
+namespace MM1 {
+namespace ViewsEnh {
+
+class ActiveQuest : public ScrollView {
+private:
+	int _selectedQuest = 0;
+
+	void moveSelection(int delta);
+
+public:
+	ActiveQuest();
+	~ActiveQuest() override {}
+
+	bool msgFocus(const FocusMessage &msg) override;
+	void draw() override;
+	bool msgAction(const ActionMessage &msg) override;
+};
+
+} // namespace ViewsEnh
+} // namespace MM1
+} // namespace MM
+
+#endif
diff --git a/engines/mm/mm1/views_enh/dialogs.h b/engines/mm/mm1/views_enh/dialogs.h
index 3d1107a4f81..39675502000 100644
--- a/engines/mm/mm1/views_enh/dialogs.h
+++ b/engines/mm/mm1/views_enh/dialogs.h
@@ -39,6 +39,7 @@
 #include "mm/mm1/views_enh/game_messages.h"
 #include "mm/mm1/views_enh/main_menu.h"
 #include "mm/mm1/views_enh/map_popup.h"
+#include "mm/mm1/views_enh/active_quest.h"
 #include "mm/mm1/views_enh/protect.h"
 #include "mm/mm1/views_enh/quick_ref.h"
 #include "mm/mm1/views_enh/rest.h"
@@ -166,6 +167,7 @@ private:
 	ViewsEnh::GameMessages _gameMessages;
 	ViewsEnh::MainMenu _mainMenu;
 	ViewsEnh::MapPopup _mapPopup;
+	ViewsEnh::ActiveQuest _activeQuest;
 	ViewsEnh::Protect _protect;
 	ViewsEnh::QuickRef _quickRef;
 	ViewsEnh::Rest _rest;
diff --git a/engines/mm/mm1/views_enh/game.cpp b/engines/mm/mm1/views_enh/game.cpp
index 052d277bdbe..2b9042260c1 100644
--- a/engines/mm/mm1/views_enh/game.cpp
+++ b/engines/mm/mm1/views_enh/game.cpp
@@ -90,6 +90,9 @@ bool Game::msgAction(const ActionMessage &msg) {
 	case KEYBIND_QUICKREF:
 		addView("QuickRef");
 		return true;
+	case KEYBIND_ACTIVE_QUEST:
+		addView("ActiveQuest");
+		return true;
 	case KEYBIND_REST:
 		addView("Rest");
 		return true;
diff --git a/engines/mm/mm1/views_enh/game_commands.cpp b/engines/mm/mm1/views_enh/game_commands.cpp
index 08928319139..a60f067efc2 100644
--- a/engines/mm/mm1/views_enh/game_commands.cpp
+++ b/engines/mm/mm1/views_enh/game_commands.cpp
@@ -42,6 +42,8 @@ GameCommands::GameCommands(UIElement *owner) :
 
 	addButton(Common::Rect(235, 117, 259, 137), KEYBIND_MAP, 12, spr);
 	addButton(Common::Rect(260, 117, 284, 137), KEYBIND_QUICKREF, 16, spr);
+	// Complete the 3x3 command grid with the native Quest Log icon
+	addButton(Common::Rect(286, 117, 310, 137), KEYBIND_ACTIVE_QUEST, 8, spr);
 
 	addButton(Common::Rect(109, 137, 122, 147), KEYBIND_MENU, 18, spr);
 	addButton(Common::Rect(235, 148, 259, 168), KEYBIND_TURN_LEFT, 20, spr);
diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index cc6c4b28fd4..615627237a1 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -138,6 +138,7 @@ MODULE_OBJS += \
 	mm1/views/trap.o \
 	mm1/views/unlock.o \
 	mm1/views/wheel_spin.o \
+	mm1/views_enh/active_quest.o \
 	mm1/views_enh/spells/cast_spell.o \
 	mm1/views_enh/spells/spellbook.o \
 	mm1/views_enh/spells/detect_magic.o \


Commit: 70c5c7792a259dc05ba6af21b4173264791f5567
    https://github.com/scummvm/scummvm/commit/70c5c7792a259dc05ba6af21b4173264791f5567
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:46+11:00

Commit Message:
MM: MM1: Record completed ruler quests for the Quest Log

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map35.cpp
    engines/mm/mm1/maps/map36.cpp
    engines/mm/mm1/maps/map43.cpp
    engines/mm/mm1/views_enh/active_quest.cpp


diff --git a/engines/mm/mm1/maps/map35.cpp b/engines/mm/mm1/maps/map35.cpp
index fbb25aa63ff..1f2a82a8f79 100644
--- a/engines/mm/mm1/maps/map35.cpp
+++ b/engines/mm/mm1/maps/map35.cpp
@@ -177,7 +177,10 @@ Common::String Map35::checkQuestComplete() {
 	int qIndex = leader._quest - 8;
 
 	if (leader._flags[5] & MATCH_FLAGS[qIndex] & 0x7f) {
-		// The quest was complete
+		// The quest was complete. Retain it in the Quest Log using
+		// the high bit of a reserved, already-serialized map cell
+		_visited[15 * MAP_W + qIndex] |= 0x80;
+
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
 			Character &c = g_globals->_party[i];
 			c._quest = 0;
diff --git a/engines/mm/mm1/maps/map36.cpp b/engines/mm/mm1/maps/map36.cpp
index aab63ef70b0..04e9b156db9 100644
--- a/engines/mm/mm1/maps/map36.cpp
+++ b/engines/mm/mm1/maps/map36.cpp
@@ -188,7 +188,10 @@ Common::String Map36::checkQuestComplete() {
 	int qIndex = leader._quest - 15;
 
 	if (leader._flags[6] & MATCH_FLAGS[qIndex] & 0x7f) {
-		// The quest was complete
+		// The quest was complete. Retain it in the Quest Log using
+		// the high bit of a reserved, already-serialized map cell
+		_visited[15 * MAP_W + qIndex] |= 0x80;
+
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
 			Character &c = g_globals->_party[i];
 			c._quest = 0;
diff --git a/engines/mm/mm1/maps/map43.cpp b/engines/mm/mm1/maps/map43.cpp
index 14c8d37d709..7f735906642 100644
--- a/engines/mm/mm1/maps/map43.cpp
+++ b/engines/mm/mm1/maps/map43.cpp
@@ -201,7 +201,10 @@ Common::String Map43::checkQuestComplete() {
 	int qIndex = leader._quest - 1;
 
 	if (leader._flags[7] & MATCH_FLAGS[qIndex] & 0x7f) {
-		// The quest was complete
+		// The quest was complete. Retain it in the Quest Log using
+		// the high bit of a reserved, already-serialized map cell
+		_visited[15 * MAP_W + qIndex] |= 0x80;
+
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
 			Character &c = g_globals->_party[i];
 			c._quest = 0;
diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
index a01c9634307..3bba3ff7bc4 100644
--- a/engines/mm/mm1/views_enh/active_quest.cpp
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -260,6 +260,37 @@ void addRanalou(Common::Array<QuestLogEntry> &entries, bool done) {
 		details);
 }
 
+byte partyQuestHistory(int mapIndex) {
+	const Maps::Map &map = *g_maps->getMap(mapIndex);
+	byte result = 0;
+	for (int i = 0; i < 7; ++i) {
+		if (map._visited[15 * MAP_W + i] & 0x80)
+			result |= 1 << i;
+	}
+	return result;
+}
+
+void addRulerHistory(Common::Array<QuestLogEntry> &entries,
+		byte flags, int firstQuest) {
+	if (flags & 0x80)
+		flags |= 0x7f;
+	for (int i = 0; i < 7; ++i) {
+		if (!(flags & (1 << i)))
+			continue;
+		Common::String title, details;
+		setFormalQuestText(title, details, firstQuest + i);
+		addEntry(entries, true,
+			Common::String::format("%s #%d", title.c_str(), i + 1),
+			details + " Completed.");
+	}
+}
+
+void addCompletedRulerQuests(Common::Array<QuestLogEntry> &entries) {
+	addRulerHistory(entries, partyFlags(10) | partyQuestHistory(43), 1);
+	addRulerHistory(entries, partyFlags(8) | partyQuestHistory(35), 8);
+	addRulerHistory(entries, partyFlags(9) | partyQuestHistory(36), 15);
+}
+
 void addOtherReliableCompletions(Common::Array<QuestLogEntry> &entries) {
 	if (g_globals->_party.hasItem(KINGS_PASS_ID))
 		addEntry(entries, true, "The King's Pass", "Obtained the King's Pass from Percella.");
@@ -275,6 +306,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	addRanalou(entries, false);
 
 	// Completed history follows and remains selectable with Up/Down
+	addCompletedRulerQuests(entries);
 	addCourier(entries, true);
 	addAlamarStory(entries, true);
 	addRanalou(entries, true);


Commit: 51e46d5a1e513bec74e49251b82d38c4a30813e4
    https://github.com/scummvm/scummvm/commit/51e46d5a1e513bec74e49251b82d38c4a30813e4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:46+11:00

Commit Message:
MM: MM1: Add the Crazed Wizard to the Quest Log

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map06.cpp
    engines/mm/mm1/maps/map06.h
    engines/mm/mm1/views_enh/active_quest.cpp


diff --git a/engines/mm/mm1/maps/map06.cpp b/engines/mm/mm1/maps/map06.cpp
index accb9f31f0d..43ceb93e80a 100644
--- a/engines/mm/mm1/maps/map06.cpp
+++ b/engines/mm/mm1/maps/map06.cpp
@@ -32,6 +32,8 @@ namespace Maps {
 #define VAL1 446
 #define VAL2 447
 #define ENCOUNTER_COMPLETE 0x80
+#define QUEST_ACTIVE_OFFSET (15 * MAP_W + 15)
+#define QUEST_COMPLETE_OFFSET (15 * MAP_W + 13)
 
 static const byte MONSTER_ID1[13] = {
 	2, 1, 8, 9, 5, 9, 2, 7, 15, 4, 11, 1, 5
@@ -178,6 +180,10 @@ void Map06::special13() {
 	Game::Encounter &enc = g_globals->_encounters;
 	g_maps->clearSpecial();
 
+	// Encounter progress also starts the side quest if the wizard
+	// hasn't been spoken to first
+	_visited[QUEST_ACTIVE_OFFSET] |= ENCOUNTER_COMPLETE;
+
 	// Record completion in the map visitation data, which is serialized
 	// in savegames, so progress survives saving and loading
 	byte &visited = _visited[g_maps->_mapOffset];
@@ -205,12 +211,16 @@ void Map06::special13() {
 
 void Map06::special26() {
 	if (completedEncounters() < 13) {
+		// Talking to the wizard starts the tracked side quest
+		_visited[QUEST_ACTIVE_OFFSET] |= ENCOUNTER_COMPLETE;
 		send(SoundMessage(STRING["maps.map06.wizard"]));
 	} else {
 		g_globals->_treasure.setGold(12000);
 		g_globals->_treasure._items[1] = BRONZE_KEY_ID;
 		g_globals->_treasure._items[2] = getRandomNumber(12) + 182;
 		clearEncounterProgress();
+		_visited[QUEST_ACTIVE_OFFSET] &= ~ENCOUNTER_COMPLETE;
+		_visited[QUEST_COMPLETE_OFFSET] |= ENCOUNTER_COMPLETE;
 		g_events->addAction(KEYBIND_SEARCH);
 	}
 }
@@ -229,6 +239,15 @@ int Map06::completedEncounters() const {
 	return result;
 }
 
+bool Map06::crazedWizardQuestActive() const {
+	return (_visited[QUEST_ACTIVE_OFFSET] & ENCOUNTER_COMPLETE) ||
+		completedEncounters() != 0;
+}
+
+bool Map06::crazedWizardQuestCompleted() const {
+	return (_visited[QUEST_COMPLETE_OFFSET] & ENCOUNTER_COMPLETE) != 0;
+}
+
 void Map06::clearEncounterProgress() {
 	for (int y = 0; y < 13; ++y) {
 		for (int x = 0; x < MAP_W; ++x)
diff --git a/engines/mm/mm1/maps/map06.h b/engines/mm/mm1/maps/map06.h
index 0b108fcfca7..397e938c445 100644
--- a/engines/mm/mm1/maps/map06.h
+++ b/engines/mm/mm1/maps/map06.h
@@ -78,6 +78,8 @@ public:
 	Map06() : Map(6, "cave2", 0x1, 1) {}
 
 	int completedEncounters() const;
+	bool crazedWizardQuestActive() const;
+	bool crazedWizardQuestCompleted() const;
 
 	/**
 	 * Handles all special stuff that happens on the map
diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
index 3bba3ff7bc4..ffc85d89078 100644
--- a/engines/mm/mm1/views_enh/active_quest.cpp
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -21,6 +21,7 @@
 
 #include "mm/mm1/views_enh/active_quest.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/maps/map06.h"
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/metaengine.h"
 
@@ -37,6 +38,10 @@ struct QuestLogEntry {
 	Common::String _details;
 };
 
+Maps::Map06 &crazedWizardMap() {
+	return *static_cast<Maps::Map06 *>(g_maps->getMap(6));
+}
+
 byte partyFlags(int index) {
 	byte result = 0;
 	for (uint i = 0; i < g_globals->_party.size(); ++i)
@@ -96,6 +101,26 @@ void addFormalQuest(Common::Array<QuestLogEntry> &entries) {
 		addEntry(entries, false, title, details);
 }
 
+void addCrazedWizard(Common::Array<QuestLogEntry> &entries, bool done) {
+	Maps::Map06 &map = crazedWizardMap();
+	if (done != map.crazedWizardQuestCompleted())
+		return;
+	if (!done && !map.crazedWizardQuestActive())
+		return;
+
+	const int count = map.completedEncounters();
+	if (done) {
+		addEntry(entries, true, "Crazed Wizard",
+			"Defeated all 13 challenge encounters and collected the reward.");
+	} else if (count < 13) {
+		addEntry(entries, false, "Crazed Wizard",
+			Common::String::format("Defeat the 13 challenge encounters. Progress: %d/13.", count));
+	} else {
+		addEntry(entries, false, "Crazed Wizard",
+			"All 13 encounters are complete. Return to the Crazed Wizard.");
+	}
+}
+
 void addCourier(Common::Array<QuestLogEntry> &entries, bool done) {
 	const byte flags = partyFlags(0);
 	const bool dogStatueDone = (flags & CHARFLAG0_DOG_STATUE) != 0;
@@ -301,12 +326,14 @@ void addOtherReliableCompletions(Common::Array<QuestLogEntry> &entries) {
 void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	// Active objectives always appear first
 	addFormalQuest(entries);
+	addCrazedWizard(entries, false);
 	addCourier(entries, false);
 	addAlamarStory(entries, false);
 	addRanalou(entries, false);
 
 	// Completed history follows and remains selectable with Up/Down
 	addCompletedRulerQuests(entries);
+	addCrazedWizard(entries, true);
 	addCourier(entries, true);
 	addAlamarStory(entries, true);
 	addRanalou(entries, true);


Commit: e81eeac3a6036e16704cc0cc47b06da5a79a6d14
    https://github.com/scummvm/scummvm/commit/e81eeac3a6036e16704cc0cc47b06da5a79a6d14
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:46+11:00

Commit Message:
MM: MM1: Track the Clerics of the South gong tones and show them in the Quest Log

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map47.cpp
    engines/mm/mm1/views_enh/active_quest.cpp


diff --git a/engines/mm/mm1/maps/map47.cpp b/engines/mm/mm1/maps/map47.cpp
index cf56a10cee9..bc40b1327a7 100644
--- a/engines/mm/mm1/maps/map47.cpp
+++ b/engines/mm/mm1/maps/map47.cpp
@@ -30,6 +30,10 @@ namespace MM1 {
 namespace Maps {
 
 #define ARRAY1 250
+#define QUEST_CLERICS_TONE_OFFSET (15 * MAP_W + 10)
+#define QUEST_CLERICS_COMPLETE_OFFSET (15 * MAP_W + 13)
+#define QUEST_CLERICS_ACTIVE_OFFSET (15 * MAP_W + 15)
+#define QUEST_LOG_FLAG 0x80
 
 void Map47::special() {
 	// Scan for special actions on the map cell
@@ -68,6 +72,10 @@ void Map47::special01() {
 			Map47 &map = *static_cast<Map47 *>(g_maps->_currentMap);
 			int toneNum = g_events->getRandomNumber(6);
 			if (toneNum < 4) {
+				// Record the tone for the quest and its Quest Log progress
+				map._data[ARRAY1 + toneNum - 1] = 1;
+				map._visited[QUEST_CLERICS_TONE_OFFSET +
+					toneNum - 1] |= QUEST_LOG_FLAG;
 				g_events->send(SoundMessage(STRING[
 					Common::String::format("maps.map47.tones.%d", toneNum)]));
 			} else {
@@ -78,12 +86,36 @@ void Map47::special01() {
 }
 
 void Map47::special02() {
-	if (_data[ARRAY1] && _data[ARRAY1 + 1] && _data[ARRAY1 + 2]) {
+	// Tones are also kept in the saved visit data, since the overlay
+	// data is reloaded from disk when a save is loaded
+	bool heard[3];
+	for (int i = 0; i < 3; ++i) {
+		heard[i] = _data[ARRAY1 + i] ||
+			(_visited[QUEST_CLERICS_TONE_OFFSET + i] & QUEST_LOG_FLAG);
+	}
+
+	if (heard[0] && heard[1] && heard[2]) {
 		send(SoundMessage(STRING["maps.map47.clerics3"]));
+		_visited[QUEST_CLERICS_COMPLETE_OFFSET] |= QUEST_LOG_FLAG;
+		_visited[QUEST_CLERICS_ACTIVE_OFFSET] &= ~QUEST_LOG_FLAG;
+
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
 			Character &c = g_globals->_party[i];
 			c._flags[11] = CHARFLAG11_CLERICS;
 		}
+
+		// Another advancement requires hearing all three tones again
+		for (int i = 0; i < 3; ++i) {
+			_data[ARRAY1 + i] = 0;
+			_visited[QUEST_CLERICS_TONE_OFFSET + i] &= ~QUEST_LOG_FLAG;
+		}
+	} else {
+		// Speaking to the Clerics starts the optional advancement quest
+		_visited[QUEST_CLERICS_ACTIVE_OFFSET] |= QUEST_LOG_FLAG;
+		send(SoundMessage(
+			0, 1, STRING["maps.map47.clerics1"],
+			0, 2, STRING["maps.map47.clerics2"]
+		));
 	}
 }
 
diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
index ffc85d89078..f0fe563be51 100644
--- a/engines/mm/mm1/views_enh/active_quest.cpp
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -31,6 +31,13 @@ namespace ViewsEnh {
 
 namespace {
 
+enum ClericsQuestStorage {
+	CLERICS_TONE_OFFSET = 15 * MAP_W + 10,
+	CLERICS_COMPLETE_OFFSET = 15 * MAP_W + 13,
+	CLERICS_ACTIVE_OFFSET = 15 * MAP_W + 15,
+	CLERICS_LOG_FLAG = 0x80
+};
+
 struct QuestLogEntry {
 	bool _completed = false;
 	Common::String _summary;
@@ -42,6 +49,10 @@ Maps::Map06 &crazedWizardMap() {
 	return *static_cast<Maps::Map06 *>(g_maps->getMap(6));
 }
 
+Maps::Map &clericsMap() {
+	return *g_maps->getMap(47);
+}
+
 byte partyFlags(int index) {
 	byte result = 0;
 	for (uint i = 0; i < g_globals->_party.size(); ++i)
@@ -121,6 +132,45 @@ void addCrazedWizard(Common::Array<QuestLogEntry> &entries, bool done) {
 	}
 }
 
+bool clericsCompleted() {
+	if (clericsMap()._visited[CLERICS_COMPLETE_OFFSET] & CLERICS_LOG_FLAG)
+		return true;
+	return (partyFlags(11) & CHARFLAG11_CLERICS) != 0;
+}
+
+int clericsTones() {
+	int result = 0;
+	for (int i = 0; i < 3; ++i) {
+		if (clericsMap()._visited[CLERICS_TONE_OFFSET + i] & CLERICS_LOG_FLAG)
+			++result;
+	}
+	return result;
+}
+
+void addClerics(Common::Array<QuestLogEntry> &entries, bool done) {
+	const bool completed = clericsCompleted();
+	const bool active =
+		(clericsMap()._visited[CLERICS_ACTIVE_OFFSET] & CLERICS_LOG_FLAG) != 0;
+
+	// The Clerics advancement is repeatable. A previous completion belongs
+	// in history, but must not suppress a later active 0/3..3/3 cycle.
+	if (done) {
+		if (!completed)
+			return;
+	} else {
+		if (!active)
+			return;
+	}
+
+	if (done) {
+		addEntry(entries, true, "Clerics of the South",
+			"Heard all three gong tones and returned to the Clerics of the South.");
+	} else {
+		addEntry(entries, false, "Clerics of the South",
+			Common::String::format("Hear all three gong tones in the Dragadune Ruins, then return to the Clerics. Progress: %d/3.", clericsTones()));
+	}
+}
+
 void addCourier(Common::Array<QuestLogEntry> &entries, bool done) {
 	const byte flags = partyFlags(0);
 	const bool dogStatueDone = (flags & CHARFLAG0_DOG_STATUE) != 0;
@@ -327,6 +377,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	// Active objectives always appear first
 	addFormalQuest(entries);
 	addCrazedWizard(entries, false);
+	addClerics(entries, false);
 	addCourier(entries, false);
 	addAlamarStory(entries, false);
 	addRanalou(entries, false);
@@ -334,6 +385,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	// Completed history follows and remains selectable with Up/Down
 	addCompletedRulerQuests(entries);
 	addCrazedWizard(entries, true);
+	addClerics(entries, true);
 	addCourier(entries, true);
 	addAlamarStory(entries, true);
 	addRanalou(entries, true);


Commit: cac041ed6b0d66ee22a72c8289a58a1aa0bd1a39
    https://github.com/scummvm/scummvm/commit/cac041ed6b0d66ee22a72c8289a58a1aa0bd1a39
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:47+11:00

Commit Message:
MM: MM1: Add the Color Bridge to the Quest Log

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map17.cpp
    engines/mm/mm1/maps/map17.h
    engines/mm/mm1/maps/map23.cpp
    engines/mm/mm1/maps/map23.h
    engines/mm/mm1/views_enh/active_quest.cpp


diff --git a/engines/mm/mm1/maps/map17.cpp b/engines/mm/mm1/maps/map17.cpp
index 83474de4845..6cc5d19f455 100644
--- a/engines/mm/mm1/maps/map17.cpp
+++ b/engines/mm/mm1/maps/map17.cpp
@@ -31,6 +31,8 @@ namespace Maps {
 
 #define VAL1 509
 #define CORRECT_ANSWERS 511
+#define COLOR_BRIDGE_VISITED_OFFSET (6 * MAP_W + 4)
+#define COLOR_BRIDGE_DISCOVERED 0x80
 
 void Map17::special() {
 	Game::Encounter &enc = g_globals->_encounters;
@@ -78,6 +80,10 @@ void Map17::special00() {
 }
 
 void Map17::special01() {
+	// Record that the Hooded Figure / Color Bridge has been discovered,
+	// using a fixed bit in the saved visit data
+	_visited[COLOR_BRIDGE_VISITED_OFFSET] |= COLOR_BRIDGE_DISCOVERED;
+
 	SoundMessage msg(STRING["maps.map17.bridge"],
 		[]() {
 			g_events->addView("ColorQuestions");
@@ -163,6 +169,12 @@ void Map17::askQuestion(uint partyIndex) {
 	}
 }
 */
+bool Map17::colorBridgeVisited() const {
+	const byte visited = _visited[COLOR_BRIDGE_VISITED_OFFSET];
+	return (visited & COLOR_BRIDGE_DISCOVERED) != 0 ||
+		(visited & 0x7f) != 0;
+}
+
 } // namespace Maps
 } // namespace MM1
 } // namespace MM
diff --git a/engines/mm/mm1/maps/map17.h b/engines/mm/mm1/maps/map17.h
index e01a676af83..dfdfb93de1a 100644
--- a/engines/mm/mm1/maps/map17.h
+++ b/engines/mm/mm1/maps/map17.h
@@ -54,6 +54,12 @@ public:
 	 * Handles all special stuff that happens on the map
 	 */
 	void special() override;
+
+	/**
+	 * True once the Hooded Figure / Color Bridge event has been encountered.
+	 * Ordinary visited-cell state is accepted for pre-patch save recovery.
+	 */
+	bool colorBridgeVisited() const;
 };
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map23.cpp b/engines/mm/mm1/maps/map23.cpp
index 2c942565c74..7eabf20d03a 100644
--- a/engines/mm/mm1/maps/map23.cpp
+++ b/engines/mm/mm1/maps/map23.cpp
@@ -31,6 +31,8 @@ namespace Maps {
 
 #define VAL1 107
 #define VAL2 108
+#define GYPSY_VISITED_OFFSET (11 * MAP_W + 9)
+#define COLOR_BRIDGE_DISCOVERED 0x80
 
 void Map23::special() {
 	// Scan for special actions on the map cell
@@ -81,6 +83,7 @@ void Map23::special01() {
 }
 
 void Map23::special02() {
+	_visited[GYPSY_VISITED_OFFSET] |= COLOR_BRIDGE_DISCOVERED;
 	g_events->addView("Gypsy");
 }
 
@@ -207,6 +210,12 @@ void Map23::fountain() {
 	));
 }
 
+bool Map23::gypsyVisited() const {
+	const byte visited = _visited[GYPSY_VISITED_OFFSET];
+	return (visited & COLOR_BRIDGE_DISCOVERED) != 0 ||
+		(visited & 0x7f) != 0;
+}
+
 } // namespace Maps
 } // namespace MM1
 } // namespace MM
diff --git a/engines/mm/mm1/maps/map23.h b/engines/mm/mm1/maps/map23.h
index 7036e3491b8..9d9f9429a1e 100644
--- a/engines/mm/mm1/maps/map23.h
+++ b/engines/mm/mm1/maps/map23.h
@@ -66,6 +66,12 @@ public:
 	 * Handles all special stuff that happens on the map
 	 */
 	void special() override;
+
+	/**
+	 * True once the Gypsy event has been encountered.
+	 * Ordinary visited-cell state is accepted for pre-patch save recovery.
+	 */
+	bool gypsyVisited() const;
 };
 
 } // namespace Maps
diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
index f0fe563be51..fe1c68e42d8 100644
--- a/engines/mm/mm1/views_enh/active_quest.cpp
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -22,6 +22,8 @@
 #include "mm/mm1/views_enh/active_quest.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/maps/map06.h"
+#include "mm/mm1/maps/map17.h"
+#include "mm/mm1/maps/map23.h"
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/metaengine.h"
 
@@ -53,6 +55,14 @@ Maps::Map &clericsMap() {
 	return *g_maps->getMap(47);
 }
 
+Maps::Map17 &colorBridgeMap() {
+	return *static_cast<Maps::Map17 *>(g_maps->getMap(17));
+}
+
+Maps::Map23 &gypsyMap() {
+	return *static_cast<Maps::Map23 *>(g_maps->getMap(23));
+}
+
 byte partyFlags(int index) {
 	byte result = 0;
 	for (uint i = 0; i < g_globals->_party.size(); ++i)
@@ -289,6 +299,47 @@ void addAlamarStory(Common::Array<QuestLogEntry> &entries, bool done) {
 	}
 }
 
+void addSigns(Common::Array<QuestLogEntry> &entries, bool done) {
+	const bool gypsySeen = gypsyMap().gypsyVisited();
+	const bool hoodedSeen = colorBridgeMap().colorBridgeVisited();
+	const bool hasCoralKey = g_globals->_party.hasItem(CORAL_KEY_ID);
+	int assigned = 0;
+
+	for (uint i = 0; i < g_globals->_party.size(); ++i) {
+		if (g_globals->_party[i]._flags[4] & CHARFLAG4_ASSIGNED)
+			++assigned;
+	}
+
+	// Assigned signs also prove a Gypsy visit in older saves.
+	const bool gypsyKnown = gypsySeen || assigned != 0;
+	const bool started = gypsyKnown || hoodedSeen || hasCoralKey;
+	const bool finished = hasCoralKey;
+
+	if (done != finished || (!done && !started))
+		return;
+
+	Common::String details;
+	if (done) {
+		details = "Passed the Color Bridge challenge and obtained the Coral Key.";
+	} else if (gypsyKnown && hoodedSeen) {
+		if (assigned == (int)g_globals->_party.size()) {
+			details = "The party knows all its signs. Use them to answer the Hooded Figure's Color Bridge challenge and claim the Coral Key.";
+		} else {
+			details = Common::String::format(
+				"The Hooded Figure guards the Color Bridge. Return to the Gypsy and learn every current party member's sign. Signs known: %d/%d.",
+				assigned, (int)g_globals->_party.size());
+		}
+	} else if (hoodedSeen) {
+		details = "The Hooded Figure at the Color Bridge demands each character's sign. Find the Gypsy and learn them.";
+	} else {
+		details = Common::String::format(
+			"The Gypsy has revealed signs for %d/%d current party members. Learn the remaining signs and/or discover where they are needed.",
+			assigned, (int)g_globals->_party.size());
+	}
+
+	addEntry(entries, done, "The Color Bridge", details);
+}
+
 void addRanalou(Common::Array<QuestLogEntry> &entries, bool done) {
 	struct PrisonerJudgmentRecord {
 		byte _flag;
@@ -380,6 +431,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	addClerics(entries, false);
 	addCourier(entries, false);
 	addAlamarStory(entries, false);
+	addSigns(entries, false);
 	addRanalou(entries, false);
 
 	// Completed history follows and remains selectable with Up/Down
@@ -388,6 +440,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	addClerics(entries, true);
 	addCourier(entries, true);
 	addAlamarStory(entries, true);
+	addSigns(entries, true);
 	addRanalou(entries, true);
 	addOtherReliableCompletions(entries);
 }


Commit: 23213164760e1ffcf6bba090d83157c832edfb0e
    https://github.com/scummvm/scummvm/commit/23213164760e1ffcf6bba090d83157c832edfb0e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:47+11:00

Commit Message:
MM: MM1: End the game when the whole party dies crossing the desert

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map_desert.cpp


diff --git a/engines/mm/mm1/maps/map_desert.cpp b/engines/mm/mm1/maps/map_desert.cpp
index d15f3fba68d..f392fed6dc1 100644
--- a/engines/mm/mm1/maps/map_desert.cpp
+++ b/engines/mm/mm1/maps/map_desert.cpp
@@ -61,8 +61,11 @@ void MapDesert::desert() {
 		}
 	}
 
-	if (deadCount)
+	if (deadCount) {
 		g_events->findView("GameParty")->redraw();
+		if (g_globals->_party.checkPartyDead())
+			return;
+	}
 
 	switch (_randomMode) {
 	case RND_BASIC:


Commit: 5d01918420b2f820e76760b3f6cbda0d3eee2191
    https://github.com/scummvm/scummvm/commit/5d01918420b2f820e76760b3f6cbda0d3eee2191
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:47+11:00

Commit Message:
MM: MM1: Fix Divine Intervention setting age from level instead of adding five years

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index eee097846cf..4d81b69690d 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1278,12 +1278,13 @@ bool Combat::divineIntervention() {
 	if (c._alignment != c._alignmentInitial || _divineInterventionUsed)
 		return false;
 
-	_divineInterventionUsed = true;
-	int age = (int)c._level + 5;
-	if (age > 255)
+	// Casting ages the caster by five years
+	const int newAge = (int)c._age + 5;
+	if (newAge > 255)
 		return false;
 
-	c._age = age;
+	_divineInterventionUsed = true;
+	c._age = newAge;
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
 		Character &partyMember = g_globals->_party[i];


Commit: 8cabedf7a4703a80009e42c1f23391565d4420bf
    https://github.com/scummvm/scummvm/commit/8cabedf7a4703a80009e42c1f23391565d4420bf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:48+11:00

Commit Message:
MM: MM1: Allow column E when choosing a Fly destination

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views/spells/fly.cpp
    engines/mm/mm1/views_enh/spells/fly.cpp


diff --git a/engines/mm/mm1/views/spells/fly.cpp b/engines/mm/mm1/views/spells/fly.cpp
index f5b95a8931b..6ee1a20ad6e 100644
--- a/engines/mm/mm1/views/spells/fly.cpp
+++ b/engines/mm/mm1/views/spells/fly.cpp
@@ -58,7 +58,7 @@ void Fly::draw() {
 
 bool Fly::msgKeypress(const KeypressMessage &msg) {
 	if (_mode == SELECT_X && msg.keycode >= Common::KEYCODE_a
-		&& msg.keycode <= Common::KEYCODE_d) {
+		&& msg.keycode <= Common::KEYCODE_e) {
 		// X map selected
 		_mode = SELECT_Y;
 		_xIndex = msg.keycode - Common::KEYCODE_a;
diff --git a/engines/mm/mm1/views_enh/spells/fly.cpp b/engines/mm/mm1/views_enh/spells/fly.cpp
index 09991bab959..d0880f56728 100644
--- a/engines/mm/mm1/views_enh/spells/fly.cpp
+++ b/engines/mm/mm1/views_enh/spells/fly.cpp
@@ -64,7 +64,7 @@ void Fly::draw() {
 
 bool Fly::msgKeypress(const KeypressMessage &msg) {
 	if (_mode == SELECT_X && msg.keycode >= Common::KEYCODE_a
-		&& msg.keycode <= Common::KEYCODE_d) {
+		&& msg.keycode <= Common::KEYCODE_e) {
 		// X map selected
 		_mode = SELECT_Y;
 		_xIndex = msg.keycode - Common::KEYCODE_a;


Commit: d76797dfd9fc23de8bc1a7188a7560053fb6c97c
    https://github.com/scummvm/scummvm/commit/d76797dfd9fc23de8bc1a7188a7560053fb6c97c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:48+11:00

Commit Message:
MM: MM1: Cap gem rewards at the 16-bit limit instead of wrapping around

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/use_item.cpp
    engines/mm/mm1/game/wheel_spin.cpp
    engines/mm/mm1/views/interactions/trivia.cpp
    engines/mm/mm1/views_enh/interactions/trivia.cpp


diff --git a/engines/mm/mm1/game/use_item.cpp b/engines/mm/mm1/game/use_item.cpp
index 7752e35fb35..77f5c31aa82 100644
--- a/engines/mm/mm1/game/use_item.cpp
+++ b/engines/mm/mm1/game/use_item.cpp
@@ -132,7 +132,7 @@ void UseItem::applyItemBonus(int id, int value){
 		case 37: c._age += value; break;
 		case 43: c._sp._current += value; break;
 		case 48: c._spellLevel._current += value; break;
-		case 49: c._gems += value; break;
+		case 49: c._gems = MIN((int)c._gems + value, 0xffff); break;
 		case 58: c._gold += 255*value; break;
 		case 62: c._food += value; break;
 		case 89: c._resistances._s._magic._current += value; break;
diff --git a/engines/mm/mm1/game/wheel_spin.cpp b/engines/mm/mm1/game/wheel_spin.cpp
index d50db9f7476..3e99beb208a 100644
--- a/engines/mm/mm1/game/wheel_spin.cpp
+++ b/engines/mm/mm1/game/wheel_spin.cpp
@@ -77,7 +77,7 @@ void WheelSpin::spin() {
 			case 3:
 				val = 15 << map[VAL4];
 				WRITE_LE_UINT16(&map[VAL3], val);
-				c._gems += val;
+				c._gems = MIN((int)c._gems + val, 0xffff);
 				line = Common::String::format("+%d %s", val,
 					STRING["maps.map16.gems"].c_str());
 				break;
diff --git a/engines/mm/mm1/views/interactions/trivia.cpp b/engines/mm/mm1/views/interactions/trivia.cpp
index 18196b7bc16..857618d3399 100644
--- a/engines/mm/mm1/views/interactions/trivia.cpp
+++ b/engines/mm/mm1/views/interactions/trivia.cpp
@@ -61,7 +61,8 @@ void Trivia::answerEntered() {
 	if (_answer.equalsIgnoreCase(_correctAnswer)) {
 		g_maps->clearSpecialAndRememberReward();
 		send(InfoMessage(STRING["maps.map21.correct"]));
-		g_globals->_party[0]._gems += 50;
+		g_globals->_party[0]._gems = MIN(
+			(int)g_globals->_party[0]._gems + 50, 0xffff);
 		Sound::sound(SOUND_3);
 
 	} else {
diff --git a/engines/mm/mm1/views_enh/interactions/trivia.cpp b/engines/mm/mm1/views_enh/interactions/trivia.cpp
index 36d57b316bb..c9c5a07f520 100644
--- a/engines/mm/mm1/views_enh/interactions/trivia.cpp
+++ b/engines/mm/mm1/views_enh/interactions/trivia.cpp
@@ -52,7 +52,8 @@ void Trivia::answerEntered() {
 	if (_answer.equalsIgnoreCase(_correctAnswer)) {
 		g_maps->clearSpecialAndRememberReward();
 		send(InfoMessage(STRING["maps.map21.correct"]));
-		g_globals->_party[0]._gems += 50;
+		g_globals->_party[0]._gems = MIN(
+			(int)g_globals->_party[0]._gems + 50, 0xffff);
 		Sound::sound(SOUND_3);
 
 	} else {


Commit: dd55214453c2cde577ac977aa54593e666825c9f
    https://github.com/scummvm/scummvm/commit/dd55214453c2cde577ac977aa54593e666825c9f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:48+11:00

Commit Message:
MM: MM1: Reset the Great Beast flags after spinning the Wheel

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/wheel_spin.cpp


diff --git a/engines/mm/mm1/game/wheel_spin.cpp b/engines/mm/mm1/game/wheel_spin.cpp
index 3e99beb208a..02bea99b2b2 100644
--- a/engines/mm/mm1/game/wheel_spin.cpp
+++ b/engines/mm/mm1/game/wheel_spin.cpp
@@ -88,6 +88,11 @@ void WheelSpin::spin() {
 			}
 		}
 
+		// Spinning the Wheel makes all four Great Beasts available again.
+		// Keep the Wheel marker (CHARFLAG2_80) and the upper bits
+		c._flags[2] &= ~(CHARFLAG2_1 | CHARFLAG2_2 |
+			CHARFLAG2_4 | CHARFLAG2_8);
+
 		_results.push_back(line);
 	}
 }


Commit: f57c2b5bbd5d7838142f6021fe0e6060a7335120
    https://github.com/scummvm/scummvm/commit/f57c2b5bbd5d7838142f6021fe0e6060a7335120
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:49+11:00

Commit Message:
MM: MM1: Give extra melee attacks to Knights, Paladins and Archers

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 4d81b69690d..8c66a36d3aa 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -803,9 +803,9 @@ void Combat::attackMonster(int monsterNum) {
 
 	_numberOfTimes = 1;
 
-	if (c._class >= CLERIC && c._level >= 8) {
+	// Extra attacks belong to the martial classes: Knight, Paladin and Archer
+	if (c._class >= KNIGHT && c._class <= ARCHER && c._level >= 8)
 		_numberOfTimes += c._level / 8;
-	}
 
 	// Affect level based on might
 	_attackAttr2 = _attackAttr1;


Commit: 68f3265fc8ce164d57fdd797ca0b29432bbca672
    https://github.com/scummvm/scummvm/commit/68f3265fc8ce164d57fdd797ca0b29432bbca672
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:49+11:00

Commit Message:
MM: MM1: Fix argument handling in the encounter debug command

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/console.cpp


diff --git a/engines/mm/mm1/console.cpp b/engines/mm/mm1/console.cpp
index 958f1999027..7321dc6ebc9 100644
--- a/engines/mm/mm1/console.cpp
+++ b/engines/mm/mm1/console.cpp
@@ -728,18 +728,18 @@ bool Console::cmdEncounter(int argc, const char **argv) {
 	} else {
 		Game::Encounter &enc = g_globals->_encounters;
 		int monsterNum = strToInt(argv[1]);
-		if (monsterNum < 1 || monsterNum > MONSTERS_COUNT) {
-			debugPrintf("monster-num must be between 1 and %d\n", MONSTERS_COUNT);
+		if (monsterNum < 1 || monsterNum > 16) {
+			debugPrintf("monster-num must be between 1 and 16\n");
 			return true;
 		}
-		int level = (argc == 3) ? strToInt(argv[2]) : 1;
-		if (level < 1 || level >= 15)
+		int level = (argc > 2) ? strToInt(argv[2]) : 1;
+		if (level < 1 || level > 12)
 			level = 1;
 
 		int count = (argc > 3) ? strToInt(argv[3]) : 1;
-		int encType = (argc > 4) ? strToInt(argv[2]) :
+		int encType = (argc > 4) ? strToInt(argv[4]) :
 			Game::NORMAL_ENCOUNTER;
-		if (encType != -1 || encType == 0 || encType == 1)
+		if (encType == -1 || encType == 0 || encType == 1)
 			enc._encounterType = (Game::EncounterType)encType;
 
 		enc.clearMonsters();


Commit: a5950db3368c52aa36d2167c205f8706c0de91a3
    https://github.com/scummvm/scummvm/commit/a5950db3368c52aa36d2167c205f8706c0de91a3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:49+11:00

Commit Message:
MM: MM1: Require enough gems to cast spells that cost gems

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spell_casting.cpp


diff --git a/engines/mm/mm1/game/spell_casting.cpp b/engines/mm/mm1/game/spell_casting.cpp
index 98c880bc699..a666eb5c68a 100644
--- a/engines/mm/mm1/game/spell_casting.cpp
+++ b/engines/mm/mm1/game/spell_casting.cpp
@@ -119,6 +119,17 @@ void SpellCasting::setSpell(const Character *chr, int lvl, int num) {
 		_spellState = SS_NOT_ENOUGH_SP;
 
 	int requiredGems = ABS(SPELLS_SP_GEMS[spellIndex]);
+
+	// Gem costs are real casting requirements, just like spell points
+	if (_spellState == SS_OK && requiredGems > chr->_gems)
+		_spellState = SS_NOT_ENOUGH_GEMS;
+
+	// Keep the enhanced spell dialog synchronized even when the spell
+	// cannot currently be afforded
+	_spellIndex = spellIndex;
+	_requiredSp = requiredSp;
+	_requiredGems = requiredGems;
+
 	if (_spellState == SS_OK)
 		setSpell(spellIndex, requiredSp, requiredGems);
 


Commit: b5a3dbbb1fd8cd5184b1b7e724f69b8addd1965d
    https://github.com/scummvm/scummvm/commit/b5a3dbbb1fd8cd5184b1b7e724f69b8addd1965d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:50+11:00

Commit Message:
MM: MM1: Fix Resurrection only working on characters who are not eradicated

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index d948ef7373b..b8dfeaa151d 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -613,7 +613,8 @@ SpellResult SpellsParty::cleric73_protectionFromElements() {
 }
 
 SpellResult SpellsParty::cleric74_resurrection() {
-	if (_destChar->_condition == ERADICATED)
+	// Only an eradicated character can be resurrected
+	if (_destChar->_condition != ERADICATED)
 		return SR_FAILED;
 
 	if (_destChar->_age < 10 || _destChar->_age > 200)


Commit: a31340bc9ae0f87c1cd4fa70d3b3c1c2f09dfabc
    https://github.com/scummvm/scummvm/commit/a31340bc9ae0f87c1cd4fa70d3b3c1c2f09dfabc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:50+11:00

Commit Message:
MM: MM1: Fix Raise Dead only working on characters who are not dead

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index b8dfeaa151d..059acab4e93 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -502,19 +502,25 @@ SpellResult SpellsParty::cleric61_moonRay() {
 }
 
 SpellResult SpellsParty::cleric62_raiseDead() {
-	if ((_destChar->_condition == ERADICATED) ||
-		(_destChar->_condition & (BAD_CONDITION | DEAD)) ==
+	// Raise Dead only works on a dead character. Eradicated
+	// characters need Resurrection
+	if (_destChar->_condition == ERADICATED ||
+		(_destChar->_condition & (BAD_CONDITION | DEAD)) !=
 		(BAD_CONDITION | DEAD))
 		return SR_FAILED;
+
 	int rnd = g_engine->getRandomNumber(100);
 	if (rnd == 100) {
-		// This is such a minute chance, why even have it?
+		// A remote chance of eradicating the character instead
+		_destChar->_condition = ERADICATED;
+		_destChar->_hpCurrent = 0;
 		Sound::sound(SOUND_3);
 		return SR_FAILED;
 	} else if (rnd > 90) {
 		return SR_FAILED;
 	}
 
+	// A character who was both dead and stoned is raised, but stays stoned
 	if (_destChar->_condition == (BAD_CONDITION | DEAD | STONE))
 		_destChar->_condition = BAD_CONDITION | STONE;
 	else


Commit: 2a2d466f830977ed8cb1adc2d7bd14152fb121a6
    https://github.com/scummvm/scummvm/commit/2a2d466f830977ed8cb1adc2d7bd14152fb121a6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:50+11:00

Commit Message:
MM: MM1: Fix Rejuvenate reporting failure on success and underflowing the age

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index 059acab4e93..41aea5ab5ba 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -532,12 +532,12 @@ SpellResult SpellsParty::cleric62_raiseDead() {
 
 SpellResult SpellsParty::cleric63_rejuvinate() {
 	if (g_engine->getRandomNumber(100) < 75) {
-		_destChar->_age = MIN(_destChar->_age - g_engine->getRandomNumber(10),
-			200);
-		return SR_FAILED;
+		const int ageReduction = g_engine->getRandomNumber(10);
+		_destChar->_age = MAX((int)_destChar->_age - ageReduction, 0);
+		return SR_SUCCESS_DONE;
 	} else {
-		// Failed, increase the user's age
-		_destChar->_age = MIN(_destChar->_age + 10, 200);
+		// Failed, increase the recipient's age
+		_destChar->_age = MIN((int)_destChar->_age + 10, 200);
 		return SR_FAILED;
 	}
 }


Commit: 55dba78dc77300c118b9e4b32c0c5c4175de8d95
    https://github.com/scummvm/scummvm/commit/55dba78dc77300c118b9e4b32c0c5c4175de8d95
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:50+11:00

Commit Message:
MM: MM1: Fix Town Portal using the wrong byte for the destination map

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index 41aea5ab5ba..e537984e1b2 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -579,7 +579,7 @@ SpellResult SpellsParty::cleric65_townPortal() {
 				maps._mapPos.y = TownData::TOWN_MAP_Y[townIndex];
 				maps.changeMap(
 					TownData::TOWN_MAP_ID1[townIndex] |
-					(TownData::TOWN_MAP_ID1[townIndex] << 8),
+					(TownData::TOWN_MAP_ID2[townIndex] << 8),
 					1);
 
 				g_events->close();


Commit: df0b2c513a4ffc614fedb40ed4980c10ccdacffb
    https://github.com/scummvm/scummvm/commit/df0b2c513a4ffc614fedb40ed4980c10ccdacffb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:50+11:00

Commit Message:
MM: MM1: Show the Town Portal destination prompt

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index e537984e1b2..ebd315e2200 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -592,6 +592,15 @@ SpellResult SpellsParty::cleric65_townPortal() {
 		}
 	);
 
+	msg._largeMessage = true;
+	msg._lines.push_back(Line(Common::String::format("1) %s  2) %s",
+		STRING["stats.towns.1"].c_str(), STRING["stats.towns.2"].c_str())));
+	msg._lines.push_back(Line(Common::String::format("3) %s  4) %s",
+		STRING["stats.towns.3"].c_str(), STRING["stats.towns.4"].c_str())));
+	msg._lines.push_back(Line(Common::String::format("5) %s",
+		STRING["stats.towns.5"].c_str())));
+	g_events->send(msg);
+
 	return SR_SUCCESS_SILENT;
 }
 


Commit: 4a9b420daff869d7f4c803db166f0e673bd74ff6
    https://github.com/scummvm/scummvm/commit/4a9b420daff869d7f4c803db166f0e673bd74ff6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:51+11:00

Commit Message:
MM: MM1: Fix the Accuracy and Speed shrines changing Might instead

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map09.cpp


diff --git a/engines/mm/mm1/maps/map09.cpp b/engines/mm/mm1/maps/map09.cpp
index 147ecb451f7..9dd4ae50f77 100644
--- a/engines/mm/mm1/maps/map09.cpp
+++ b/engines/mm/mm1/maps/map09.cpp
@@ -95,7 +95,10 @@ void Map09::special06() {
 		Character &c = g_globals->_party[i];
 		if (!(c._flags[11] & CHARFLAG11_GOT_ACCURACY)) {
 			c._flags[11] |= CHARFLAG11_GOT_ACCURACY;
-			c._might._base = MIN((int)c._accuracy._base + 4, 255);
+			if (c._accuracy._base < 30) {
+				c._accuracy._current = c._accuracy._base =
+					c._accuracy._base + 4;
+			}
 		}
 	}
 
@@ -107,7 +110,10 @@ void Map09::special07() {
 		Character &c = g_globals->_party[i];
 		if (!(c._flags[11] & CHARFLAG11_GOT_SPEED)) {
 			c._flags[11] |= CHARFLAG11_GOT_SPEED;
-			c._might._base = MIN((int)c._speed._base + 4, 255);
+			if (c._speed._base < 30) {
+				c._speed._current = c._speed._base =
+					c._speed._base + 4;
+			}
 		}
 	}
 


Commit: 4dc64ee02e1b9024e3a24b6f48072b5afcd70d4c
    https://github.com/scummvm/scummvm/commit/4dc64ee02e1b9024e3a24b6f48072b5afcd70d4c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:51+11:00

Commit Message:
MM: MM1: Show the Psychic Blast result and fix its victim index

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/encounter.cpp
    engines/mm/mm1/game/encounter.h
    engines/mm/mm1/maps/map09.cpp
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/game/encounter.cpp b/engines/mm/mm1/game/encounter.cpp
index e1cfa2c5883..f573e9fa100 100644
--- a/engines/mm/mm1/game/encounter.cpp
+++ b/engines/mm/mm1/game/encounter.cpp
@@ -33,13 +33,16 @@ namespace MM {
 namespace MM1 {
 namespace Game {
 
-void Encounter::execute() {
+void Encounter::execute(const Common::String &preCombatLog) {
 	if (g_events->isPresent("Combat") ||
 			g_events->isPresent("Encounter")) {
 		warning("MM1: Ignoring nested encounter request");
 		return;
 	}
 
+	// Clear stale event text, or retain text supplied by this encounter
+	_preCombatLog = preCombatLog;
+
 	if (!g_globals->_encountersOn)
 		return;
 
diff --git a/engines/mm/mm1/game/encounter.h b/engines/mm/mm1/game/encounter.h
index e1a9928f850..5a6b74fbf5a 100644
--- a/engines/mm/mm1/game/encounter.h
+++ b/engines/mm/mm1/game/encounter.h
@@ -23,6 +23,7 @@
 #define MM1_GAME_ENCOUNTER_H
 
 #include "common/array.h"
+#include "common/str.h"
 #include "mm/mm1/data/character.h"
 #include "mm/mm1/data/monsters.h"
 #include "mm/mm1/game/game_logic.h"
@@ -64,12 +65,13 @@ public:
 	byte _fleeThreshold = 0;
 	bool _manual = false;
 	int _levelIndex = 0;
+	Common::String _preCombatLog;
 
 public:
 	/**
 	 * Start an encounter
 	 */
-	void execute();
+	void execute(const Common::String &preCombatLog = Common::String());
 
 	/**
 	 * Chooses whether an encounter can be fled
diff --git a/engines/mm/mm1/maps/map09.cpp b/engines/mm/mm1/maps/map09.cpp
index 9dd4ae50f77..d998d9fd018 100644
--- a/engines/mm/mm1/maps/map09.cpp
+++ b/engines/mm/mm1/maps/map09.cpp
@@ -199,22 +199,41 @@ void Map09::special18() {
 			0, 2, STRING["maps.map09.protection"]
 		));
 	} else {
-		g_globals->_currCharacter = &g_globals->_party[
-			getRandomNumber(g_globals->_party.size()) - 1
+		// getRandomNumber() is inclusive, so pass the last valid index
+		Character &victim = g_globals->_party[
+			getRandomNumber(g_globals->_party.size() - 1)
 		];
+		g_globals->_currCharacter = &victim;
 
-		if (!(g_globals->_currCharacter->_condition & BAD_CONDITION)) {
+		Common::String effectLine;
+		if (!(victim._condition & BAD_CONDITION)) {
 			// Chosen character is okay, so blast them
-			g_globals->_currCharacter->_condition = BAD_CONDITION | DEAD;
+			victim._condition = BAD_CONDITION | DEAD;
+			effectLine = Common::String::format("%s dies.", victim._name);
 		} else {
 			// Chosen character is disabled, so instead
 			// remove the SP from all members of the party
-			for (uint i = 0; i < g_globals->_party.size(); ++i) {
+			for (uint i = 0; i < g_globals->_party.size(); ++i)
 				g_globals->_party[i]._sp._current = 0;
-			}
+			effectLine = "The party's spell points are drained.";
 		}
 
-		g_globals->_encounters.execute();
+		// Keep the result until combat opens, then add it to the Combat
+		// Log. This state is deliberately not serialized
+		g_globals->_encounters._preCombatLog = Common::String::format(
+			"Psychic blast! %s", effectLine.c_str());
+
+		SoundMessage msg(
+			0, 1, STRING["maps.map09.psychic_blast"],
+			0, 2, effectLine,
+			[]() {
+				Common::String preCombatLog =
+					g_globals->_encounters._preCombatLog;
+				g_globals->_encounters.execute(preCombatLog);
+			}
+		);
+		msg._delaySeconds = 3;
+		send(msg);
 	}
 }
 
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index bdf9568b169..754d23f920a 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -107,6 +107,10 @@ bool Combat::msgGame(const GameMessage &msg) {
 
 		loadMonsters();
 		addEncounterToCombatLog();
+		if (!g_globals->_encounters._preCombatLog.empty()) {
+			addCombatLog(g_globals->_encounters._preCombatLog);
+			g_globals->_encounters._preCombatLog.clear();
+		}
 		setupCanAttacks();
 		setupHandicap();
 


Commit: 5be96b01b93189db3ebb75c5f0cda6f5bce103b1
    https://github.com/scummvm/scummvm/commit/5be96b01b93189db3ebb75c5f0cda6f5bce103b1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:51+11:00

Commit Message:
MM: MM1: Fix Acid Rain hitting monsters in melee range

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 8c66a36d3aa..cbc2342aca7 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1492,7 +1492,11 @@ bool Combat::acidRain() {
 	for (int i = 0; i < 5; ++i)
 		ss._damage += getRandomNumber(10);
 
-	iterateMonsters2();
+	// Acid Rain affects only monsters outside hand-to-hand combat. Keep the
+	// starting monster chosen above instead of resetting it to the first one
+	_selectedSpellMonster = -1;
+	_spellMonsterCount = _remainingMonsters.size();
+	iterateMonsters2Inner();
 	return true;
 }
 


Commit: 2c225de84868e72d1c2cd66ad77d098a685369fb
    https://github.com/scummvm/scummvm/commit/2c225de84868e72d1c2cd66ad77d098a685369fb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:51+11:00

Commit Message:
MM: MM1: Stop dead monsters recovering or regenerating before cleanup

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index cbc2342aca7..488ff4bc666 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -513,6 +513,10 @@ bool Combat::monsterChanges() {
 	for (uint i = 0; i < _remainingMonsters.size(); ++i) {
 		monsterSetPtr(i);
 
+		// Dead monsters stay dead until combat cleanup removes them
+		if (_remainingMonsters[i]->_status == MONFLAG_DEAD)
+			continue;
+
 		if ((_monsterP->_counterFlags & COUNTER_REGENERATE) &&
 			(_monsterP->_defaultHP != _remainingMonsters[i]->_hp)) {
 			_monstersRegenerate = true;


Commit: 7da82425583df6ddcfc9bf2521da7d703a03324b
    https://github.com/scummvm/scummvm/commit/7da82425583df6ddcfc9bf2521da7d703a03324b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:52+11:00

Commit Message:
MM: MM1: Time the Turn Undead and Holy Word results like other combat spells

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 488ff4bc666..0461c7a4906 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1168,11 +1168,11 @@ bool Combat::monsterLevelThreshold() const {
 }
 
 void Combat::turnUndead() {
-	if (_turnUndeadUsed) {
-		// Already been used in the current combat, can't be used again
-		displaySpellResult(InfoMessage(15, 1, STRING["spells.no_effect"]));
+	// It can only be used once per combat. Further casts have no effect
+	Common::String result = STRING["spells.no_effect"];
+	int resultX = 15;
 
-	} else {
+	if (!_turnUndeadUsed) {
 		_turnUndeadUsed = true;
 
 		for (uint i = 0; i < _remainingMonsters.size(); ++i) {
@@ -1187,12 +1187,16 @@ void Combat::turnUndead() {
 			}
 		}
 
-		if (_monstersDestroyedCtr)
-			displaySpellResult(InfoMessage(5, 1, STRING["spells.monsters_destroyed"]));
-		else
-			displaySpellResult(InfoMessage(15, 1, STRING["spells.no_effect"]));
+		if (_monstersDestroyedCtr) {
+			result = STRING["spells.monsters_destroyed"];
+			resultX = 5;
+		}
 	}
 
+	InfoMessage msg(resultX, 1, result);
+	msg._delaySeconds = 3;
+	displaySpellResult(msg);
+
 	g_globals->_combatParty[_currentChar]->_checked = true;
 }
 
@@ -1313,10 +1317,14 @@ void Combat::holyWord() {
 		}
 	}
 
-	if (_monstersDestroyedCtr)
-		displaySpellResult(InfoMessage(5, 1, STRING["spells.monsters_destroyed"]));
-	else
-		displaySpellResult(InfoMessage(15, 1, STRING["spells.no_effect"]));
+	// The result is timed, like the Turn Undead result
+	InfoMessage msg(_monstersDestroyedCtr ? 5 : 15, 1,
+		STRING[_monstersDestroyedCtr ? "spells.monsters_destroyed" : "spells.no_effect"]);
+	msg._delaySeconds = 3;
+	msg._callback = []() {
+		g_globals->_combat->characterDone();
+	};
+	displaySpellResult(msg);
 	g_globals->_combatParty[_currentChar]->_checked = true;
 }
 


Commit: 3a4c76295405b5d06ee88e676054172a258fa99c
    https://github.com/scummvm/scummvm/commit/3a4c76295405b5d06ee88e676054172a258fa99c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:52+11:00

Commit Message:
MM: MM1: Don't damage monsters that resist a damage spell

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 0461c7a4906..e32d906e9a4 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -1096,7 +1096,9 @@ void Combat::iterateMonsters2Inner() {
 		}
 	}
 
-	updateMonsterStatus();
+	// A monster that wasn't affected by the spell takes no damage
+	if (affects)
+		updateMonsterStatus();
 
 	msg._delaySeconds = 3;
 


Commit: da62c2b7c2f21b58ca7868110aa70cf4e5efacf0
    https://github.com/scummvm/scummvm/commit/da62c2b7c2f21b58ca7868110aa70cf4e5efacf0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:52+11:00

Commit Message:
MM: MM1: Give item drops after combat and fix the treasure tier offset

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/combat.cpp


diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index e32d906e9a4..aa8148bf64c 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -336,6 +336,10 @@ void Combat::defeatedMonsters() {
 		setTreasure();
 	}
 
+	// Convert the highest defeated-monster treasure rating into the
+	// original random item rolls and container quality
+	selectTreasure();
+
 	// Count number of active characters
 	for (uint i = 0; i < g_globals->_combatParty.size(); ++i) {
 		if (!(g_globals->_combatParty[i]->_condition & BAD_CONDITION))
@@ -414,7 +418,8 @@ void Combat::selectTreasure2(int count) {
 	byte itemId = TREASURES_ARR1[idx];
 	byte rndCount = TREASURES_ARR2[idx];
 
-	for (idx = 0; idx < count; ++idx)
+	// count is a 1-based treasure tier, with tier 1 at the family base
+	for (idx = 1; idx < count; ++idx)
 		itemId += rndCount;
 	itemId += getRandomNumber(rndCount) - 1;
 


Commit: 6e82ba8ee27c56808d1b0d8fe5b78e91f33d1ffb
    https://github.com/scummvm/scummvm/commit/6e82ba8ee27c56808d1b0d8fe5b78e91f33d1ffb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:53+11:00

Commit Message:
MM: MM1: Fix Prismatic Light depending on state left by earlier spells

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index ebd315e2200..3dfcd48c3d8 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -998,6 +998,12 @@ SpellResult SpellsParty::wizard75_prismaticLight() {
 	if (getRandomNumber(100) < 20)
 		return SR_FAILED;
 
+	// Generate a new unpredictable effect for this cast rather
+	// than reusing state left behind by an earlier spell
+	ss._mmVal1 = getRandomNumber(100);
+	ss._resistanceIndex = 0;
+	ss._damage = 1;
+
 	if (ss._mmVal1 < 50) {
 		uint count = getRandomNumber(4);
 		ss._damage <<= count;


Commit: 0af2f3cc452c6006dd8b7096f3d3599dbe000812
    https://github.com/scummvm/scummvm/commit/0af2f3cc452c6006dd8b7096f3d3599dbe000812
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:53+11:00

Commit Message:
MM: MM1: Make Etherealize move one square in the facing direction

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/spells_party.cpp


diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index 3dfcd48c3d8..1e5dbd7b56e 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -941,9 +941,24 @@ SpellResult SpellsParty::wizard62_disintegration() {
 }
 
 SpellResult SpellsParty::wizard63_etherialize() {
-	g_events->draw();
-	g_maps->_currentState = 0;
-	g_maps->step(Common::Point(1, 0));
+	Maps::Maps &maps = *g_maps;
+
+	// Etherealize moves exactly one square in the direction the party is
+	// facing, ignoring walls and special barriers
+	const Common::Point delta = maps.getMoveDelta(maps._forwardMask);
+	const Common::Point newPos = maps._mapPos + delta;
+
+	// Maps::step() performs its own changeMap() and UPDATE when crossing a
+	// map boundary. On an in-map move, refresh the Game view so the current
+	// square's walls and state match the new position
+	const bool crossesMapBoundary =
+		newPos.x < 0 || newPos.y < 0 ||
+		newPos.x >= MAP_W || newPos.y >= MAP_H;
+
+	maps.step(delta);
+
+	if (!crossesMapBoundary)
+		g_events->send("Game", GameMessage("UPDATE"));
 
 	return SR_SUCCESS_SILENT;
 }


Commit: 6fd16e662ec40b55ec40905b9ff1d5248ff5a773
    https://github.com/scummvm/scummvm/commit/6fd16e662ec40b55ec40905b9ff1d5248ff5a773
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:53+11:00

Commit Message:
MM: MM1: Make Gather collect gems and food and stop gold overflowing

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/character.cpp
    engines/mm/mm1/data/character.h
    engines/mm/mm1/views/character_info.cpp


diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index ef420b459cc..97bc9dfa7f9 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -21,6 +21,7 @@
 
 #include "common/algorithm.h"
 #include "mm/mm1/data/character.h"
+#include "mm/mm1/data/locations.h"
 #include "mm/shared/utils/strings.h"
 #include "mm/mm1/mm1.h"
 
@@ -288,13 +289,56 @@ void Character::clear() {
 
 
 void Character::gatherGold() {
-	uint total = 0;
+	// Gold is stored in the original MM1 24-bit range. Move as much as
+	// possible to this character and leave any overflow with its owner
+	static const uint32 MAX_GOLD = 0xffffff;
+
+	if (_gold >= MAX_GOLD)
+		return;
+
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
-		total += g_globals->_party[i]._gold;
-		g_globals->_party[i]._gold = 0;
+		Character &src = g_globals->_party[i];
+		if (&src == this)
+			continue;
+
+		const uint32 room = MAX_GOLD - _gold;
+		const uint32 amount = MIN(src._gold, room);
+		_gold += amount;
+		src._gold -= amount;
+
+		if (_gold == MAX_GOLD)
+			break;
 	}
+}
+
+void Character::gatherResources() {
+	// Gather gold, gems and food, each only up to the destination
+	// character's carrying limit. Any excess stays with the source
+	// character instead of being destroyed
+	gatherGold();
 
-	_gold = total;
+	for (uint i = 0; i < g_globals->_party.size(); ++i) {
+		Character &src = g_globals->_party[i];
+		if (&src == this)
+			continue;
+
+		if (_gems < 0xffff) {
+			const uint32 room = 0xffff - _gems;
+			const uint32 amount = MIN<uint32>(src._gems, room);
+			_gems += amount;
+			src._gems -= amount;
+		}
+
+		if (_food < MAX_FOOD) {
+			const uint room = MAX_FOOD - _food;
+			const uint amount = MIN<uint>(src._food, room);
+			_food += amount;
+			src._food -= amount;
+		}
+
+		if (_gems == 0xffff && _food == MAX_FOOD)
+			break;
+	}
 }
 
 Character::TradeResult Character::trade(int whoTo, int itemIndex) {
diff --git a/engines/mm/mm1/data/character.h b/engines/mm/mm1/data/character.h
index 341a99dbfe7..7c3ab1e064b 100644
--- a/engines/mm/mm1/data/character.h
+++ b/engines/mm/mm1/data/character.h
@@ -507,6 +507,12 @@ struct Character : public PrimaryAttributes {
 	 */
 	void gatherGold();
 
+	/**
+	 * Gathers gold, gems and food into the character, up to
+	 * their carrying limits
+	 */
+	void gatherResources();
+
 	/**
 	 * Trade an item to another
 	 */
diff --git a/engines/mm/mm1/views/character_info.cpp b/engines/mm/mm1/views/character_info.cpp
index 7fed07f0e6a..381b78d307b 100644
--- a/engines/mm/mm1/views/character_info.cpp
+++ b/engines/mm/mm1/views/character_info.cpp
@@ -147,7 +147,7 @@ bool CharacterInfo::msgKeypress(const KeypressMessage &msg) {
 			redraw();
 			break;
 		case Common::KEYCODE_g:
-			g_globals->_currCharacter->gatherGold();
+			g_globals->_currCharacter->gatherResources();
 			redraw();
 			break;
 		case Common::KEYCODE_q:


Commit: d2c09603aa49d46194d8a7b2dc8e3d5ce11e40d8
    https://github.com/scummvm/scummvm/commit/d2c09603aa49d46194d8a7b2dc8e3d5ce11e40d8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:53+11:00

Commit Message:
MM: MM1: Fix large gold and gem transfers between characters

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views/character_info.cpp
    engines/mm/mm1/views_enh/character_inventory.cpp
    engines/mm/mm1/views_enh/trade.cpp


diff --git a/engines/mm/mm1/views/character_info.cpp b/engines/mm/mm1/views/character_info.cpp
index 381b78d307b..af8448582d4 100644
--- a/engines/mm/mm1/views/character_info.cpp
+++ b/engines/mm/mm1/views/character_info.cpp
@@ -419,7 +419,8 @@ void CharacterInfo::tradeHowMuch() {
 	escToGoBack(0);
 	writeString(10, 20, STRING["dialogs.character.how_much"]);
 
-	_textEntry.display(20, 20, 5, true, abortFunc, enterFunc);
+	_textEntry.display(20, 20, _tradeKind == TK_GOLD ? 8 : 5, true,
+		abortFunc, enterFunc);
 }
 
 void CharacterInfo::combatUseItem(Inventory &inv, Inventory::Entry &invEntry, bool isEquipped) {
diff --git a/engines/mm/mm1/views_enh/character_inventory.cpp b/engines/mm/mm1/views_enh/character_inventory.cpp
index d298d551c28..3c2f1be6925 100644
--- a/engines/mm/mm1/views_enh/character_inventory.cpp
+++ b/engines/mm/mm1/views_enh/character_inventory.cpp
@@ -346,14 +346,25 @@ void CharacterInventory::trade(const Common::String &mode, int amount, Character
 	Character &src = *g_globals->_currCharacter;
 
 	if (mode == "GEMS") {
-		amount = MIN<uint16>(amount, src._gems);
-		src._gems -= amount;
-		destChar->_gems = MIN(destChar->_gems + amount, 0xffff);
+		// Gems are a 16-bit value (maximum 65,535). Keep the calculation
+		// wide enough for a five-digit entered amount, and limit the
+		// transfer to both the source balance and the destination capacity
+		// so that excess gems don't disappear
+		uint32 transfer = MIN<uint32>((uint32)amount, (uint32)src._gems);
+		transfer = MIN<uint32>(transfer, 0xffffU - (uint32)destChar->_gems);
+		src._gems -= (uint16)transfer;
+		destChar->_gems += (uint16)transfer;
 
 	} else if (mode == "GOLD") {
-		amount = MIN<uint16>(amount, src._gold);
-		src._gold -= amount;
-		destChar->_gold += amount;
+		// Gold is a 24-bit value. The old 16-bit limit truncated both the
+		// requested amount and the source gold above 65,535
+		uint32 transfer = MIN<uint32>((uint32)amount, src._gold);
+		if (destChar->_gold >= 0xffffff)
+			transfer = 0;
+		else
+			transfer = MIN<uint32>(transfer, 0xffffff - destChar->_gold);
+		src._gold -= transfer;
+		destChar->_gold += transfer;
 
 	} else if (mode == "FOOD") {
 		amount = MIN<uint16>(amount, src._food);
diff --git a/engines/mm/mm1/views_enh/trade.cpp b/engines/mm/mm1/views_enh/trade.cpp
index 3200924d408..191c019b90b 100644
--- a/engines/mm/mm1/views_enh/trade.cpp
+++ b/engines/mm/mm1/views_enh/trade.cpp
@@ -119,9 +119,11 @@ void Trade::setMode(TradeMode mode) {
 		break;
 
 	default:
-		// How much
+		// How much. Gold is a 24-bit value (maximum 16,777,215),
+		// so gold transfers need an 8-digit entry field
 		draw();
-		_textEntry.display(70, 157, 5, true, abortFunc, enterFunc);
+		_textEntry.display(70, 157, _mode == TRADE_GOLD ? 8 : 5, true,
+			abortFunc, enterFunc);
 	}
 }
 


Commit: f529776da178a16ca22d8909dc01f1cc1a2c1973
    https://github.com/scummvm/scummvm/commit/f529776da178a16ca22d8909dc01f1cc1a2c1973
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:54+11:00

Commit Message:
MM: MM1: Add Gather and Share controls to the enhanced inventory screen

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/character_inventory.cpp
    engines/mm/mm1/views_enh/character_inventory.h
    engines/mm/mm1/views_enh/trade.cpp
    engines/mm/mm1/views_enh/trade.h


diff --git a/engines/mm/mm1/views_enh/character_inventory.cpp b/engines/mm/mm1/views_enh/character_inventory.cpp
index 3c2f1be6925..1686e2a61f6 100644
--- a/engines/mm/mm1/views_enh/character_inventory.cpp
+++ b/engines/mm/mm1/views_enh/character_inventory.cpp
@@ -31,6 +31,16 @@ namespace MM {
 namespace MM1 {
 namespace ViewsEnh {
 
+// Existing inventory buttons end at x=239 and Exit begins at x=275. The G/S
+// control fits in the spare gap without moving any existing control. Its icon
+// is drawn from the engine's native blank-button surface with a vertical split,
+// avoiding the unrelated necklace art previously borrowed from ITEMS.ICN.
+static const int GS_BUTTON_X = 245;
+static const int GS_BUTTON_Y = 101;
+static const int GS_BUTTON_W = 24;
+static const int GS_BUTTON_H = 36;
+static const int GS_ICON_SIZE = 20;
+
 CharacterInventory::CharacterInventory() : ItemsView("CharacterInventory") {
 	setup();
 }
@@ -96,9 +106,62 @@ bool CharacterInventory::msgGame(const GameMessage &msg) {
 
 void CharacterInventory::draw() {
 	ItemsView::draw();
+	drawGatherShare();
 	drawTitle();
 }
 
+void CharacterInventory::drawGatherShare() {
+	if (g_events->isInCombat())
+		return;
+
+	Graphics::ManagedSurface s = getSurface();
+	const int pressOffset = _gsPressed != GS_NONE ? 1 : 0;
+	const int iconX = GS_BUTTON_X + (GS_BUTTON_W - GS_ICON_SIZE) / 2 +
+		_bounds.borderSize() + pressOffset;
+	const int iconY = GS_BUTTON_Y + _bounds.borderSize() + pressOffset;
+
+	// Native blank button + a simple center divider. This reads as two halves
+	// without requiring a new ICN resource and keeps the UI style consistent.
+	s.blitFrom(g_globals->_blankButton, Common::Point(iconX, iconY));
+	s.vLine(iconX + GS_ICON_SIZE / 2, iconY + 3,
+		iconY + GS_ICON_SIZE - 4, 0);
+
+	setReduced(true);
+	writeString(GS_BUTTON_X + GS_BUTTON_W / 2, 123,
+		"\x01""37G/\x01""37S", ALIGN_MIDDLE);
+}
+
+CharacterInventory::GatherShareButton CharacterInventory::gatherShareAt(
+		const Common::Point &pos) const {
+	if (g_events->isInCombat())
+		return GS_NONE;
+
+	Common::Rect r(GS_BUTTON_X, GS_BUTTON_Y,
+		GS_BUTTON_X + GS_BUTTON_W, GS_BUTTON_Y + GS_BUTTON_H);
+	r.translate(_innerBounds.left, _innerBounds.top);
+	if (!r.contains(pos))
+		return GS_NONE;
+
+	return pos.x < r.left + GS_BUTTON_W / 2 ? GS_GATHER : GS_SHARE;
+}
+
+void CharacterInventory::gatherShare(GatherShareButton action) {
+	if (action == GS_GATHER) {
+		InfoMessage msg(Common::String::format(
+			"Gather all gold, gems and food onto %s?",
+			g_globals->_currCharacter->_name));
+		msg._fontReduced = true;
+		msg._callback = []() {
+			g_globals->_currCharacter->gatherResources();
+			g_events->redraw();
+		};
+		send(msg);
+	} else if (action == GS_SHARE) {
+		addView("Trade");
+		send("Trade", GameMessage("SHARE"));
+	}
+}
+
 void CharacterInventory::drawTitle() {
 	const Character &c = *g_globals->_currCharacter;
 	const Common::String fmt = STRING[(_mode == ARMS_MODE) ?
@@ -150,6 +213,12 @@ bool CharacterInventory::msgKeypress(const KeypressMessage &msg) {
 		case Common::KEYCODE_u:
 			selectButton(BTN_USE);
 			return true;
+		case Common::KEYCODE_g:
+			gatherShare(GS_GATHER);
+			return true;
+		case Common::KEYCODE_s:
+			gatherShare(GS_SHARE);
+			return true;
 		default:
 			break;
 		}
@@ -158,6 +227,36 @@ bool CharacterInventory::msgKeypress(const KeypressMessage &msg) {
 	return ItemsView::msgKeypress(msg);
 }
 
+bool CharacterInventory::msgMouseDown(const MouseDownMessage &msg) {
+	if (msg._button == MouseMessage::MB_LEFT) {
+		const GatherShareButton action = gatherShareAt(msg._pos);
+		if (action != GS_NONE) {
+			_gsPressed = action;
+			draw();
+			return true;
+		}
+	}
+
+	return ItemsView::msgMouseDown(msg);
+}
+
+bool CharacterInventory::msgMouseUp(const MouseUpMessage &msg) {
+	if (_gsPressed != GS_NONE) {
+		const GatherShareButton pressed = _gsPressed;
+		const GatherShareButton released =
+			msg._button == MouseMessage::MB_LEFT ? gatherShareAt(msg._pos) : GS_NONE;
+
+		_gsPressed = GS_NONE;
+		draw();
+
+		if (released == pressed)
+			gatherShare(pressed);
+		return true;
+	}
+
+	return ItemsView::msgMouseUp(msg);
+}
+
 bool CharacterInventory::msgAction(const ActionMessage &msg) {
 	if (endDelay())
 		return true;
@@ -359,10 +458,11 @@ void CharacterInventory::trade(const Common::String &mode, int amount, Character
 		// Gold is a 24-bit value. The old 16-bit limit truncated both the
 		// requested amount and the source gold above 65,535
 		uint32 transfer = MIN<uint32>((uint32)amount, src._gold);
-		if (destChar->_gold >= 0xffffff)
+		if (destChar->_gold >= 0xffffff) {
 			transfer = 0;
-		else
+		} else {
 			transfer = MIN<uint32>(transfer, 0xffffff - destChar->_gold);
+		}
 		src._gold -= transfer;
 		destChar->_gold += transfer;
 
diff --git a/engines/mm/mm1/views_enh/character_inventory.h b/engines/mm/mm1/views_enh/character_inventory.h
index d7a55b951bc..f882c694807 100644
--- a/engines/mm/mm1/views_enh/character_inventory.h
+++ b/engines/mm/mm1/views_enh/character_inventory.h
@@ -44,8 +44,10 @@ protected:
 	DisplayMode _mode = ARMS_MODE;
 
 private:
+	enum GatherShareButton { GS_NONE, GS_GATHER, GS_SHARE };
 	Common::String _tradeMode;
 	int _tradeAmount;
+	GatherShareButton _gsPressed = GS_NONE;
 
 	/**
 	 * Populates the list of items
@@ -57,6 +59,21 @@ private:
 	 */
 	void drawTitle();
 
+	/**
+	 * Draw the compact G/S Gather/Share control in the spare inventory gap.
+	 */
+	void drawGatherShare();
+
+	/**
+	 * Return which half of the G/S control is under the mouse pointer.
+	 */
+	GatherShareButton gatherShareAt(const Common::Point &pos) const;
+
+	/**
+	 * Execute Gather or open the Share resource chooser.
+	 */
+	void gatherShare(GatherShareButton action);
+
 	/**
 	 * Equip an item
 	 */
@@ -128,6 +145,8 @@ public:
 	bool msgGame(const GameMessage &msg) override;
 	void draw() override;
 	bool msgKeypress(const KeypressMessage &msg) override;
+	bool msgMouseDown(const MouseDownMessage &msg) override;
+	bool msgMouseUp(const MouseUpMessage &msg) override;
 	bool msgAction(const ActionMessage &msg) override;
 };
 
diff --git a/engines/mm/mm1/views_enh/trade.cpp b/engines/mm/mm1/views_enh/trade.cpp
index 191c019b90b..d8b6e8846c7 100644
--- a/engines/mm/mm1/views_enh/trade.cpp
+++ b/engines/mm/mm1/views_enh/trade.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/trade.h"
+#include "mm/mm1/data/party.h"
 #include "mm/mm1/globals.h"
 
 namespace MM {
@@ -33,8 +34,10 @@ Trade::Trade() : ScrollView("Trade") {
 bool Trade::msgFocus(const FocusMessage &msg) {
 	_btnIcons.load("view.icn");
 
-	if (dynamic_cast<TextEntry *>(msg._priorView) == nullptr)
+	if (dynamic_cast<TextEntry *>(msg._priorView) == nullptr) {
+		_shareMode = false;
 		setMode(TRADE_OPTIONS);
+	}
 
 	return ScrollView::msgFocus(msg);
 }
@@ -44,6 +47,17 @@ bool Trade::msgUnfocus(const UnfocusMessage &msg) {
 	return ScrollView::msgUnfocus(msg);
 }
 
+bool Trade::msgGame(const GameMessage &msg) {
+	if (msg._name == "SHARE") {
+		_shareMode = true;
+		setMode(TRADE_OPTIONS);
+		redraw();
+		return true;
+	}
+
+	return ScrollView::msgGame(msg);
+}
+
 void Trade::draw() {
 	ScrollView::draw();
 
@@ -62,17 +76,34 @@ bool Trade::msgKeypress(const KeypressMessage &msg) {
 	case TRADE_OPTIONS:
 		switch (msg.keycode) {
 		case Common::KEYCODE_m:
-			setMode(TRADE_GEMS);
+			if (_shareMode) {
+				Party::share(TK_GEMS);
+				close();
+			} else {
+				setMode(TRADE_GEMS);
+			}
 			break;
 		case Common::KEYCODE_g:
-			setMode(TRADE_GOLD);
+			if (_shareMode) {
+				Party::share(TK_GOLD);
+				close();
+			} else {
+				setMode(TRADE_GOLD);
+			}
 			break;
 		case Common::KEYCODE_f:
-			setMode(TRADE_FOOD);
+			if (_shareMode) {
+				Party::share(TK_FOOD);
+				close();
+			} else {
+				setMode(TRADE_FOOD);
+			}
 			break;
 		case Common::KEYCODE_i:
-			close();
-			send(InfoMessage(STRING["enhdialogs.trade.items_help"]));
+			if (!_shareMode) {
+				close();
+				send(InfoMessage(STRING["enhdialogs.trade.items_help"]));
+			}
 			break;
 		default:
 			break;
@@ -114,13 +145,14 @@ void Trade::setMode(TradeMode mode) {
 		addButton(&_btnIcons, Common::Point(80, 0), 32, Common::KEYCODE_g);
 		addButton(&_btnIcons, Common::Point(158, 0), 34, Common::KEYCODE_m);
 		addButton(&_btnIcons, Common::Point(80, 20), 36, Common::KEYCODE_f);
-		addButton(&_btnIcons, Common::Point(158, 20), 26, Common::KEYCODE_i);
+		if (!_shareMode)
+			addButton(&_btnIcons, Common::Point(158, 20), 26, Common::KEYCODE_i);
 		addButton(&g_globals->_escSprites, Common::Point(0, 20), 0, KEYBIND_ESCAPE);
 		break;
 
 	default:
-		// How much. Gold is a 24-bit value (maximum 16,777,215),
-		// so gold transfers need an 8-digit entry field
+		// Gold is stored as a 24-bit value (maximum 16,777,215),
+		// so gold transfers need an 8-digit entry field.
 		draw();
 		_textEntry.display(70, 157, _mode == TRADE_GOLD ? 8 : 5, true,
 			abortFunc, enterFunc);
@@ -128,15 +160,20 @@ void Trade::setMode(TradeMode mode) {
 }
 
 void Trade::drawOptions() {
-	writeString(0, 5, STRING["enhdialogs.trade.which"]);
+	writeString(0, 5, _shareMode ? STRING["dialogs.character.share_all"] :
+		STRING["enhdialogs.trade.which"]);
 	writeString(28, 25, STRING["enhdialogs.misc.exit"]);
 	writeString(108, 5, STRING["enhdialogs.trade.gold"]);
 	writeString(186, 5, STRING["enhdialogs.trade.gems"]);
 	writeString(108, 25, STRING["enhdialogs.trade.food"]);
-	writeString(186, 25, STRING["enhdialogs.trade.items"]);
+	if (!_shareMode)
+		writeString(186, 25, STRING["enhdialogs.trade.items"]);
 }
 
 void Trade::amountEntered(uint amount) {
+	if (_shareMode)
+		return;
+
 	close();
 	send("CharacterInventory", GameMessage("TRADE", TRADE_NAMES[_mode], amount));
 }
diff --git a/engines/mm/mm1/views_enh/trade.h b/engines/mm/mm1/views_enh/trade.h
index 377ffdf4951..c00a294e5c0 100644
--- a/engines/mm/mm1/views_enh/trade.h
+++ b/engines/mm/mm1/views_enh/trade.h
@@ -34,6 +34,7 @@ private:
 	enum TradeMode { TRADE_OPTIONS, TRADE_GEMS, TRADE_GOLD, TRADE_FOOD };
 	const char *const TRADE_NAMES[4] = { nullptr, "GEMS", "GOLD", "FOOD" };
 	TradeMode _mode = TRADE_OPTIONS;
+	bool _shareMode = false;
 	Shared::Xeen::SpriteResource _btnIcons;
 	TextEntry _textEntry;
 	static void abortFunc();
@@ -60,6 +61,7 @@ public:
 
 	bool msgFocus(const FocusMessage &msg) override;
 	bool msgUnfocus(const UnfocusMessage &msg) override;
+	bool msgGame(const GameMessage &msg) override;
 	void draw() override;
 	bool msgKeypress(const KeypressMessage &msg) override;
 	bool msgAction(const ActionMessage &msg) override;


Commit: 095a8119e7ba3973f19810f40e4f2ff464a88c32
    https://github.com/scummvm/scummvm/commit/095a8119e7ba3973f19810f40e4f2ff464a88c32
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:54+11:00

Commit Message:
MM: MM1: Fix the experience needed to train for the next level

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/character.cpp
    engines/mm/mm1/data/character.h
    engines/mm/mm1/views/locations/training.cpp
    engines/mm/mm1/views_enh/locations/training.cpp


diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index 97bc9dfa7f9..8ac1ec48e3a 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -719,6 +719,35 @@ size_t Character::getPerformanceTotal() const {
 		+ totalFlags;
 }
 
+uint32 Character::experienceForNextLevel() const {
+	if (_level._base >= MAX_LEVEL)
+		return 0;
+
+	uint32 expTotal;
+	const uint32 expAmount =
+		(_class == KNIGHT || _class == CLERIC || _class == ROBBER) ?
+		150000 : 200000;
+
+	if (_class == KNIGHT || _class == CLERIC || _class == ROBBER)
+		expTotal = 1500;
+	else
+		expTotal = 2000;
+
+	// The requirement doubles for each level up to level 8, after which
+	// each further level adds a fixed amount
+	for (int level = _level._base - 1, ctr = 0; level > 0; --level) {
+		expTotal *= 2;
+
+		if (++ctr >= 7) {
+			while (--level > 0)
+				expTotal += expAmount;
+			break;
+		}
+	}
+
+	return expTotal;
+}
+
 byte Character::statColor(int amount, int threshold) const {
 	if (amount < 1)
 		return 6;
diff --git a/engines/mm/mm1/data/character.h b/engines/mm/mm1/data/character.h
index 7c3ab1e064b..744fc8fb3b3 100644
--- a/engines/mm/mm1/data/character.h
+++ b/engines/mm/mm1/data/character.h
@@ -579,6 +579,12 @@ struct Character : public PrimaryAttributes {
 	 */
 	void loadFaceSprites();
 
+	/**
+	 * Experience required to train for the next level.
+	 * Uses the same progression as the Training Hall
+	 */
+	uint32 experienceForNextLevel() const;
+
 	/**
 	 * Returns the color to use in enhanced mode to
 	 * represent the color of a character attribute
diff --git a/engines/mm/mm1/views/locations/training.cpp b/engines/mm/mm1/views/locations/training.cpp
index 8d4b5340438..18e605f9272 100644
--- a/engines/mm/mm1/views/locations/training.cpp
+++ b/engines/mm/mm1/views/locations/training.cpp
@@ -68,31 +68,17 @@ void Training::checkCharacter() {
 	_class = c._class;
 
 	if (_class == KNIGHT || _class == CLERIC || _class == ROBBER) {
-		_expTotal = 1500;
-		_expAmount = 150000;
-
 		if (_currLevel != 0) {
 			_cost = _currLevel >= 8 ? 3000 :
 				TRAINING_COSTS1[_currLevel - 1];
 		}
 	} else {
-		_expTotal = 2000;
-		_expAmount = 200000;
 		_cost = _currLevel >= 8 ? 4000 :
 			TRAINING_COSTS2[_currLevel - 1];
 	}
 
-	for (int level = _currLevel - 1, ctr = 0; level > 0; --level) {
-		_expTotal *= 16;
-
-		if (++ctr >= 7) {
-			while (--level > 0)
-				_expTotal += _expAmount;
-			break;
-		}
-	}
-
-	_remainingExp = _expTotal - c._exp;
+	_expTotal = c.experienceForNextLevel();
+	_remainingExp = (int)_expTotal - (int)c._exp;
 	_canTrain = _remainingExp <= 0;
 	_canAfford = (int)c._gold >= _cost;
 }
diff --git a/engines/mm/mm1/views_enh/locations/training.cpp b/engines/mm/mm1/views_enh/locations/training.cpp
index d45ef0b4ca5..22bfb316788 100644
--- a/engines/mm/mm1/views_enh/locations/training.cpp
+++ b/engines/mm/mm1/views_enh/locations/training.cpp
@@ -134,31 +134,17 @@ void Training::checkCharacter() {
 	_class = c._class;
 
 	if (_class == KNIGHT || _class == CLERIC || _class == ROBBER) {
-		_expTotal = 1500;
-		_expAmount = 150000;
-
 		if (_currLevel != 0) {
 			_cost = _currLevel >= 8 ? 3000 :
 				TRAINING_COSTS1[_currLevel - 1];
 		}
 	} else {
-		_expTotal = 2000;
-		_expAmount = 200000;
 		_cost = _currLevel >= 8 ? 4000 :
 			TRAINING_COSTS2[_currLevel - 1];
 	}
 
-	for (int level = _currLevel - 1, ctr = 0; level > 0; --level) {
-		_expTotal *= 16;
-
-		if (++ctr >= 7) {
-			while (--level > 0)
-				_expTotal += _expAmount;
-			break;
-		}
-	}
-
-	_remainingExp = _expTotal - c._exp;
+	_expTotal = c.experienceForNextLevel();
+	_remainingExp = (int)_expTotal - (int)c._exp;
 	_canTrain = _remainingExp <= 0;
 	_canAfford = (int)c._gold >= _cost;
 }


Commit: 444fbc95751f67315d94d099d71fc04b2469694f
    https://github.com/scummvm/scummvm/commit/444fbc95751f67315d94d099d71fc04b2469694f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:54+11:00

Commit Message:
MM: MM1: Update the enhanced Training Hall when switching characters

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/locations/training.cpp
    engines/mm/mm1/views_enh/locations/training.h


diff --git a/engines/mm/mm1/views_enh/locations/training.cpp b/engines/mm/mm1/views_enh/locations/training.cpp
index 22bfb316788..7cbe4b7bd11 100644
--- a/engines/mm/mm1/views_enh/locations/training.cpp
+++ b/engines/mm/mm1/views_enh/locations/training.cpp
@@ -37,8 +37,11 @@ Training::Training() : Location("Training", LOC_TRAINING) {
 }
 
 bool Training::msgFocus(const FocusMessage &msg) {
-	Location::msgFocus(msg);
+	if (!g_globals->_currCharacter && !g_globals->_party.empty())
+		g_globals->_currCharacter = &g_globals->_party[0];
+
 	checkCharacter();
+	Location::msgFocus(msg);
 	return true;
 }
 
@@ -74,12 +77,18 @@ void Training::draw() {
 }
 
 bool Training::msgGame(const GameMessage &msg) {
-	Location::msgGame(msg);
 	if (msg._name == "UPDATE")
 		checkCharacter();
+
+	Location::msgGame(msg);
 	return true;
 }
 
+void Training::charSwitched(Character *priorChar) {
+	checkCharacter();
+	PartyView::charSwitched(priorChar);
+}
+
 bool Training::msgKeypress(const KeypressMessage &msg) {
 	// If a delay is active, end it
 	if (endDelay())
diff --git a/engines/mm/mm1/views_enh/locations/training.h b/engines/mm/mm1/views_enh/locations/training.h
index 5cbc03e8730..045ead3d8ae 100644
--- a/engines/mm/mm1/views_enh/locations/training.h
+++ b/engines/mm/mm1/views_enh/locations/training.h
@@ -48,6 +48,11 @@ private:
 	 */
 	void checkCharacter();
 
+	/**
+	 * Recalculate training fields after selecting another character
+	 */
+	void charSwitched(Character *priorChar) override;
+
 	/**
 	 * Validates if training is allowed, then trains.
 	 */


Commit: ddc42b8c24b568c3de60a1222ea48c18947a740f
    https://github.com/scummvm/scummvm/commit/ddc42b8c24b568c3de60a1222ea48c18947a740f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:55+11:00

Commit Message:
MM: MM1: Stop the encounter dialog redrawing over the combat screen

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/encounter.cpp


diff --git a/engines/mm/mm1/views_enh/encounter.cpp b/engines/mm/mm1/views_enh/encounter.cpp
index 37cbb2866db..ca2867ee5fe 100644
--- a/engines/mm/mm1/views_enh/encounter.cpp
+++ b/engines/mm/mm1/views_enh/encounter.cpp
@@ -197,10 +197,12 @@ void Encounter::timeout() {
 		break;
 
 	case BATTLE:
-		// Switch to combat view
+		// Finish the underlying game redraw before starting combat,
+		// and don't redraw this closed Encounter afterwards
 		close();
+		g_events->drawElements();
 		send("Combat", GameMessage("COMBAT"));
-		break;
+		return;
 
 	default:
 		break;


Commit: ce10a29b9e9d814920b3f897a4a3e6bda5f9dffd
    https://github.com/scummvm/scummvm/commit/ce10a29b9e9d814920b3f897a4a3e6bda5f9dffd
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:55+11:00

Commit Message:
MM: MM1: Send the party to the Sorpigal Inn after the enhanced Keeper dialog

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/interactions/keeper.cpp


diff --git a/engines/mm/mm1/views_enh/interactions/keeper.cpp b/engines/mm/mm1/views_enh/interactions/keeper.cpp
index 480aed0af23..3b9db4d5aee 100644
--- a/engines/mm/mm1/views_enh/interactions/keeper.cpp
+++ b/engines/mm/mm1/views_enh/interactions/keeper.cpp
@@ -70,6 +70,7 @@ void Keeper::viewAction() {
 	case 4:
 		if (!_isWorthy) {
 			leave();
+			map.sorpigalInn();
 		} else {
 			addText(STRING["maps.map54.keeper6"]);
 			redraw();
@@ -78,6 +79,7 @@ void Keeper::viewAction() {
 
 	default:
 		leave();
+		map.sorpigalInn();
 		break;
 	}
 }


Commit: 71928aa6204ece5355678be0b98b1b4a67bf45ce
    https://github.com/scummvm/scummvm/commit/71928aa6204ece5355678be0b98b1b4a67bf45ce
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:55+11:00

Commit Message:
MM: MM1: Stop re-running King Alamar's event after the imposter is defeated

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views/interactions/alamar.cpp
    engines/mm/mm1/views_enh/interactions/alamar.cpp


diff --git a/engines/mm/mm1/views/interactions/alamar.cpp b/engines/mm/mm1/views/interactions/alamar.cpp
index cc3e012ddc7..9241d99adc7 100644
--- a/engines/mm/mm1/views/interactions/alamar.cpp
+++ b/engines/mm/mm1/views/interactions/alamar.cpp
@@ -83,6 +83,10 @@ bool Alamar::msgKeypress(const KeypressMessage &msg) {
 	MM1::Maps::Map49 &map = *static_cast<MM1::Maps::Map49 *>(g_maps->_currentMap);
 	close();
 
+	// Alamar has already been defeated, so there's nothing more to do
+	if (_succeeded)
+		return true;
+
 	if (map[HAS_EYE]) {
 		map[VAL1]++;
 		map.updateGame();
diff --git a/engines/mm/mm1/views_enh/interactions/alamar.cpp b/engines/mm/mm1/views_enh/interactions/alamar.cpp
index a424efa20a7..0c7365ab49e 100644
--- a/engines/mm/mm1/views_enh/interactions/alamar.cpp
+++ b/engines/mm/mm1/views_enh/interactions/alamar.cpp
@@ -82,6 +82,10 @@ void Alamar::viewAction() {
 	MM1::Maps::Map49 &map = *static_cast<MM1::Maps::Map49 *>(g_maps->_currentMap);
 	close();
 
+	// Alamar has already been defeated, so there's nothing more to do
+	if (_succeeded)
+		return;
+
 	if (map[HAS_EYE]) {
 		map[VAL1]++;
 		map.updateGame();


Commit: d704cd74715c79d3668ad484e3bd3151c3365d88
    https://github.com/scummvm/scummvm/commit/d704cd74715c79d3668ad484e3bd3151c3365d88
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:55+11:00

Commit Message:
MM: MM1: Fix the Hooded Figure color quiz reading the wrong sign flag

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views/color_questions.cpp
    engines/mm/mm1/views_enh/color_questions.cpp


diff --git a/engines/mm/mm1/views/color_questions.cpp b/engines/mm/mm1/views/color_questions.cpp
index 192212ed8c4..52fd37c193c 100644
--- a/engines/mm/mm1/views/color_questions.cpp
+++ b/engines/mm/mm1/views/color_questions.cpp
@@ -38,23 +38,32 @@ bool ColorQuestions::msgFocus(const FocusMessage &msg) {
 	TextView::msgFocus(msg);
 	_showingResponse = false;
 
-	// Find first non-incapacitated party member
+	// Find first non-incapacitated party member.
 	_charIndex = -1;
 	moveToNextChar();
 
+	if (_charIndex < 0 || _charIndex >= (int)g_globals->_party.size()) {
+		close();
+		g_globals->_party.checkPartyDead();
+		return true;
+	}
+
 	return true;
 }
 
 void ColorQuestions::draw() {
 	clearSurface();
 
+	if (_charIndex < 0 || _charIndex >= (int)g_globals->_party.size())
+		return;
+
+	g_globals->_currCharacter = &g_globals->_party[_charIndex];
+
 	if (!_showingResponse) {
 		writeString(0, 0, STRING["maps.map17.color"]);
 		writeString(0, 2, STRING["maps.map17.options"]);
-
 	} else {
 		const Character &c = g_globals->_party[_charIndex];
-
 		Common::String result = STRING[c.hasBadCondition() ?
 			"maps.map17.wrong" : "maps.map17.correct"];
 		writeString(16, 2, result);
@@ -65,27 +74,35 @@ bool ColorQuestions::msgKeypress(const KeypressMessage &msg) {
 	if (endDelay())
 		return true;
 
-	if (!_showingResponse && msg.keycode >= Common::KEYCODE_1 && msg.keycode <= Common::KEYCODE_9) {
-		Maps::Map17 &map = *static_cast<Maps::Map17 *>(g_maps->_currentMap);
-		map[COLOR] = msg.ascii - '1';
+	if (_charIndex < 0 || _charIndex >= (int)g_globals->_party.size())
+		return true;
+
+	if (!_showingResponse && msg.keycode >= Common::KEYCODE_1 &&
+			msg.keycode <= Common::KEYCODE_8) {
+		Maps::Map17 *mapP = dynamic_cast<Maps::Map17 *>(g_maps->_currentMap);
+		if (!mapP) {
+			close();
+			return true;
+		}
+
+		Maps::Map17 &map = *mapP;
+		map[COLOR] = msg.keycode - Common::KEYCODE_1;
 
 		Character &c = g_globals->_party[_charIndex];
-		int color = c._flags[2] & 0xf;
+		const byte sign = c._flags[4];
 
-		// If a color hasn't been designated yet from talking to Gypsy,
-		// or it has but the wrong color is selected, eradicate them
-		if (!color || (color & 7) != map[COLOR]) {
+		// Gypsy stores the assigned sign in flag byte 4, not byte 2.
+		if (!(sign & CHARFLAG4_ASSIGNED) ||
+				(sign & CHARFLAG4_SIGN) != map[COLOR]) {
 			c._condition = ERADICATED;
 		} else {
 			map[CORRECT_ANSWERS]++;
 			c._flags[4] |= CHARFLAG4_80;
 		}
 
-		// Show the response
 		_showingResponse = true;
 		redraw();
 		delaySeconds(2);
-
 		return true;
 	}
 
diff --git a/engines/mm/mm1/views_enh/color_questions.cpp b/engines/mm/mm1/views_enh/color_questions.cpp
index 93988cc88c4..3605cf6074d 100644
--- a/engines/mm/mm1/views_enh/color_questions.cpp
+++ b/engines/mm/mm1/views_enh/color_questions.cpp
@@ -38,19 +38,30 @@ bool ColorQuestions::msgFocus(const FocusMessage &msg) {
 	ScrollView::msgFocus(msg);
 	_showingResponse = false;
 
-	// Find first non-incapacitated party member
+	// Find first non-incapacitated party member.
 	_charIndex = -1;
 	moveToNextChar();
 
+	// The original view indexed the party unconditionally in draw().
+	// Guard an empty/no-eligible party so opening the Hooded Figure quiz
+	// can never dereference past the party array.
+	if (_charIndex < 0 || _charIndex >= (int)g_globals->_party.size()) {
+		close();
+		g_globals->_party.checkPartyDead();
+		return true;
+	}
+
 	return true;
 }
 
 void ColorQuestions::draw() {
-	// Highlight next character to answer question
+	if (_charIndex < 0 || _charIndex >= (int)g_globals->_party.size())
+		return;
+
+	// Highlight next character to answer question.
 	g_globals->_currCharacter = &g_globals->_party[_charIndex];
 	g_events->send("GameParty", GameMessage("CHAR_HIGHLIGHT", (int)true));
 
-	// Draw view
 	ScrollView::draw();
 	setReduced(false);
 
@@ -61,12 +72,10 @@ void ColorQuestions::draw() {
 		for (int option = 0; option < 8; ++option) {
 			Common::String prefix = Common::String::format("%c) ", '1' + option);
 			writeString(20 + 105 * (option % 2), 10 + (option / 2) * 8, prefix, ALIGN_RIGHT);
-			writeString(STRING[Common::String::format("colors.%d", option + 1)]);
+			writeString(STRING[Common::String::format("colors.%d", option)]);
 		}
-
 	} else {
 		const Character &c = g_globals->_party[_charIndex];
-
 		Common::String result = STRING[c.hasBadCondition() ?
 			"maps.map17.wrong" : "maps.map17.correct"];
 		writeLine(1, result, ALIGN_MIDDLE);
@@ -77,27 +86,35 @@ bool ColorQuestions::msgKeypress(const KeypressMessage &msg) {
 	if (endDelay())
 		return true;
 
-	if (!_showingResponse && msg.keycode >= Common::KEYCODE_1 && msg.keycode <= Common::KEYCODE_9) {
-		Maps::Map17 &map = *static_cast<Maps::Map17 *>(g_maps->_currentMap);
-		map[COLOR] = msg.ascii - '1';
+	if (_charIndex < 0 || _charIndex >= (int)g_globals->_party.size())
+		return true;
+
+	if (!_showingResponse && msg.keycode >= Common::KEYCODE_1 &&
+			msg.keycode <= Common::KEYCODE_8) {
+		Maps::Map17 *mapP = dynamic_cast<Maps::Map17 *>(g_maps->_currentMap);
+		if (!mapP) {
+			close();
+			return true;
+		}
+
+		Maps::Map17 &map = *mapP;
+		map[COLOR] = msg.keycode - Common::KEYCODE_1;
 
 		Character &c = g_globals->_party[_charIndex];
-		int color = c._flags[2] & 0xf;
+		const byte sign = c._flags[4];
 
-		// If a color hasn't been designated yet from talking to Gypsy,
-		// or it has but the wrong color is selected, eradicate them
-		if (!color || (color & 7) != map[COLOR]) {
+		// Gypsy stores the assigned sign in flag byte 4, not byte 2.
+		if (!(sign & CHARFLAG4_ASSIGNED) ||
+				(sign & CHARFLAG4_SIGN) != map[COLOR]) {
 			c._condition = ERADICATED;
 		} else {
 			map[CORRECT_ANSWERS]++;
 			c._flags[4] |= CHARFLAG4_80;
 		}
 
-		// Show the response
 		_showingResponse = true;
 		redraw();
 		delaySeconds(2);
-
 		return true;
 	}
 


Commit: b96993cde79eb71183bbf73bb33828587f506aff
    https://github.com/scummvm/scummvm/commit/b96993cde79eb71183bbf73bb33828587f506aff
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:55+11:00

Commit Message:
MM: MM1: Let the Dog Statue give the Gold Key again and award its XP once

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map42.cpp
    engines/mm/mm1/views/interactions/dog_statue.cpp
    engines/mm/mm1/views_enh/interactions/dog_statue.cpp


diff --git a/engines/mm/mm1/maps/map42.cpp b/engines/mm/mm1/maps/map42.cpp
index 1e63372ba46..c7962ee0739 100644
--- a/engines/mm/mm1/maps/map42.cpp
+++ b/engines/mm/mm1/maps/map42.cpp
@@ -152,10 +152,23 @@ void Map42::special17() {
 }
 
 void Map42::dogSuccess() {
+	bool alreadyCompleted = false;
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
-		Character &c = g_globals->_party[i];
-		c._flags[0] = CHARFLAG0_DOG_STATUE;
-		c._exp += 10000;
+		if (g_globals->_party[i]._flags[0] & CHARFLAG0_DOG_STATUE) {
+			alreadyCompleted = true;
+			break;
+		}
+	}
+
+	// The quest transition replaces the courier-stage byte with DOG_STATUE.
+	// Award the completion XP only on that first transition, so recovering a
+	// missed Gold Key later doesn't duplicate it
+	if (!alreadyCompleted) {
+		for (uint i = 0; i < g_globals->_party.size(); ++i) {
+			Character &c = g_globals->_party[i];
+			c._flags[0] = CHARFLAG0_DOG_STATUE;
+			c._exp += 10000;
+		}
 	}
 
 	g_globals->_treasure._items[2] = GOLD_KEY_ID;
diff --git a/engines/mm/mm1/views/interactions/dog_statue.cpp b/engines/mm/mm1/views/interactions/dog_statue.cpp
index 7d7c0a87ad3..5388fb7c20f 100644
--- a/engines/mm/mm1/views/interactions/dog_statue.cpp
+++ b/engines/mm/mm1/views/interactions/dog_statue.cpp
@@ -36,16 +36,28 @@ DogStatue::DogStatue() : TextView("DogStatue") {
 bool DogStatue::msgFocus(const FocusMessage &msg) {
 	Sound::sound(SOUND_2);
 
-	_completedQuests = false;
+	bool alreadyCompleted = false;
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
-		if ((g_globals->_party[i]._flags[0] &
-			(CHARFLAG0_COURIER3 | CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)) ==
-			(CHARFLAG0_COURIER3 | CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)) {
-			_completedQuests = true;
+		if (g_globals->_party[i]._flags[0] & CHARFLAG0_DOG_STATUE) {
+			alreadyCompleted = true;
 			break;
 		}
 	}
 
+	_completedQuests = alreadyCompleted &&
+		!g_globals->_party.hasItem(GOLD_KEY_ID);
+
+	if (!alreadyCompleted) {
+		for (uint i = 0; i < g_globals->_party.size(); ++i) {
+			if ((g_globals->_party[i]._flags[0] &
+				(CHARFLAG0_COURIER3 | CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)) ==
+				(CHARFLAG0_COURIER3 | CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)) {
+				_completedQuests = true;
+				break;
+			}
+		}
+	}
+
 	return TextView::msgFocus(msg);
 }
 
@@ -65,6 +77,12 @@ void DogStatue::draw() {
 }
 
 bool DogStatue::msgKeypress(const KeypressMessage &msg) {
+	if (_completedQuests) {
+		close();
+		g_events->addAction(KEYBIND_SEARCH);
+		return true;
+	}
+
 	if (msg.keycode == Common::KEYCODE_y || msg.keycode == Common::KEYCODE_n) {
 		MM1::Maps::Map42 &map = *static_cast<MM1::Maps::Map42 *>(g_maps->_currentMap);
 		close();
@@ -78,6 +96,12 @@ bool DogStatue::msgKeypress(const KeypressMessage &msg) {
 }
 
 bool DogStatue::msgAction(const ActionMessage &msg) {
+	if (_completedQuests) {
+		close();
+		g_events->addAction(KEYBIND_SEARCH);
+		return true;
+	}
+
 	if (msg._action == KEYBIND_ESCAPE || msg._action == KEYBIND_SELECT) {
 		MM1::Maps::Map42 &map = *static_cast<MM1::Maps::Map42 *>(g_maps->_currentMap);
 		close();
diff --git a/engines/mm/mm1/views_enh/interactions/dog_statue.cpp b/engines/mm/mm1/views_enh/interactions/dog_statue.cpp
index 628d77c3d0d..60020a0a95c 100644
--- a/engines/mm/mm1/views_enh/interactions/dog_statue.cpp
+++ b/engines/mm/mm1/views_enh/interactions/dog_statue.cpp
@@ -36,16 +36,28 @@ DogStatue::DogStatue() : Interaction("DogStatue") {
 bool DogStatue::msgFocus(const FocusMessage &msg) {
 	Sound::sound(SOUND_2);
 
-	_completedQuests = false;
+	bool alreadyCompleted = false;
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
-		if ((g_globals->_party[i]._flags[0] &
-			(CHARFLAG0_COURIER3 | CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)) ==
-			(CHARFLAG0_COURIER3 | CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)) {
-			_completedQuests = true;
+		if (g_globals->_party[i]._flags[0] & CHARFLAG0_DOG_STATUE) {
+			alreadyCompleted = true;
 			break;
 		}
 	}
 
+	_completedQuests = alreadyCompleted &&
+		!g_globals->_party.hasItem(GOLD_KEY_ID);
+
+	if (!alreadyCompleted) {
+		for (uint i = 0; i < g_globals->_party.size(); ++i) {
+			if ((g_globals->_party[i]._flags[0] &
+				(CHARFLAG0_COURIER3 | CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)) ==
+				(CHARFLAG0_COURIER3 | CHARFLAG0_FOUND_CHEST | CHARFLAG0_40)) {
+				_completedQuests = true;
+				break;
+			}
+		}
+	}
+
 	clearButtons();
 
 	if (_completedQuests) {
@@ -72,6 +84,7 @@ bool DogStatue::msgKeypress(const KeypressMessage &msg) {
 	if (_completedQuests) {
 		close();
 		map.dogSuccess();
+		g_events->addAction(KEYBIND_SEARCH);
 
 	} else if (msg.keycode == Common::KEYCODE_y || msg.keycode == Common::KEYCODE_n) {
 		close();
@@ -87,8 +100,10 @@ void DogStatue::viewAction() {
 	MM1::Maps::Map42 &map = *static_cast<MM1::Maps::Map42 *>(g_maps->_currentMap);
 
 	close();
-	if (_completedQuests)
+	if (_completedQuests) {
 		map.dogSuccess();
+		g_events->addAction(KEYBIND_SEARCH);
+	}
 }
 
 } // namespace Interactions


Commit: 58c193cf62c97cf32a9af3fe0a3d67c47fd5ebb1
    https://github.com/scummvm/scummvm/commit/58c193cf62c97cf32a9af3fe0a3d67c47fd5ebb1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:56+11:00

Commit Message:
MM: MM1: Fix the Statue of Judgement worthiness check and result text

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map30.cpp


diff --git a/engines/mm/mm1/maps/map30.cpp b/engines/mm/mm1/maps/map30.cpp
index b0edcb25408..ae14a3678d9 100644
--- a/engines/mm/mm1/maps/map30.cpp
+++ b/engines/mm/mm1/maps/map30.cpp
@@ -116,7 +116,7 @@ void Map30::special03() {
 
 Common::String Map30::worthiness() {
 	Character &c = *g_globals->_currCharacter;
-	if ((c._flags[1] & ~CHARFLAG1_WORTHY) == ~CHARFLAG1_WORTHY)
+	if ((c._flags[1] & 0x7f) == 0x7f)
 		return worthy();
 	else
 		return unworthy();
@@ -128,18 +128,23 @@ Common::String Map30::worthy() {
 	c._exp += val;
 
 	Common::String line = Common::String::format(
-		STRING["maps.map30.worthy"].c_str(), '0' + (c._worthiness / 5));
+		STRING["maps.map30.worthy"].c_str(), c._worthiness / 32);
 	line = Common::String::format("%s%d %s",
 		line.c_str(), val, STRING["maps.map30.experience"].c_str());
 
 	if (c._worthiness & 0x80) {
 		int attrNum = getRandomNumber(7) - 1;
-		line += Common::String::format(", +3 %s",
-			STRING[Common::String::format("maps.map30.attributes.%d", attrNum)].c_str());
-
+		const Common::String attrName =
+			STRING[Common::String::format("maps.map30.attributes.%d", attrNum)];
 		AttributePair &attrib = c.getAttribute(attrNum);
-		if (attrib._base < 43)
+
+		// Attributes with a base of 43 or more don't increase
+		if (attrib._base < 43) {
 			attrib._current = attrib._base = attrib._base + 3;
+			line += Common::String::format(", +3 %s", attrName.c_str());
+		} else {
+			line += Common::String::format(", %s unchanged", attrName.c_str());
+		}
 	}
 
 	c._worthiness = 0;


Commit: 59c72a2c77612416cff77558a55be23012dada34
    https://github.com/scummvm/scummvm/commit/59c72a2c77612416cff77558a55be23012dada34
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:56+11:00

Commit Message:
MM: MM1: Mark the Statue of Judgement reward as claimed for the Quest Log

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map30.cpp


diff --git a/engines/mm/mm1/maps/map30.cpp b/engines/mm/mm1/maps/map30.cpp
index ae14a3678d9..f063ebce124 100644
--- a/engines/mm/mm1/maps/map30.cpp
+++ b/engines/mm/mm1/maps/map30.cpp
@@ -147,6 +147,11 @@ Common::String Map30::worthy() {
 		}
 	}
 
+	// Mark that this character has claimed the Statue's result, for the
+	// Quest Log. The unused high bit of the prisoner flag byte is
+	// already saved, so no save format change is needed
+	c._flags[1] |= CHARFLAG1_WORTHY;
+
 	c._worthiness = 0;
 	return line;
 }


Commit: 1dd23b63c503c169ec5ad2e3c3f64c39214ebbc6
    https://github.com/scummvm/scummvm/commit/1dd23b63c503c169ec5ad2e3c3f64c39214ebbc6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:56+11:00

Commit Message:
MM: MM1: Record Lord Ironfist's quest discoveries where they happen

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map01.cpp
    engines/mm/mm1/maps/map15.cpp
    engines/mm/mm1/maps/map43.cpp


diff --git a/engines/mm/mm1/maps/map01.cpp b/engines/mm/mm1/maps/map01.cpp
index eff38c4d897..f8a437e0898 100644
--- a/engines/mm/mm1/maps/map01.cpp
+++ b/engines/mm/mm1/maps/map01.cpp
@@ -123,6 +123,14 @@ void Map01::special08() {
 		STRING["maps.map01.secret"],
 		[](const Common::KeyState &) {
 			g_events->close();
+
+			// Consume the secret for this map visit, and record Lord
+			// Ironfist's quest #3 (discover the secret of Portsmith) at
+			// the discovery site. Winning the fight isn't required
+			g_maps->clearSpecial();
+			for (uint i = 0; i < g_globals->_party.size(); ++i)
+				g_globals->_party[i]._flags[7] |= CHARFLAG7_4;
+
 			Game::Encounter &enc = g_globals->_encounters;
 			enc.clearMonsters();
 
diff --git a/engines/mm/mm1/maps/map15.cpp b/engines/mm/mm1/maps/map15.cpp
index bb3426edbed..dc1df5fb1b8 100644
--- a/engines/mm/mm1/maps/map15.cpp
+++ b/engines/mm/mm1/maps/map15.cpp
@@ -197,6 +197,11 @@ void Map15::cove() {
 			Map15 &map = *static_cast<Map15 *>(g_maps->_currentMap);
 			g_maps->clearSpecial();
 
+			// Discovering Pirate's Secret Cove satisfies Lord Ironfist's
+			// quest #4 for the whole party
+			for (uint i = 0; i < g_globals->_party.size(); ++i)
+				g_globals->_party[i]._flags[7] |= CHARFLAG7_8;
+
 			for (uint i = 0; i < g_globals->_party.size(); ++i) {
 				Character &c = g_globals->_party[i];
 				int idx = c._backpack.indexOf(map[ITEM_ID]);
diff --git a/engines/mm/mm1/maps/map43.cpp b/engines/mm/mm1/maps/map43.cpp
index 7f735906642..a00bb3447a1 100644
--- a/engines/mm/mm1/maps/map43.cpp
+++ b/engines/mm/mm1/maps/map43.cpp
@@ -31,8 +31,10 @@ namespace Maps {
 
 #define TREASURE_STATE 142
 
+// Ironfist's quest completions are recorded where they're discovered,
+// so inventory items are no longer used as surrogate proof
 static const byte MATCH_ITEMS[7] = {
-	0, 0, MAP_OF_DESERT_ID, 0, 0, 0, 0
+	0, 0, 0, 0, 0, 0, 0
 };
 static const byte MATCH_FLAGS[8] = {
 	1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80
@@ -154,7 +156,7 @@ void Map43::updateFlags() {
 
 			// Scan list of items to match against
 			for (int arrIdx = 0; arrIdx < 7; ++arrIdx) {
-				if (itemId == MATCH_ITEMS[arrIdx]) {
+				if (MATCH_ITEMS[arrIdx] && itemId == MATCH_ITEMS[arrIdx]) {
 					c._flags[7] |= MATCH_FLAGS[arrIdx];
 					break;
 				}


Commit: e71ef500756210d775a48769be1be1ec358d4660
    https://github.com/scummvm/scummvm/commit/e71ef500756210d775a48769be1be1ec358d4660
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:57+11:00

Commit Message:
MM: MM1: Knock out characters due to heat

Fixes ending game from knocked out characters at 0 HP
due to heat, and ends the game

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map15.cpp


diff --git a/engines/mm/mm1/maps/map15.cpp b/engines/mm/mm1/maps/map15.cpp
index dc1df5fb1b8..76a11ccb059 100644
--- a/engines/mm/mm1/maps/map15.cpp
+++ b/engines/mm/mm1/maps/map15.cpp
@@ -74,7 +74,12 @@ void Map15::special() {
 				for (uint i = 0; i < g_globals->_party.size(); ++i) {
 					Character &c = g_globals->_party[i];
 					c._hpCurrent = MAX((int)c._hpCurrent - 15, 0);
+					if (!c._hpCurrent) {
+						if (!(c._condition & BAD_CONDITION))
+							c._condition = UNCONSCIOUS;
+					}
 				}
+				checkPartyDead();
 			}
 		}
 	} else if (getRandomNumber(100) != 100) {


Commit: 44c79282a8d137acba3cafc7bdc09a4848891fc7
    https://github.com/scummvm/scummvm/commit/44c79282a8d137acba3cafc7bdc09a4848891fc7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:57+11:00

Commit Message:
MM: MM1: Keep offering the White Queen Idol until the party has it

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map53.cpp


diff --git a/engines/mm/mm1/maps/map53.cpp b/engines/mm/mm1/maps/map53.cpp
index 45a0a948fb1..2cf7e54d2f5 100644
--- a/engines/mm/mm1/maps/map53.cpp
+++ b/engines/mm/mm1/maps/map53.cpp
@@ -29,8 +29,6 @@ namespace MM {
 namespace MM1 {
 namespace Maps {
 
-#define VAL1 379
-
 void Map53::special() {
 	// Scan for special actions on the map cell
 	for (uint i = 0; i < 15; ++i) {
@@ -52,11 +50,13 @@ void Map53::special() {
 }
 
 void Map53::special00() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
-	if (!_data[VAL1]) {
+	// Keep offering the White Queen Idol until the party actually has it.
+	// The old private map byte could suppress the reward even though the
+	// idol never reached the party
+	if (!g_globals->_party.hasItem(W_QUEEN_IDOL_ID))
 		g_globals->_treasure._items[2] = W_QUEEN_IDOL_ID;
-		_data[VAL1]++;
-	}
+
+	send(SoundMessage(STRING["maps.wall_painted"]));
 }
 
 void Map53::special01() {


Commit: 32859f673645d8c274ca80215a8e3c7b302ac9af
    https://github.com/scummvm/scummvm/commit/32859f673645d8c274ca80215a8e3c7b302ac9af
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:57+11:00

Commit Message:
MM: MM1: Only offer the shipwreck search on actual shipwreck squares

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map25.cpp


diff --git a/engines/mm/mm1/maps/map25.cpp b/engines/mm/mm1/maps/map25.cpp
index 0de74988bc6..be93fc7afb4 100644
--- a/engines/mm/mm1/maps/map25.cpp
+++ b/engines/mm/mm1/maps/map25.cpp
@@ -116,7 +116,25 @@ void Map25::special() {
 		}
 		break;
 
-	default:
+	default: {
+		// Only the six marked wrecks and the Jolly Raven
+		// should display the shipwreck search prompt
+		static const uint16 SHIPWRECK_OFFSETS[] = {
+			142, 155, 161, 179, 185, 197, 216
+		};
+		bool isShipwreck = false;
+		for (uint i = 0; i < ARRAYSIZE(SHIPWRECK_OFFSETS); ++i) {
+			if (g_maps->_mapOffset == SHIPWRECK_OFFSETS[i]) {
+				isShipwreck = true;
+				break;
+			}
+		}
+
+		if (!isShipwreck) {
+			none160();
+			break;
+		}
+
 		send(SoundMessage(
 			STRING["maps.map25.ship"],
 			[]() {
@@ -139,6 +157,7 @@ void Map25::special() {
 		));
 		break;
 	}
+	}
 }
 
 void Map25::special00() {


Commit: a0f271067cc40359fdd6097b08c409cc14ed4889
    https://github.com/scummvm/scummvm/commit/a0f271067cc40359fdd6097b08c409cc14ed4889
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:57+11:00

Commit Message:
MM: MM1: Show Okrim's ghost after winning the fight and award his ring

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map37.cpp
    engines/mm/mm1/views/combat.cpp
    engines/mm/mm1/views_enh/combat.cpp
    engines/mm/mm1/views_enh/interactions/ghost.cpp


diff --git a/engines/mm/mm1/maps/map37.cpp b/engines/mm/mm1/maps/map37.cpp
index 62933fdc3d9..a3996fa8743 100644
--- a/engines/mm/mm1/maps/map37.cpp
+++ b/engines/mm/mm1/maps/map37.cpp
@@ -98,14 +98,16 @@ void Map37::special03() {
 
 void Map37::special04() {
 	Game::Encounter &enc = g_globals->_encounters;
-	g_maps->clearSpecial();
 
 	if (_data[VAL1]) {
+		// Okrim has actually been defeated. Consume the square for this
+		// map visit and present his post-battle ghost interaction
+		g_maps->clearSpecial();
 		g_events->addView("Ghost");
 
 	} else {
-		_data[VAL1]++;
-
+		// Don't mark Okrim defeated before combat. The combat view sets
+		// VAL1 only after a real victory, so retreating can't skip him
 		enc.clearMonsters();
 		enc.addMonster(19, 12);
 		for (int i = 1; i < 4; ++i)
diff --git a/engines/mm/mm1/views/combat.cpp b/engines/mm/mm1/views/combat.cpp
index c9dbca9f58c..9014a3b4595 100644
--- a/engines/mm/mm1/views/combat.cpp
+++ b/engines/mm/mm1/views/combat.cpp
@@ -871,10 +871,25 @@ void Combat::displaySpellResult(const InfoMessage &msg) {
 }
 
 void Combat::combatDone() {
+	// Okrim's map script expects a second processing of his square after
+	// victory, but the square was already cleared before combat. Detect a
+	// real victory here and hand off directly to his ghost
+	const bool okrimVictory = _mode == DEFEATED_MONSTERS &&
+		g_maps->_mapId == 37 && g_maps->_mapPos == Common::Point(12, 12);
+
+	if (okrimVictory) {
+		// Map37::VAL1. Set only after victory so fleeing can't satisfy it
+		(*g_maps->_currentMap)[232] = 1;
+		g_maps->clearSpecial();
+	}
+
 	Game::Combat::combatDone();
 
 	close();
 	g_events->send("Game", GameMessage("UPDATE"));
+
+	if (okrimVictory)
+		g_events->addView("Ghost");
 }
 
 } // namespace Views
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 754d23f920a..dcb544da761 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -1370,10 +1370,25 @@ void Combat::displaySpellResult(const InfoMessage &msg) {
 }
 
 void Combat::combatDone() {
+	// Okrim's map script expects a second processing of his square after
+	// victory, but the square was already cleared before combat. Detect a
+	// real victory here and hand off directly to his ghost
+	const bool okrimVictory = _mode == DEFEATED_MONSTERS &&
+		g_maps->_mapId == 37 && g_maps->_mapPos == Common::Point(12, 12);
+
+	if (okrimVictory) {
+		// Map37::VAL1. Set only after victory so fleeing can't satisfy it
+		(*g_maps->_currentMap)[232] = 1;
+		g_maps->clearSpecial();
+	}
+
 	Game::Combat::combatDone();
 
 	close();
 	g_events->send("Game", GameMessage("UPDATE"));
+
+	if (okrimVictory)
+		g_events->addView("Ghost");
 }
 
 } // namespace ViewsEnh
diff --git a/engines/mm/mm1/views_enh/interactions/ghost.cpp b/engines/mm/mm1/views_enh/interactions/ghost.cpp
index b33bf96499b..7c51f6a8ede 100644
--- a/engines/mm/mm1/views_enh/interactions/ghost.cpp
+++ b/engines/mm/mm1/views_enh/interactions/ghost.cpp
@@ -45,22 +45,33 @@ bool Ghost::msgFocus(const FocusMessage &msg) {
 }
 
 bool Ghost::msgKeypress(const KeypressMessage &msg) {
-	if (!_buttons.empty()) {
-		MM1::Maps::Map37 &map = *static_cast<MM1::Maps::Map37 *>(g_maps->_currentMap);
-
-		if (msg.keycode == Common::KEYCODE_y) {
-			g_globals->_party[0]._condition = ERADICATED;
-			close();
-			return true;
-
-		} else if (msg.keycode == Common::KEYCODE_n) {
-			map[MM1::Maps::MAP_29] = 32;
-			map[MM1::Maps::MAP_47] = 8;
-
-			addText(STRING["maps.map37.okrim2"]);
-			clearButtons();
-			return true;
-		}
+	if (_buttons.empty()) {
+		viewAction();
+		return true;
+	}
+
+	MM1::Maps::Map37 &map = *static_cast<MM1::Maps::Map37 *>(g_maps->_currentMap);
+
+	if (msg.keycode == Common::KEYCODE_y) {
+		g_globals->_party[0]._condition = ERADICATED;
+
+		// Match Classic/original MM1: Okrim's Ring is awarded whether
+		// the party accepts or declines the ghost's demand.
+		g_globals->_treasure._items[2] = RING_OF_OKRIM_ID;
+		close();
+		g_events->addAction(KEYBIND_SEARCH);
+		return true;
+
+	} else if (msg.keycode == Common::KEYCODE_n) {
+		map[MM1::Maps::MAP_29] = 32;
+		map[MM1::Maps::MAP_47] = 8;
+
+		// Preserve Enhanced's acknowledgement text, but stage the same
+		// Ring reward that Classic places in treasure immediately.
+		g_globals->_treasure._items[2] = RING_OF_OKRIM_ID;
+		addText(STRING["maps.map37.okrim2"]);
+		clearButtons();
+		return true;
 	}
 
 	return true;
@@ -69,6 +80,7 @@ bool Ghost::msgKeypress(const KeypressMessage &msg) {
 void Ghost::viewAction() {
 	if (_buttons.empty()) {
 		close();
+		g_events->addAction(KEYBIND_SEARCH);
 	}
 }
 


Commit: 870ed9690e1b04e40a59b41c2e8fe02cdf067c04
    https://github.com/scummvm/scummvm/commit/870ed9690e1b04e40a59b41c2e8fe02cdf067c04
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:58+11:00

Commit Message:
MM: MM1: Add vertical scrolling for long messages in the enhanced interface

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map10.cpp
    engines/mm/mm1/views_enh/game_messages.cpp
    engines/mm/mm1/views_enh/scroll_text.cpp
    engines/mm/mm1/views_enh/scroll_text.h


diff --git a/engines/mm/mm1/maps/map10.cpp b/engines/mm/mm1/maps/map10.cpp
index 215750197a2..072738944e3 100644
--- a/engines/mm/mm1/maps/map10.cpp
+++ b/engines/mm/mm1/maps/map10.cpp
@@ -75,10 +75,14 @@ void Map10::special00() {
 }
 
 void Map10::special01() {
-	send(SoundMessage(
+	// Ranalou's long dialogue uses the large message frame. The enhanced
+	// messages view scrolls it if the wrapped text still doesn't fit
+	SoundMessage msg(
 		STRING["maps.map10.ranalou1"],
 		[]() {
-			g_events->send(InfoMessage(STRING["maps.map10.ranalou2"]));
+			InfoMessage info(STRING["maps.map10.ranalou2"]);
+			info._largeMessage = true;
+			g_events->send(info);
 			Sound::sound(SOUND_3);
 
 			for (uint i = 0; i < g_globals->_party.size(); ++i)
@@ -91,7 +95,9 @@ void Map10::special01() {
 			g_maps->_mapPos = Common::Point(15, 0);
 			g_maps->_currentMap->updateGame();
 		}
-	));
+	);
+	msg._largeMessage = true;
+	send(msg);
 }
 
 void Map10::special02() {
diff --git a/engines/mm/mm1/views_enh/game_messages.cpp b/engines/mm/mm1/views_enh/game_messages.cpp
index 5a65789acd1..e9630a0f7b0 100644
--- a/engines/mm/mm1/views_enh/game_messages.cpp
+++ b/engines/mm/mm1/views_enh/game_messages.cpp
@@ -59,9 +59,11 @@ void GameMessages::draw() {
 }
 
 bool GameMessages::msgFocus(const FocusMessage &msg) {
-	MetaEngine::setKeybindingMode(_callback || _keyCallback ?
-		KeybindingMode::KBMODE_MENUS :
-		KeybindingMode::KBMODE_NORMAL);
+	MetaEngine::setKeybindingMode(isScrolling() ?
+		KeybindingMode::KBMODE_QUEST_LOG :
+		(_callback || _keyCallback ?
+			KeybindingMode::KBMODE_MENUS :
+			KeybindingMode::KBMODE_NORMAL));
 	return true;
 }
 
@@ -88,6 +90,16 @@ bool GameMessages::msgInfo(const InfoMessage &msg) {
 	for (const auto &line : msg._lines)
 		addText(line._text, -1, 0, (line.x > 0) ? ALIGN_MIDDLE : line._align, 0);
 
+	// Long Enhanced messages used to draw every wrapped line even after the
+	// text had passed the bottom of the scroll frame. Enable vertical scrolling
+	// only when the wrapped text actually overflows the current message box.
+	setScrolling(lineCount() > visibleLineCount());
+	MetaEngine::setKeybindingMode(isScrolling() ?
+		KeybindingMode::KBMODE_QUEST_LOG :
+		(_callback || _keyCallback ?
+			KeybindingMode::KBMODE_MENUS :
+			KeybindingMode::KBMODE_NORMAL));
+
 	if (msg._delaySeconds)
 		delaySeconds(msg._delaySeconds);
 
@@ -95,6 +107,28 @@ bool GameMessages::msgInfo(const InfoMessage &msg) {
 }
 
 bool GameMessages::msgKeypress(const KeypressMessage &msg) {
+	if (isScrolling()) {
+		const int page = visibleLineCount() > 1 ?
+			(int)visibleLineCount() - 1 : 1;
+
+		switch (msg.keycode) {
+		case Common::KEYCODE_UP:
+			scrollLines(-1);
+			return true;
+		case Common::KEYCODE_DOWN:
+			scrollLines(1);
+			return true;
+		case Common::KEYCODE_PAGEUP:
+			scrollLines(-page);
+			return true;
+		case Common::KEYCODE_PAGEDOWN:
+			scrollLines(page);
+			return true;
+		default:
+			break;
+		}
+	}
+
 	if (_keyCallback) {
 		_keyCallback(msg);
 
@@ -120,6 +154,19 @@ bool GameMessages::msgKeypress(const KeypressMessage &msg) {
 }
 
 bool GameMessages::msgAction(const ActionMessage &msg) {
+	if (isScrolling()) {
+		switch (msg._action) {
+		case KEYBIND_QUEST_PREVIOUS:
+			scrollLines(-1);
+			return true;
+		case KEYBIND_QUEST_NEXT:
+			scrollLines(1);
+			return true;
+		default:
+			break;
+		}
+	}
+
 	if (_callback || _keyCallback) {
 		switch (msg._action) {
 		case KEYBIND_ESCAPE:
@@ -153,18 +200,34 @@ bool GameMessages::msgAction(const ActionMessage &msg) {
 }
 
 bool GameMessages::msgMouseDown(const MouseDownMessage &msg) {
-	// If yes/no prompting, also pass events to buttons view
 	if (_callback)
 		return send("MessagesYesNo", msg);
 
-	return msgAction(ActionMessage(msg._button == MouseMessage::MB_RIGHT ?
-		KEYBIND_ESCAPE : KEYBIND_SELECT));
+	// Consume normal dialogue clicks here. Advance on mouse release so
+	// one physical click can never advance two dialogue pages.
+	return msg._button == MouseMessage::MB_LEFT ||
+		msg._button == MouseMessage::MB_RIGHT;
 }
 
 bool GameMessages::msgMouseUp(const MouseUpMessage &msg) {
-	// If yes/no prompting, also pass events to buttons view
 	if (_callback)
 		return send("MessagesYesNo", msg);
+
+	if (msg._button == MouseMessage::MB_LEFT) {
+		// For a long message, left-click advances by a page until the last
+		// page is visible. A further click then performs the normal close.
+		if (isScrolling() && canScrollDown()) {
+			const int page = visibleLineCount() > 1 ?
+				(int)visibleLineCount() - 1 : 1;
+			scrollLines(page);
+			return true;
+		}
+		return msgAction(ActionMessage(KEYBIND_SELECT));
+	}
+
+	if (msg._button == MouseMessage::MB_RIGHT)
+		return msgAction(ActionMessage(KEYBIND_ESCAPE));
+
 	return false;
 }
 
diff --git a/engines/mm/mm1/views_enh/scroll_text.cpp b/engines/mm/mm1/views_enh/scroll_text.cpp
index b30aeeb0eed..752bc984b44 100644
--- a/engines/mm/mm1/views_enh/scroll_text.cpp
+++ b/engines/mm/mm1/views_enh/scroll_text.cpp
@@ -28,6 +28,7 @@ namespace MM1 {
 namespace ViewsEnh {
 
 #define FONT_HEIGHT 8
+#define LINE_HEIGHT 10
 
 ScrollText::ScrollText(const Common::String &name) :
 		ScrollView(name) {
@@ -40,6 +41,65 @@ ScrollText::ScrollText(const Common::String &name, UIElement *owner) :
 void ScrollText::setBounds(const Common::Rect &r) {
 	ScrollView::setBounds(r);
 	_rowCount = _innerBounds.height() / FONT_HEIGHT;
+
+	if (_scrolling)
+		setScrolling(true);
+}
+
+size_t ScrollText::visibleLineCount() const {
+	const int height = _innerBounds.height();
+	if (height < FONT_HEIGHT)
+		return 0;
+
+	return (height - FONT_HEIGHT) / LINE_HEIGHT + 1;
+}
+
+void ScrollText::setScrolling(bool enabled) {
+	_scrolling = enabled;
+	if (!_scrolling) {
+		_topLine = 0;
+		return;
+	}
+
+	const size_t visible = visibleLineCount();
+	const size_t maxTop = (_lines.size() > visible) ?
+		_lines.size() - visible : 0;
+	if (_topLine > maxTop)
+		_topLine = maxTop;
+}
+
+bool ScrollText::canScrollUp() const {
+	return _scrolling && _topLine != 0;
+}
+
+bool ScrollText::canScrollDown() const {
+	if (!_scrolling)
+		return false;
+
+	const size_t visible = visibleLineCount();
+	return _topLine + visible < _lines.size();
+}
+
+bool ScrollText::scrollLines(int delta) {
+	if (!_scrolling || !delta)
+		return false;
+
+	const size_t visible = visibleLineCount();
+	const size_t maxTop = (_lines.size() > visible) ?
+		_lines.size() - visible : 0;
+	int newTop = (int)_topLine + delta;
+
+	if (newTop < 0)
+		newTop = 0;
+	if ((size_t)newTop > maxTop)
+		newTop = (int)maxTop;
+
+	if ((size_t)newTop == _topLine)
+		return false;
+
+	_topLine = (size_t)newTop;
+	redraw();
+	return true;
 }
 
 void ScrollText::addLine(const Common::String &str,
@@ -61,7 +121,6 @@ void ScrollText::addLine(const Common::String &str,
 
 void ScrollText::addText(const Common::String &s,
 		int lineNum, byte color, TextAlign align, int xp) {
-	const int LINE_HEIGHT = 10;
 	Common::String str = s;
 
 	if (lineNum == -1)
@@ -80,10 +139,27 @@ void ScrollText::addText(const Common::String &s,
 void ScrollText::draw() {
 	ScrollView::draw();
 
-	// Iterate through displaying any text
-	for (Lines::const_iterator i = begin(); i != end(); ++i) {
-		setTextColor(i->_color);
-		writeString(i->_pos.x, i->_pos.y, i->_str, i->_align);
+	if (!_scrolling) {
+		// Existing behavior for ordinary ScrollText users.
+		for (Lines::const_iterator i = begin(); i != end(); ++i) {
+			setTextColor(i->_color);
+			writeString(i->_pos.x, i->_pos.y, i->_str, i->_align);
+		}
+		return;
+	}
+
+	// Scrollable text is clipped by line: only lines whose complete glyph
+	// height fits inside the message's inner bounds are drawn.
+	const size_t visible = visibleLineCount();
+	const size_t endLine = (_topLine + visible < _lines.size()) ?
+		_topLine + visible : _lines.size();
+
+	for (size_t idx = _topLine; idx < endLine; ++idx) {
+		const Line &line = _lines[idx];
+		Common::Point pt = line._pos;
+		pt.y -= (int)_topLine * LINE_HEIGHT;
+		setTextColor(line._color);
+		writeString(pt.x, pt.y, line._str, line._align);
 	}
 }
 
diff --git a/engines/mm/mm1/views_enh/scroll_text.h b/engines/mm/mm1/views_enh/scroll_text.h
index 11310cdae55..fefb49f522d 100644
--- a/engines/mm/mm1/views_enh/scroll_text.h
+++ b/engines/mm/mm1/views_enh/scroll_text.h
@@ -51,6 +51,8 @@ public:
 private:
 	size_t _rowCount = 0;
 	Lines _lines;
+	bool _scrolling = false;
+	size_t _topLine = 0;
 public:
 	ScrollText(const Common::String &name);
 	ScrollText(const Common::String &name, UIElement *owner);
@@ -65,11 +67,32 @@ public:
 	Lines::const_iterator begin() const { return _lines.begin(); }
 	Lines::const_iterator end() const { return _lines.end(); }
 
+	/**
+	 * Returns the number of wrapped text lines.
+	 */
+	size_t lineCount() const { return _lines.size(); }
+
+	/**
+	 * Returns how many text lines fit wholly inside the current inner bounds.
+	 */
+	size_t visibleLineCount() const;
+
+	/**
+	 * Enable or disable vertical scrolling. Scrolling is opt-in so existing
+	 * ScrollText users retain their current drawing behavior.
+	 */
+	void setScrolling(bool enabled);
+	bool isScrolling() const { return _scrolling; }
+	bool canScrollUp() const;
+	bool canScrollDown() const;
+	bool scrollLines(int delta);
+
 	/**
 	 * Clear the lines
 	 */
 	void clear() {
 		_lines.clear();
+		_topLine = 0;
 	}
 
 	/**


Commit: 3b51434c3f30f3c4eb77f8a8fbba9ef51ebb44c8
    https://github.com/scummvm/scummvm/commit/3b51434c3f30f3c4eb77f8a8fbba9ef51ebb44c8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:58+11:00

Commit Message:
MM: MM1: Stop the Hooded Figure quizzing party members who have no sign

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map17.cpp


diff --git a/engines/mm/mm1/maps/map17.cpp b/engines/mm/mm1/maps/map17.cpp
index 6cc5d19f455..f31febcbb05 100644
--- a/engines/mm/mm1/maps/map17.cpp
+++ b/engines/mm/mm1/maps/map17.cpp
@@ -84,6 +84,28 @@ void Map17::special01() {
 	// using a fixed bit in the saved visit data
 	_visited[COLOR_BRIDGE_VISITED_OFFSET] |= COLOR_BRIDGE_DISCOVERED;
 
+	bool allAssigned = !g_globals->_party.empty();
+	for (uint i = 0; i < g_globals->_party.size(); ++i) {
+		if (!(g_globals->_party[i]._flags[4] & CHARFLAG4_ASSIGNED)) {
+			allAssigned = false;
+			break;
+		}
+	}
+
+	if (!allAssigned) {
+		// Without a Gypsy sign the quiz can't be answered correctly, so
+		// don't open the Y/N and quiz chain in that state
+		g_maps->clearSpecial();
+		SoundMessage info(
+			"The Hooded Figure bars the Color Bridge. "
+			"Each party member must first learn a sign from the Gypsy.");
+		info._largeMessage = true;
+		send(info);
+		return;
+	}
+
+	// Everyone knows their sign. Declining the bridge prompt doesn't
+	// consume the special, so the quiz can be retried
 	SoundMessage msg(STRING["maps.map17.bridge"],
 		[]() {
 			g_events->addView("ColorQuestions");


Commit: 22df6248526f800ba83157e637d1b23784549597
    https://github.com/scummvm/scummvm/commit/22df6248526f800ba83157e637d1b23784549597
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:58+11:00

Commit Message:
MM: MM1: Fix the Volcano stabilizer dials and keep them across save and load

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map11.cpp
    engines/mm/mm1/maps/maps.cpp


diff --git a/engines/mm/mm1/maps/map11.cpp b/engines/mm/mm1/maps/map11.cpp
index 8484fc75d89..83f52a20e8e 100644
--- a/engines/mm/mm1/maps/map11.cpp
+++ b/engines/mm/mm1/maps/map11.cpp
@@ -56,7 +56,25 @@ void Map11::special() {
 		pit();
 
 	} else {
-		if (_data[VAL1] == 66 && _data[VAL2] == 74) {
+		auto upperDial = [](byte c) -> byte {
+			return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
+		};
+
+		// The dials are stored in VAL2 and VAL3. A copy of whether each is
+		// correct is kept in the saved visit data, since the overlay data
+		// is reloaded from disk when a save is loaded
+		const byte dial1 = upperDial(_data[VAL2]);
+		const byte dial2 = upperDial(_data[VAL3]);
+		const bool rawStable = dial1 == 'B' && dial2 == 'J';
+		const bool savedStable = (_visited[3 * MAP_W + 6] & 0x80) != 0 &&
+			(_visited[3 * MAP_W + 8] & 0x80) != 0;
+
+		if (rawStable) {
+			_visited[3 * MAP_W + 6] |= 0x80;
+			_visited[3 * MAP_W + 8] |= 0x80;
+		}
+
+		if (rawStable || savedStable) {
 			g_maps->_mapPos = Common::Point(7, 11);
 		} else {
 			g_maps->_mapPos.x = getRandomNumber(15);
@@ -146,8 +164,11 @@ void Map11::selectDial(int dialIndex) {
 			if (ks.keycode >= Common::KEYCODE_a &&
 					ks.keycode <= Common::KEYCODE_z) {
 				Map11 &map = *static_cast<Map11 *>(g_maps->_currentMap);
+				// Derive the letter from the keycode, since KeyState::ascii
+				// can be 0 or the wrong case depending on the backend
+				const char c = 'A' + (ks.keycode - Common::KEYCODE_a);
 				g_events->close();
-				map.setDialChar(ks.ascii);
+				map.setDialChar(c);
 				map.none160();
 			}
 		}
@@ -168,7 +189,18 @@ void Map11::challenge() {
 }
 
 void Map11::setDialChar(char c) {
+	if (c >= 'a' && c <= 'z')
+		c -= 'a' - 'A';
+
 	_data[VAL2 + _dialIndex] = c;
+
+	const int visitedOffset = (_dialIndex == 0) ?
+		3 * MAP_W + 6 : 3 * MAP_W + 8;
+	const char target = (_dialIndex == 0) ? 'B' : 'J';
+	if (c == target)
+		_visited[visitedOffset] |= 0x80;
+	else
+		_visited[visitedOffset] &= ~0x80;
 }
 
 void Map11::clue() {
diff --git a/engines/mm/mm1/maps/maps.cpp b/engines/mm/mm1/maps/maps.cpp
index 3178dc7d2e9..9a01eac74c7 100644
--- a/engines/mm/mm1/maps/maps.cpp
+++ b/engines/mm/mm1/maps/maps.cpp
@@ -428,6 +428,14 @@ void Maps::changeMap(uint16 id, byte section, bool loadingSave) {
 	}
 	if (loadingSave)
 		_currentMap->restoreVisitState();
+
+	// The Volcano stabilizer settings are visit-local, but must survive a
+	// save and load made during the same visit. Clear them only on a
+	// genuine re-entry
+	if (!loadingSave && _mapId == 11) {
+		_currentMap->_visited[3 * MAP_W + 6] &= ~0x80;
+		_currentMap->_visited[3 * MAP_W + 8] &= ~0x80;
+	}
 	if (loadingSave && _mapId == 4) {
 		// The theft count lives in overlay data that is reloaded from disk,
 		// so rebuild it from the claimed treasure squares


Commit: 44919fbe4de435f7c45225c28afa13b44b14d1cf
    https://github.com/scummvm/scummvm/commit/44919fbe4de435f7c45225c28afa13b44b14d1cf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:58+11:00

Commit Message:
MM: MM1: Show the current and selected Volcano dial letter

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map11.cpp


diff --git a/engines/mm/mm1/maps/map11.cpp b/engines/mm/mm1/maps/map11.cpp
index 83f52a20e8e..d397f8e9a42 100644
--- a/engines/mm/mm1/maps/map11.cpp
+++ b/engines/mm/mm1/maps/map11.cpp
@@ -159,7 +159,16 @@ void Map11::selectDial(int dialIndex) {
 	Common::String msg = Common::String::format(
 		STRING["maps.map11.dial"].c_str(), '1' + dialIndex);
 
-	send(SoundMessage(msg,
+	// Show the dial's current setting under the prompt
+	byte current = _data[VAL2 + dialIndex];
+	if (current >= 'a' && current <= 'z')
+		current -= 'a' - 'A';
+	Common::String currentLine = Common::String::format("Current: %c",
+		(current >= 'A' && current <= 'Z') ? current : '-');
+
+	send(SoundMessage(
+		0, 1, msg,
+		0, 2, currentLine,
 		[](const Common::KeyState &ks) {
 			if (ks.keycode >= Common::KEYCODE_a &&
 					ks.keycode <= Common::KEYCODE_z) {
@@ -201,6 +210,12 @@ void Map11::setDialChar(char c) {
 		_visited[visitedOffset] |= 0x80;
 	else
 		_visited[visitedOffset] &= ~0x80;
+
+	// Echo the chosen value, so it's clear what was recorded
+	Common::String line1 = Common::String::format(
+		STRING["maps.map11.dial"].c_str(), '1' + _dialIndex);
+	Common::String line2 = Common::String::format("Selected: %c", c);
+	send(InfoMessage(0, 1, line1, 0, 2, line2));
 }
 
 void Map11::clue() {


Commit: c4c4d7d875be58434e8614550d98ea59c3c2d5c5
    https://github.com/scummvm/scummvm/commit/c4c4d7d875be58434e8614550d98ea59c3c2d5c5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:59+11:00

Commit Message:
MM: MM1: Keep conveyor belts switched off across save and load

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map06.cpp
    engines/mm/mm1/maps/map06.h
    engines/mm/mm1/maps/map40.cpp
    engines/mm/mm1/maps/map40.h


diff --git a/engines/mm/mm1/maps/map06.cpp b/engines/mm/mm1/maps/map06.cpp
index 43ceb93e80a..bb43b4147b4 100644
--- a/engines/mm/mm1/maps/map06.cpp
+++ b/engines/mm/mm1/maps/map06.cpp
@@ -155,6 +155,15 @@ void Map06::special06() {
 			Map06 &map = *static_cast<Map06 *>(g_maps->_currentMap);
 			byte &val1 = map[VAL1];
 			val1 = val1 ? 0 : 1;
+
+			// The overlay byte is reloaded when a save is loaded, so
+			// remember this visit's OFF state on the button cell
+			byte &visited = map._visited[g_maps->_mapOffset];
+			if (val1)
+				visited &= ~VISITED_TRANSIENT_STATE;
+			else
+				visited |= VISITED_TRANSIENT_STATE;
+
 			g_events->addKeypress(Common::KEYCODE_SPACE);
 		}
 	));
@@ -255,6 +264,12 @@ void Map06::clearEncounterProgress() {
 	}
 }
 
+void Map06::restoreVisitState() {
+	const byte buttonOffset = _data[51 + 6];
+	if (_visited[buttonOffset] & VISITED_TRANSIENT_STATE)
+		_data[VAL1] = 0;
+}
+
 void Map06::slide() {
 	_data[VAL2]++;
 
diff --git a/engines/mm/mm1/maps/map06.h b/engines/mm/mm1/maps/map06.h
index 397e938c445..c4fd4e5a41e 100644
--- a/engines/mm/mm1/maps/map06.h
+++ b/engines/mm/mm1/maps/map06.h
@@ -85,6 +85,7 @@ public:
 	 * Handles all special stuff that happens on the map
 	 */
 	void special() override;
+	void restoreVisitState() override;
 };
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map40.cpp b/engines/mm/mm1/maps/map40.cpp
index af47a646eb6..2b524820a60 100644
--- a/engines/mm/mm1/maps/map40.cpp
+++ b/engines/mm/mm1/maps/map40.cpp
@@ -95,10 +95,20 @@ void Map40::special04() {
 		[]() {
 			Map40 &map = *static_cast<Map40 *>(g_maps->_currentMap);
 			map[VAL1]++;
+
+			// Remember the disabled conveyor across a save and load in
+			// this map visit. A normal map transition clears the marker
+			map._visited[g_maps->_mapOffset] |= VISITED_TRANSIENT_STATE;
 		}
 	));
 }
 
+void Map40::restoreVisitState() {
+	const byte buttonOffset = _data[51 + 4];
+	if (_visited[buttonOffset] & VISITED_TRANSIENT_STATE)
+		_data[VAL1] = 1;
+}
+
 void Map40::special05() {
 	encounter(&_data[588], &_data[601]);
 }
diff --git a/engines/mm/mm1/maps/map40.h b/engines/mm/mm1/maps/map40.h
index 1973c07662b..c1954587172 100644
--- a/engines/mm/mm1/maps/map40.h
+++ b/engines/mm/mm1/maps/map40.h
@@ -85,6 +85,7 @@ public:
 	 * Handles all special stuff that happens on the map
 	 */
 	void special() override;
+	void restoreVisitState() override;
 
 	/**
 	 * Called when party chooses to resist Lord Archer


Commit: 5b1709feaa9cb4db30d1dd4540c3a2efa055f306
    https://github.com/scummvm/scummvm/commit/5b1709feaa9cb4db30d1dd4540c3a2efa055f306
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:59+11:00

Commit Message:
MM: MM1: Stop the enhanced conveyor message replaying its dismiss key

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map40.cpp


diff --git a/engines/mm/mm1/maps/map40.cpp b/engines/mm/mm1/maps/map40.cpp
index 2b524820a60..158e7669f26 100644
--- a/engines/mm/mm1/maps/map40.cpp
+++ b/engines/mm/mm1/maps/map40.cpp
@@ -23,6 +23,7 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
 
 namespace MM {
@@ -55,7 +56,17 @@ void Map40::special() {
 		_data[VAL2]++;
 		g_maps->_mapPos.y++;
 		updateGame();
-		send(SoundMessage(STRING["maps.map40.conveyor_belt"]));
+
+		SoundMessage msg(STRING["maps.map40.conveyor_belt"]);
+		if (g_engine->isEnhanced()) {
+			// Enhanced messages normally forward the key that dismissed them
+			// back to the game, which would immediately trigger another
+			// forced step and message. Consume the dismissing input instead
+			msg._keyCallback = [](const Common::KeyState &) {
+				g_events->close();
+			};
+		}
+		send(msg);
 	}
 }
 


Commit: 435b6c438c41281e32c8368ef483fbec5a4408b5
    https://github.com/scummvm/scummvm/commit/435b6c438c41281e32c8368ef483fbec5a4408b5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:59+11:00

Commit Message:
MM: MM1: Save Arenko's tree-climbing progress and pay his reward only once

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/arenko.cpp
    engines/mm/mm1/maps/map28.cpp
    engines/mm/mm1/maps/map28.h
    engines/mm/mm1/views/interactions/arenko.cpp
    engines/mm/mm1/views_enh/interactions/arenko.cpp


diff --git a/engines/mm/mm1/game/arenko.cpp b/engines/mm/mm1/game/arenko.cpp
index 3bb664ca698..35607413f04 100644
--- a/engines/mm/mm1/game/arenko.cpp
+++ b/engines/mm/mm1/game/arenko.cpp
@@ -21,6 +21,7 @@
 
 #include "mm/mm1/game/arenko.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/maps/map28.h"
 
 
 namespace MM {
@@ -28,19 +29,34 @@ namespace MM1 {
 namespace Game {
 
 void Arenko::giveGold() {
+	Maps::Map28 &map = *static_cast<Maps::Map28 *>(g_maps->_currentMap);
+	if (map.arenkoQuestCompleted())
+		return;
+
 	g_globals->_treasure.setGold((getRandomNumber(8) + 8) * 256);
+	map.completeArenkoQuest();
 	g_maps->clearSpecial();
 	g_events->addAction(KEYBIND_SEARCH);
 }
 
 void Arenko::giveGems() {
+	Maps::Map28 &map = *static_cast<Maps::Map28 *>(g_maps->_currentMap);
+	if (map.arenkoQuestCompleted())
+		return;
+
 	g_globals->_treasure.setGems(getRandomNumber(50) + 200);
+	map.completeArenkoQuest();
 	g_maps->clearSpecial();
 	g_events->addAction(KEYBIND_SEARCH);
 }
 
 void Arenko::giveItem() {
+	Maps::Map28 &map = *static_cast<Maps::Map28 *>(g_maps->_currentMap);
+	if (map.arenkoQuestCompleted())
+		return;
+
 	g_globals->_treasure._items[2] = getRandomNumber(22) + 196;
+	map.completeArenkoQuest();
 	g_maps->clearSpecial();
 	g_events->addAction(KEYBIND_SEARCH);
 }
diff --git a/engines/mm/mm1/maps/map28.cpp b/engines/mm/mm1/maps/map28.cpp
index a0bc36cadf7..cf05688b785 100644
--- a/engines/mm/mm1/maps/map28.cpp
+++ b/engines/mm/mm1/maps/map28.cpp
@@ -33,6 +33,98 @@ namespace Maps {
 #define VAL1 63
 #define VAL2 64
 
+namespace {
+
+enum ArenkoStorage {
+	ARENKO_STATE_FIRST_OFFSET = 15 * MAP_W + 11,
+	ARENKO_STATE_CELL_COUNT = 5,
+	ARENKO_STATE_BIT = 0x80,
+	ARENKO_REWARDED_STATE = 21
+};
+
+} // namespace
+
+void Map28::load() {
+	Map::load();
+	restoreArenkoQuest();
+	// Tree marks occupy only the western tree area, not the state cells.
+	for (int i = 0; i < MAP_SIZE; ++i) {
+		if (i % MAP_W < 7 && (_visited[i] & ARENKO_STATE_BIT) &&
+				i != _data[51] && i != _data[52] && i != _data[53])
+			_states[i] &= ~CELL_SPECIAL;
+	}
+}
+
+byte Map28::arenkoStoredState() const {
+	byte state = 0;
+	for (int i = 0; i < ARENKO_STATE_CELL_COUNT; ++i) {
+		if (_visited[ARENKO_STATE_FIRST_OFFSET + i] & ARENKO_STATE_BIT)
+			state |= 1 << i;
+	}
+	return state;
+}
+
+void Map28::setArenkoStoredState(byte state) {
+	for (int i = 0; i < ARENKO_STATE_CELL_COUNT; ++i) {
+		byte &value = _visited[ARENKO_STATE_FIRST_OFFSET + i];
+		value &= ~ARENKO_STATE_BIT;
+		if (state & (1 << i))
+			value |= ARENKO_STATE_BIT;
+	}
+}
+
+void Map28::restoreArenkoQuest() {
+	const byte state = arenkoStoredState();
+	if (!state)
+		return;
+
+	_data[VAL2] = 1;
+	_data[VAL1] = MIN((int)state - 1, 19);
+	if (state >= ARENKO_REWARDED_STATE && _data[51] < MAP_SIZE)
+		_states[_data[51]] &= ~CELL_SPECIAL;
+}
+
+bool Map28::arenkoQuestActive() const {
+	return arenkoStoredState() != 0;
+}
+
+bool Map28::arenkoQuestCompleted() const {
+	return arenkoStoredState() >= ARENKO_REWARDED_STATE;
+}
+
+int Map28::arenkoClimbs() const {
+	const byte state = arenkoStoredState();
+	return state ? MIN((int)state - 1, 19) : 0;
+}
+
+void Map28::startArenkoQuest() {
+	if (!arenkoStoredState())
+		setArenkoStoredState(1);
+	_data[VAL1] = arenkoClimbs();
+	_data[VAL2] = 1;
+}
+
+void Map28::recordArenkoClimb() {
+	if (_visited[g_maps->_mapOffset] & ARENKO_STATE_BIT)
+		return;
+	_visited[g_maps->_mapOffset] |= ARENKO_STATE_BIT;
+	if (arenkoQuestCompleted())
+		return;
+	if (!arenkoQuestActive())
+		startArenkoQuest();
+
+	const int climbed = MIN(arenkoClimbs() + 1, 19);
+	_data[VAL1] = climbed;
+	_data[VAL2] = 1;
+	setArenkoStoredState(climbed + 1);
+}
+
+void Map28::completeArenkoQuest() {
+	_data[VAL1] = 19;
+	_data[VAL2] = 1;
+	setArenkoStoredState(ARENKO_REWARDED_STATE);
+}
+
 void Map28::special() {
 	// Scan for special actions on the map cell
 	for (uint i = 0; i < 3; ++i) {
@@ -63,7 +155,7 @@ void Map28::special() {
 
 				} else {
 					g_maps->clearSpecial();
-					map[VAL1]++;
+					map.recordArenkoClimb();
 
 					int val = g_events->getRandomNumber(10);
 					switch (val) {
@@ -126,6 +218,7 @@ void Map28::special() {
 
 					default:
 						g_globals->_activeSpells._s.cursed = val;
+						g_events->send(SoundMessage(STRING["maps.map28.cursed"]));
 						break;
 					}
 				}
diff --git a/engines/mm/mm1/maps/map28.h b/engines/mm/mm1/maps/map28.h
index 4fb89087448..6dcc15b8dac 100644
--- a/engines/mm/mm1/maps/map28.h
+++ b/engines/mm/mm1/maps/map28.h
@@ -31,6 +31,10 @@ namespace Maps {
 class Map28 : public Map {
 	typedef void (Map28:: *SpecialFn)();
 private:
+	byte arenkoStoredState() const;
+	void setArenkoStoredState(byte state);
+	void restoreArenkoQuest();
+
 	void special00();
 	void special01();
 	void special02();
@@ -45,6 +49,15 @@ private:
 public:
 	Map28() : Map(28, "aread3", 0x106, 2) {}
 
+	void load() override;
+
+	bool arenkoQuestActive() const;
+	bool arenkoQuestCompleted() const;
+	int arenkoClimbs() const;
+	void startArenkoQuest();
+	void recordArenkoClimb();
+	void completeArenkoQuest();
+
 	/**
 	 * Handles all special stuff that happens on the map
 	 */
diff --git a/engines/mm/mm1/views/interactions/arenko.cpp b/engines/mm/mm1/views/interactions/arenko.cpp
index 3ec90b5fbeb..f64d7302229 100644
--- a/engines/mm/mm1/views/interactions/arenko.cpp
+++ b/engines/mm/mm1/views/interactions/arenko.cpp
@@ -38,9 +38,9 @@ Arenko::Arenko() : TextView("Arenko") {
 bool Arenko::msgFocus(const FocusMessage &msg) {
 	MM1::Maps::Map28 &map = *static_cast<MM1::Maps::Map28 *>(g_maps->_currentMap);
 
-	if (!map[VAL1]) {
+	if (!map.arenkoQuestActive()) {
 		_line = STRING["maps.map28.arenko"];
-		map[VAL2] = 1;
+		map.startArenkoQuest();
 
 	} else if (map[VAL1] < 19) {
 		_line = STRING["maps.map28.keep_climbing"];
diff --git a/engines/mm/mm1/views_enh/interactions/arenko.cpp b/engines/mm/mm1/views_enh/interactions/arenko.cpp
index d6108e6a319..f374d11fea8 100644
--- a/engines/mm/mm1/views_enh/interactions/arenko.cpp
+++ b/engines/mm/mm1/views_enh/interactions/arenko.cpp
@@ -39,9 +39,9 @@ bool Arenko::msgFocus(const FocusMessage &msg) {
 	Maps::Map28 &map = *static_cast<Maps::Map28 *>(g_maps->_currentMap);
 	clearButtons();
 
-	if (!map[VAL1]) {
+	if (!map.arenkoQuestActive()) {
 		addText(STRING["maps.map28.arenko"]);
-		map[VAL2] = 1;
+		map.startArenkoQuest();
 
 	} else if (map[VAL1] < 19) {
 		addText(STRING["maps.map28.keep_climbing"]);


Commit: 3ce1e2d068ef9484934e22873ad2ede83a7d2f66
    https://github.com/scummvm/scummvm/commit/3ce1e2d068ef9484934e22873ad2ede83a7d2f66
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:12:59+11:00

Commit Message:
MM: MM1: Add Arenko's Challenge to the Quest Log

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/active_quest.cpp


diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
index fe1c68e42d8..42128712856 100644
--- a/engines/mm/mm1/views_enh/active_quest.cpp
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/maps/map06.h"
 #include "mm/mm1/maps/map17.h"
 #include "mm/mm1/maps/map23.h"
+#include "mm/mm1/maps/map28.h"
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/metaengine.h"
 
@@ -232,6 +233,27 @@ void addCourier(Common::Array<QuestLogEntry> &entries, bool done) {
 	addEntry(entries, done, "The Courier Mission", details);
 }
 
+void addArenko(Common::Array<QuestLogEntry> &entries, bool done) {
+	Maps::Map28 &map = *static_cast<Maps::Map28 *>(g_maps->getMap(28));
+	if (done != map.arenkoQuestCompleted())
+		return;
+	if (!done && !map.arenkoQuestActive())
+		return;
+
+	const int climbed = map.arenkoClimbs();
+	Common::String details;
+	if (done) {
+		details = "Climbed 19 trees and claimed Arenko's reward.";
+	} else if (climbed >= 19) {
+		details = "Trees climbed: 19/19. Return to Arenko for a reward.";
+	} else {
+		details = Common::String::format(
+				"Climb trees in D3. Progress: %d/19.", climbed);
+	}
+
+	addEntry(entries, done, "Arenko's Challenge", details);
+}
+
 void addAlamarStory(Common::Array<QuestLogEntry> &entries, bool done) {
 	const byte flags = partyFlags(13);
 	const bool won = (flags & CHARFLAG13_80) != 0;
@@ -433,6 +455,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	addAlamarStory(entries, false);
 	addSigns(entries, false);
 	addRanalou(entries, false);
+	addArenko(entries, false);
 
 	// Completed history follows and remains selectable with Up/Down
 	addCompletedRulerQuests(entries);
@@ -442,6 +465,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	addAlamarStory(entries, true);
 	addSigns(entries, true);
 	addRanalou(entries, true);
+	addArenko(entries, true);
 	addOtherReliableCompletions(entries);
 }
 


Commit: b36880c10a266bbc53da09206c4a2dd9db585371
    https://github.com/scummvm/scummvm/commit/b36880c10a266bbc53da09206c4a2dd9db585371
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:00+11:00

Commit Message:
MM: MM1: Add Og's chess challenge to the Quest Log

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map29.cpp
    engines/mm/mm1/maps/map29.h
    engines/mm/mm1/views_enh/active_quest.cpp


diff --git a/engines/mm/mm1/maps/map29.cpp b/engines/mm/mm1/maps/map29.cpp
index 5cdb9b14ab2..341cbe0c42d 100644
--- a/engines/mm/mm1/maps/map29.cpp
+++ b/engines/mm/mm1/maps/map29.cpp
@@ -31,6 +31,11 @@ namespace Maps {
 
 #define ANSWER_OFFSET 67
 #define BEAST_FLAG 110
+// Og is the D4 special at (7,1). Use the fixed visit-data cell rather than
+// reading _data, since the overlay data isn't loaded until this map is
+// entered but the Quest Log can be opened anywhere
+#define OG_CELL_OFFSET (1 * MAP_W + 7)
+#define OG_QUEST_LOG_FLAG 0x80
 
 void Map29::special() {
 	Game::Encounter &enc = g_globals->_encounters;
@@ -180,6 +185,7 @@ void Map29::chessAnswer(const Common::String &answer) {
 			16, 2, STRING["maps.map19.correct"],
 			[]() {
 				MM1::Maps::Map29 &map29 = *static_cast<MM1::Maps::Map29 *>(g_maps->_currentMap);
+				map29.markOgQuestCompleted();
 
 				for (uint i = 0; i < g_globals->_party.size(); ++i) {
 					Character &c = g_globals->_party[i];
@@ -213,6 +219,18 @@ void Map29::begone() {
 	send(msg);
 }
 
+bool Map29::ogQuestVisited() const {
+	return (_visited[OG_CELL_OFFSET] & 0x7f) != 0;
+}
+
+bool Map29::ogQuestCompleted() const {
+	return (_visited[OG_CELL_OFFSET] & OG_QUEST_LOG_FLAG) != 0;
+}
+
+void Map29::markOgQuestCompleted() {
+	_visited[OG_CELL_OFFSET] |= OG_QUEST_LOG_FLAG;
+}
+
 } // namespace Maps
 } // namespace MM1
 } // namespace MM
diff --git a/engines/mm/mm1/maps/map29.h b/engines/mm/mm1/maps/map29.h
index d2f91194f1e..716c0ab2e2c 100644
--- a/engines/mm/mm1/maps/map29.h
+++ b/engines/mm/mm1/maps/map29.h
@@ -57,6 +57,14 @@ public:
 	 * Chess answer handler
 	 */
 	void chessAnswer(const Common::String &answer);
+
+	/**
+	 * Og side-quest state. The completion marker reuses the high metadata
+	 * bit of Og's existing visited cell, so the save format is unchanged
+	 */
+	bool ogQuestVisited() const;
+	bool ogQuestCompleted() const;
+	void markOgQuestCompleted();
 };
 
 } // namespace Maps
diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
index 42128712856..ec3e919b12b 100644
--- a/engines/mm/mm1/views_enh/active_quest.cpp
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -25,6 +25,7 @@
 #include "mm/mm1/maps/map17.h"
 #include "mm/mm1/maps/map23.h"
 #include "mm/mm1/maps/map28.h"
+#include "mm/mm1/maps/map29.h"
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/metaengine.h"
 
@@ -64,6 +65,10 @@ Maps::Map23 &gypsyMap() {
 	return *static_cast<Maps::Map23 *>(g_maps->getMap(23));
 }
 
+Maps::Map29 &ogMap() {
+	return *static_cast<Maps::Map29 *>(g_maps->getMap(29));
+}
+
 byte partyFlags(int index) {
 	byte result = 0;
 	for (uint i = 0; i < g_globals->_party.size(); ++i)
@@ -321,6 +326,36 @@ void addAlamarStory(Common::Array<QuestLogEntry> &entries, bool done) {
 	}
 }
 
+void addOg(Common::Array<QuestLogEntry> &entries, bool done) {
+	Maps::Map29 &map = ogMap();
+	const bool completed = map.ogQuestCompleted();
+	const bool hasBlack = g_globals->_party.hasItem(B_QUEEN_IDOL_ID);
+	const bool hasWhite = g_globals->_party.hasItem(W_QUEEN_IDOL_ID);
+	const bool started = completed || map.ogQuestVisited();
+
+	if (done != completed || (!done && !started))
+		return;
+
+	if (done) {
+		addEntry(entries, true, "Restore Og's Sight",
+			"Restored Og's sight using the Black Queen Idol and White Queen Idol.");
+		return;
+	}
+
+	Common::String details;
+	if (hasBlack && hasWhite) {
+		details = "Both Queen Idols found. Bring them to Og in D4 and solve his chess challenge.";
+	} else if (hasBlack) {
+		details = "Black Queen Idol found. Find the White Queen Idol, then return to Og in D4.";
+	} else if (hasWhite) {
+		details = "White Queen Idol found. Find the Black Queen Idol, then return to Og in D4.";
+	} else {
+		details = "Find the Black Queen Idol and White Queen Idol, then return to Og in D4.";
+	}
+
+	addEntry(entries, false, "Restore Og's Sight", details);
+}
+
 void addSigns(Common::Array<QuestLogEntry> &entries, bool done) {
 	const bool gypsySeen = gypsyMap().gypsyVisited();
 	const bool hoodedSeen = colorBridgeMap().colorBridgeVisited();
@@ -453,6 +488,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	addClerics(entries, false);
 	addCourier(entries, false);
 	addAlamarStory(entries, false);
+	addOg(entries, false);
 	addSigns(entries, false);
 	addRanalou(entries, false);
 	addArenko(entries, false);
@@ -463,6 +499,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	addClerics(entries, true);
 	addCourier(entries, true);
 	addAlamarStory(entries, true);
+	addOg(entries, true);
 	addSigns(entries, true);
 	addRanalou(entries, true);
 	addArenko(entries, true);


Commit: 8f9559aa6632eafa6dff49bd22d9251564cd6663
    https://github.com/scummvm/scummvm/commit/8f9559aa6632eafa6dff49bd22d9251564cd6663
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:00+11:00

Commit Message:
MM: MM1: Mark arrival squares as exits on the enhanced minimap

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map00.cpp
    engines/mm/mm1/maps/map01.cpp
    engines/mm/mm1/maps/map02.cpp
    engines/mm/mm1/maps/map03.cpp
    engines/mm/mm1/maps/map04.cpp
    engines/mm/mm1/maps/map05.cpp
    engines/mm/mm1/maps/map06.cpp
    engines/mm/mm1/maps/map07.cpp
    engines/mm/mm1/maps/map08.cpp
    engines/mm/mm1/maps/map09.cpp
    engines/mm/mm1/maps/map10.cpp
    engines/mm/mm1/maps/map11.cpp
    engines/mm/mm1/maps/map12.cpp
    engines/mm/mm1/maps/map13.cpp
    engines/mm/mm1/maps/map14.cpp
    engines/mm/mm1/maps/map18.cpp
    engines/mm/mm1/maps/map19.cpp
    engines/mm/mm1/maps/map20.cpp
    engines/mm/mm1/maps/map23.cpp
    engines/mm/mm1/maps/map25.cpp
    engines/mm/mm1/maps/map28.cpp
    engines/mm/mm1/maps/map29.cpp
    engines/mm/mm1/maps/map30.cpp
    engines/mm/mm1/maps/map32.cpp
    engines/mm/mm1/maps/map33.cpp
    engines/mm/mm1/maps/map34.cpp
    engines/mm/mm1/maps/map35.cpp
    engines/mm/mm1/maps/map36.cpp
    engines/mm/mm1/maps/map37.cpp
    engines/mm/mm1/maps/map38.cpp
    engines/mm/mm1/maps/map39.cpp
    engines/mm/mm1/maps/map40.cpp
    engines/mm/mm1/maps/map41.cpp
    engines/mm/mm1/maps/map42.cpp
    engines/mm/mm1/maps/map43.cpp
    engines/mm/mm1/maps/map44.cpp
    engines/mm/mm1/maps/map45.cpp
    engines/mm/mm1/maps/map46.cpp
    engines/mm/mm1/maps/map47.cpp
    engines/mm/mm1/maps/map49.cpp
    engines/mm/mm1/maps/map50.cpp
    engines/mm/mm1/maps/map51.cpp
    engines/mm/mm1/maps/map52.cpp
    engines/mm/mm1/maps/map53.cpp
    engines/mm/mm1/maps/maps.cpp
    engines/mm/mm1/maps/maps.h


diff --git a/engines/mm/mm1/maps/map00.cpp b/engines/mm/mm1/maps/map00.cpp
index d694661e9fe..b9b76bfb24b 100644
--- a/engines/mm/mm1/maps/map00.cpp
+++ b/engines/mm/mm1/maps/map00.cpp
@@ -92,7 +92,7 @@ void Map00::special04() {
 		STRING["maps.passage_outside1"],
 		[]() {
 			g_maps->_mapPos = Common::Point(10, 10);
-			g_maps->changeMap(0xa11, 2);
+			g_maps->changeMapFromExit(0xa11, 2);
 		}
 	));
 }
@@ -118,7 +118,7 @@ void Map00::special09() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0xa11, 1);
+			g_maps->changeMapFromExit(0xa11, 1);
 		}
 	));
 }
@@ -215,7 +215,7 @@ void Map00::special23() {
 		[](const Common::KeyState &keyState) {
 			g_events->focusedView()->close();
 			if (!g_globals->_activeSpells._s.levitate)
-				g_maps->changeMap(0xa11, 1);
+				g_maps->changeMapFromExit(0xa11, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map01.cpp b/engines/mm/mm1/maps/map01.cpp
index f8a437e0898..6117722473e 100644
--- a/engines/mm/mm1/maps/map01.cpp
+++ b/engines/mm/mm1/maps/map01.cpp
@@ -91,7 +91,7 @@ void Map01::special04() {
 		STRING["maps.passage_outside2"],
 		[]() {
 			g_maps->_mapPos = Common::Point(3, 3);
-			g_maps->changeMap(0x101, 2);
+			g_maps->changeMapFromExit(0x101, 2);
 		}
 	));
 }
@@ -152,7 +152,7 @@ void Map01::special09() {
 		STRING["maps.stairs_down"],
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 0);
-			g_maps->changeMap(0xc01, 1);
+			g_maps->changeMapFromExit(0xc01, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map02.cpp b/engines/mm/mm1/maps/map02.cpp
index 9ba95b384a0..74daba2f411 100644
--- a/engines/mm/mm1/maps/map02.cpp
+++ b/engines/mm/mm1/maps/map02.cpp
@@ -97,7 +97,7 @@ void Map02::special04() {
 		STRING["maps.passage_outside2"],
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 7);
-			g_maps->changeMap(0x801, 2);
+			g_maps->changeMapFromExit(0x801, 2);
 		}
 	));
 }
@@ -159,7 +159,7 @@ void Map02::special15() {
 
 			uint16 id = PORTAL_DEST_ID[index];
 			byte section = PORTAL_DEST_SECTION[index];
-			g_maps->changeMap(id, section);
+			g_maps->changeMapFromExit(id, section);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map03.cpp b/engines/mm/mm1/maps/map03.cpp
index 557e234aa7d..e14e55318d0 100644
--- a/engines/mm/mm1/maps/map03.cpp
+++ b/engines/mm/mm1/maps/map03.cpp
@@ -127,7 +127,7 @@ void Map03::special04() {
 		STRING["maps.passage_outside1"],
 		[]() {
 			g_maps->_mapPos = Common::Point(9, 11);
-			g_maps->changeMap(0x112, 2);
+			g_maps->changeMapFromExit(0x112, 2);
 		}
 	));
 }
@@ -157,7 +157,7 @@ void Map03::special09() {
 		STRING["maps.stairs_down"],
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 0);
-			g_maps->changeMap(5, 1);
+			g_maps->changeMapFromExit(5, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map04.cpp b/engines/mm/mm1/maps/map04.cpp
index e7c3f424136..d8ca11ac260 100644
--- a/engines/mm/mm1/maps/map04.cpp
+++ b/engines/mm/mm1/maps/map04.cpp
@@ -114,7 +114,7 @@ void Map04::special04() {
 			[]() {
 				g_maps->_mapPos.x = 13;
 				g_maps->_mapPos.y = 1;
-				g_maps->changeMap(0xa00, 2);
+				g_maps->changeMapFromExit(0xa00, 2);
 			}
 		));
 	} else {
@@ -152,7 +152,7 @@ void Map04::special09() {
 			[]() {
 				g_maps->_mapPos.x = 0;
 				g_maps->_mapPos.y = 7;
-				g_maps->changeMap(0x202, 1);
+				g_maps->changeMapFromExit(0x202, 1);
 			}
 		));
 	} else {
diff --git a/engines/mm/mm1/maps/map05.cpp b/engines/mm/mm1/maps/map05.cpp
index 7fc973a5a88..b18b59bd25f 100644
--- a/engines/mm/mm1/maps/map05.cpp
+++ b/engines/mm/mm1/maps/map05.cpp
@@ -58,7 +58,7 @@ void Map05::special00() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0x604, 1);
+			g_maps->changeMapFromExit(0x604, 1);
 		}
 	));
 }
@@ -114,7 +114,7 @@ void Map05::special07() {
 		[]() {
 			g_maps->_mapPos.x = 8;
 			g_maps->_mapPos.y = 8;
-			g_maps->changeMap(1, 1);
+			g_maps->changeMapFromExit(1, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map06.cpp b/engines/mm/mm1/maps/map06.cpp
index bb43b4147b4..3d960e3fd45 100644
--- a/engines/mm/mm1/maps/map06.cpp
+++ b/engines/mm/mm1/maps/map06.cpp
@@ -98,7 +98,7 @@ void Map06::special00() {
 		STRING["maps.passage_outside2"],
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 11);
-			g_maps->changeMap(0xa11, 2);
+			g_maps->changeMapFromExit(0xa11, 2);
 		}
 	));
 }
@@ -109,7 +109,7 @@ void Map06::special01() {
 		STRING["maps.map06.portal"],
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 0);
-			g_maps->changeMap(0xc01, 1);
+			g_maps->changeMapFromExit(0xc01, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map07.cpp b/engines/mm/mm1/maps/map07.cpp
index 382dc34f782..f561e2d6cbb 100644
--- a/engines/mm/mm1/maps/map07.cpp
+++ b/engines/mm/mm1/maps/map07.cpp
@@ -61,7 +61,7 @@ void Map07::special00() {
 		STRING["maps.stairs_up"],
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 15);
-			g_maps->changeMap(0xc03, 1);
+			g_maps->changeMapFromExit(0xc03, 1);
 		}
 	));
 }
@@ -72,7 +72,7 @@ void Map07::special01() {
 		STRING["maps.map07.portal"],
 		[]() {
 			g_maps->_mapPos = Common::Point(2, 7);
-			g_maps->changeMap(0x202, 1);
+			g_maps->changeMapFromExit(0x202, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map08.cpp b/engines/mm/mm1/maps/map08.cpp
index 6364384d4c0..edead47ebd3 100644
--- a/engines/mm/mm1/maps/map08.cpp
+++ b/engines/mm/mm1/maps/map08.cpp
@@ -67,7 +67,7 @@ void Map08::special00() {
 		STRING["maps.stairs_up"],
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 7);
-			g_maps->changeMap(0xb1a, 1);
+			g_maps->changeMapFromExit(0xb1a, 1);
 		}
 	));
 }
@@ -78,7 +78,7 @@ void Map08::special01() {
 		STRING["maps.stairs_up"],
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 1);
-			g_maps->changeMap(0xa00, 2);
+			g_maps->changeMapFromExit(0xa00, 2);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map09.cpp b/engines/mm/mm1/maps/map09.cpp
index d998d9fd018..3a9d0369bc4 100644
--- a/engines/mm/mm1/maps/map09.cpp
+++ b/engines/mm/mm1/maps/map09.cpp
@@ -58,7 +58,7 @@ void Map09::special00() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0x802, 1);
+			g_maps->changeMapFromExit(0x802, 1);
 		}
 	));
 }
@@ -69,7 +69,7 @@ void Map09::special01() {
 		STRING["maps.map09.passage_outside"],
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 0);
-			g_maps->changeMap(0xf05, 3);
+			g_maps->changeMapFromExit(0xf05, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map10.cpp b/engines/mm/mm1/maps/map10.cpp
index 072738944e3..1360dba20e3 100644
--- a/engines/mm/mm1/maps/map10.cpp
+++ b/engines/mm/mm1/maps/map10.cpp
@@ -69,7 +69,7 @@ void Map10::special00() {
 		STRING["maps.passage_outside1"],
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 7);
-			g_maps->changeMap(0x101, 2);
+			g_maps->changeMapFromExit(0x101, 2);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map11.cpp b/engines/mm/mm1/maps/map11.cpp
index d397f8e9a42..8d18456d40f 100644
--- a/engines/mm/mm1/maps/map11.cpp
+++ b/engines/mm/mm1/maps/map11.cpp
@@ -92,7 +92,7 @@ void Map11::special00() {
 		STRING["maps.map11.ladder"],
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 2);
-			g_maps->changeMap(0xf04, 2);
+			g_maps->changeMapFromExit(0xf04, 2);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map12.cpp b/engines/mm/mm1/maps/map12.cpp
index 87cb1fa3a3a..ee45675c68e 100644
--- a/engines/mm/mm1/maps/map12.cpp
+++ b/engines/mm/mm1/maps/map12.cpp
@@ -59,7 +59,7 @@ void Map12::special00() {
 		STRING["maps.map12.ladder_up"],
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 13);
-			g_maps->changeMap(0x106, 2);
+			g_maps->changeMapFromExit(0x106, 2);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map13.cpp b/engines/mm/mm1/maps/map13.cpp
index 7f5b1143611..e328b496078 100644
--- a/engines/mm/mm1/maps/map13.cpp
+++ b/engines/mm/mm1/maps/map13.cpp
@@ -81,7 +81,7 @@ void Map13::special00() {
 		STRING["maps.map13.passage_outside"],
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 4);
-			g_maps->changeMap(0x703, 2);
+			g_maps->changeMapFromExit(0x703, 2);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map14.cpp b/engines/mm/mm1/maps/map14.cpp
index eb2a90e0431..7e3a473b5dc 100644
--- a/engines/mm/mm1/maps/map14.cpp
+++ b/engines/mm/mm1/maps/map14.cpp
@@ -155,7 +155,7 @@ void Map14::special04() {
 			STRING["maps.map14.passage"],
 			[]() {
 				g_maps->_mapPos = Common::Point(4, 4);
-				g_maps->changeMap(0x706, 3);
+				g_maps->changeMapFromExit(0x706, 3);
 			}
 		));
 	} else {
diff --git a/engines/mm/mm1/maps/map18.cpp b/engines/mm/mm1/maps/map18.cpp
index dad8fd83ef3..1eade86a4ec 100644
--- a/engines/mm/mm1/maps/map18.cpp
+++ b/engines/mm/mm1/maps/map18.cpp
@@ -58,7 +58,7 @@ void Map18::special00() {
 		STRING["maps.map18.passage"],
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 7);
-			g_maps->changeMap(0xb1a, 1);
+			g_maps->changeMapFromExit(0xb1a, 1);
 		}
 	));
 }
@@ -67,7 +67,7 @@ void Map18::special01() {
 	visitedExit();
 	if (_data[VAL1] & 0x80) {
 		g_maps->_mapPos = Common::Point(0, 7);
-		g_maps->changeMap(0x508, 3);
+		g_maps->changeMapFromExit(0x508, 3);
 
 	} else if (_data[VAL1] != 0) {
 		_data[VAL1] = 0;
@@ -94,7 +94,7 @@ void Map18::special02() {
 	visitedExit();
 	if (_data[VAL2] & 0x80) {
 		g_maps->_mapPos = Common::Point(7, 15);
-		g_maps->changeMap(0xf08, 3);
+		g_maps->changeMapFromExit(0xf08, 3);
 
 	} else if (_data[VAL2] != 0) {
 		_data[VAL2] = 0;
@@ -122,7 +122,7 @@ void Map18::special03() {
 	send(SoundMessage(STRING["maps.map18.ruins"],
 		[]() {
 			g_maps->_mapPos = Common::Point(2, 2);
-			g_maps->changeMap(0xf03, 3);
+			g_maps->changeMapFromExit(0xf03, 3);
 		}
 	));
 }
@@ -158,7 +158,7 @@ void Map18::special08() {
 		STRING["maps.map18.cave"],
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 7);
-			g_maps->changeMap(0x202, 1);
+			g_maps->changeMapFromExit(0x202, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map19.cpp b/engines/mm/mm1/maps/map19.cpp
index 775fe322435..995bab01726 100644
--- a/engines/mm/mm1/maps/map19.cpp
+++ b/engines/mm/mm1/maps/map19.cpp
@@ -68,7 +68,7 @@ void Map19::special01() {
 		STRING["maps.map19.cave"],
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 0);
-			g_maps->changeMap(0xa00, 1);
+			g_maps->changeMapFromExit(0xa00, 1);
 		}
 	));
 }
@@ -79,7 +79,7 @@ void Map19::special02() {
 		STRING["maps.map19.stairs_down"],
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 1);
-			g_maps->changeMap(0xf02, 3);
+			g_maps->changeMapFromExit(0xf02, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map20.cpp b/engines/mm/mm1/maps/map20.cpp
index fc17fffe6a2..b909513c127 100644
--- a/engines/mm/mm1/maps/map20.cpp
+++ b/engines/mm/mm1/maps/map20.cpp
@@ -75,7 +75,7 @@ void Map20::special00() {
 		STRING["maps.map20.stairs_down"],
 		[]() {
 			g_maps->_mapPos = Common::Point(1, 15);
-			g_maps->changeMap(0xc03, 1);
+			g_maps->changeMapFromExit(0xc03, 1);
 		}
 	));
 }
@@ -86,7 +86,7 @@ void Map20::special01() {
 		STRING["maps.map20.cave"],
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 0);
-			g_maps->changeMap(0x51b, 1);
+			g_maps->changeMapFromExit(0x51b, 1);
 		}
 	));
 }
@@ -198,7 +198,7 @@ void Map20::special08() {
 
 void Map20::goToCastle() {
 	g_maps->_mapPos = Common::Point(15, 8);
-	g_maps->changeMap(0xa11, 3);
+	g_maps->changeMapFromExit(0xa11, 3);
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map23.cpp b/engines/mm/mm1/maps/map23.cpp
index 7eabf20d03a..72429c1c2bd 100644
--- a/engines/mm/mm1/maps/map23.cpp
+++ b/engines/mm/mm1/maps/map23.cpp
@@ -66,7 +66,7 @@ void Map23::special00() {
 		STRING["maps.map23.passage"],
 		[]() {
 			g_maps->_mapPos = Common::Point(12, 0);
-			g_maps->changeMap(0x604, 1);
+			g_maps->changeMapFromExit(0x604, 1);
 		}
 	));
 }
@@ -77,7 +77,7 @@ void Map23::special01() {
 		STRING["maps.map23.cave"],
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 0);
-			g_maps->changeMap(1, 1);
+			g_maps->changeMapFromExit(1, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map25.cpp b/engines/mm/mm1/maps/map25.cpp
index be93fc7afb4..8bf56e194cc 100644
--- a/engines/mm/mm1/maps/map25.cpp
+++ b/engines/mm/mm1/maps/map25.cpp
@@ -166,7 +166,7 @@ void Map25::special00() {
 		STRING["maps.map25.portal"],
 		[]() {
 			g_maps->_mapPos = Common::Point(12, 4);
-			g_maps->changeMap(0x201, 3);
+			g_maps->changeMapFromExit(0x201, 3);
 		}
 	));
 }
@@ -179,7 +179,7 @@ void Map25::special01() {
 			STRING["maps.map25.key"],
 			[]() {
 				g_maps->_mapPos = Common::Point(7, 5);
-				g_maps->changeMap(0x212, 1);
+				g_maps->changeMapFromExit(0x212, 1);
 			}
 		));
 	}
diff --git a/engines/mm/mm1/maps/map28.cpp b/engines/mm/mm1/maps/map28.cpp
index cf05688b785..d134d862b67 100644
--- a/engines/mm/mm1/maps/map28.cpp
+++ b/engines/mm/mm1/maps/map28.cpp
@@ -238,7 +238,7 @@ void Map28::special01() {
 		STRING["maps.map28.cave"],
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 8);
-			g_maps->changeMap(0x601, 1);
+			g_maps->changeMapFromExit(0x601, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map29.cpp b/engines/mm/mm1/maps/map29.cpp
index 341cbe0c42d..da75bb73925 100644
--- a/engines/mm/mm1/maps/map29.cpp
+++ b/engines/mm/mm1/maps/map29.cpp
@@ -95,7 +95,7 @@ void Map29::special00() {
 		STRING["maps.map29.algary"],
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 15);
-			g_maps->changeMap(0x203, 1);
+			g_maps->changeMapFromExit(0x203, 1);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map30.cpp b/engines/mm/mm1/maps/map30.cpp
index f063ebce124..12172416b4b 100644
--- a/engines/mm/mm1/maps/map30.cpp
+++ b/engines/mm/mm1/maps/map30.cpp
@@ -76,7 +76,7 @@ void Map30::special00() {
 		STRING["maps.map30.passage"],
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 7);
-			g_maps->changeMap(0x802, 1);
+			g_maps->changeMapFromExit(0x802, 1);
 		}
 	));
 }
@@ -88,7 +88,7 @@ void Map30::special01() {
 		STRING["maps.map30.ruins"],
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 15);
-			g_maps->changeMap(0x107, 3);
+			g_maps->changeMapFromExit(0x107, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map32.cpp b/engines/mm/mm1/maps/map32.cpp
index 3d818bb4975..a57e8ab36d5 100644
--- a/engines/mm/mm1/maps/map32.cpp
+++ b/engines/mm/mm1/maps/map32.cpp
@@ -67,7 +67,7 @@ void Map32::special00() {
 			0, 2, STRING["maps.map32.key"],
 			[]() {
 				g_maps->_mapPos = Common::Point(7, 0);
-				g_maps->changeMap(0xb1a, 3);
+				g_maps->changeMapFromExit(0xb1a, 3);
 			}
 		));
 	}
@@ -82,7 +82,7 @@ void Map32::special02() {
 
 	if (_data[VAL2] & 0x80) {
 		g_maps->_mapPos = Common::Point(0, 7);
-		g_maps->changeMap(0xb07, 3);
+		g_maps->changeMapFromExit(0xb07, 3);
 
 	} else if (_data[VAL2] != 0) {
 		_data[VAL2] = 0;
diff --git a/engines/mm/mm1/maps/map33.cpp b/engines/mm/mm1/maps/map33.cpp
index 786909d2db2..20fbaacc940 100644
--- a/engines/mm/mm1/maps/map33.cpp
+++ b/engines/mm/mm1/maps/map33.cpp
@@ -152,7 +152,7 @@ void Map33::special01() {
 		STRING["maps.map33.building"],
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 0);
-			g_maps->changeMap(0xf01, 3);
+			g_maps->changeMapFromExit(0xf01, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map34.cpp b/engines/mm/mm1/maps/map34.cpp
index 86df82fdca7..4b85ff80f0a 100644
--- a/engines/mm/mm1/maps/map34.cpp
+++ b/engines/mm/mm1/maps/map34.cpp
@@ -62,7 +62,7 @@ void Map34::special01() {
 		STRING["maps.passage_outside1"],
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 15);
-			g_maps->changeMap(0xf01, 2);
+			g_maps->changeMapFromExit(0xf01, 2);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map35.cpp b/engines/mm/mm1/maps/map35.cpp
index 1f2a82a8f79..37b0894d278 100644
--- a/engines/mm/mm1/maps/map35.cpp
+++ b/engines/mm/mm1/maps/map35.cpp
@@ -74,7 +74,7 @@ void Map35::special01() {
 		STRING["maps.map35.exit"],
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 10);
-			g_maps->changeMap(0xa00, 2);
+			g_maps->changeMapFromExit(0xa00, 2);
 		}
 	));
 }
@@ -83,7 +83,7 @@ void Map35::special02() {
 	visitedExit();
 
 	g_maps->_mapPos = Common::Point(14, 9);
-	g_maps->changeMap(0xa00, 2);
+	g_maps->changeMapFromExit(0xa00, 2);
 	send(SoundMessage(STRING["maps.map35.slide"]));
 }
 
diff --git a/engines/mm/mm1/maps/map36.cpp b/engines/mm/mm1/maps/map36.cpp
index 04e9b156db9..8c3e8cc8378 100644
--- a/engines/mm/mm1/maps/map36.cpp
+++ b/engines/mm/mm1/maps/map36.cpp
@@ -71,7 +71,7 @@ void Map36::special01() {
 		STRING["maps.map36.exit"],
 		[]() {
 			g_maps->_mapPos = Common::Point(11, 2);
-			g_maps->changeMap(0xa00, 2);
+			g_maps->changeMapFromExit(0xa00, 2);
 		}
 	));
 }
@@ -80,7 +80,7 @@ void Map36::special02() {
 	visitedExit();
 
 	g_maps->_mapPos = Common::Point(12, 2);
-	g_maps->changeMap(0xa00, 2);
+	g_maps->changeMapFromExit(0xa00, 2);
 	send(SoundMessage(STRING["maps.map36.slide"]));
 }
 
@@ -106,7 +106,7 @@ void Map36::special06() {
 	visitedExit();
 
 	g_maps->_mapPos = Common::Point(7, 4);
-	g_maps->changeMap(0x705, 3);
+	g_maps->changeMapFromExit(0x705, 3);
 	send(SoundMessage(STRING["maps.map36.pit"]));
 }
 
diff --git a/engines/mm/mm1/maps/map37.cpp b/engines/mm/mm1/maps/map37.cpp
index a3996fa8743..ec62eb29d99 100644
--- a/engines/mm/mm1/maps/map37.cpp
+++ b/engines/mm/mm1/maps/map37.cpp
@@ -74,7 +74,7 @@ void Map37::special01() {
 			STRING["maps.map37.opening"],
 			[]() {
 				g_maps->_mapPos = Common::Point(13, 5);
-				g_maps->changeMap(0xa00, 2);
+				g_maps->changeMapFromExit(0xa00, 2);
 			}
 		));
 	}
@@ -87,7 +87,7 @@ void Map37::special02() {
 		STRING["maps.stairs_down"],
 		[]() {
 			g_maps->_mapPos.x = 15;
-			g_maps->changeMap(0x703, 3);
+			g_maps->changeMapFromExit(0x703, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map38.cpp b/engines/mm/mm1/maps/map38.cpp
index 6d6c4c0725e..59b47958f78 100644
--- a/engines/mm/mm1/maps/map38.cpp
+++ b/engines/mm/mm1/maps/map38.cpp
@@ -68,7 +68,7 @@ void Map38::special03() {
 		STRING["maps.stairs_up"],
 		[]() {
 			g_maps->_mapPos.x = 0;
-			g_maps->changeMap(0xf03, 3);
+			g_maps->changeMapFromExit(0xf03, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map39.cpp b/engines/mm/mm1/maps/map39.cpp
index 3e1d733e067..c8e1e1b7648 100644
--- a/engines/mm/mm1/maps/map39.cpp
+++ b/engines/mm/mm1/maps/map39.cpp
@@ -67,7 +67,7 @@ void Map39::special01() {
 		STRING["maps.map39.stairs_up"],
 		[]() {
 			g_maps->_mapPos = Common::Point(9, 9);
-			g_maps->changeMap(0x703, 2);
+			g_maps->changeMapFromExit(0x703, 2);
 		}
 	));
 }
@@ -78,7 +78,7 @@ void Map39::special02() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0x702, 3);
+			g_maps->changeMapFromExit(0x702, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map40.cpp b/engines/mm/mm1/maps/map40.cpp
index 158e7669f26..ecac8dab959 100644
--- a/engines/mm/mm1/maps/map40.cpp
+++ b/engines/mm/mm1/maps/map40.cpp
@@ -95,7 +95,7 @@ void Map40::special03() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0xf02, 3);
+			g_maps->changeMapFromExit(0xf02, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map41.cpp b/engines/mm/mm1/maps/map41.cpp
index d4774e2accb..3e4acd41d76 100644
--- a/engines/mm/mm1/maps/map41.cpp
+++ b/engines/mm/mm1/maps/map41.cpp
@@ -58,7 +58,7 @@ void Map41::special01() {
 		STRING["maps.map41.stairs_up"],
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 2);
-			g_maps->changeMap(0x101, 2);
+			g_maps->changeMapFromExit(0x101, 2);
 		}
 	));
 }
@@ -69,7 +69,7 @@ void Map41::special02() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0x704, 3);
+			g_maps->changeMapFromExit(0x704, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map42.cpp b/engines/mm/mm1/maps/map42.cpp
index c7962ee0739..7a79f2ce857 100644
--- a/engines/mm/mm1/maps/map42.cpp
+++ b/engines/mm/mm1/maps/map42.cpp
@@ -139,7 +139,7 @@ void Map42::special15() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0xf04, 3);
+			g_maps->changeMapFromExit(0xf04, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map43.cpp b/engines/mm/mm1/maps/map43.cpp
index a00bb3447a1..5f2cc0090fe 100644
--- a/engines/mm/mm1/maps/map43.cpp
+++ b/engines/mm/mm1/maps/map43.cpp
@@ -76,7 +76,7 @@ void Map43::special01() {
 			STRING["maps.map43.exit"],
 			[]() {
 				g_maps->_mapPos = Common::Point(9, 13);
-				g_maps->changeMap(0x101, 2);
+				g_maps->changeMapFromExit(0x101, 2);
 			}
 		));
 	} else {
diff --git a/engines/mm/mm1/maps/map44.cpp b/engines/mm/mm1/maps/map44.cpp
index d63a7da6eab..b8ea2644f2e 100644
--- a/engines/mm/mm1/maps/map44.cpp
+++ b/engines/mm/mm1/maps/map44.cpp
@@ -60,7 +60,7 @@ void Map44::special01() {
 		STRING["maps.passage_outside1"],
 		[]() {
 			g_maps->_mapPos = Common::Point(12, 12);
-			g_maps->changeMap(0x112, 2);
+			g_maps->changeMapFromExit(0x112, 2);
 		}
 	));
 }
@@ -71,7 +71,7 @@ void Map44::special02() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0xf05, 3);
+			g_maps->changeMapFromExit(0xf05, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map45.cpp b/engines/mm/mm1/maps/map45.cpp
index 5a3ab9443bb..27325ba20ff 100644
--- a/engines/mm/mm1/maps/map45.cpp
+++ b/engines/mm/mm1/maps/map45.cpp
@@ -60,7 +60,7 @@ void Map45::special01() {
 		STRING["maps.map45.passage"],
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 12);
-			g_maps->changeMap(5, 1);
+			g_maps->changeMapFromExit(5, 1);
 		}
 	));
 }
@@ -75,7 +75,7 @@ void Map45::special03() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0x107, 1);
+			g_maps->changeMapFromExit(0x107, 1);
 		}
 	));
 }
@@ -86,7 +86,7 @@ void Map45::special04() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0xa00, 3);
+			g_maps->changeMapFromExit(0xa00, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map46.cpp b/engines/mm/mm1/maps/map46.cpp
index ce78e564094..30e7b926416 100644
--- a/engines/mm/mm1/maps/map46.cpp
+++ b/engines/mm/mm1/maps/map46.cpp
@@ -63,7 +63,7 @@ void Map46::special02() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0x705, 3);
+			g_maps->changeMapFromExit(0x705, 3);
 		}
 	));
 }
@@ -74,7 +74,7 @@ void Map46::special03() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0xf05, 3);
+			g_maps->changeMapFromExit(0xf05, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map47.cpp b/engines/mm/mm1/maps/map47.cpp
index bc40b1327a7..be6d6bd32d0 100644
--- a/engines/mm/mm1/maps/map47.cpp
+++ b/engines/mm/mm1/maps/map47.cpp
@@ -125,7 +125,7 @@ void Map47::special03() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0xa00, 3);
+			g_maps->changeMapFromExit(0xa00, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map49.cpp b/engines/mm/mm1/maps/map49.cpp
index 23041f7ae3e..8abc3109f80 100644
--- a/engines/mm/mm1/maps/map49.cpp
+++ b/engines/mm/mm1/maps/map49.cpp
@@ -63,7 +63,7 @@ void Map49::special01() {
 		STRING["maps.passage_outside1"],
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 7);
-			g_maps->changeMap(0xb1a, 2);
+			g_maps->changeMapFromExit(0xb1a, 2);
 		}
 	));
 }
@@ -72,7 +72,7 @@ void Map49::special02() {
 	visitedExit();
 
 	g_maps->_mapPos = Common::Point(15, 7);
-	g_maps->changeMap(0xb1a, 2);
+	g_maps->changeMapFromExit(0xb1a, 2);
 	send(SoundMessage(STRING["maps.map49.chute"]));
 }
 
@@ -124,7 +124,7 @@ void Map49::special07() {
 	reduceHP();
 	reduceHP();
 	g_maps->_mapPos = Common::Point(4, 7);
-	g_maps->changeMap(0xb1a, 2);
+	g_maps->changeMapFromExit(0xb1a, 2);
 
 	send(SoundMessage(STRING["maps.map49.catapult"]));
 }
diff --git a/engines/mm/mm1/maps/map50.cpp b/engines/mm/mm1/maps/map50.cpp
index afa5336aede..c063082ea82 100644
--- a/engines/mm/mm1/maps/map50.cpp
+++ b/engines/mm/mm1/maps/map50.cpp
@@ -60,7 +60,7 @@ void Map50::special01() {
 		STRING["maps.passage_outside1"],
 		[]() {
 			g_maps->_mapPos = Common::Point(10, 5);
-			g_maps->changeMap(0x11b, 2);
+			g_maps->changeMapFromExit(0x11b, 2);
 		}
 	));
 }
@@ -71,7 +71,7 @@ void Map50::special02() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0x701, 3);
+			g_maps->changeMapFromExit(0x701, 3);
 		}
 	));
 }
@@ -82,7 +82,7 @@ void Map50::special03() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0xe00, 3);
+			g_maps->changeMapFromExit(0xe00, 3);
 		}
 	));
 }
@@ -93,7 +93,7 @@ void Map50::special04() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0x201, 3);
+			g_maps->changeMapFromExit(0x201, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map51.cpp b/engines/mm/mm1/maps/map51.cpp
index f0462f07248..a8a552b234b 100644
--- a/engines/mm/mm1/maps/map51.cpp
+++ b/engines/mm/mm1/maps/map51.cpp
@@ -68,7 +68,7 @@ void Map51::special02() {
 	send(SoundMessage(
 		STRING["maps.stairs_down"],
 		[]() {
-			g_maps->changeMap(0xe00, 3);
+			g_maps->changeMapFromExit(0xe00, 3);
 		}
 	));
 }
@@ -79,7 +79,7 @@ void Map51::special03() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0xf01, 3);
+			g_maps->changeMapFromExit(0xf01, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map52.cpp b/engines/mm/mm1/maps/map52.cpp
index aff073b9f1d..c854c2af5a7 100644
--- a/engines/mm/mm1/maps/map52.cpp
+++ b/engines/mm/mm1/maps/map52.cpp
@@ -67,7 +67,7 @@ void Map52::special03() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0x701, 3);
+			g_maps->changeMapFromExit(0x701, 3);
 		}
 	));
 }
@@ -78,7 +78,7 @@ void Map52::special04() {
 	send(SoundMessage(
 		STRING["maps.stairs_up"],
 		[]() {
-			g_maps->changeMap(0xf01, 3);
+			g_maps->changeMapFromExit(0xf01, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map53.cpp b/engines/mm/mm1/maps/map53.cpp
index 2cf7e54d2f5..cfd0e988297 100644
--- a/engines/mm/mm1/maps/map53.cpp
+++ b/engines/mm/mm1/maps/map53.cpp
@@ -69,7 +69,7 @@ void Map53::special03() {
 	send(SoundMessage(
 		STRING["maps.map53.ladder"],
 		[]() {
-			g_maps->changeMap(0xf01, 3);
+			g_maps->changeMapFromExit(0xf01, 3);
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/maps.cpp b/engines/mm/mm1/maps/maps.cpp
index 9a01eac74c7..9b1318d1261 100644
--- a/engines/mm/mm1/maps/maps.cpp
+++ b/engines/mm/mm1/maps/maps.cpp
@@ -454,6 +454,23 @@ void Maps::changeMap(uint16 id, byte section, bool loadingSave) {
 	g_events->send("Game", GameMessage("UPDATE"));
 }
 
+void Maps::changeMapFromExit(uint16 id, byte section) {
+	// Run the normal transition first. Unlike calling the destination
+	// special(), this can't bounce the party straight back through
+	// stairs and doors, or show a second transition prompt
+	changeMap(id, section);
+
+	// changeMap() records a newly entered destination as VISITED_NORMAL.
+	// This change came through an entrance or exit, so keep the metadata
+	// bits and correct only the tile type to VISITED_EXIT
+	byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x];
+	visited = (visited & 0xf0) | VISITED_EXIT;
+
+	// changeMap() has already sent the normal Game UPDATE. Repaint once
+	// more so the enhanced minimap shows the exit immediately
+	g_events->redrawViews();
+}
+
 void Maps::visitedTile() {
 	byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x];
 	if (!(visited & 0x7f))
diff --git a/engines/mm/mm1/maps/maps.h b/engines/mm/mm1/maps/maps.h
index 9f7c1fe776f..c7157cff04e 100644
--- a/engines/mm/mm1/maps/maps.h
+++ b/engines/mm/mm1/maps/maps.h
@@ -180,6 +180,13 @@ public:
 	 */
 	void changeMap(uint16 id, byte section, bool loadingSave = false);
 
+	/**
+	 * Change maps through a known entrance/exit/stair transition and
+	 * mark the destination arrival square as an exit without firing
+	 * the destination square's special action
+	 */
+	void changeMapFromExit(uint16 id, byte section);
+
 	/**
 	 * Mark a tile as visited
 	 */


Commit: 2d4934fce9b8112df4e8c62048ab33b1a7e7eb72
    https://github.com/scummvm/scummvm/commit/2d4934fce9b8112df4e8c62048ab33b1a7e7eb72
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:00+11:00

Commit Message:
MM: MM1: Discover teleport landing squares and repaint exit markers immediately

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map.cpp


diff --git a/engines/mm/mm1/maps/map.cpp b/engines/mm/mm1/maps/map.cpp
index 9cbccc1087f..acd13887e1d 100644
--- a/engines/mm/mm1/maps/map.cpp
+++ b/engines/mm/mm1/maps/map.cpp
@@ -105,10 +105,16 @@ void Map::reduceHP() {
 }
 
 void Map::updateGame() {
+	// Scripted same-map teleports often assign _mapPos directly and then
+	// call updateGame(), bypassing Maps::step(). Discover the landing square
+	// before repainting, so the minimap can draw it immediately
+	g_maps->visitedTile();
 	g_events->send("Game", GameMessage("UPDATE"));
 }
 
 void Map::redrawGame() {
+	// Keep redraw-only relocation paths consistent with updateGame()
+	g_maps->visitedTile();
 	g_events->send("Game", GameMessage("REDRAW"));
 }
 
@@ -139,6 +145,11 @@ void Map::visitedSpecial() {
 void Map::visitedExit() {
 	byte &visited = _visited[g_maps->_mapOffset];
 	visited = (visited & 0x80) | VISITED_EXIT;
+
+	// Show the exit marker before an entrance prompt can immediately move
+	// the party to another map, otherwise the source map's minimap would
+	// never be repainted
+	g_events->redrawViews();
 }
 
 void Map::visitedBusiness() {


Commit: 77cf3b9000654df16a14e86dd7cac9a3214a0283
    https://github.com/scummvm/scummvm/commit/77cf3b9000654df16a14e86dd7cac9a3214a0283
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:00+11:00

Commit Message:
MM: MM1: Use proper map names in the enhanced interface

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map.cpp


diff --git a/engines/mm/mm1/maps/map.cpp b/engines/mm/mm1/maps/map.cpp
index acd13887e1d..bde59573422 100644
--- a/engines/mm/mm1/maps/map.cpp
+++ b/engines/mm/mm1/maps/map.cpp
@@ -29,10 +29,73 @@ namespace MM {
 namespace MM1 {
 namespace Maps {
 
+static const char *const MM1_MAP_DESCRIPTIONS[] = {
+	"Sorpigal",
+	"Portsmith",
+	"Algary",
+	"Dusk",
+	"Erliquin",
+	"Sorpigal Dungeon",
+	"Crazed Wizard Cave",
+	"Portsmith Dungeon",
+	"Erliquin Dungeon",
+	"Dusk Dungeon",
+	"Caves at the Korin Bluffs",
+	"Volcano",
+	"Cave of Square Magic",
+	"Medusa Lair",
+	"Area A-1",
+	"Area A-2",
+	"Area A-3",
+	"Area A-4",
+	"Area B-1",
+	"Area B-2",
+	"Area B-3",
+	"Area B-4",
+	"Area C-1",
+	"Area C-2",
+	"Area C-3",
+	"Area C-4",
+	"Area D-1",
+	"Area D-2",
+	"Area D-3",
+	"Area D-4",
+	"Area E-1",
+	"Area E-2",
+	"Area E-3",
+	"Area E-4",
+	"Castle Doom",
+	"Castle Blackridge North",
+	"Castle Blackridge South",
+	"Ancient Wizard Lair - L1",
+	"Ancient Wizard Lair - L2",
+	"Warrior's Stronghold - L1",
+	"Warrior's Stronghold - L2",
+	"Stronghold - L1",
+	"Stronghold - L2",
+	"Castle White Wolf",
+	"Castle Dragadune - L1",
+	"Castle Dragadune - L2",
+	"Castle Dragadune - L3",
+	"Castle Dragadune - L4",
+	nullptr, // Unidentified internal map
+	"Castle Alamar",
+	"Fabled Building of Gold - L1",
+	"Fabled Building of Gold - L2",
+	"Fabled Building of Gold - L3",
+	"Fabled Building of Gold - L4",
+	"Soul Maze",
+	"Astral Plane",
+};
+
 Map::Map(uint index, const Common::String &name, uint16 id,
 		byte defaultSection, const char *desc) :
 		_mapIndex(index), _name(name), _id(id), _defaultSection(defaultSection) {
-	_description = desc ? Common::String(desc) : _name;
+	const char *mapDescription = desc;
+	if (!mapDescription && index < ARRAYSIZE(MM1_MAP_DESCRIPTIONS))
+		mapDescription = MM1_MAP_DESCRIPTIONS[index];
+
+	_description = mapDescription ? Common::String(mapDescription) : _name;
 	_description.setChar(toupper(_description[0]), 0);
 	if (_description.hasPrefix("Area")) {
 		_description.setChar(toupper(_description[4]), 4);


Commit: 0db82a0af21d8052b705d84ddadebea49c97a4a2
    https://github.com/scummvm/scummvm/commit/0db82a0af21d8052b705d84ddadebea49c97a4a2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:01+11:00

Commit Message:
MM: MM1: Refresh the character displays after the Statue of Judgement

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views/interactions/giant.cpp
    engines/mm/mm1/views_enh/interactions/giant.cpp


diff --git a/engines/mm/mm1/views/interactions/giant.cpp b/engines/mm/mm1/views/interactions/giant.cpp
index a1817b07a8b..6fb5e1dbb0a 100644
--- a/engines/mm/mm1/views/interactions/giant.cpp
+++ b/engines/mm/mm1/views/interactions/giant.cpp
@@ -90,6 +90,10 @@ void Giant::charSelected(uint charIndex) {
 	writeString(0, 1, line);
 	Sound::sound(SOUND_2);
 
+	// The Statue may have changed XP and a primary attribute. Repaint any
+	// visible character views now rather than at a later refresh
+	g_events->redrawViews();
+
 	delaySeconds(5);
 }
 
diff --git a/engines/mm/mm1/views_enh/interactions/giant.cpp b/engines/mm/mm1/views_enh/interactions/giant.cpp
index 756adbed44f..d69bee2756f 100644
--- a/engines/mm/mm1/views_enh/interactions/giant.cpp
+++ b/engines/mm/mm1/views_enh/interactions/giant.cpp
@@ -55,6 +55,10 @@ void Giant::charSwitched(Character *priorChar) {
 	addText(line);
 	Sound::sound(SOUND_2);
 
+	// The Statue may have changed XP and a primary attribute. Repaint any
+	// visible character views now rather than at a later refresh
+	g_events->redrawViews();
+
 	delaySeconds(5);
 	redraw();
 }


Commit: 996b68c3b5eafd264c6e46440c315696071b0725
    https://github.com/scummvm/scummvm/commit/996b68c3b5eafd264c6e46440c315696071b0725
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:01+11:00

Commit Message:
MM: MM1: Restart Lord Inspectron's quest cycle at his first quest

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map35.cpp


diff --git a/engines/mm/mm1/maps/map35.cpp b/engines/mm/mm1/maps/map35.cpp
index 37b0894d278..556b1354ec2 100644
--- a/engines/mm/mm1/maps/map35.cpp
+++ b/engines/mm/mm1/maps/map35.cpp
@@ -159,6 +159,9 @@ void Map35::acceptQuest() {
 			c._flags[8] = CHARFLAG8_80;
 			c._flags[5] = CHARFLAG5_80;
 		}
+
+		// Restart Inspectron's seven-quest cycle at quest #1
+		questNum = 8;
 	}
 
 	// Assign the quest to all party characters


Commit: 8fc0b768440c0718b1f59040e180fba2d12c0611
    https://github.com/scummvm/scummvm/commit/8fc0b768440c0718b1f59040e180fba2d12c0611
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:01+11:00

Commit Message:
MM: MM1: Name the cleric fire protection spell correctly

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/spells/cast_spell.cpp
    engines/mm/mm1/views_enh/spells/spellbook.cpp


diff --git a/engines/mm/mm1/views_enh/spells/cast_spell.cpp b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
index eeb7c31e19a..f8f548b10f4 100644
--- a/engines/mm/mm1/views_enh/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
@@ -73,6 +73,10 @@ void CastSpell::draw() {
 	Common::String spellName = STRING["enhdialogs.cast_spell.none"];
 	if (spellNum >= 0 && spellNum < 47) {
 		spellName = STRING[Common::String::format("spells.cleric.%d", spellNum)];
+
+		// Cleric 2-5 gives fire protection, but the string is "Ice"
+		if (spellNum == 12)
+			spellName = "Protection From Fire";
 	} else if (spellNum >= 47) {
 		spellName = STRING[Common::String::format("spells.wizard.%d", spellNum - 47)];
 	}
@@ -228,9 +232,11 @@ void CastSpell::castSpell(Character *target) {
 
 	Character &c = *g_globals->_currCharacter;
 	if (isInCombat()) {
-		const Common::String spellName = _spellIndex < 47 ?
+		Common::String spellName = _spellIndex < 47 ?
 			STRING[Common::String::format("spells.cleric.%d", _spellIndex)] :
 			STRING[Common::String::format("spells.wizard.%d", _spellIndex - 47)];
+		if (_spellIndex == 12)
+			spellName = "Protection From Fire";
 		const Common::String logLine = target ?
 			Common::String::format("%s casts %s on %s.", c._name,
 				spellName.c_str(), target->_name) :
diff --git a/engines/mm/mm1/views_enh/spells/spellbook.cpp b/engines/mm/mm1/views_enh/spells/spellbook.cpp
index cf936a49870..005b26d779e 100644
--- a/engines/mm/mm1/views_enh/spells/spellbook.cpp
+++ b/engines/mm/mm1/views_enh/spells/spellbook.cpp
@@ -139,6 +139,10 @@ void Spellbook::draw() {
 				spellIndex
 			)];
 
+			// Cleric 2-5 gives fire protection, but the string is "Ice"
+			if (!_isWizard && spellIndex == 12)
+				spellName = "Protection From Fire";
+
 			// Mark the spell being assigned, and existing favorites
 			if (_favoriteAssignMode && spellIndex == _favoriteAssignSpell) {
 				setTextColor(15);


Commit: 469cff3a216ea6d3be2145389972ddcb027da6da
    https://github.com/scummvm/scummvm/commit/469cff3a216ea6d3be2145389972ddcb027da6da
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:01+11:00

Commit Message:
MM: MM1: Let the numpad PgUp and PgDn keys page the enhanced spellbook

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/spells/spellbook.cpp


diff --git a/engines/mm/mm1/views_enh/spells/spellbook.cpp b/engines/mm/mm1/views_enh/spells/spellbook.cpp
index 005b26d779e..7ea7695b711 100644
--- a/engines/mm/mm1/views_enh/spells/spellbook.cpp
+++ b/engines/mm/mm1/views_enh/spells/spellbook.cpp
@@ -162,6 +162,14 @@ void Spellbook::draw() {
 }
 
 bool Spellbook::msgKeypress(const KeypressMessage &msg) {
+	// Some keyboard paths report the navigation-labelled numpad keys as
+	// KP9 and KP3. Treat them as Page Up and Page Down only while Num Lock
+	// is off, so numeric keypad input still works with it on
+	const bool pageUpKey = msg.keycode == Common::KEYCODE_PAGEUP ||
+		(msg.keycode == Common::KEYCODE_KP9 && !(msg.flags & Common::KBD_NUM));
+	const bool pageDownKey = msg.keycode == Common::KEYCODE_PAGEDOWN ||
+		(msg.keycode == Common::KEYCODE_KP3 && !(msg.flags & Common::KBD_NUM));
+
 	if (msg.keycode >= Common::KEYCODE_0 && msg.keycode <= Common::KEYCODE_9) {
 		const int shortcut = (msg.keycode == Common::KEYCODE_0) ?
 			9 : msg.keycode - Common::KEYCODE_1;
@@ -181,12 +189,12 @@ bool Spellbook::msgKeypress(const KeypressMessage &msg) {
 		else
 			beginFavoriteAssignment();
 
-	} else if (msg.keycode == Common::KEYCODE_PAGEUP) {
+	} else if (pageUpKey) {
 		if (_topIndex > 0) {
 			_topIndex = MAX(_topIndex - 10, 0);
 			redraw();
 		}
-	} else if (msg.keycode == Common::KEYCODE_PAGEDOWN) {
+	} else if (pageDownKey) {
 		int newTopIndex = _topIndex + 10;
 		if (newTopIndex < _count) {
 			_topIndex = newTopIndex;


Commit: 284a5a2d1d62e94177ae6e014c614165dd0ad584
    https://github.com/scummvm/scummvm/commit/284a5a2d1d62e94177ae6e014c614165dd0ad584
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:02+11:00

Commit Message:
MM: MM1: Minor bugfixes

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map02.cpp
    engines/mm/mm1/maps/map07.cpp
    engines/mm/mm1/maps/map13.cpp
    engines/mm/mm1/maps/map14.cpp
    engines/mm/mm1/maps/map21.cpp
    engines/mm/mm1/maps/map23.cpp
    engines/mm/mm1/maps/map27.cpp
    engines/mm/mm1/maps/map31.cpp
    engines/mm/mm1/maps/map38.cpp
    engines/mm/mm1/maps/map44.cpp
    engines/mm/mm1/views/spells/teleport.cpp
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/maps/map02.cpp b/engines/mm/mm1/maps/map02.cpp
index 74daba2f411..a3e85b2a6a4 100644
--- a/engines/mm/mm1/maps/map02.cpp
+++ b/engines/mm/mm1/maps/map02.cpp
@@ -32,7 +32,7 @@ namespace Maps {
 static const byte PORTAL_DEST_X[4] = { 8, 15, 0, 5 };
 static const byte PORTAL_DEST_Y[4] = { 4, 0, 0, 14 };
 static const uint16 PORTAL_DEST_ID[4] = { 0xc03, 0xa00, 0xf01, 0x604 };
-static const byte PORTAL_DEST_SECTION[4] = { 3, 0, 1, 4 };
+static const byte PORTAL_DEST_SECTION[4] = { 1, 0, 2, 1 };
 
 void Map02::special() {
 	// Scan for special actions on the map cell
@@ -154,6 +154,8 @@ void Map02::special15() {
 		STRING["maps.map02.portal"],
 		[]() {
 			int index = g_maps->_mapPos.x - 9;
+			if (index < 0 || index >= 4)
+				return;
 			g_maps->_mapPos.x = PORTAL_DEST_X[index];
 			g_maps->_mapPos.y = PORTAL_DEST_Y[index];
 
diff --git a/engines/mm/mm1/maps/map07.cpp b/engines/mm/mm1/maps/map07.cpp
index f561e2d6cbb..68efc7a2f72 100644
--- a/engines/mm/mm1/maps/map07.cpp
+++ b/engines/mm/mm1/maps/map07.cpp
@@ -179,7 +179,10 @@ void Map07::special15() {
 		Character &c = g_globals->_party[i];
 		if (!(c._flags[11] & CHARFLAG11_GOT_MIGHT)) {
 			c._flags[11] |= CHARFLAG11_GOT_MIGHT;
-			c._might._base = MIN((int)c._might._base + 4, 255);
+			if (c._might._base < 30) {
+				c._might._current = c._might._base =
+					c._might._base + 4;
+			}
 		}
 	}
 
diff --git a/engines/mm/mm1/maps/map13.cpp b/engines/mm/mm1/maps/map13.cpp
index e328b496078..49a44b6fcf5 100644
--- a/engines/mm/mm1/maps/map13.cpp
+++ b/engines/mm/mm1/maps/map13.cpp
@@ -157,10 +157,12 @@ void Map13::special10() {
 }
 
 void Map13::special18() {
+	g_maps->clearSpecial();
 	encounter(getRandomNumber(4) + 2, 2, 5);
 }
 
 void Map13::special22() {
+	g_maps->clearSpecial();
 	encounter(getRandomNumber(3) + 3, 9, 6);
 }
 
diff --git a/engines/mm/mm1/maps/map14.cpp b/engines/mm/mm1/maps/map14.cpp
index 7e3a473b5dc..8ea8857dd3a 100644
--- a/engines/mm/mm1/maps/map14.cpp
+++ b/engines/mm/mm1/maps/map14.cpp
@@ -168,9 +168,9 @@ void Map14::special05() {
 		Character &c = g_globals->_party[i];
 		if (!(c._flags[11] & CHARFLAG11_GOT_ENDURANCE)) {
 			c._flags[11] |= CHARFLAG11_GOT_ENDURANCE;
-			int endurance = c._endurance._base + 4;
-			if (endurance < 30) {
-				c._endurance._base = c._endurance._current = endurance;
+			if (c._endurance._base < 30) {
+				c._endurance._current = c._endurance._base =
+					c._endurance._base + 4;
 			}
 		}
 	}
diff --git a/engines/mm/mm1/maps/map21.cpp b/engines/mm/mm1/maps/map21.cpp
index 6aed3edc9a2..31c2bc17116 100644
--- a/engines/mm/mm1/maps/map21.cpp
+++ b/engines/mm/mm1/maps/map21.cpp
@@ -108,6 +108,7 @@ void Map21::special01() {
 		[]() {
 			Map21 &map = *static_cast<Map21 *>(g_maps->_currentMap);
 			map[TRIVIA_ENABLED]++;
+			g_events->send(InfoMessage("Trivia Island admission granted."));
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map23.cpp b/engines/mm/mm1/maps/map23.cpp
index 72429c1c2bd..d72201b73c4 100644
--- a/engines/mm/mm1/maps/map23.cpp
+++ b/engines/mm/mm1/maps/map23.cpp
@@ -88,6 +88,8 @@ void Map23::special02() {
 }
 
 void Map23::special03() {
+	g_maps->clearSpecial();
+
 	if (g_globals->_activeSpells._s.levitate) {
 		Common::String line1 = Common::String::format("%s %s",
 			STRING["maps.map23.pit"].c_str(),
diff --git a/engines/mm/mm1/maps/map27.cpp b/engines/mm/mm1/maps/map27.cpp
index 69a2163e82b..b55d2fcedef 100644
--- a/engines/mm/mm1/maps/map27.cpp
+++ b/engines/mm/mm1/maps/map27.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/maps/map27.h"
+#include "mm/mm1/data/locations.h"
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
@@ -94,9 +95,11 @@ void Map27::special03() {
 		0, 2, STRING["maps.map27.alignment"]
 	));
 
+	TempleData alignmentData;
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
 		Character &c = g_globals->_party[i];
 		c._alignment = c._alignmentInitial;
+		c._alignmentCtr = alignmentData.ALIGNMENT_VALS[c._alignment - 1];
 	}
 }
 
@@ -113,7 +116,9 @@ void Map27::special04() {
 			Inventory &inv = (invNum == 0) ? c._equipped : c._backpack;
 
 			for (int itemNum = (int)inv.size() - 1; itemNum >= 0; --itemNum) {
-				Item *item = g_globals->_items.getItem(c._equipped[itemNum]._id);
+				if (!inv[itemNum])
+					continue;
+				Item *item = g_globals->_items.getItem(inv[itemNum]._id);
 				if (item->_constBonus_id == EQUIP_CURSED)
 					inv.removeAt(itemNum);
 			}
diff --git a/engines/mm/mm1/maps/map31.cpp b/engines/mm/mm1/maps/map31.cpp
index 37d4c2a37e0..d9a71cd9493 100644
--- a/engines/mm/mm1/maps/map31.cpp
+++ b/engines/mm/mm1/maps/map31.cpp
@@ -71,8 +71,10 @@ void Map31::special00() {
 
 		if (!(c._flags[11] & CHARFLAG11_GOT_INTELLIGENCE)) {
 			c._flags[11] |= CHARFLAG11_GOT_INTELLIGENCE;
-			c._intelligence._current = c._intelligence._base =
-				MIN(c._intelligence._base + 4, 30);
+			if (c._intelligence._base < 30) {
+				c._intelligence._current = c._intelligence._base =
+					c._intelligence._base + 4;
+			}
 		}
 	}
 }
@@ -85,6 +87,7 @@ void Map31::special02() {
 	if (_data[VAL1]) {
 		none160();
 	} else {
+		g_maps->clearSpecial();
 		encounter();
 	}
 }
@@ -126,6 +129,7 @@ void Map31::hostile() {
 
 void Map31::neutral() {
 	_data[VAL1]--;
+	g_maps->clearSpecial();
 	encounter();
 }
 
diff --git a/engines/mm/mm1/maps/map38.cpp b/engines/mm/mm1/maps/map38.cpp
index 59b47958f78..f1151664605 100644
--- a/engines/mm/mm1/maps/map38.cpp
+++ b/engines/mm/mm1/maps/map38.cpp
@@ -46,6 +46,8 @@ void Map38::special() {
 
 	g_maps->_mapPos = Common::Point(getRandomNumber(16) - 1,
 		getRandomNumber(16) - 1);
+	// This random relocation bypasses updateGame() and redrawGame()
+	g_maps->visitedTile();
 	send(SoundMessage(STRING["maps.map38.ringing"]));
 }
 
diff --git a/engines/mm/mm1/maps/map44.cpp b/engines/mm/mm1/maps/map44.cpp
index b8ea2644f2e..234261eec3b 100644
--- a/engines/mm/mm1/maps/map44.cpp
+++ b/engines/mm/mm1/maps/map44.cpp
@@ -87,7 +87,10 @@ void Map44::special04() {
 		Character &c = g_globals->_party[i];
 		if (!(c._flags[11] & CHARFLAG11_GOT_LUCK)) {
 			c._flags[11] |= CHARFLAG11_GOT_LUCK;
-			c._luck._current = c._luck._base = c._luck._base + 4;
+			if (c._luck._base < 30) {
+				c._luck._current = c._luck._base =
+					c._luck._base + 4;
+			}
 		}
 	}
 }
diff --git a/engines/mm/mm1/views/spells/teleport.cpp b/engines/mm/mm1/views/spells/teleport.cpp
index f7de86a28ec..cefac2d2b15 100644
--- a/engines/mm/mm1/views/spells/teleport.cpp
+++ b/engines/mm/mm1/views/spells/teleport.cpp
@@ -72,7 +72,7 @@ bool Teleport::msgKeypress(const KeypressMessage &msg) {
 		redraw();
 	}
 
-	return true;
+	return false;
 }
 
 bool Teleport::msgAction(const ActionMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index dcb544da761..c3eae093ac9 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -1042,6 +1042,11 @@ void Combat::writeDefeat() {
 
 	// Restore the combat view's size for any later combat screens
 	setBounds(oldBounds);
+
+	if (beginCombatLogEvent())
+		addCombatLog(Common::String::format(
+			"Victory. Each active character receives %d XP.",
+			_totalExperience));
 }
 
 void Combat::highlightNextRound() {


Commit: a46d864450cc6b026c82ff210029be77876c47de
    https://github.com/scummvm/scummvm/commit/a46d864450cc6b026c82ff210029be77876c47de
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:02+11:00

Commit Message:
MM: MM1: Keep fire protection active when a fight starts in the hot zone

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map15.cpp


diff --git a/engines/mm/mm1/maps/map15.cpp b/engines/mm/mm1/maps/map15.cpp
index 76a11ccb059..962b939c3ed 100644
--- a/engines/mm/mm1/maps/map15.cpp
+++ b/engines/mm/mm1/maps/map15.cpp
@@ -58,7 +58,6 @@ void Map15::special() {
 			if (_data[VAL1] > 14)
 				_data[VAL1] = 14;
 
-			g_globals->_activeSpells._s.fire = 0;
 			enc.clearMonsters();
 			for (uint i = 0; i < _data[VAL1]; ++i)
 				enc.addMonster(7, 8);


Commit: 8f71fbdd537eb5a1d978bc5b6c7e895e3e6612b8
    https://github.com/scummvm/scummvm/commit/8f71fbdd537eb5a1d978bc5b6c7e895e3e6612b8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:02+11:00

Commit Message:
MM: MM1: Pay Og's reward immediately, take the Queen Idols and show his clue

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map29.cpp


diff --git a/engines/mm/mm1/maps/map29.cpp b/engines/mm/mm1/maps/map29.cpp
index da75bb73925..786c6e2b25c 100644
--- a/engines/mm/mm1/maps/map29.cpp
+++ b/engines/mm/mm1/maps/map29.cpp
@@ -181,23 +181,47 @@ void Map29::chessAnswer(const Common::String &answer) {
 	if (answer.equalsIgnoreCase(properAnswer)) {
 		redrawGame();
 
-		InfoMessage msg(
-			16, 2, STRING["maps.map19.correct"],
-			[]() {
-				MM1::Maps::Map29 &map29 = *static_cast<MM1::Maps::Map29 *>(g_maps->_currentMap);
-				map29.markOgQuestCompleted();
+		// Original MM1 behavior: the reward is committed immediately, the
+		// Queen Idols vanish, and Og explains both the +25,000 EXP reward and
+		// the Mt. Doom prisoner clue before the party is sent away.
+		markOgQuestCompleted();
+
+		for (uint i = 0; i < g_globals->_party.size(); ++i) {
+			Character &c = g_globals->_party[i];
+			c._exp += 25000;
+		}
 
-				for (uint i = 0; i < g_globals->_party.size(); ++i) {
-					Character &c = g_globals->_party[i];
-					c._exp += 25000;
+		auto consumeIdol = [](byte itemId) {
+			for (uint i = 0; i < g_globals->_party.size(); ++i) {
+				Character &c = g_globals->_party[i];
+				int idx = c._backpack.indexOf(itemId);
+				if (idx != -1) {
+					c._backpack.removeAt(idx);
+					return;
 				}
 
+				idx = c._equipped.indexOf(itemId);
+				if (idx != -1) {
+					c._equipped.removeAt(idx);
+					return;
+				}
+			}
+		};
+		consumeIdol(B_QUEEN_IDOL_ID);
+		consumeIdol(W_QUEEN_IDOL_ID);
+
+		InfoMessage msg(
+			0, 0, "YOU HAVE RESTORED MY SIGHT! (+25000 EXP)",
+			0, 1, "I SEE AN IMPORTANT PRISONER IN A CASTLE ON MT. DOOM! HE HAS YOUR SIGHT.",
+			[](const Common::KeyState &) {
+				// Keep Og's reward/clue visible until the player dismisses it.
+				// Only then perform the original forced move toward the Algary exit.
+				g_events->focusedView()->close();
 				g_maps->_mapPos.y = 7;
-				map29.updateGame();
+				g_maps->_currentMap->updateGame();
 			}
 		);
-
-		msg._delaySeconds = 2;
+		msg._largeMessage = true;
 		send(msg);
 		Sound::sound(SOUND_3);
 		Sound::sound(SOUND_3);


Commit: 240c5e17217e8d7c8aabf9c419806cd2d204f824
    https://github.com/scummvm/scummvm/commit/240c5e17217e8d7c8aabf9c419806cd2d204f824
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:02+11:00

Commit Message:
MM: MM1: Fix the swamp encounter check testing the wrong wall byte

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/maps/map29.cpp


diff --git a/engines/mm/mm1/maps/map29.cpp b/engines/mm/mm1/maps/map29.cpp
index 786c6e2b25c..d4ce1157ae6 100644
--- a/engines/mm/mm1/maps/map29.cpp
+++ b/engines/mm/mm1/maps/map29.cpp
@@ -55,7 +55,10 @@ void Map29::special() {
 	}
 
 	switch (_walls[g_maps->_mapOffset]) {
-	case 0:
+	case 0xff:
+		// The original overlay compares the cell wall byte with 0xff here.
+		// 0x00 was a transcription error that made the intended 1-in-50
+		// swamp encounter branch unreachable
 		if (getRandomNumber(50) == 50) {
 			int id1 = getRandomNumber(16);
 			int monsterCount = getRandomNumber(id1 < 15 ? 13 : 4);


Commit: 9680b442f0b5a246993432830cd033b3cdbdd48f
    https://github.com/scummvm/scummvm/commit/9680b442f0b5a246993432830cd033b3cdbdd48f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:02+11:00

Commit Message:
MM: MM1: Show an arrow beside the experience when a character can level up

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/character.cpp
    engines/mm/mm1/data/character.h
    engines/mm/mm1/views_enh/character_info.cpp


diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index 8ac1ec48e3a..e3b2b0f73ac 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -748,6 +748,11 @@ uint32 Character::experienceForNextLevel() const {
 	return expTotal;
 }
 
+bool Character::canLevelUp() const {
+	return _level._base < MAX_LEVEL &&
+		_exp >= experienceForNextLevel();
+}
+
 byte Character::statColor(int amount, int threshold) const {
 	if (amount < 1)
 		return 6;
diff --git a/engines/mm/mm1/data/character.h b/engines/mm/mm1/data/character.h
index 744fc8fb3b3..2992ae726e1 100644
--- a/engines/mm/mm1/data/character.h
+++ b/engines/mm/mm1/data/character.h
@@ -585,6 +585,11 @@ struct Character : public PrimaryAttributes {
 	 */
 	uint32 experienceForNextLevel() const;
 
+	/**
+	 * Returns true when current experience is sufficient for training
+	 */
+	bool canLevelUp() const;
+
 	/**
 	 * Returns the color to use in enhanced mode to
 	 * represent the color of a character attribute
diff --git a/engines/mm/mm1/views_enh/character_info.cpp b/engines/mm/mm1/views_enh/character_info.cpp
index 59ce2792a35..59ba3d5c97b 100644
--- a/engines/mm/mm1/views_enh/character_info.cpp
+++ b/engines/mm/mm1/views_enh/character_info.cpp
@@ -305,6 +305,54 @@ void CharacterInfo::drawStats() {
 		} else {
 			setTextColor(c.statColor(CURR[i], BASE[i]));
 			writeNumber(pt.x, pt.y, CURR[i]);
+
+			// Show an upward arrow after the experience when the character
+			// has enough to train for the next level. The font's right-arrow
+			// glyph is rotated to point up, in the colour used for SP == 0
+			if (i == 13 && c.canLevelUp()) {
+				XeenFont &font = _fontReduced ? g_globals->_fontReduced : g_globals->_fontNormal;
+				XeenFont::setColors(c.statColor(0, c._sp._base));
+				Graphics::ManagedSurface glyph, mask;
+				const int gw = font.getMaxCharWidth() + 2;
+				const int gh = font.getFontHeight() + 2;
+				glyph.create(gw, gh, Graphics::PixelFormat::createFormatCLUT8());
+				mask.create(gw, gh, Graphics::PixelFormat::createFormatCLUT8());
+				// Draw on two different backgrounds, so pixels the glyph
+				// really touched can be told apart from the background
+				glyph.clear(0);
+				mask.clear(255);
+				font.drawChar(&glyph, '^', 1, 1, 9);
+				font.drawChar(&mask, '^', 1, 1, 9);
+				int left = gw, top = gh, right = -1, bottom = -1;
+				for (int y = 0; y < gh; ++y) {
+					for (int x = 0; x < gw; ++x) {
+						if (*(const byte *)glyph.getBasePtr(x, y) == *(const byte *)mask.getBasePtr(x, y)) {
+							if (x < left) left = x;
+							if (x > right) right = x;
+							if (y < top) top = y;
+							if (y > bottom) bottom = y;
+						}
+					}
+				}
+				if (right >= left && bottom >= top) {
+					Graphics::ManagedSurface s = getSurface();
+					const int destX = _bounds.borderSize() + _textPos.x + 2;
+					const int destY = _bounds.borderSize() + _textPos.y +
+						(font.getFontHeight() - (right - left + 1)) / 2;
+					for (int y = top; y <= bottom; ++y) {
+						for (int x = left; x <= right; ++x) {
+							const byte pixel = *(const byte *)glyph.getBasePtr(x, y);
+							if (pixel != *(const byte *)mask.getBasePtr(x, y)) continue;
+							// Counterclockwise: the rightmost source column becomes the top row
+							const int dx = destX + y - top;
+							const int dy = destY + right - x;
+							if (dx >= 0 && dx < s.w && dy >= 0 && dy < s.h)
+								s.fillRect(Common::Rect(dx, dy, dx + 1, dy + 1), pixel);
+						}
+					}
+				}
+				XeenFont::setColors(_colorsNum);
+			}
 		}
 	}
 


Commit: e795e40efd18fd804086b2d3bab6cfacc240933f
    https://github.com/scummvm/scummvm/commit/e795e40efd18fd804086b2d3bab6cfacc240933f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:03+11:00

Commit Message:
MM: MM1: Move the combat Go back and Delay currently text clear of the Escape button

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index c3eae093ac9..ab3a7f20cbc 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -608,7 +608,7 @@ bool Combat::msgMouseUp(const MouseUpMessage &msg) {
 
 void Combat::writeOptions() {
 	if (_option != OPTION_NONE)
-		writeString(30, 170, STRING["enhdialogs.misc.go_back"]);
+		writeString(40, 170, STRING["enhdialogs.misc.go_back"]);
 
 	switch (_option) {
 	case OPTION_NONE:
@@ -786,7 +786,7 @@ bool Combat::beginCombatLogEvent() {
 
 void Combat::writeDelaySelect() {
 	writeBottomText(0, 0, STRING["dialogs.combat.set_delay"]);
-	writeBottomText(0, 3, Common::String::format(
+	writeBottomText(40, 3, Common::String::format(
 		STRING["dialogs.combat.delay_currently"].c_str(),
 		g_globals->_delay));
 }


Commit: c406bdf04b59c637c88d5828eb94a4d5378d18a1
    https://github.com/scummvm/scummvm/commit/c406bdf04b59c637c88d5828eb94a4d5378d18a1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:03+11:00

Commit Message:
MM: MM1: Record which monsters the party has encountered

Thanks to dragoo84

Changed paths:
  A engines/mm/mm1/data/bestiary.h
    engines/mm/mm1/data/monsters.cpp
    engines/mm/mm1/data/monsters.h
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/globals.h
    engines/mm/mm1/mm1.cpp
    engines/mm/mm1/mm1.h


diff --git a/engines/mm/mm1/data/bestiary.h b/engines/mm/mm1/data/bestiary.h
new file mode 100644
index 00000000000..4dfe2dff86f
--- /dev/null
+++ b/engines/mm/mm1/data/bestiary.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MM1_DATA_BESTIARY_H
+#define MM1_DATA_BESTIARY_H
+
+#include "common/stream.h"
+#include "mm/mm1/data/monsters.h"
+
+namespace MM {
+namespace MM1 {
+
+/**
+ * Tracks which monsters the party has encountered. It's saved as an optional
+ * extension after the normal savegame data, so older saves still load
+ */
+class BestiaryState {
+private:
+	byte _seen[(MONSTERS_COUNT + 7) / 8] = {};
+
+public:
+	void clear() {
+		memset(_seen, 0, sizeof(_seen));
+	}
+
+	bool hasSeen(uint id) const {
+		return id >= 1 && id <= MONSTERS_COUNT &&
+			(_seen[(id - 1) / 8] & (1 << ((id - 1) % 8)));
+	}
+
+	void discover(uint id) {
+		if (id >= 1 && id <= MONSTERS_COUNT)
+			_seen[(id - 1) / 8] |= 1 << ((id - 1) % 8);
+	}
+
+	uint count() const {
+		uint result = 0;
+		for (uint id = 1; id <= MONSTERS_COUNT; ++id) {
+			if (hasSeen(id))
+				++result;
+		}
+
+		return result;
+	}
+
+	void save(Common::WriteStream &stream) const {
+		stream.writeUint32BE(0x42535431);	// 'BST1' extension marker
+		stream.writeUint16BE(MONSTERS_COUNT);
+		stream.write(_seen, sizeof(_seen));
+	}
+
+	void load(Common::SeekableReadStream &stream) {
+		clear();
+
+		const int64 start = stream.pos();
+		if (stream.size() - start < 6 + (int64)sizeof(_seen))
+			return;
+
+		if (stream.readUint32BE() != 0x42535431 ||
+				stream.readUint16BE() != MONSTERS_COUNT) {
+			stream.seek(start);
+			return;
+		}
+
+		if (stream.read(_seen, sizeof(_seen)) != sizeof(_seen))
+			clear();
+	}
+};
+
+} // namespace MM1
+} // namespace MM
+
+#endif
diff --git a/engines/mm/mm1/data/monsters.cpp b/engines/mm/mm1/data/monsters.cpp
index 6591527f36b..8edb49a1129 100644
--- a/engines/mm/mm1/data/monsters.cpp
+++ b/engines/mm/mm1/data/monsters.cpp
@@ -51,6 +51,7 @@ bool Monsters::load() {
 
 	for (int lineNum = 0; lineNum < MONSTERS_COUNT; ++lineNum) {
 		Monster &mon = _monsters[lineNum];
+		mon._bestiaryId = lineNum + 1;
 		Common::String line = f.readLine();
 		assert(line.size() > 20 && line[0] == '"' && line[16] == '"');
 
diff --git a/engines/mm/mm1/data/monsters.h b/engines/mm/mm1/data/monsters.h
index 250a8e7c12a..2e950675713 100644
--- a/engines/mm/mm1/data/monsters.h
+++ b/engines/mm/mm1/data/monsters.h
@@ -78,6 +78,7 @@ enum MonsterCounter {
 };
 
 struct Monster {
+	uint16 _bestiaryId = 0;	// Database ID, independent of battle position
 	Common::String _name; // char _name[15];
 	byte _count;
 	byte _fleeThreshold;
diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index aa8148bf64c..26a0363020d 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -99,6 +99,7 @@ void Combat::loadMonsters() {
 	// Set up hp and ac for the encounter monsters
 	for (uint i = 0; i < enc._monsterList.size(); ++i) {
 		Monster &mon = enc._monsterList[i];
+		g_globals->_bestiary.discover(mon._bestiaryId);
 		_monsterP = &mon;
 		int val = getRandomNumber(8);
 
diff --git a/engines/mm/mm1/globals.h b/engines/mm/mm1/globals.h
index 706f587f76b..167ee1e077b 100644
--- a/engines/mm/mm1/globals.h
+++ b/engines/mm/mm1/globals.h
@@ -27,6 +27,7 @@
 #include "mm/shared/utils/strings_data.h"
 #include "mm/shared/utils/xeen_font.h"
 #include "mm/xeen/sprites.h"
+#include "mm/mm1/data/bestiary.h"
 #include "mm/mm1/data/game_state.h"
 #include "mm/mm1/data/items.h"
 #include "mm/mm1/data/monsters.h"
@@ -56,6 +57,7 @@ public:
 	Game::Encounter _encounters;
 	Game::Combat *_combat = nullptr;
 	Monsters *_monsters = nullptr;
+	BestiaryState _bestiary;
 	Treasure _treasure;
 	byte _delay = 5;
 	int _nonCombatEffectCtr = 0, _combatEffectCtr = 0;
diff --git a/engines/mm/mm1/mm1.cpp b/engines/mm/mm1/mm1.cpp
index efe62566216..f4cdc8d97e5 100644
--- a/engines/mm/mm1/mm1.cpp
+++ b/engines/mm/mm1/mm1.cpp
@@ -160,6 +160,35 @@ bool MM1Engine::canLoadGameStateCurrently(Common::U32String *msg) {
 	return true;
 }
 
+Common::Error MM1Engine::loadGameStream(Common::SeekableReadStream *stream) {
+	Common::Serializer s(stream, nullptr);
+	Common::Error result = synchronizeSave(s);
+
+	if (result.getCode() == Common::kNoError) {
+		if (stream->err())
+			return Common::kReadingFailed;
+
+		// Optional extension appended after the standard savegame data
+		g_globals->_bestiary.load(*stream);
+	}
+
+	return result;
+}
+
+Common::Error MM1Engine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
+	Common::Serializer s(nullptr, stream);
+	Common::Error result = synchronizeSave(s);
+
+	if (result.getCode() == Common::kNoError) {
+		// Keep the core savegame data intact and append the extension
+		g_globals->_bestiary.save(*stream);
+		if (stream->err())
+			return Common::kWritingFailed;
+	}
+
+	return result;
+}
+
 Common::Error MM1Engine::synchronizeSave(Common::Serializer &s) {
 	// Get/set the version
 	byte version = SAVEGAME_VERSION;
diff --git a/engines/mm/mm1/mm1.h b/engines/mm/mm1/mm1.h
index cf55ef0c901..72d1ad6817d 100644
--- a/engines/mm/mm1/mm1.h
+++ b/engines/mm/mm1/mm1.h
@@ -100,18 +100,12 @@ public:
 	/**
 	 * Load a savegame
 	 */
-	Common::Error loadGameStream(Common::SeekableReadStream *stream) override {
-		Common::Serializer s(stream, nullptr);
-		return synchronizeSave(s);
-	}
+	Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
 
 	/**
 	 * Save a savegame
 	 */
-	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
-		Common::Serializer s(nullptr, stream);
-		return synchronizeSave(s);
-	}
+	Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
 };
 
 extern MM1Engine *g_engine;


Commit: 67c86938d5be07b671c64408c3a5db3cdef05268
    https://github.com/scummvm/scummvm/commit/67c86938d5be07b671c64408c3a5db3cdef05268
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:03+11:00

Commit Message:
MM: MM1: Add the enhanced Bestiary window

Thanks to dragoo84

Changed paths:
  A engines/mm/mm1/views_enh/bestiary.cpp
  A engines/mm/mm1/views_enh/bestiary.h
  A engines/mm/mm1/views_enh/bestiary_stats.h
    engines/mm/mm1/metaengine.cpp
    engines/mm/mm1/metaengine.h
    engines/mm/mm1/views_enh/dialogs.h
    engines/mm/module.mk


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index 98d2151e53f..e075155345f 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -75,6 +75,19 @@ static const KeybindingRecord QUEST_LOG_KEYS[] = {
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
 };
 
+static const KeybindingRecord BESTIARY_KEYS[] = {
+	{ KEYBIND_ESCAPE, "ESCAPE", _s("Close bestiary"), "ESCAPE", nullptr },
+	{ KEYBIND_BESTIARY, "BESTIARY_CLOSE", _s("Close bestiary"), "n", nullptr },
+	{ KEYBIND_SELECT, "SELECT", _s("Show monster image"), "RETURN", nullptr },
+	{ KEYBIND_BESTIARY_UP, "BESTIARY_UP", _s("Previous monster"), "UP", nullptr },
+	{ KEYBIND_BESTIARY_DOWN, "BESTIARY_DOWN", _s("Next monster"), "DOWN", nullptr },
+	{ KEYBIND_BESTIARY_PAGEUP, "BESTIARY_PAGEUP", _s("Previous page"), "PAGEUP", nullptr },
+	{ KEYBIND_BESTIARY_PAGEDOWN, "BESTIARY_PAGEDOWN", _s("Next page"), "PAGEDOWN", nullptr },
+	{ KEYBIND_BESTIARY_HOME, "BESTIARY_HOME", _s("First monster"), "HOME", nullptr },
+	{ KEYBIND_BESTIARY_END, "BESTIARY_END", _s("Last monster"), "END", nullptr },
+	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
+};
+
 static const KeybindingRecord COMBAT_KEYS[] = {
 	// I18N: Combat command in Might & Magic 1
 	{ KEYBIND_COMBAT_ATTACK, "COMBAT_ATTACK", _s("Attack"), "a", nullptr },
@@ -152,6 +165,7 @@ static const char *const kPartyKeymapId = "mm1_party";
 static const char *const kNormalKeymapId = "mm1_normal";
 static const char *const kCombatKeymapId = "mm1_combat";
 static const char *const kQuestLogKeymapId = "mm1_quest_log";
+static const char *const kBestiaryKeymapId = "mm1_bestiary";
 static const char *const kCheatsKeymapId = "mm1_cheats";
 
 static Common::Keymap *g_menuKeymap = nullptr;
@@ -160,6 +174,7 @@ static Common::Keymap *g_partyKeymap = nullptr;
 static Common::Keymap *g_normalKeymap = nullptr;
 static Common::Keymap *g_combatKeymap = nullptr;
 static Common::Keymap *g_questLogKeymap = nullptr;
+static Common::Keymap *g_bestiaryKeymap = nullptr;
 static Common::Keymap *g_cheatsKeymap = nullptr;
 
 struct KeysRecord {
@@ -175,6 +190,7 @@ static const KeysRecord ALL_RECORDS[] = {
 	{ kPartyKeymapId,   _s("Might and Magic 1 - Party"), PARTY_KEYS },
 	{ kCombatKeymapId,  _s("Might and Magic 1 - Combat"), COMBAT_KEYS },
 	{ kQuestLogKeymapId, _s("Might and Magic 1 - Quest Log"), QUEST_LOG_KEYS },
+	{ kBestiaryKeymapId, _s("Might and Magic 1 - Bestiary"), BESTIARY_KEYS },
 #if 0
 	{ kCheatsKeymapId,  _s("Might and Magic 1 - Cheats"), CHEAT_KEYS },
 #endif
@@ -191,6 +207,7 @@ static void initKeymapPointers() {
 	g_normalKeymap = mapper->getKeymap(kNormalKeymapId);
 	g_combatKeymap = mapper->getKeymap(kCombatKeymapId);
 	g_questLogKeymap = mapper->getKeymap(kQuestLogKeymapId);
+	g_bestiaryKeymap = mapper->getKeymap(kBestiaryKeymapId);
 	g_cheatsKeymap = mapper->getKeymap(kCheatsKeymapId);
 }
 
@@ -214,11 +231,15 @@ Common::KeymapArray MetaEngine::initKeymaps() {
 				act->addDefaultInputMapping(r->_joy);
 			if (r->_action == KEYBIND_SELECT)
 				act->addDefaultInputMapping("SPACE");
+			if (recPtr->_keys == BESTIARY_KEYS && r->_action == KEYBIND_SELECT)
+				act->addDefaultInputMapping("KP_ENTER");
 
 			if (r->_action == KEYBIND_FORWARDS || r->_action == KEYBIND_BACKWARDS ||
 					r->_action == KEYBIND_TURN_LEFT || r->_action == KEYBIND_TURN_RIGHT ||
-					r->_action == KEYBIND_STRAFE_LEFT || r->_action == KEYBIND_STRAFE_RIGHT)
-				// Allow movement actions to be triggered on keyboard repeats
+					r->_action == KEYBIND_STRAFE_LEFT || r->_action == KEYBIND_STRAFE_RIGHT ||
+					r->_action == KEYBIND_BESTIARY_UP || r->_action == KEYBIND_BESTIARY_DOWN ||
+					r->_action == KEYBIND_BESTIARY_PAGEUP || r->_action == KEYBIND_BESTIARY_PAGEDOWN)
+				// Allow movement and list actions to be triggered on keyboard repeats
 				act->allowKbdRepeats();
 
 			keyMap->addAction(act);
@@ -241,6 +262,8 @@ static bool isKeymapEnabledForMode(const Common::Keymap *keymap, KeybindingMode
 		return mode == KBMODE_COMBAT;
 	if (keymap == g_questLogKeymap)
 		return mode == KBMODE_QUEST_LOG;
+	if (keymap == g_bestiaryKeymap)
+		return mode == KBMODE_BESTIARY;
 	if (keymap == g_cheatsKeymap)
 		return mode == KBMODE_NORMAL;
 
@@ -257,6 +280,7 @@ void MetaEngine::setKeybindingMode(KeybindingMode mode) {
 		g_normalKeymap,
 		g_combatKeymap,
 		g_questLogKeymap,
+		g_bestiaryKeymap,
 		g_cheatsKeymap
 	};
 
diff --git a/engines/mm/mm1/metaengine.h b/engines/mm/mm1/metaengine.h
index d3e32357dbb..3a9832dce1a 100644
--- a/engines/mm/mm1/metaengine.h
+++ b/engines/mm/mm1/metaengine.h
@@ -43,6 +43,10 @@ enum KeybindingAction {
 	KEYBIND_MAP,
 	KEYBIND_ORDER,
 	KEYBIND_PROTECT,
+	KEYBIND_BESTIARY,
+	KEYBIND_BESTIARY_UP, KEYBIND_BESTIARY_DOWN,
+	KEYBIND_BESTIARY_PAGEUP, KEYBIND_BESTIARY_PAGEDOWN,
+	KEYBIND_BESTIARY_HOME, KEYBIND_BESTIARY_END,
 	KEYBIND_REST,
 	KEYBIND_SEARCH,
 	KEYBIND_BASH,
@@ -100,7 +104,8 @@ enum KeybindingMode {
 	KBMODE_PARTY_MENUS, ///< Keys for menus that allow switching party members
 	KBMODE_NORMAL,      ///< Keys available when normal in-game
 	KBMODE_COMBAT,      ///< Keys when in combat mode
-	KBMODE_QUEST_LOG    ///< Keys when viewing the quest log
+	KBMODE_QUEST_LOG,   ///< Keys when viewing the quest log
+	KBMODE_BESTIARY     ///< Keys when viewing the bestiary
 };
 
 class MetaEngine {
diff --git a/engines/mm/mm1/views_enh/bestiary.cpp b/engines/mm/mm1/views_enh/bestiary.cpp
new file mode 100644
index 00000000000..03eeac876bf
--- /dev/null
+++ b/engines/mm/mm1/views_enh/bestiary.cpp
@@ -0,0 +1,382 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mm/mm1/views_enh/bestiary.h"
+#include "mm/mm1/views_enh/bestiary_stats.h"
+#include "mm/mm1/globals.h"
+#include "mm/mm1/metaengine.h"
+
+namespace MM {
+namespace MM1 {
+namespace ViewsEnh {
+
+Bestiary::Bestiary() : ScrollView("Bestiary") {
+	setBounds(Common::Rect(0, 0, 320, 200));
+	addButton(&g_globals->_escSprites, Common::Point(134, 170), 0, KEYBIND_ESCAPE, true);
+	addButton(Common::Rect(8, 168, 76, 183), KEYBIND_BESTIARY_PAGEUP);
+	addButton(Common::Rect(244, 168, 304, 183), KEYBIND_BESTIARY_PAGEDOWN);
+}
+
+void Bestiary::buildOrder() {
+	if (!_monsterIds.empty())
+		return;
+
+	// Insertion sort of the display IDs by monster name
+	for (int id = 1; id <= MONSTERS_COUNT; ++id) {
+		_monsterIds.push_back(id);
+
+		int pos = id - 1;
+		while (pos > 0 && (*g_globals->_monsters)[id]._name.compareToIgnoreCase(
+				(*g_globals->_monsters)[_monsterIds[pos - 1]]._name) < 0) {
+			_monsterIds[pos] = _monsterIds[pos - 1];
+			--pos;
+		}
+
+		_monsterIds[pos] = id;
+	}
+}
+
+bool Bestiary::msgFocus(const FocusMessage &msg) {
+	buildOrder();
+	ScrollView::msgFocus(msg);
+	MetaEngine::setKeybindingMode(KBMODE_BESTIARY);
+	return true;
+}
+
+void Bestiary::draw() {
+	ScrollView::draw();
+	setTextColor(0);
+	setReduced(false);
+	writeLine(0, "Bestiary", ALIGN_MIDDLE);
+
+	setReduced(true);
+	writeString(0, 17, Common::String::format("Discovered: %u/%u",
+		g_globals->_bestiary.count(), (uint)MONSTERS_COUNT), ALIGN_MIDDLE);
+
+	for (int row = 0; row < PAGE_SIZE; ++row) {
+		const int index = _topIndex + row;
+		if (index >= MONSTERS_COUNT)
+			break;
+
+		const int id = _monsterIds[index];
+		if (index == _selectedIndex) {
+			setTextColor(15);
+			writeString(28, 33 + row * 9, ">");
+		}
+
+		setTextColor(g_globals->_bestiary.hasSeen(id) ? 37 : 1);
+		writeString(44, 33 + row * 9, Common::String::format("%03d  %s",
+			index + 1, (*g_globals->_monsters)[id]._name.c_str()));
+	}
+
+	setTextColor(0);
+	writeString(0, 145, Common::String::format("%d-%d of %d",
+		_topIndex + 1, _topIndex + PAGE_SIZE, MONSTERS_COUNT), ALIGN_MIDDLE);
+	writeString(0, 155, "Up/Down: select  Enter: details", ALIGN_MIDDLE);
+
+	setTextColor(_topIndex > 0 ? 37 : 1);
+	writeString(8, 172, "Previous");
+	setTextColor(_topIndex < MONSTERS_COUNT - PAGE_SIZE ? 37 : 1);
+	writeString(262, 172, "Next");
+
+	setTextColor(0);
+	writeString(152, 172, STRING["enhdialogs.misc.go_back"]);
+}
+
+void Bestiary::select(int index) {
+	_selectedIndex = CLIP(index, 0, MONSTERS_COUNT - 1);
+
+	if (_selectedIndex < _topIndex)
+		_topIndex = _selectedIndex;
+	else if (_selectedIndex >= _topIndex + PAGE_SIZE)
+		_topIndex = _selectedIndex - PAGE_SIZE + 1;
+
+	_topIndex = CLIP(_topIndex, 0, MONSTERS_COUNT - PAGE_SIZE);
+	redraw();
+}
+
+void Bestiary::scroll(int delta) {
+	const int oldTop = _topIndex;
+	_topIndex = CLIP(_topIndex + delta, 0, MONSTERS_COUNT - PAGE_SIZE);
+	_selectedIndex = CLIP(_selectedIndex + _topIndex - oldTop,
+		_topIndex, _topIndex + PAGE_SIZE - 1);
+	redraw();
+}
+
+void Bestiary::openImage() {
+	g_events->send("BestiaryImage", GameMessage("MONSTER", _monsterIds[_selectedIndex]));
+	addView("BestiaryImage");
+}
+
+bool Bestiary::msgAction(const ActionMessage &msg) {
+	switch (msg._action) {
+	case KEYBIND_ESCAPE:
+	case KEYBIND_BESTIARY:
+		close();
+		break;
+	case KEYBIND_SELECT:
+		openImage();
+		break;
+	case KEYBIND_BESTIARY_UP:
+		select(_selectedIndex - 1);
+		break;
+	case KEYBIND_BESTIARY_DOWN:
+		select(_selectedIndex + 1);
+		break;
+	case KEYBIND_BESTIARY_PAGEUP:
+		scroll(-PAGE_SIZE);
+		break;
+	case KEYBIND_BESTIARY_PAGEDOWN:
+		scroll(PAGE_SIZE);
+		break;
+	case KEYBIND_BESTIARY_HOME:
+		select(0);
+		break;
+	case KEYBIND_BESTIARY_END:
+		select(MONSTERS_COUNT - 1);
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+bool Bestiary::msgKeypress(const KeypressMessage &msg) {
+	// Some keyboard paths report the navigation-labelled numpad keys as
+	// KP#. Numeric keypad input is preserved while Num Lock is on
+	if (!(msg.flags & Common::KBD_NUM)) {
+		switch (msg.keycode) {
+		case Common::KEYCODE_KP9:
+			scroll(-PAGE_SIZE);
+			return true;
+		case Common::KEYCODE_KP3:
+			scroll(PAGE_SIZE);
+			return true;
+		case Common::KEYCODE_KP8:
+			select(_selectedIndex - 1);
+			return true;
+		case Common::KEYCODE_KP2:
+			select(_selectedIndex + 1);
+			return true;
+		case Common::KEYCODE_KP7:
+			select(0);
+			return true;
+		case Common::KEYCODE_KP1:
+			select(MONSTERS_COUNT - 1);
+			return true;
+		default:
+			break;
+		}
+	}
+
+	switch (msg.keycode) {
+	case Common::KEYCODE_UP:
+		select(_selectedIndex - 1);
+		break;
+	case Common::KEYCODE_DOWN:
+		select(_selectedIndex + 1);
+		break;
+	case Common::KEYCODE_PAGEUP:
+		scroll(-PAGE_SIZE);
+		break;
+	case Common::KEYCODE_PAGEDOWN:
+		scroll(PAGE_SIZE);
+		break;
+	case Common::KEYCODE_HOME:
+		select(0);
+		break;
+	case Common::KEYCODE_END:
+		select(MONSTERS_COUNT - 1);
+		break;
+	case Common::KEYCODE_RETURN:
+	case Common::KEYCODE_KP_ENTER:
+		openImage();
+		break;
+	case Common::KEYCODE_n:
+	case Common::KEYCODE_ESCAPE:
+		close();
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
+bool Bestiary::msgMouseUp(const MouseUpMessage &msg) {
+	if (ScrollView::msgMouseUp(msg))
+		return true;
+
+	const int y = msg._pos.y - _innerBounds.top;
+	if (msg._button == MouseMessage::MB_LEFT && _innerBounds.contains(msg._pos) &&
+			y >= 33 && y < 33 + PAGE_SIZE * 9) {
+		select(_topIndex + (y - 33) / 9);
+		return true;
+	}
+
+	return false;
+}
+
+BestiaryImage::BestiaryImage() : ScrollView("BestiaryImage") {
+	setBounds(Common::Rect(0, 0, 320, 200));
+	addButton(&g_globals->_escSprites, Common::Point(134, 170), 0, KEYBIND_ESCAPE, true);
+	addButton(Common::Rect(148, 168, 220, 183), KEYBIND_ESCAPE);
+	addButton(Common::Rect(8, 168, 76, 183), KEYBIND_BESTIARY_PAGEUP);
+	addButton(Common::Rect(240, 168, 304, 183), KEYBIND_BESTIARY_PAGEDOWN);
+}
+
+bool BestiaryImage::msgGame(const GameMessage &msg) {
+	if (msg._name != "MONSTER" || msg._value < 1 || msg._value > MONSTERS_COUNT)
+		return false;
+
+	_monsterId = msg._value;
+	_statsPage = 0;
+
+	const Monster &monster = (*g_globals->_monsters)[_monsterId];
+	_image = g_globals->_monsters->getMonsterImage(monster._imgNum);
+	return true;
+}
+
+bool BestiaryImage::msgFocus(const FocusMessage &msg) {
+	ScrollView::msgFocus(msg);
+	MetaEngine::setKeybindingMode(KBMODE_BESTIARY);
+	return true;
+}
+
+void BestiaryImage::changePage(int delta) {
+	_statsPage = g_globals->_bestiary.hasSeen(_monsterId) ?
+		CLIP(_statsPage + delta, 0, 1) : 0;
+	redraw();
+}
+
+void BestiaryImage::draw() {
+	ScrollView::draw();
+	setReduced(false);
+	setTextColor(0);
+
+	const Monster &monster = (*g_globals->_monsters)[_monsterId];
+	writeLine(0, monster._name, ALIGN_MIDDLE);
+
+	setReduced(true);
+	const bool seen = g_globals->_bestiary.hasSeen(_monsterId);
+	setTextColor(seen ? 37 : 1);
+	writeString(0, 16, !seen ? "Undiscovered" :
+		(_statsPage == 0 ? "Discovered - Stats & specials (1/2)" :
+			"Discovered - Defenses & specials (2/2)"), ALIGN_MIDDLE);
+
+	// Keep the original 104x96 sprite centered between two 94-pixel columns
+	getSurface().blitFrom(_image, Common::Point((_bounds.width() - _image.w) / 2, 48));
+
+	if (!seen) {
+		writeString(0, 151, "Stats unlock after an encounter.", ALIGN_MIDDLE);
+	} else {
+		setTextColor(0);
+
+		for (int col = 0; col < 2; ++col) {
+			const int x = col ? 210 : 0;
+			int y = 32;
+			const Common::Array<Common::String> lines =
+				bestiaryColumn(monster, seen, col != 0, _statsPage);
+
+			for (uint i = 0; i < lines.size(); ++i) {
+				const Common::StringArray wrapped = splitLines(lines[i], 94);
+				for (uint j = 0; j < wrapped.size(); ++j) {
+					writeString(x, y, wrapped[j]);
+					y += 9;
+				}
+			}
+		}
+
+		writeString(104, 142, "HP: base value");
+		writeString(104, 151, "MR: rating");
+	}
+
+	setTextColor(seen && _statsPage > 0 ? 37 : 1);
+	writeString(8, 172, "Stats");
+	setTextColor(seen && _statsPage == 0 ? 37 : 1);
+	writeString(244, 172, "Defenses");
+	setTextColor(0);
+	writeString(152, 172, "Back to list");
+}
+
+bool BestiaryImage::msgAction(const ActionMessage &msg) {
+	switch (msg._action) {
+	case KEYBIND_ESCAPE:
+	case KEYBIND_SELECT:
+	case KEYBIND_BESTIARY:
+		close();
+		break;
+	case KEYBIND_BESTIARY_PAGEUP:
+	case KEYBIND_BESTIARY_HOME:
+		changePage(-1);
+		break;
+	case KEYBIND_BESTIARY_PAGEDOWN:
+	case KEYBIND_BESTIARY_END:
+		changePage(1);
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+bool BestiaryImage::msgKeypress(const KeypressMessage &msg) {
+	if (!(msg.flags & Common::KBD_NUM)) {
+		if (msg.keycode == Common::KEYCODE_KP9) {
+			changePage(-1);
+			return true;
+		}
+
+		if (msg.keycode == Common::KEYCODE_KP3) {
+			changePage(1);
+			return true;
+		}
+	}
+
+	switch (msg.keycode) {
+	case Common::KEYCODE_LEFT:
+	case Common::KEYCODE_PAGEUP:
+	case Common::KEYCODE_HOME:
+		changePage(-1);
+		break;
+	case Common::KEYCODE_RIGHT:
+	case Common::KEYCODE_PAGEDOWN:
+	case Common::KEYCODE_END:
+		changePage(1);
+		break;
+	case Common::KEYCODE_ESCAPE:
+	case Common::KEYCODE_RETURN:
+	case Common::KEYCODE_KP_ENTER:
+	case Common::KEYCODE_n:
+		close();
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
+} // namespace ViewsEnh
+} // namespace MM1
+} // namespace MM
diff --git a/engines/mm/mm1/views_enh/bestiary.h b/engines/mm/mm1/views_enh/bestiary.h
new file mode 100644
index 00000000000..b0b1ffec239
--- /dev/null
+++ b/engines/mm/mm1/views_enh/bestiary.h
@@ -0,0 +1,104 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MM1_VIEWS_ENH_BESTIARY_H
+#define MM1_VIEWS_ENH_BESTIARY_H
+
+#include "common/array.h"
+#include "mm/mm1/views_enh/scroll_view.h"
+
+namespace MM {
+namespace MM1 {
+namespace ViewsEnh {
+
+/**
+ * Lists every monster in the game alphabetically. Monsters the party
+ * hasn't encountered yet are greyed out
+ */
+class Bestiary : public ScrollView {
+private:
+	static const int PAGE_SIZE = 12;
+	int _topIndex = 0;
+	int _selectedIndex = 0;
+	Common::Array<int> _monsterIds;
+
+	/**
+	 * Builds the alphabetical list. Only the display order is sorted, so
+	 * discovery bits and image records keep their original IDs
+	 */
+	void buildOrder();
+
+	/**
+	 * Selects a list entry, scrolling it into view
+	 */
+	void select(int index);
+
+	/**
+	 * Scrolls the list by a number of entries
+	 */
+	void scroll(int delta);
+
+	/**
+	 * Opens the detail view for the selected monster
+	 */
+	void openImage();
+
+public:
+	Bestiary();
+	~Bestiary() override {}
+
+	bool msgFocus(const FocusMessage &msg) override;
+	void draw() override;
+	bool msgAction(const ActionMessage &msg) override;
+	bool msgKeypress(const KeypressMessage &msg) override;
+	bool msgMouseUp(const MouseUpMessage &msg) override;
+};
+
+/**
+ * Shows a monster's picture and, once it's been encountered, its statistics
+ */
+class BestiaryImage : public ScrollView {
+private:
+	int _monsterId = 1;
+	int _statsPage = 0;
+	Graphics::ManagedSurface _image;
+
+	/**
+	 * Switches between the stats and defenses pages
+	 */
+	void changePage(int delta);
+
+public:
+	BestiaryImage();
+	~BestiaryImage() override {}
+
+	bool msgGame(const GameMessage &msg) override;
+	bool msgFocus(const FocusMessage &msg) override;
+	void draw() override;
+	bool msgAction(const ActionMessage &msg) override;
+	bool msgKeypress(const KeypressMessage &msg) override;
+};
+
+} // namespace ViewsEnh
+} // namespace MM1
+} // namespace MM
+
+#endif
diff --git a/engines/mm/mm1/views_enh/bestiary_stats.h b/engines/mm/mm1/views_enh/bestiary_stats.h
new file mode 100644
index 00000000000..ca10055e705
--- /dev/null
+++ b/engines/mm/mm1/views_enh/bestiary_stats.h
@@ -0,0 +1,222 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MM1_VIEWS_ENH_BESTIARY_STATS_H
+#define MM1_VIEWS_ENH_BESTIARY_STATS_H
+
+#include "common/array.h"
+#include "mm/mm1/data/monsters.h"
+
+namespace MM {
+namespace MM1 {
+namespace ViewsEnh {
+
+/**
+ * Returns the stat lines for a monster's database record, not a live combat
+ * instance. Monsters that haven't been seen yield no lines
+ */
+inline Common::Array<Common::String> makeBestiaryStats(const Monster &m, bool seen, int page) {
+	Common::Array<Common::String> lines;
+	if (!seen)
+		return lines;
+
+	if (page == 0) {
+		lines.push_back(Common::String::format("Base HP: %u", (uint)m._defaultHP));
+		lines.push_back(Common::String::format("Armor class: %u", (uint)m._defaultAC));
+		lines.push_back(Common::String::format("Speed: %u", (uint)m._speed));
+		lines.push_back(Common::String::format("Attacks: %u", (uint)m._numberOfAttacks));
+		lines.push_back(Common::String::format("Max damage/hit: %u", (uint)m._maxDamage));
+		lines.push_back(Common::String::format("Experience: %u", (uint)m._experience));
+
+		// Combat compares this rating with a roll depending on caster level
+		lines.push_back(Common::String::format("Magic resist rating: %u",
+			(uint)(m._resistUndead & MAGIC_RESISTANCE)));
+		lines.push_back(Common::String::format("Undead: %s",
+			(m._resistUndead & IS_UNDEAD) ? "Yes" : "No"));
+		lines.push_back(Common::String::format("Regenerates: %s",
+			(m._counterFlags & COUNTER_REGENERATE) ? "Yes" : "No"));
+		return lines;
+	}
+
+	static const char *const LABELS[8] = {
+		"Fire resistance", "Cold resistance", "Electricity resistance",
+		"Energy resistance", "Sleep immunity", "Fear immunity",
+		"Paralysis immunity", "Normal weapon immunity"
+	};
+	static const byte FLAGS[8] = {
+		MONRES_FIRE, MONRES_COLD, MONRES_ELECTRICITY, MONRES_ENERGY,
+		MONRES_ASLEEP, MONRES_FEAR, MONRES_PARALYSIS, MONRES_PHYSICAL_ATTACK
+	};
+	for (int i = 0; i < 8; ++i) {
+		lines.push_back(Common::String::format("%s: %s", LABELS[i],
+			(m._resistances & FLAGS[i]) ? "Yes" : "No"));
+	}
+
+	// Same 1-based order as SpellsMonsters::SPELLS
+	static const char *const SPELLS[33] = {
+		"None", "Curse", "Energy blast", "Fire breath", "Blindness",
+		"Poison spray", "Acid spray", "Sleep", "Paralyze", "Dispel",
+		"Lightning bolt", "Strange gas", "Explosion", "Fireball",
+		"Fire breath", "Gaze", "Acid arrow", "Elements", "Cold beam",
+		"Dancing sword", "Magic drain", "Finger of death", "Sun ray",
+		"Disintegration", "Commands energy", "Poison", "Lightning",
+		"Frost", "Spikes", "Acid", "Fire", "Energy", "Swarm"
+	};
+	if (m._specialAbility & HAS_RANGED_ATTACK) {
+		lines.push_back(Common::String::format("Special: Ranged (max damage %u)",
+			(uint)(m._specialAbility & ATTACK_VALUE)));
+	} else {
+		lines.push_back(Common::String::format("Special: %s",
+			m._specialAbility < 33 ? SPELLS[m._specialAbility] : "Unknown"));
+	}
+	lines.push_back(Common::String::format("Special uses/battle: %u",
+		(uint)(m._counterFlags & COUNTER_BITS)));
+
+	// MonsterTouch dispatches byte value N to ACTIONS[N - 1]
+	static const char *const TOUCHES[26] = {
+		"None", "Takes food", "Disease", "Paralysis", "Poison",
+		"Steals gems", "Reduces endurance", "Sleep", "Paralysis", "Disease",
+		"Steals gold", "Steals an item", "Poison", "Blindness",
+		"Drains 1 level", "Petrification", "Aging", "Drains 2 levels",
+		"Death", "Unconsciousness", "Drains might", "Drains attributes",
+		"Steals backpack", "Steals gold and gems", "Eradication",
+		"Drains spell points"
+	};
+	const char *touch = (m._bonusOnTouch & 0x80) ? "None" :
+		(m._bonusOnTouch < 26 ? TOUCHES[m._bonusOnTouch] : "Unknown");
+	lines.push_back(Common::String::format("Touch: %s", touch));
+	lines.push_back(Common::String::format("Advances into melee: %s",
+		(m._counterFlags & COUNTER_ADVANCES) ? "Yes" : "No"));
+	return lines;
+}
+
+/**
+ * Returns the effect a monster spell inflicts, from the actual spell
+ * handlers. Poison breath is damage, unlike poison spray, and Commands
+ * Energy also deals damage rather than setting a status
+ */
+inline const char *bestiarySpellStatus(byte spell) {
+	switch (spell) {
+	case 1:
+		return "Curse";
+	case 4:
+		return "Blindness";
+	case 5:
+		return "Poison";
+	case 7:
+		return "Sleep";
+	case 8:
+		return "Paralysis";
+	case 11:
+	case 15:
+		return "Petrification";
+	case 21:
+		return "Death";
+	case 23:
+		return "Eradication";
+	default:
+		return "None";
+	}
+}
+
+/**
+ * Returns the text lines for one of the two columns beside a monster's
+ * picture. The left column holds stats or defenses depending on the page,
+ * and the right column holds the special abilities
+ */
+inline Common::Array<Common::String> bestiaryColumn(const Monster &m, bool seen, bool specials, int page) {
+	Common::Array<Common::String> out;
+	if (!seen)
+		return out;
+
+	const Common::Array<Common::String> stats = makeBestiaryStats(m, true, 0);
+	const Common::Array<Common::String> abilities = makeBestiaryStats(m, true, 1);
+
+	if (specials) {
+		if (m._specialAbility & HAS_RANGED_ATTACK) {
+			out.push_back("Ranged attack");
+			out.push_back(Common::String::format("Max damage: %u",
+				(uint)(m._specialAbility & ATTACK_VALUE)));
+		} else {
+			out.push_back("Ability:");
+			out.push_back(abilities[8].substr(9));
+
+			const char *status = bestiarySpellStatus(m._specialAbility);
+			if (Common::String(status) != "None") {
+				out.push_back("Inflicts:");
+				out.push_back(status);
+			}
+
+			if (m._specialAbility == 9)
+				out.push_back("Dispels buffs");
+
+			if (m._specialAbility == 20) {
+				out.push_back("Drains SP");
+				out.push_back("Dispels buffs");
+			}
+		}
+
+		out.push_back(Common::String::format("Uses/battle: %u",
+			(uint)(m._counterFlags & COUNTER_BITS)));
+		out.push_back(abilities[10]);
+		out.push_back(Common::String::format("Regen: %s",
+			(m._counterFlags & COUNTER_REGENERATE) ? "Yes" : "No"));
+		out.push_back(stats[7]);
+		out.push_back(Common::String::format("Advances: %s",
+			(m._counterFlags & COUNTER_ADVANCES) ? "Yes" : "No"));
+
+	} else if (page == 0) {
+		for (uint i = 0; i < 4; ++i)
+			out.push_back(stats[i]);
+
+		out.push_back(Common::String::format("Max dmg/hit: %u", (uint)m._maxDamage));
+		out.push_back(Common::String::format("XP: %u", (uint)m._experience));
+		out.push_back(Common::String::format("Magic resist: %u",
+			(uint)(m._resistUndead & MAGIC_RESISTANCE)));
+
+	} else {
+		static const char *const LABELS[8] = {
+			"Fire", "Cold", "Electricity", "Energy",
+			"Sleep", "Fear", "Paralysis", "Normal weapons"
+		};
+		static const byte FLAGS[8] = {
+			MONRES_FIRE, MONRES_COLD, MONRES_ELECTRICITY, MONRES_ENERGY,
+			MONRES_ASLEEP, MONRES_FEAR, MONRES_PARALYSIS, MONRES_PHYSICAL_ATTACK
+		};
+
+		out.push_back("Resists damage:");
+		for (int i = 0; i < 8; ++i) {
+			if (i == 4)
+				out.push_back("Immune to:");
+
+			out.push_back(Common::String::format("%s: %s", LABELS[i],
+				(m._resistances & FLAGS[i]) ? "Yes" : "No"));
+		}
+	}
+
+	return out;
+}
+
+} // namespace ViewsEnh
+} // namespace MM1
+} // namespace MM
+
+#endif
diff --git a/engines/mm/mm1/views_enh/dialogs.h b/engines/mm/mm1/views_enh/dialogs.h
index 39675502000..dc292fe85ae 100644
--- a/engines/mm/mm1/views_enh/dialogs.h
+++ b/engines/mm/mm1/views_enh/dialogs.h
@@ -40,6 +40,7 @@
 #include "mm/mm1/views_enh/main_menu.h"
 #include "mm/mm1/views_enh/map_popup.h"
 #include "mm/mm1/views_enh/active_quest.h"
+#include "mm/mm1/views_enh/bestiary.h"
 #include "mm/mm1/views_enh/protect.h"
 #include "mm/mm1/views_enh/quick_ref.h"
 #include "mm/mm1/views_enh/rest.h"
@@ -168,6 +169,8 @@ private:
 	ViewsEnh::MainMenu _mainMenu;
 	ViewsEnh::MapPopup _mapPopup;
 	ViewsEnh::ActiveQuest _activeQuest;
+	ViewsEnh::Bestiary _bestiary;
+	ViewsEnh::BestiaryImage _bestiaryImage;
 	ViewsEnh::Protect _protect;
 	ViewsEnh::QuickRef _quickRef;
 	ViewsEnh::Rest _rest;
diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index 615627237a1..1962bcda0d7 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -139,6 +139,7 @@ MODULE_OBJS += \
 	mm1/views/unlock.o \
 	mm1/views/wheel_spin.o \
 	mm1/views_enh/active_quest.o \
+	mm1/views_enh/bestiary.o \
 	mm1/views_enh/spells/cast_spell.o \
 	mm1/views_enh/spells/spellbook.o \
 	mm1/views_enh/spells/detect_magic.o \


Commit: f1cbc37dd5100657a5a9e93af044d2ea067064f0
    https://github.com/scummvm/scummvm/commit/f1cbc37dd5100657a5a9e93af044d2ea067064f0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:03+11:00

Commit Message:
MM: MM1: Open the Bestiary from the exploration screen with N

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/metaengine.cpp
    engines/mm/mm1/views_enh/game.cpp


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index e075155345f..48ff1145f1a 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -138,6 +138,7 @@ static const KeybindingRecord NORMAL_KEYS[] = {
 	{ KEYBIND_ORDER, "ORDER", _s("Reorder party"), "o", nullptr },
 	// I18N: Action of hero party in Might & Magic 1
 	{ KEYBIND_PROTECT, "PROTECT", _s("Protect"), "p", nullptr },
+	{ KEYBIND_BESTIARY, "BESTIARY", _s("Bestiary"), "n", nullptr },
 	// I18N: Action of hero party in Might & Magic 1
 	{ KEYBIND_REST, "REST", _s("Rest"), "r", nullptr },
 	// I18N: Action of hero party in Might & Magic 1
diff --git a/engines/mm/mm1/views_enh/game.cpp b/engines/mm/mm1/views_enh/game.cpp
index 2b9042260c1..7786f48d825 100644
--- a/engines/mm/mm1/views_enh/game.cpp
+++ b/engines/mm/mm1/views_enh/game.cpp
@@ -84,6 +84,9 @@ bool Game::msgAction(const ActionMessage &msg) {
 	case KEYBIND_MENU:
 		g_engine->openMainMenuDialog();
 		return true;
+	case KEYBIND_BESTIARY:
+		addView("Bestiary");
+		return true;
 	case KEYBIND_PROTECT:
 		addView("Protect");
 		return true;


Commit: 5864bb85a94afc96011a200ec4960242c1265f7a
    https://github.com/scummvm/scummvm/commit/5864bb85a94afc96011a200ec4960242c1265f7a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:04+11:00

Commit Message:
MM: MM1: Add a skull button to the game frame that opens the Bestiary

Thanks to dragoo84

Changed paths:
  A engines/mm/mm1/views_enh/game_frame.h
    engines/mm/mm1/globals.cpp
    engines/mm/mm1/views_enh/game.cpp
    engines/mm/mm1/views_enh/game.h


diff --git a/engines/mm/mm1/globals.cpp b/engines/mm/mm1/globals.cpp
index 7602c112fa3..dfbaca941fd 100644
--- a/engines/mm/mm1/globals.cpp
+++ b/engines/mm/mm1/globals.cpp
@@ -24,6 +24,7 @@
 #include "graphics/fonts/ttf.h"
 #include "image/bmp.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/views_enh/game_frame.h"
 #include "mm/mm1/mm1.h"
 #include "mm/shared/utils/strings.h"
 #include "graphics/fontman.h"
@@ -99,6 +100,8 @@ bool Globals::load(bool isEnhanced) {
 			}
 		}
 
+		ViewsEnh::decorateGameFrame(_gameBackground);
+
 		_symbols.load();
 
 		{
diff --git a/engines/mm/mm1/views_enh/game.cpp b/engines/mm/mm1/views_enh/game.cpp
index 7786f48d825..042fc69388a 100644
--- a/engines/mm/mm1/views_enh/game.cpp
+++ b/engines/mm/mm1/views_enh/game.cpp
@@ -21,6 +21,7 @@
 
 #include "common/file.h"
 #include "mm/mm1/views_enh/game.h"
+#include "mm/mm1/views_enh/game_frame.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/metaengine.h"
 #include "mm/mm1/mm1.h"
@@ -42,6 +43,7 @@ bool Game::msgFocus(const FocusMessage &msg) {
 }
 
 bool Game::msgUnfocus(const UnfocusMessage &msg) {
+	_bestiaryPressed = false;
 	MetaEngine::setKeybindingMode(KeybindingMode::KBMODE_MENUS);
 	return true;
 }
@@ -53,6 +55,28 @@ void Game::draw() {
 	UIElement::draw();
 }
 
+bool Game::msgMouseDown(const MouseDownMessage &msg) {
+	if (isFocused() && msg._button == MouseMessage::MB_LEFT &&
+			bestiaryButtonBounds().contains(msg._pos)) {
+		_bestiaryPressed = true;
+		return true;
+	}
+
+	return TextView::msgMouseDown(msg);
+}
+
+bool Game::msgMouseUp(const MouseUpMessage &msg) {
+	if (msg._button == MouseMessage::MB_LEFT && _bestiaryPressed) {
+		_bestiaryPressed = false;
+		if (isFocused() && bestiaryButtonBounds().contains(msg._pos))
+			addView("Bestiary");
+
+		return true;
+	}
+
+	return TextView::msgMouseUp(msg);
+}
+
 bool Game::msgKeypress(const KeypressMessage &msg) {
 	switch (msg.keycode) {
 	case Common::KEYCODE_F5:
diff --git a/engines/mm/mm1/views_enh/game.h b/engines/mm/mm1/views_enh/game.h
index 7a6c4a69a4b..c3dd447ce16 100644
--- a/engines/mm/mm1/views_enh/game.h
+++ b/engines/mm/mm1/views_enh/game.h
@@ -35,6 +35,7 @@ namespace ViewsEnh {
 
 class Game : public Views::TextView {
 private:
+	bool _bestiaryPressed = false;
 	ViewsEnh::GameView _view;
 	GameCommands _commands;
 	GameParty _party;
@@ -45,6 +46,8 @@ public:
 	bool msgFocus(const FocusMessage &msg) override;
 	bool msgUnfocus(const UnfocusMessage &msg) override;
 	void draw() override;
+	bool msgMouseDown(const MouseDownMessage &msg) override;
+	bool msgMouseUp(const MouseUpMessage &msg) override;
 	bool msgKeypress(const KeypressMessage &msg) override;
 	bool msgAction(const ActionMessage &msg) override;
 	bool msgGame(const GameMessage &msg) override;
diff --git a/engines/mm/mm1/views_enh/game_frame.h b/engines/mm/mm1/views_enh/game_frame.h
new file mode 100644
index 00000000000..01d1bccf6fe
--- /dev/null
+++ b/engines/mm/mm1/views_enh/game_frame.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MM1_VIEWS_ENH_GAME_FRAME_H
+#define MM1_VIEWS_ENH_GAME_FRAME_H
+
+#include "common/rect.h"
+#include "graphics/managed_surface.h"
+
+namespace MM {
+namespace MM1 {
+namespace ViewsEnh {
+
+/**
+ * Area of the game frame's lower-right jewel, used as the Bestiary button
+ */
+inline Common::Rect bestiaryButtonBounds() {
+	// The original jewel, excluding the sidebar: 16 x 16 pixels
+	return Common::Rect(216, 131, 232, 147);
+}
+
+/**
+ * Adjusts the game background for MM1. The background comes from Xeen
+ * resources, so some parts need replacing, and a skull is drawn on the
+ * jewel to mark the Bestiary button
+ */
+inline void decorateGameFrame(Graphics::ManagedSurface &s) {
+	if (s.w != 320 || s.h != 200 || s.format.bytesPerPixel != 1)
+		return;
+
+	// Xeen filled these background slots with clairvoyance/levitation
+	// sprites. MM1 has no such overlays, so mirror the intact stone
+	// strip from the right instead
+	for (int y = 32; y < 120; ++y) {
+		if (y >= 55 && y < 82)
+			continue;
+
+		for (int x = 0; x < 8; ++x)
+			*(byte *)s.getBasePtr(x, y) = *(const byte *)s.getBasePtr(231 - x, y);
+	}
+
+	// Replace only the jewel's inset and keep the existing gold frame,
+	// sampling a light stone shade from the background's own palette
+	const byte stone = *(const byte *)s.getBasePtr(224, 131);
+	for (int y = 134; y < 145; ++y) {
+		for (int x = 219; x < 230; ++x)
+			*(byte *)s.getBasePtr(x, y) = stone;
+	}
+
+	static const char *const SKULL[9] = {
+		"..#####..",
+		".#######.",
+		"#########",
+		"#########",
+		"#..###..#",
+		"#..###..#",
+		".###.###.",
+		"..#####..",
+		"..#.#.#.."
+	};
+	for (int y = 0; y < 9; ++y) {
+		for (int x = 0; x < 9; ++x) {
+			if (SKULL[y][x] == '#')
+				*(byte *)s.getBasePtr(220 + x, 135 + y) = 0;
+		}
+	}
+}
+
+} // namespace ViewsEnh
+} // namespace MM1
+} // namespace MM
+
+#endif


Commit: 8a3e2bfeac6540aa7bc36d449be55895c961faf0
    https://github.com/scummvm/scummvm/commit/8a3e2bfeac6540aa7bc36d449be55895c961faf0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:04+11:00

Commit Message:
MM: MM1: Open the Bestiary from combat with N

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/metaengine.cpp
    engines/mm/mm1/views_enh/combat.cpp


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index 48ff1145f1a..5ab16e026f9 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -117,6 +117,7 @@ static const KeybindingRecord COMBAT_KEYS[] = {
 	{ KEYBIND_DELAY, "DELAY", _s("Delay"), "d", nullptr },
 	// I18N: Combat command in Might & Magic 1
 	{ KEYBIND_PROTECT, "PROTECT", _s("Protect"), "p", nullptr },
+	{ KEYBIND_BESTIARY, "BESTIARY", _s("Bestiary"), "n", nullptr },
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }
 };
 
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index ab3a7f20cbc..d76ef5695c0 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -498,6 +498,9 @@ bool Combat::msgAction(const ActionMessage &msg) {
 	case KEYBIND_COMBAT_FIGHT:
 		fight();
 		break;
+	case KEYBIND_BESTIARY:
+		addView("Bestiary");
+		break;
 	case KEYBIND_PROTECT:
 		addView("Protect");
 		break;


Commit: a7f23d6f00abfb4c20e1885f877944ccdf64cb16
    https://github.com/scummvm/scummvm/commit/a7f23d6f00abfb4c20e1885f877944ccdf64cb16
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:04+11:00

Commit Message:
MM: MM1: Call removeCharge through a pointer to the chosen inventory

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/game/use_item.cpp


diff --git a/engines/mm/mm1/game/use_item.cpp b/engines/mm/mm1/game/use_item.cpp
index 77f5c31aa82..5dd73eff4b3 100644
--- a/engines/mm/mm1/game/use_item.cpp
+++ b/engines/mm/mm1/game/use_item.cpp
@@ -54,8 +54,8 @@ Common::String UseItem::combatUseItem(Inventory &inv, Inventory::Entry &invEntry
 				applyItemBonus(item->_tempBonus_id, item->_tempBonus_value);
 
 				if (g_globals->_combatEffectCtr)
-					(isEquipped ? g_globals->_currCharacter->_equipped :
-						g_globals->_currCharacter->_backpack).removeCharge(&invEntry);
+					(isEquipped ? &g_globals->_currCharacter->_equipped :
+						&g_globals->_currCharacter->_backpack)->removeCharge(&invEntry);
 
 				g_globals->_party.updateAC();
 				msg = STRING["dialogs.character.use_combat.done"];
@@ -104,8 +104,8 @@ Common::String UseItem::nonCombatUseItem(Inventory &inv, Inventory::Entry &invEn
 				applyItemBonus (item->_tempBonus_id, item->_tempBonus_value);
 
 				if (g_globals->_nonCombatEffectCtr)
-					(isEquipped ? g_globals->_currCharacter->_equipped :
-						g_globals->_currCharacter->_backpack).removeCharge(&invEntry);
+					(isEquipped ? &g_globals->_currCharacter->_equipped :
+						&g_globals->_currCharacter->_backpack)->removeCharge(&invEntry);
 
 				g_globals->_party.updateAC();
 				msg = STRING["spells.done"];


Commit: 60f27065f4d67e473bc7472803fd64a5775f15d9
    https://github.com/scummvm/scummvm/commit/60f27065f4d67e473bc7472803fd64a5775f15d9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:05+11:00

Commit Message:
MM: MM1: Tidy comments, braces and whitespace

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/data/character.cpp
    engines/mm/mm1/data/character.h
    engines/mm/mm1/data/monsters.h
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/game/combat.h
    engines/mm/mm1/game/encounter.cpp
    engines/mm/mm1/game/spell_casting.cpp
    engines/mm/mm1/game/spells_monsters.cpp
    engines/mm/mm1/game/spells_party.cpp
    engines/mm/mm1/game/wheel_spin.cpp
    engines/mm/mm1/globals.h
    engines/mm/mm1/maps/map.cpp
    engines/mm/mm1/maps/map01.cpp
    engines/mm/mm1/maps/map06.cpp
    engines/mm/mm1/maps/map09.cpp
    engines/mm/mm1/maps/map10.cpp
    engines/mm/mm1/maps/map11.cpp
    engines/mm/mm1/maps/map15.cpp
    engines/mm/mm1/maps/map17.cpp
    engines/mm/mm1/maps/map21.cpp
    engines/mm/mm1/maps/map24.cpp
    engines/mm/mm1/maps/map25.cpp
    engines/mm/mm1/maps/map29.cpp
    engines/mm/mm1/maps/map29.h
    engines/mm/mm1/maps/map30.cpp
    engines/mm/mm1/maps/map31.cpp
    engines/mm/mm1/maps/map33.cpp
    engines/mm/mm1/maps/map35.cpp
    engines/mm/mm1/maps/map36.cpp
    engines/mm/mm1/maps/map37.cpp
    engines/mm/mm1/maps/map38.cpp
    engines/mm/mm1/maps/map40.cpp
    engines/mm/mm1/maps/map43.cpp
    engines/mm/mm1/maps/map47.cpp
    engines/mm/mm1/maps/map51.cpp
    engines/mm/mm1/maps/maps.cpp
    engines/mm/mm1/maps/maps.h
    engines/mm/mm1/metaengine.cpp
    engines/mm/mm1/metaengine.h
    engines/mm/mm1/mm1.cpp
    engines/mm/mm1/views/combat.cpp
    engines/mm/mm1/views/interactions/giant.cpp
    engines/mm/mm1/views_enh/active_quest.cpp
    engines/mm/mm1/views_enh/character_info.cpp
    engines/mm/mm1/views_enh/character_inventory.cpp
    engines/mm/mm1/views_enh/combat.cpp
    engines/mm/mm1/views_enh/encounter.cpp
    engines/mm/mm1/views_enh/game.cpp
    engines/mm/mm1/views_enh/game_commands.cpp
    engines/mm/mm1/views_enh/interactions/giant.cpp
    engines/mm/mm1/views_enh/interactions/hacker.cpp
    engines/mm/mm1/views_enh/items_view.cpp
    engines/mm/mm1/views_enh/locations/training.cpp
    engines/mm/mm1/views_enh/map.cpp
    engines/mm/mm1/views_enh/protect.cpp
    engines/mm/mm1/views_enh/search.cpp
    engines/mm/mm1/views_enh/spells/cast_spell.cpp
    engines/mm/mm1/views_enh/spells/spellbook.cpp
    engines/mm/mm1/views_enh/which_character.cpp


diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index e3b2b0f73ac..ac1a09d12f4 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -261,6 +261,7 @@ void Character::clear() {
 	_intelligence = _might = _personality = _endurance = 0;
 	_speed = _accuracy = _luck = 0;
 	_level = 1;
+	// MM1 characters begin at age 18.
 	_age = 18;
 	_ageDayCtr = 0;
 	_exp = 0;
@@ -290,7 +291,7 @@ void Character::clear() {
 
 void Character::gatherGold() {
 	// Gold is stored in the original MM1 24-bit range. Move as much as
-	// possible to this character and leave any overflow with its owner
+	// possible to this character and leave any overflow with its owner.
 	static const uint32 MAX_GOLD = 0xffffff;
 
 	if (_gold >= MAX_GOLD)
@@ -312,9 +313,9 @@ void Character::gatherGold() {
 }
 
 void Character::gatherResources() {
-	// Gather gold, gems and food, each only up to the destination
-	// character's carrying limit. Any excess stays with the source
-	// character instead of being destroyed
+	// Original Character Profile G: gather gold, gems and food, each only up
+	// to the destination character's carrying limit. Any excess stays with
+	// the source character instead of being destroyed.
 	gatherGold();
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
@@ -462,8 +463,8 @@ void Character::updateAttributes() {
 }
 
 void Character::updateAC() {
-	// Leather Skin stores the caster's level as a party-wide AC bonus.
-	// Rebuild from base so repeated updates don't stack it.
+	// Leather Skin stores the caster's current level as a party-wide
+	// AC bonus. Rebuild current AC from base so repeated updates do not stack.
 	int ac = _ac._base +
 		g_globals->_activeSpells._s.leather_skin;
 
diff --git a/engines/mm/mm1/data/character.h b/engines/mm/mm1/data/character.h
index 2992ae726e1..3964ea24dc4 100644
--- a/engines/mm/mm1/data/character.h
+++ b/engines/mm/mm1/data/character.h
@@ -508,8 +508,8 @@ struct Character : public PrimaryAttributes {
 	void gatherGold();
 
 	/**
-	 * Gathers gold, gems and food into the character, up to
-	 * their carrying limits
+	 * Original character-profile Gather command: gathers gold, gems and
+	 * food into the character up to their carrying limits.
 	 */
 	void gatherResources();
 
@@ -581,12 +581,12 @@ struct Character : public PrimaryAttributes {
 
 	/**
 	 * Experience required to train for the next level.
-	 * Uses the same progression as the Training Hall
+	 * Uses the same progression as the Training Hall.
 	 */
 	uint32 experienceForNextLevel() const;
 
 	/**
-	 * Returns true when current experience is sufficient for training
+	 * Returns true when current experience is sufficient for training.
 	 */
 	bool canLevelUp() const;
 
diff --git a/engines/mm/mm1/data/monsters.h b/engines/mm/mm1/data/monsters.h
index 2e950675713..1bef9341c39 100644
--- a/engines/mm/mm1/data/monsters.h
+++ b/engines/mm/mm1/data/monsters.h
@@ -78,7 +78,7 @@ enum MonsterCounter {
 };
 
 struct Monster {
-	uint16 _bestiaryId = 0;	// Database ID, independent of battle position
+	uint16 _bestiaryId = 0; // Runtime database ID, independent of battle position.
 	Common::String _name; // char _name[15];
 	byte _count;
 	byte _fleeThreshold;
diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 26a0363020d..1b69e7732d6 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -262,8 +262,10 @@ void Combat::dispelParty() {
 
 	g_globals->_currCharacter = tmpC;
 
-	for (uint i = 0; i < _remainingMonsters.size(); ++i)
+	for (uint i = 0; i < _remainingMonsters.size(); ++i) {
 		_remainingMonsters[i]->_status = 0;
+	}
+
 }
 
 void Combat::combatLoop(bool checkMonstersFirst) {
@@ -338,7 +340,7 @@ void Combat::defeatedMonsters() {
 	}
 
 	// Convert the highest defeated-monster treasure rating into the
-	// original random item rolls and container quality
+	// original random item rolls and container quality.
 	selectTreasure();
 
 	// Count number of active characters
@@ -419,7 +421,7 @@ void Combat::selectTreasure2(int count) {
 	byte itemId = TREASURES_ARR1[idx];
 	byte rndCount = TREASURES_ARR2[idx];
 
-	// count is a 1-based treasure tier, with tier 1 at the family base
+	// count is a 1-based treasure tier; tier 1 starts at the family base.
 	for (idx = 1; idx < count; ++idx)
 		itemId += rndCount;
 	itemId += getRandomNumber(rndCount) - 1;
@@ -519,7 +521,7 @@ bool Combat::monsterChanges() {
 	for (uint i = 0; i < _remainingMonsters.size(); ++i) {
 		monsterSetPtr(i);
 
-		// Dead monsters stay dead until combat cleanup removes them
+		// Dead monsters must remain dead until combat cleanup removes them.
 		if (_remainingMonsters[i]->_status == MONFLAG_DEAD)
 			continue;
 
@@ -954,6 +956,7 @@ void Combat::iterateSelectedMonster1() {
 	iterateMonsters1Inner();
 }
 
+
 void Combat::iterateMonsters1Inner() {
 	Common::String line1 = Common::String::format("%s %s",
 		g_globals->_currCharacter->_name,
@@ -1043,6 +1046,7 @@ void Combat::iterateSelectedMonster2() {
 	iterateMonsters2Inner();
 }
 
+
 void Combat::iterateMonsters2Inner() {
 	Common::String line1 = Common::String::format("%s %s",
 		g_globals->_currCharacter->_name,
@@ -1133,7 +1137,7 @@ void Combat::iterateMonsters2Inner() {
 }
 
 void Combat::characterDone() {
-	// Spell damage marks defeated monsters dead but does not remove them
+	// Spell damage marks defeated monsters dead but does not remove them.
 	removeDeadMonsters();
 	_selectedSpellMonster = -1;
 	g_globals->_combatParty[_currentChar]->_checked = true;
@@ -1160,6 +1164,7 @@ bool Combat::setSpellTarget(int monsterNum) {
 	return true;
 }
 
+
 void Combat::spellFailed() {
 	g_globals->_combatParty[_currentChar]->_checked = true;
 	Sound::sound(SOUND_2);
@@ -1188,8 +1193,8 @@ void Combat::turnUndead() {
 			Monster *monster = _monsterP;
 
 			if ((monster->_resistUndead & IS_UNDEAD) &&
-					(getRandomNumber(20) + g_globals->_currCharacter->_level) >=
-					(monster->_level * 2 + 10)) {
+						(getRandomNumber(20) + g_globals->_currCharacter->_level) >=
+						(monster->_level * 2 + 10)) {
 				destroyMonster();
 				++_monstersDestroyedCtr;
 			}
@@ -1256,14 +1261,15 @@ void Combat::fireball2() {
 	ss._resistanceIndex = 1;
 	ss._damage = 0;
 
-	// Fireball affects up to five monsters outside hand-to-hand range
+	// Fireball affects up to five monsters outside hand-to-hand range.
 	_selectedSpellMonster = -1;
 	_destMonsterNum = _attackersCount;
 	_spellMonsterCount = _remainingMonsters.size();
 	ss._resistanceTypeOrTargetCount = MIN(
 		5, (int)_remainingMonsters.size() - _destMonsterNum);
 
-	for (uint i = 0; i < g_globals->_currCharacter->_level._current; ++i)
+	for (uint i = 0;
+			i < g_globals->_currCharacter->_level._current; ++i)
 		ss._damage += getRandomNumber(6);
 
 	iterateMonsters2Inner();
@@ -1325,7 +1331,7 @@ void Combat::holyWord() {
 		}
 	}
 
-	// The result is timed, like the Turn Undead result
+	// Holy Word result must be timed, like the existing Turn Undead result.
 	InfoMessage msg(_monstersDestroyedCtr ? 5 : 15, 1,
 		STRING[_monstersDestroyedCtr ? "spells.monsters_destroyed" : "spells.no_effect"]);
 	msg._delaySeconds = 3;
@@ -1388,7 +1394,7 @@ void Combat::identifyMonster() {
 #undef COL2
 
 bool Combat::fireball() {
-	// Fireball cannot target monsters engaged in hand-to-hand combat
+	// Fireball cannot target monsters engaged in hand-to-hand combat.
 	if (_attackersCount >= (int)_remainingMonsters.size())
 		return false;
 
@@ -1411,6 +1417,7 @@ void Combat::makeRoom() {
 		g_globals->_combatParty[i]->_canAttack = true;
 }
 
+
 int Combat::getHeroismBonus(uint index) const {
 	return index < MAX_PARTY_SIZE ? _heroismBonus[index] : 0;
 }
@@ -1465,7 +1472,7 @@ void Combat::slow() {
 		_monsterP->_speed = MAX(_monsterP->_speed / 2, 1);
 	}
 
-	// Slow can be cast repeatedly; record this only for the current battle
+	// Slow can be cast repeatedly; record this only for the current battle.
 	++_slowCasts;
 }
 
@@ -1605,7 +1612,7 @@ void Combat::monsterAttackInner() {
 	Encounter &enc = g_globals->_encounters;
 	Character &c = *g_globals->_currCharacter;
 
-	// Include the Leather Skin bonus in the enemy hit roll
+	// Use the level-based Leather Skin bonus in the actual enemy hit roll.
 	_destAC = c._ac._base +
 		g_globals->_activeSpells._s.leather_skin;
 	int monsterIndex = getMonsterIndex();
diff --git a/engines/mm/mm1/game/combat.h b/engines/mm/mm1/game/combat.h
index 80057589299..258074de86f 100644
--- a/engines/mm/mm1/game/combat.h
+++ b/engines/mm/mm1/game/combat.h
@@ -81,12 +81,12 @@ protected:
 	bool _turnUndeadUsed = false;
 	bool _divineInterventionUsed = false;
 	size_t _spellMonsterCount = 0;
-	// Transient combat-only display state; never serialized
+	// Transient combat-only display state; never serialized.
 	int _slowCasts = 0;
 	int _weakenCasts = 0;
 	int _heroismBonus[MAX_PARTY_SIZE] = {};
 	bool _heroismIsSuper[MAX_PARTY_SIZE] = {};
-	// -1 means the spell has not been cast; a real roll may be zero
+	// -1 means the spell has not been cast; a real roll may be zero.
 	int _powerBonus[MAX_PARTY_SIZE] = {};
 	int _quicknessBonus[MAX_PARTY_SIZE] = {};
 
@@ -365,9 +365,7 @@ public:
 	void fingerOfDeath();
 	void disintegration();
 
-	/**
-	 * End combat through the interface-specific cleanup path
-	 */
+	/** End combat through the interface-specific cleanup path. */
 	void endCombat();
 };
 
diff --git a/engines/mm/mm1/game/encounter.cpp b/engines/mm/mm1/game/encounter.cpp
index f573e9fa100..b4eee3b5d72 100644
--- a/engines/mm/mm1/game/encounter.cpp
+++ b/engines/mm/mm1/game/encounter.cpp
@@ -40,7 +40,7 @@ void Encounter::execute(const Common::String &preCombatLog) {
 		return;
 	}
 
-	// Clear stale event text, or retain text supplied by this encounter
+	// Clear stale event text, or retain text supplied by this encounter.
 	_preCombatLog = preCombatLog;
 
 	if (!g_globals->_encountersOn)
diff --git a/engines/mm/mm1/game/spell_casting.cpp b/engines/mm/mm1/game/spell_casting.cpp
index a666eb5c68a..91dd370db2f 100644
--- a/engines/mm/mm1/game/spell_casting.cpp
+++ b/engines/mm/mm1/game/spell_casting.cpp
@@ -120,12 +120,14 @@ void SpellCasting::setSpell(const Character *chr, int lvl, int num) {
 
 	int requiredGems = ABS(SPELLS_SP_GEMS[spellIndex]);
 
-	// Gem costs are real casting requirements, just like spell points
+	// Gem costs are real casting requirements, just like spell points.
+	// The error state already existed, but the resource check was missing,
+	// allowing gem-cost spells to be cast with too few (or zero) gems.
 	if (_spellState == SS_OK && requiredGems > chr->_gems)
 		_spellState = SS_NOT_ENOUGH_GEMS;
 
 	// Keep the enhanced spell dialog synchronized even when the spell
-	// cannot currently be afforded
+	// cannot currently be afforded.
 	_spellIndex = spellIndex;
 	_requiredSp = requiredSp;
 	_requiredGems = requiredGems;
diff --git a/engines/mm/mm1/game/spells_monsters.cpp b/engines/mm/mm1/game/spells_monsters.cpp
index 071c1ca3a6b..3873cf435e4 100644
--- a/engines/mm/mm1/game/spells_monsters.cpp
+++ b/engines/mm/mm1/game/spells_monsters.cpp
@@ -662,7 +662,7 @@ void SpellsMonsters::writeConditionEffect() {
 	}
 
 	add(STRING[Common::String::format("spells.char_effects.%d",
-		effectNum)]);
+			effectNum)]);
 	add('!');
 }
 
@@ -686,6 +686,8 @@ void SpellsMonsters::handlePartyEffects() {
 		writeConditionEffect();
 	}
 
+	// Party-wide condition effects temporarily select each target.
+	// Restore the combat character that was active beforehand.
 	g_globals->_currCharacter = priorCharacter;
 }
 
diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index 1e5dbd7b56e..1753e63f807 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -139,7 +139,8 @@ SpellResult SpellsParty::cast(uint spell, Character *destChar) {
 	assert(spell < SPELLS_COUNT);
 	_destChar = destChar;
 
-	// Don't carry damage or resistance data between spells
+	// Reset transient party-spell calculation state. These fields
+	// must never carry damage or resistance data between spells.
 	SpellsState &ss = g_globals->_spellsState;
 	ss._mmVal1 = 0;
 	ss._resistanceIndex = 0;
@@ -502,8 +503,8 @@ SpellResult SpellsParty::cleric61_moonRay() {
 }
 
 SpellResult SpellsParty::cleric62_raiseDead() {
-	// Raise Dead only works on a dead character. Eradicated
-	// characters need Resurrection
+	// Raise Dead is valid only for a character who is actually Dead.
+	// Eradicated characters require Resurrection.
 	if (_destChar->_condition == ERADICATED ||
 		(_destChar->_condition & (BAD_CONDITION | DEAD)) !=
 		(BAD_CONDITION | DEAD))
@@ -511,16 +512,18 @@ SpellResult SpellsParty::cleric62_raiseDead() {
 
 	int rnd = g_engine->getRandomNumber(100);
 	if (rnd == 100) {
-		// A remote chance of eradicating the character instead
+		// Original spell description: a remote chance of eradicating
+		// the character instead of raising them.
 		_destChar->_condition = ERADICATED;
 		_destChar->_hpCurrent = 0;
 		Sound::sound(SOUND_3);
 		return SR_FAILED;
 	} else if (rnd > 90) {
+		// Moderate ordinary failure chance: target remains Dead.
 		return SR_FAILED;
 	}
 
-	// A character who was both dead and stoned is raised, but stays stoned
+	// A character who was both Dead and Stoned is raised but remains Stoned.
 	if (_destChar->_condition == (BAD_CONDITION | DEAD | STONE))
 		_destChar->_condition = BAD_CONDITION | STONE;
 	else
@@ -536,7 +539,7 @@ SpellResult SpellsParty::cleric63_rejuvinate() {
 		_destChar->_age = MAX((int)_destChar->_age - ageReduction, 0);
 		return SR_SUCCESS_DONE;
 	} else {
-		// Failed, increase the recipient's age
+		// Failed, increase the recipient's age.
 		_destChar->_age = MIN((int)_destChar->_age + 10, 200);
 		return SR_FAILED;
 	}
@@ -896,7 +899,7 @@ SpellResult SpellsParty::wizard48_timeDistortion() {
 	if (getRandomNumber(100) < map[Maps::MAP_DISPEL_THRESHOLD])
 		return SR_FAILED;
 
-	// End combat through the normal cleanup and view-transition path
+	// End combat through the normal cleanup and view-transition path.
 	g_globals->_combat->endCombat();
 	return SR_SUCCESS_SILENT;
 }
@@ -944,13 +947,13 @@ SpellResult SpellsParty::wizard63_etherialize() {
 	Maps::Maps &maps = *g_maps;
 
 	// Etherealize moves exactly one square in the direction the party is
-	// facing, ignoring walls and special barriers
+	// facing, ignoring walls and special barriers.
 	const Common::Point delta = maps.getMoveDelta(maps._forwardMask);
 	const Common::Point newPos = maps._mapPos + delta;
 
-	// Maps::step() performs its own changeMap() and UPDATE when crossing a
-	// map boundary. On an in-map move, refresh the Game view so the current
-	// square's walls and state match the new position
+	// Maps::step() performs its own changeMap() + UPDATE when crossing a
+	// map boundary. On an in-map move, explicitly refresh the Game view
+	// so _mapOffset, _currentWalls and _currentState match the new square.
 	const bool crossesMapBoundary =
 		newPos.x < 0 || newPos.y < 0 ||
 		newPos.x >= MAP_W || newPos.y >= MAP_H;
@@ -1014,7 +1017,7 @@ SpellResult SpellsParty::wizard75_prismaticLight() {
 		return SR_FAILED;
 
 	// Generate a new unpredictable effect for this cast rather
-	// than reusing state left behind by an earlier spell
+	// than reusing state left behind by an earlier spell.
 	ss._mmVal1 = getRandomNumber(100);
 	ss._resistanceIndex = 0;
 	ss._damage = 1;
diff --git a/engines/mm/mm1/game/wheel_spin.cpp b/engines/mm/mm1/game/wheel_spin.cpp
index 02bea99b2b2..024d467f381 100644
--- a/engines/mm/mm1/game/wheel_spin.cpp
+++ b/engines/mm/mm1/game/wheel_spin.cpp
@@ -89,7 +89,8 @@ void WheelSpin::spin() {
 		}
 
 		// Spinning the Wheel makes all four Great Beasts available again.
-		// Keep the Wheel marker (CHARFLAG2_80) and the upper bits
+		// Preserve CHARFLAG2_80 (the Wheel marker) and unrelated upper bits;
+		// only the four beast-completion flags are reset.
 		c._flags[2] &= ~(CHARFLAG2_1 | CHARFLAG2_2 |
 			CHARFLAG2_4 | CHARFLAG2_8);
 
diff --git a/engines/mm/mm1/globals.h b/engines/mm/mm1/globals.h
index 167ee1e077b..63c7d55cb0a 100644
--- a/engines/mm/mm1/globals.h
+++ b/engines/mm/mm1/globals.h
@@ -64,6 +64,7 @@ public:
 	bool _minimapOn = true;
 	Common::Array<SuggestedName> *_suggestedNames = nullptr;
 
+
 	// Console flags
 	bool _intangible = false;
 	bool _encountersOn = true;
diff --git a/engines/mm/mm1/maps/map.cpp b/engines/mm/mm1/maps/map.cpp
index bde59573422..3a26e642335 100644
--- a/engines/mm/mm1/maps/map.cpp
+++ b/engines/mm/mm1/maps/map.cpp
@@ -170,13 +170,14 @@ void Map::reduceHP() {
 void Map::updateGame() {
 	// Scripted same-map teleports often assign _mapPos directly and then
 	// call updateGame(), bypassing Maps::step(). Discover the landing square
-	// before repainting, so the minimap can draw it immediately
+	// before repainting so the Enhanced minimap can immediately draw it and
+	// the party arrow.
 	g_maps->visitedTile();
 	g_events->send("Game", GameMessage("UPDATE"));
 }
 
 void Map::redrawGame() {
-	// Keep redraw-only relocation paths consistent with updateGame()
+	// Keep REDRAW-only relocation paths consistent with updateGame().
 	g_maps->visitedTile();
 	g_events->send("Game", GameMessage("REDRAW"));
 }
diff --git a/engines/mm/mm1/maps/map01.cpp b/engines/mm/mm1/maps/map01.cpp
index 6117722473e..bbff2c58aca 100644
--- a/engines/mm/mm1/maps/map01.cpp
+++ b/engines/mm/mm1/maps/map01.cpp
@@ -124,9 +124,14 @@ void Map01::special08() {
 		[](const Common::KeyState &) {
 			g_events->close();
 
-			// Consume the secret for this map visit, and record Lord
-			// Ironfist's quest #3 (discover the secret of Portsmith) at
-			// the discovery site. Winning the fight isn't required
+			// This is a one-shot special encounter for the current map visit.
+			// Consume the special when the encounter actually starts so winning
+			// the fight does not immediately trigger the Queen again.
+			// Consume the Portsmith secret for this visit and record
+			// Lord Ironfist quest #3 (Discover the Secret of Portsmith)
+			// at the actual discovery site. In the original game,
+			// discovering the secret is sufficient; full victory is
+			// not required.
 			g_maps->clearSpecial();
 			for (uint i = 0; i < g_globals->_party.size(); ++i)
 				g_globals->_party[i]._flags[7] |= CHARFLAG7_4;
diff --git a/engines/mm/mm1/maps/map06.cpp b/engines/mm/mm1/maps/map06.cpp
index 3d960e3fd45..e1b7127ebe3 100644
--- a/engines/mm/mm1/maps/map06.cpp
+++ b/engines/mm/mm1/maps/map06.cpp
@@ -156,8 +156,9 @@ void Map06::special06() {
 			byte &val1 = map[VAL1];
 			val1 = val1 ? 0 : 1;
 
-			// The overlay byte is reloaded when a save is loaded, so
-			// remember this visit's OFF state on the button cell
+			// The original overlay byte is reloaded when a ScummVM save is
+			// loaded. Remember the current visit's OFF state on the button
+			// cell so a save/load does not reactivate the conveyors.
 			byte &visited = map._visited[g_maps->_mapOffset];
 			if (val1)
 				visited &= ~VISITED_TRANSIENT_STATE;
@@ -189,12 +190,12 @@ void Map06::special13() {
 	Game::Encounter &enc = g_globals->_encounters;
 	g_maps->clearSpecial();
 
-	// Encounter progress also starts the side quest if the wizard
-	// hasn't been spoken to first
+	// Encounter progress also initiates the side quest if the wizard
+	// has not been visited first. A hidden quest remains hidden.
 	_visited[QUEST_ACTIVE_OFFSET] |= ENCOUNTER_COMPLETE;
 
-	// Record completion in the map visitation data, which is serialized
-	// in savegames, so progress survives saving and loading
+	// Record completion in the map visitation data, which is serialized in
+	// savegames. This preserves progress when a game is saved and reloaded.
 	byte &visited = _visited[g_maps->_mapOffset];
 	if (visited & ENCOUNTER_COMPLETE) {
 		checkPartyDead();
@@ -212,8 +213,8 @@ void Map06::special13() {
 	for (int i = 0; i < monsterCount; ++i)
 		enc.addMonster(id1, id2);
 
-	// Without manual mode, execute() discards the scripted monsters
-	// and generates an unrelated random encounter
+	// The monster list above is scripted. Without manual mode execute()
+	// discards it and generates an unrelated random encounter.
 	enc._manual = true;
 	enc.execute();
 }
@@ -237,7 +238,8 @@ void Map06::special26() {
 int Map06::completedEncounters() const {
 	int result = 0;
 
-	// The thirteen challenges occupy map rows 0 through 12
+	// The thirteen challenges occupy map rows 0 through 12. Scanning
+	// those rows avoids depending on overlay data when another map is active.
 	for (int y = 0; y < 13; ++y) {
 		for (int x = 0; x < MAP_W; ++x) {
 			if (_visited[y * MAP_W + x] & ENCOUNTER_COMPLETE)
diff --git a/engines/mm/mm1/maps/map09.cpp b/engines/mm/mm1/maps/map09.cpp
index 3a9d0369bc4..ed5348d5b9d 100644
--- a/engines/mm/mm1/maps/map09.cpp
+++ b/engines/mm/mm1/maps/map09.cpp
@@ -199,7 +199,7 @@ void Map09::special18() {
 			0, 2, STRING["maps.map09.protection"]
 		));
 	} else {
-		// getRandomNumber() is inclusive, so pass the last valid index
+		// getRandomNumber() is inclusive, so pass the last valid index.
 		Character &victim = g_globals->_party[
 			getRandomNumber(g_globals->_party.size() - 1)
 		];
@@ -218,8 +218,8 @@ void Map09::special18() {
 			effectLine = "The party's spell points are drained.";
 		}
 
-		// Keep the result until combat opens, then add it to the Combat
-		// Log. This state is deliberately not serialized
+		// Preserve the result until combat opens, then consume it once in
+		// the Combat Log. This state is deliberately not serialized.
 		g_globals->_encounters._preCombatLog = Common::String::format(
 			"Psychic blast! %s", effectLine.c_str());
 
diff --git a/engines/mm/mm1/maps/map10.cpp b/engines/mm/mm1/maps/map10.cpp
index 1360dba20e3..088d741907a 100644
--- a/engines/mm/mm1/maps/map10.cpp
+++ b/engines/mm/mm1/maps/map10.cpp
@@ -75,8 +75,9 @@ void Map10::special00() {
 }
 
 void Map10::special01() {
-	// Ranalou's long dialogue uses the large message frame. The enhanced
-	// messages view scrolls it if the wrapped text still doesn't fit
+	// Ranalou's long Enhanced dialogue uses the large message frame. The
+	// Enhanced GameMessages view will provide vertical scrolling if wrapping
+	// still produces more lines than can fit in that frame.
 	SoundMessage msg(
 		STRING["maps.map10.ranalou1"],
 		[]() {
diff --git a/engines/mm/mm1/maps/map11.cpp b/engines/mm/mm1/maps/map11.cpp
index 8d18456d40f..a775ddb74cc 100644
--- a/engines/mm/mm1/maps/map11.cpp
+++ b/engines/mm/mm1/maps/map11.cpp
@@ -69,6 +69,9 @@ void Map11::special() {
 		const bool savedStable = (_visited[3 * MAP_W + 6] & 0x80) != 0 &&
 			(_visited[3 * MAP_W + 8] & 0x80) != 0;
 
+		// If the live overlay contains the correct settings, heal the
+		// save-safe mirror as well. This makes old/partially patched visits
+		// converge on one state without changing the save format.
 		if (rawStable) {
 			_visited[3 * MAP_W + 6] |= 0x80;
 			_visited[3 * MAP_W + 8] |= 0x80;
@@ -85,7 +88,6 @@ void Map11::special() {
 		send(SoundMessage(STRING["maps.map11.poof"]));
 	}
 }
-
 void Map11::special00() {
 	visitedExit();
 	send(SoundMessage(
@@ -173,8 +175,9 @@ void Map11::selectDial(int dialIndex) {
 			if (ks.keycode >= Common::KEYCODE_a &&
 					ks.keycode <= Common::KEYCODE_z) {
 				Map11 &map = *static_cast<Map11 *>(g_maps->_currentMap);
-				// Derive the letter from the keycode, since KeyState::ascii
-				// can be 0 or the wrong case depending on the backend
+				// Derive the selected letter from keycode, not KeyState::ascii.
+				// This avoids backend/keymapper differences where ascii may be 0
+				// or have unexpected case even though the A-Z keycode is valid.
 				const char c = 'A' + (ks.keycode - Common::KEYCODE_a);
 				g_events->close();
 				map.setDialChar(c);
@@ -211,7 +214,8 @@ void Map11::setDialChar(char c) {
 	else
 		_visited[visitedOffset] &= ~0x80;
 
-	// Echo the chosen value, so it's clear what was recorded
+	// Echo the chosen value below the original A-Z instruction so there is
+	// unambiguous visual feedback about what the engine recorded.
 	Common::String line1 = Common::String::format(
 		STRING["maps.map11.dial"].c_str(), '1' + _dialIndex);
 	Common::String line2 = Common::String::format("Selected: %c", c);
diff --git a/engines/mm/mm1/maps/map15.cpp b/engines/mm/mm1/maps/map15.cpp
index 962b939c3ed..6d2c376bd7c 100644
--- a/engines/mm/mm1/maps/map15.cpp
+++ b/engines/mm/mm1/maps/map15.cpp
@@ -201,8 +201,8 @@ void Map15::cove() {
 			Map15 &map = *static_cast<Map15 *>(g_maps->_currentMap);
 			g_maps->clearSpecial();
 
-			// Discovering Pirate's Secret Cove satisfies Lord Ironfist's
-			// quest #4 for the whole party
+			// Discovering Pirate's Secret Cove satisfies
+			// Lord Ironfist quest #4 for the entire party.
 			for (uint i = 0; i < g_globals->_party.size(); ++i)
 				g_globals->_party[i]._flags[7] |= CHARFLAG7_8;
 
diff --git a/engines/mm/mm1/maps/map17.cpp b/engines/mm/mm1/maps/map17.cpp
index f31febcbb05..bc2f53c7453 100644
--- a/engines/mm/mm1/maps/map17.cpp
+++ b/engines/mm/mm1/maps/map17.cpp
@@ -80,8 +80,9 @@ void Map17::special00() {
 }
 
 void Map17::special01() {
-	// Record that the Hooded Figure / Color Bridge has been discovered,
-	// using a fixed bit in the saved visit data
+	// Record that the Hooded Figure / Color Bridge has been discovered using
+	// a fixed visited-cell metadata bit. This is safe even when the map is not
+	// later loaded; reward persistence uses 0x40, not this 0x80 metadata bit.
 	_visited[COLOR_BRIDGE_VISITED_OFFSET] |= COLOR_BRIDGE_DISCOVERED;
 
 	bool allAssigned = !g_globals->_party.empty();
@@ -93,8 +94,8 @@ void Map17::special01() {
 	}
 
 	if (!allAssigned) {
-		// Without a Gypsy sign the quiz can't be answered correctly, so
-		// don't open the Y/N and quiz chain in that state
+		// Without a Gypsy sign the Hooded Figure quiz cannot be answered
+		// correctly. Avoid opening the Y/N/quiz chain in this invalid state.
 		g_maps->clearSpecial();
 		SoundMessage info(
 			"The Hooded Figure bars the Color Bridge. "
@@ -104,9 +105,10 @@ void Map17::special01() {
 		return;
 	}
 
-	// Everyone knows their sign. Declining the bridge prompt doesn't
-	// consume the special, so the quiz can be retried
-	SoundMessage msg(STRING["maps.map17.bridge"],
+	// All current party members know their Gypsy signs. Keep the original
+	// retryable bridge-prompt behavior: declining does not consume the special.
+	SoundMessage msg(
+		STRING["maps.map17.bridge"],
 		[]() {
 			g_events->addView("ColorQuestions");
 		}
diff --git a/engines/mm/mm1/maps/map21.cpp b/engines/mm/mm1/maps/map21.cpp
index 31c2bc17116..518bf09840b 100644
--- a/engines/mm/mm1/maps/map21.cpp
+++ b/engines/mm/mm1/maps/map21.cpp
@@ -108,7 +108,9 @@ void Map21::special01() {
 		[]() {
 			Map21 &map = *static_cast<Map21 *>(g_maps->_currentMap);
 			map[TRIVIA_ENABLED]++;
-			g_events->send(InfoMessage("Trivia Island admission granted."));
+			g_events->send(InfoMessage(
+				"Trivia Island admission granted."
+			));
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map24.cpp b/engines/mm/mm1/maps/map24.cpp
index c564195114f..37194fcf725 100644
--- a/engines/mm/mm1/maps/map24.cpp
+++ b/engines/mm/mm1/maps/map24.cpp
@@ -59,7 +59,7 @@ void Map24::special01() {
 		STRING["maps.map24.kilburn"],
 		[]() {
 			Map24 &map = *static_cast<Map24 *>(g_maps->_currentMap);
-			// Meeting Kilburn satisfies Lord Ironfist's second quest
+			// Meeting Kilburn satisfies Lord Ironfist's second quest.
 			for (uint i = 0; i < g_globals->_party.size(); ++i)
 				g_globals->_party[i]._flags[7] |= CHARFLAG7_2;
 			if (!map.addItem(MAP_OF_DESERT_ID)) {
diff --git a/engines/mm/mm1/maps/map25.cpp b/engines/mm/mm1/maps/map25.cpp
index 8bf56e194cc..57a59172c7e 100644
--- a/engines/mm/mm1/maps/map25.cpp
+++ b/engines/mm/mm1/maps/map25.cpp
@@ -118,7 +118,7 @@ void Map25::special() {
 
 	default: {
 		// Only the six marked wrecks and the Jolly Raven
-		// should display the shipwreck search prompt
+		// should display the shipwreck search prompt.
 		static const uint16 SHIPWRECK_OFFSETS[] = {
 			142, 155, 161, 179, 185, 197, 216
 		};
@@ -134,7 +134,7 @@ void Map25::special() {
 			none160();
 			break;
 		}
-
+	}
 		send(SoundMessage(
 			STRING["maps.map25.ship"],
 			[]() {
@@ -157,7 +157,6 @@ void Map25::special() {
 		));
 		break;
 	}
-	}
 }
 
 void Map25::special00() {
diff --git a/engines/mm/mm1/maps/map29.cpp b/engines/mm/mm1/maps/map29.cpp
index d4ce1157ae6..5b5e20a99ca 100644
--- a/engines/mm/mm1/maps/map29.cpp
+++ b/engines/mm/mm1/maps/map29.cpp
@@ -31,9 +31,10 @@ namespace Maps {
 
 #define ANSWER_OFFSET 67
 #define BEAST_FLAG 110
-// Og is the D4 special at (7,1). Use the fixed visit-data cell rather than
-// reading _data, since the overlay data isn't loaded until this map is
-// entered but the Quest Log can be opened anywhere
+// Og is the D4 special at (7,1): visited-array offset 1 * 16 + 7 = 23.
+// Use the fixed cell index here rather than reading _data[53]. The overlay
+// _data array is not loaded until Map29 itself is entered, while the Quest
+// Log can be opened anywhere in the world.
 #define OG_CELL_OFFSET (1 * MAP_W + 7)
 #define OG_QUEST_LOG_FLAG 0x80
 
diff --git a/engines/mm/mm1/maps/map29.h b/engines/mm/mm1/maps/map29.h
index 716c0ab2e2c..63cb30a90af 100644
--- a/engines/mm/mm1/maps/map29.h
+++ b/engines/mm/mm1/maps/map29.h
@@ -60,7 +60,7 @@ public:
 
 	/**
 	 * Og side-quest state. The completion marker reuses the high metadata
-	 * bit of Og's existing visited cell, so the save format is unchanged
+	 * bit of Og's existing visited cell, so save format version 1 is unchanged.
 	 */
 	bool ogQuestVisited() const;
 	bool ogQuestCompleted() const;
diff --git a/engines/mm/mm1/maps/map30.cpp b/engines/mm/mm1/maps/map30.cpp
index 12172416b4b..1cbd8910099 100644
--- a/engines/mm/mm1/maps/map30.cpp
+++ b/engines/mm/mm1/maps/map30.cpp
@@ -138,18 +138,22 @@ Common::String Map30::worthy() {
 			STRING[Common::String::format("maps.map30.attributes.%d", attrNum)];
 		AttributePair &attrib = c.getAttribute(attrNum);
 
-		// Attributes with a base of 43 or more don't increase
+		// The Statue uses the same rule for all seven attributes. Apply the
+		// reward to both current and base immediately, but preserve the
+		// original MM1 threshold: attributes with base >= 43 do not increase.
 		if (attrib._base < 43) {
 			attrib._current = attrib._base = attrib._base + 3;
 			line += Common::String::format(", +3 %s", attrName.c_str());
 		} else {
+			// Do not falsely advertise +3 when the original cap blocks it.
 			line += Common::String::format(", %s unchanged", attrName.c_str());
 		}
 	}
 
-	// Mark that this character has claimed the Statue's result, for the
-	// Quest Log. The unused high bit of the prisoner flag byte is
-	// already saved, so no save format change is needed
+	// Custom Quest Log completion marker: this character has actually
+	// claimed the Statue of Judgement result after all six prisoners.
+	// CHARFLAG1_WORTHY already occupies the unused high bit in the
+	// serialized prisoner-quest flag byte; no save-format change needed.
 	c._flags[1] |= CHARFLAG1_WORTHY;
 
 	c._worthiness = 0;
diff --git a/engines/mm/mm1/maps/map31.cpp b/engines/mm/mm1/maps/map31.cpp
index d9a71cd9493..4d22ac0cc93 100644
--- a/engines/mm/mm1/maps/map31.cpp
+++ b/engines/mm/mm1/maps/map31.cpp
@@ -68,7 +68,6 @@ void Map31::special00() {
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
 		Character &c = g_globals->_party[i];
-
 		if (!(c._flags[11] & CHARFLAG11_GOT_INTELLIGENCE)) {
 			c._flags[11] |= CHARFLAG11_GOT_INTELLIGENCE;
 			if (c._intelligence._base < 30) {
diff --git a/engines/mm/mm1/maps/map33.cpp b/engines/mm/mm1/maps/map33.cpp
index 20fbaacc940..188db48842c 100644
--- a/engines/mm/mm1/maps/map33.cpp
+++ b/engines/mm/mm1/maps/map33.cpp
@@ -196,8 +196,9 @@ void Map33::search(const Common::String &msg) {
 			Game::Encounter &enc = g_globals->_encounters;
 			int monsterCount = g_events->getRandomNumber(6) + 7;
 
-			// Consume the special for this map visit before combat,
-			// so winning cannot immediately retrigger it
+			// The E4 coffin/crypt encounters are renewable only after leaving
+			// and re-entering the area. Consume the current special for this
+			// map visit before combat so winning cannot immediately retrigger it.
 			g_maps->clearSpecial();
 
 			enc.clearMonsters();
diff --git a/engines/mm/mm1/maps/map35.cpp b/engines/mm/mm1/maps/map35.cpp
index 556b1354ec2..83bc2376bf5 100644
--- a/engines/mm/mm1/maps/map35.cpp
+++ b/engines/mm/mm1/maps/map35.cpp
@@ -160,7 +160,7 @@ void Map35::acceptQuest() {
 			c._flags[5] = CHARFLAG5_80;
 		}
 
-		// Restart Inspectron's seven-quest cycle at quest #1
+		// Restart Inspectron's seven-quest cycle at quest #1.
 		questNum = 8;
 	}
 
@@ -181,7 +181,7 @@ Common::String Map35::checkQuestComplete() {
 
 	if (leader._flags[5] & MATCH_FLAGS[qIndex] & 0x7f) {
 		// The quest was complete. Retain it in the Quest Log using
-		// the high bit of a reserved, already-serialized map cell
+		// the high bit of a reserved, already-serialized map cell.
 		_visited[15 * MAP_W + qIndex] |= 0x80;
 
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
diff --git a/engines/mm/mm1/maps/map36.cpp b/engines/mm/mm1/maps/map36.cpp
index 8c3e8cc8378..7755a532d6f 100644
--- a/engines/mm/mm1/maps/map36.cpp
+++ b/engines/mm/mm1/maps/map36.cpp
@@ -189,7 +189,7 @@ Common::String Map36::checkQuestComplete() {
 
 	if (leader._flags[6] & MATCH_FLAGS[qIndex] & 0x7f) {
 		// The quest was complete. Retain it in the Quest Log using
-		// the high bit of a reserved, already-serialized map cell
+		// the high bit of a reserved, already-serialized map cell.
 		_visited[15 * MAP_W + qIndex] |= 0x80;
 
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
diff --git a/engines/mm/mm1/maps/map37.cpp b/engines/mm/mm1/maps/map37.cpp
index ec62eb29d99..b751afdfb3c 100644
--- a/engines/mm/mm1/maps/map37.cpp
+++ b/engines/mm/mm1/maps/map37.cpp
@@ -101,13 +101,13 @@ void Map37::special04() {
 
 	if (_data[VAL1]) {
 		// Okrim has actually been defeated. Consume the square for this
-		// map visit and present his post-battle ghost interaction
+		// map visit and present his post-battle ghost interaction.
 		g_maps->clearSpecial();
 		g_events->addView("Ghost");
 
 	} else {
-		// Don't mark Okrim defeated before combat. The combat view sets
-		// VAL1 only after a real victory, so retreating can't skip him
+		// Do not mark Okrim defeated before combat. Enhanced combat sets
+		// VAL1 only after a real victory, so retreating cannot skip him.
 		enc.clearMonsters();
 		enc.addMonster(19, 12);
 		for (int i = 1; i < 4; ++i)
diff --git a/engines/mm/mm1/maps/map38.cpp b/engines/mm/mm1/maps/map38.cpp
index f1151664605..e8e8e753fb3 100644
--- a/engines/mm/mm1/maps/map38.cpp
+++ b/engines/mm/mm1/maps/map38.cpp
@@ -46,7 +46,7 @@ void Map38::special() {
 
 	g_maps->_mapPos = Common::Point(getRandomNumber(16) - 1,
 		getRandomNumber(16) - 1);
-	// This random relocation bypasses updateGame() and redrawGame()
+	// This random relocation bypasses updateGame()/redrawGame().
 	g_maps->visitedTile();
 	send(SoundMessage(STRING["maps.map38.ringing"]));
 }
diff --git a/engines/mm/mm1/maps/map40.cpp b/engines/mm/mm1/maps/map40.cpp
index ecac8dab959..65f0a35fece 100644
--- a/engines/mm/mm1/maps/map40.cpp
+++ b/engines/mm/mm1/maps/map40.cpp
@@ -59,9 +59,10 @@ void Map40::special() {
 
 		SoundMessage msg(STRING["maps.map40.conveyor_belt"]);
 		if (g_engine->isEnhanced()) {
-			// Enhanced messages normally forward the key that dismissed them
-			// back to the game, which would immediately trigger another
-			// forced step and message. Consume the dismissing input instead
+			// Enhanced messages have focus and normally forward the key used
+			// to dismiss them back to Game. On the conveyor that immediately
+			// triggers another forced step/message, making input feel stuck.
+			// Consume the dismissing input for this message instead.
 			msg._keyCallback = [](const Common::KeyState &) {
 				g_events->close();
 			};
@@ -107,8 +108,8 @@ void Map40::special04() {
 			Map40 &map = *static_cast<Map40 *>(g_maps->_currentMap);
 			map[VAL1]++;
 
-			// Remember the disabled conveyor across a save and load in
-			// this map visit. A normal map transition clears the marker
+			// Preserve the disabled conveyor across a ScummVM save/load in
+			// this map visit. A normal map transition clears this marker.
 			map._visited[g_maps->_mapOffset] |= VISITED_TRANSIENT_STATE;
 		}
 	));
diff --git a/engines/mm/mm1/maps/map43.cpp b/engines/mm/mm1/maps/map43.cpp
index 5f2cc0090fe..8205a96fec4 100644
--- a/engines/mm/mm1/maps/map43.cpp
+++ b/engines/mm/mm1/maps/map43.cpp
@@ -31,8 +31,8 @@ namespace Maps {
 
 #define TREASURE_STATE 142
 
-// Ironfist's quest completions are recorded where they're discovered,
-// so inventory items are no longer used as surrogate proof
+// Lord Ironfist's quest completions are now recorded at the actual
+// discovery sites. Do not substitute inventory items as surrogate proof.
 static const byte MATCH_ITEMS[7] = {
 	0, 0, 0, 0, 0, 0, 0
 };
@@ -204,7 +204,7 @@ Common::String Map43::checkQuestComplete() {
 
 	if (leader._flags[7] & MATCH_FLAGS[qIndex] & 0x7f) {
 		// The quest was complete. Retain it in the Quest Log using
-		// the high bit of a reserved, already-serialized map cell
+		// the high bit of a reserved, already-serialized map cell.
 		_visited[15 * MAP_W + qIndex] |= 0x80;
 
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
diff --git a/engines/mm/mm1/maps/map47.cpp b/engines/mm/mm1/maps/map47.cpp
index be6d6bd32d0..6ec444a8acc 100644
--- a/engines/mm/mm1/maps/map47.cpp
+++ b/engines/mm/mm1/maps/map47.cpp
@@ -99,18 +99,23 @@ void Map47::special02() {
 		_visited[QUEST_CLERICS_COMPLETE_OFFSET] |= QUEST_LOG_FLAG;
 		_visited[QUEST_CLERICS_ACTIVE_OFFSET] &= ~QUEST_LOG_FLAG;
 
+		// Vanilla MM1 behavior: becoming worthy recharges all seven
+		// permanent-stat locations by clearing their GOT_* bits.
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
 			Character &c = g_globals->_party[i];
 			c._flags[11] = CHARFLAG11_CLERICS;
 		}
 
-		// Another advancement requires hearing all three tones again
+		// Quest Log persistence must describe only the current tone cycle.
+		// After a successful Clerics turn-in, both temporary and persisted
+		// Loud/Sharp/Mellow state are cleared so another recharge requires
+		// hearing all three tones again.
 		for (int i = 0; i < 3; ++i) {
 			_data[ARRAY1 + i] = 0;
 			_visited[QUEST_CLERICS_TONE_OFFSET + i] &= ~QUEST_LOG_FLAG;
 		}
 	} else {
-		// Speaking to the Clerics starts the optional advancement quest
+		// Speaking to the Clerics starts the optional advancement quest.
 		_visited[QUEST_CLERICS_ACTIVE_OFFSET] |= QUEST_LOG_FLAG;
 		send(SoundMessage(
 			0, 1, STRING["maps.map47.clerics1"],
diff --git a/engines/mm/mm1/maps/map51.cpp b/engines/mm/mm1/maps/map51.cpp
index a8a552b234b..7465696110b 100644
--- a/engines/mm/mm1/maps/map51.cpp
+++ b/engines/mm/mm1/maps/map51.cpp
@@ -125,8 +125,9 @@ void Map51::special14() {
 }
 
 void Map51::special17() {
-	// The endpoint is both a hard-coded encounter and the Thundranium reward.
-	// Preload the item before combat, as with Ranalou's Thundranium cache
+	// Building of Gold L2 (15,15): the original endpoint is both a
+	// hard-coded encounter and the Thundranium reward. Preload the item
+	// before combat, matching MM1's existing Ranalou Thundranium pattern.
 	g_maps->clearSpecialAndRememberReward();
 	g_globals->_treasure._items[2] = THUNDRANIUM_ID;
 	encounter(&_data[000], &_data[000]);
diff --git a/engines/mm/mm1/maps/maps.cpp b/engines/mm/mm1/maps/maps.cpp
index 9b1318d1261..86938270013 100644
--- a/engines/mm/mm1/maps/maps.cpp
+++ b/engines/mm/mm1/maps/maps.cpp
@@ -415,9 +415,8 @@ void Maps::step(const Common::Point &delta) {
 }
 
 void Maps::changeMap(uint16 id, byte section, bool loadingSave) {
-	// A genuine transition starts a fresh visit, so rewards can be claimed
-	// again. Loading a save retains claimed rewards and other visit-local
-	// scripted state
+	// A genuine transition starts a fresh visit. Loading retains claimed
+	// rewards and other visit-local scripted state.
 	select(id, section);
 
 	for (uint i = 0; i < MAP_SIZE; ++i) {
@@ -446,7 +445,6 @@ void Maps::changeMap(uint16 id, byte section, bool loadingSave) {
 		}
 		(*_currentMap)[MAP04_TREASURE_STOLEN] = stolenCount;
 	}
-
 	loadTiles();
 
 	visitedTile();
@@ -456,18 +454,19 @@ void Maps::changeMap(uint16 id, byte section, bool loadingSave) {
 
 void Maps::changeMapFromExit(uint16 id, byte section) {
 	// Run the normal transition first. Unlike calling the destination
-	// special(), this can't bounce the party straight back through
-	// stairs and doors, or show a second transition prompt
+	// special(), this cannot bounce the party straight back through
+	// stairs/doors or show a second transition prompt.
 	changeMap(id, section);
 
-	// changeMap() records a newly entered destination as VISITED_NORMAL.
-	// This change came through an entrance or exit, so keep the metadata
-	// bits and correct only the tile type to VISITED_EXIT
+	// changeMap()->visitedTile() records a newly entered destination as
+	// VISITED_NORMAL. We know this particular map change came through an
+	// entrance/exit, so preserve metadata bits and correct only the tile
+	// type to VISITED_EXIT.
 	byte &visited = _currentMap->_visited[_mapPos.y * MAP_W + _mapPos.x];
 	visited = (visited & 0xf0) | VISITED_EXIT;
 
-	// changeMap() has already sent the normal Game UPDATE. Repaint once
-	// more so the enhanced minimap shows the exit immediately
+	// changeMap() has already sent the normal Game UPDATE; repaint once
+	// more so the Enhanced minimap shows the white exit immediately.
 	g_events->redrawViews();
 }
 
diff --git a/engines/mm/mm1/maps/maps.h b/engines/mm/mm1/maps/maps.h
index c7157cff04e..36d229106a6 100644
--- a/engines/mm/mm1/maps/maps.h
+++ b/engines/mm/mm1/maps/maps.h
@@ -49,7 +49,7 @@ enum Visited {
 	VISITED_NONE = 0, VISITED_NORMAL = 1, VISITED_SPECIAL = 2,
 	VISITED_EXIT = 3, VISITED_BUSINESS = 4,
 	// Visit-local scripted state. Serialized so save/load can preserve the
-	// current map visit, but cleared when the map is entered normally again
+	// current map visit, but cleared when the map is entered normally again.
 	VISITED_TRANSIENT_STATE = 0x20,
 	VISITED_REWARD_CLAIMED = 0x40
 };
@@ -183,7 +183,7 @@ public:
 	/**
 	 * Change maps through a known entrance/exit/stair transition and
 	 * mark the destination arrival square as an exit without firing
-	 * the destination square's special action
+	 * the destination square's special action.
 	 */
 	void changeMapFromExit(uint16 id, byte section);
 
@@ -200,7 +200,7 @@ public:
 	void clearSpecial();
 
 	/**
-	 * Disables a claimed reward for save/load during this map visit
+	 * Disables a claimed reward for save/load during this map visit.
 	 */
 	void clearSpecialAndRememberReward();
 
diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index 5ab16e026f9..13a55cc9bb8 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -112,7 +112,6 @@ static const KeybindingRecord COMBAT_KEYS[] = {
 	{ KEYBIND_COMBAT_LOG_UP, "COMBAT_LOG_UP", _s("Combat log up"), "UP", nullptr },
 	{ KEYBIND_COMBAT_LOG_DOWN, "COMBAT_LOG_DOWN", _s("Combat log down"), "DOWN", nullptr },
 	{ KEYBIND_ESCAPE, "COMBAT_LOG_CLOSE", _s("Close combat log"), "ESCAPE", nullptr },
-
 	// I18N: Combat command in Might & Magic 1
 	{ KEYBIND_DELAY, "DELAY", _s("Delay"), "d", nullptr },
 	// I18N: Combat command in Might & Magic 1
@@ -241,7 +240,7 @@ Common::KeymapArray MetaEngine::initKeymaps() {
 					r->_action == KEYBIND_STRAFE_LEFT || r->_action == KEYBIND_STRAFE_RIGHT ||
 					r->_action == KEYBIND_BESTIARY_UP || r->_action == KEYBIND_BESTIARY_DOWN ||
 					r->_action == KEYBIND_BESTIARY_PAGEUP || r->_action == KEYBIND_BESTIARY_PAGEDOWN)
-				// Allow movement and list actions to be triggered on keyboard repeats
+				// Allow movement actions to be triggered on keyboard repeats
 				act->allowKbdRepeats();
 
 			keyMap->addAction(act);
diff --git a/engines/mm/mm1/metaengine.h b/engines/mm/mm1/metaengine.h
index 3a9832dce1a..c8e912abd66 100644
--- a/engines/mm/mm1/metaengine.h
+++ b/engines/mm/mm1/metaengine.h
@@ -84,7 +84,6 @@ enum KeybindingAction {
 	KEYBIND_COMBAT_LOG,
 	KEYBIND_COMBAT_LOG_UP,
 	KEYBIND_COMBAT_LOG_DOWN,
-
 	// Cheats
 	KEYBIND_CHEAT_COLLISIONS, KEYBIND_CHEAT_DESTROY,
 	KEYBIND_CHEAT_DESTROY_CREATURES, KEYBIND_CHEAT_EQUIPMENT,
diff --git a/engines/mm/mm1/mm1.cpp b/engines/mm/mm1/mm1.cpp
index f4cdc8d97e5..5ec34f8f9b7 100644
--- a/engines/mm/mm1/mm1.cpp
+++ b/engines/mm/mm1/mm1.cpp
@@ -163,7 +163,6 @@ bool MM1Engine::canLoadGameStateCurrently(Common::U32String *msg) {
 Common::Error MM1Engine::loadGameStream(Common::SeekableReadStream *stream) {
 	Common::Serializer s(stream, nullptr);
 	Common::Error result = synchronizeSave(s);
-
 	if (result.getCode() == Common::kNoError) {
 		if (stream->err())
 			return Common::kReadingFailed;
@@ -171,21 +170,18 @@ Common::Error MM1Engine::loadGameStream(Common::SeekableReadStream *stream) {
 		// Optional extension appended after the standard savegame data
 		g_globals->_bestiary.load(*stream);
 	}
-
 	return result;
 }
 
 Common::Error MM1Engine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
 	Common::Serializer s(nullptr, stream);
 	Common::Error result = synchronizeSave(s);
-
 	if (result.getCode() == Common::kNoError) {
-		// Keep the core savegame data intact and append the extension
+		// Keep the version-1 core intact; append before standard save metadata.
 		g_globals->_bestiary.save(*stream);
 		if (stream->err())
 			return Common::kWritingFailed;
 	}
-
 	return result;
 }
 
@@ -200,7 +196,7 @@ Common::Error MM1Engine::synchronizeSave(Common::Serializer &s) {
 	// If we're loading a savegame, switch to the game view
 	if (s.isLoading()) {
 		// Discard any location animation retained by the enhanced
-		// GameView before restoring the saved game state
+		// GameView before restoring the saved game state.
 		if (g_engine->isEnhanced())
 			g_events->send("View", GameMessage("LOCATION", -1));
 		g_events->replaceView("Game", true);
diff --git a/engines/mm/mm1/views/combat.cpp b/engines/mm/mm1/views/combat.cpp
index 9014a3b4595..c66eb077ce2 100644
--- a/engines/mm/mm1/views/combat.cpp
+++ b/engines/mm/mm1/views/combat.cpp
@@ -41,6 +41,7 @@ static uint combatMessageDelayFrames() {
 	return ((uint)g_globals->_delay + 1) * FRAME_RATE / 2;
 }
 
+
 Combat::Combat() : TextView("Combat") {
 }
 
diff --git a/engines/mm/mm1/views/interactions/giant.cpp b/engines/mm/mm1/views/interactions/giant.cpp
index 6fb5e1dbb0a..25247d7f335 100644
--- a/engines/mm/mm1/views/interactions/giant.cpp
+++ b/engines/mm/mm1/views/interactions/giant.cpp
@@ -90,8 +90,8 @@ void Giant::charSelected(uint charIndex) {
 	writeString(0, 1, line);
 	Sound::sound(SOUND_2);
 
-	// The Statue may have changed XP and a primary attribute. Repaint any
-	// visible character views now rather than at a later refresh
+	// Keep any visible character/party information synchronized with the
+	// just-awarded XP/attribute values.
 	g_events->redrawViews();
 
 	delaySeconds(5);
diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
index ec3e919b12b..5096ae53159 100644
--- a/engines/mm/mm1/views_enh/active_quest.cpp
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -482,7 +482,7 @@ void addOtherReliableCompletions(Common::Array<QuestLogEntry> &entries) {
 }
 
 void buildQuestList(Common::Array<QuestLogEntry> &entries) {
-	// Active objectives always appear first
+	// Active objectives always appear first.
 	addFormalQuest(entries);
 	addCrazedWizard(entries, false);
 	addClerics(entries, false);
@@ -493,7 +493,7 @@ void buildQuestList(Common::Array<QuestLogEntry> &entries) {
 	addRanalou(entries, false);
 	addArenko(entries, false);
 
-	// Completed history follows and remains selectable with Up/Down
+	// Completed history follows and remains selectable with Up/Down.
 	addCompletedRulerQuests(entries);
 	addCrazedWizard(entries, true);
 	addClerics(entries, true);
diff --git a/engines/mm/mm1/views_enh/character_info.cpp b/engines/mm/mm1/views_enh/character_info.cpp
index 59ba3d5c97b..69e6679fcb7 100644
--- a/engines/mm/mm1/views_enh/character_info.cpp
+++ b/engines/mm/mm1/views_enh/character_info.cpp
@@ -306,10 +306,11 @@ void CharacterInfo::drawStats() {
 			setTextColor(c.statColor(CURR[i], BASE[i]));
 			writeNumber(pt.x, pt.y, CURR[i]);
 
-			// Show an upward arrow after the experience when the character
-			// has enough to train for the next level. The font's right-arrow
-			// glyph is rotated to point up, in the colour used for SP == 0
+			// LEVEL-UP-READY: Experience is derived from current XP/level.
+			// Rotate the Xeen right-arrow glyph upward immediately after XP.
+			// Its color is exactly the color used for SP == 0.
 			if (i == 13 && c.canLevelUp()) {
+				// LEVEL-UP-ARROW-ROTATED-V3: rotate only this glyph, not the font.
 				XeenFont &font = _fontReduced ? g_globals->_fontReduced : g_globals->_fontNormal;
 				XeenFont::setColors(c.statColor(0, c._sp._base));
 				Graphics::ManagedSurface glyph, mask;
@@ -317,8 +318,7 @@ void CharacterInfo::drawStats() {
 				const int gh = font.getFontHeight() + 2;
 				glyph.create(gw, gh, Graphics::PixelFormat::createFormatCLUT8());
 				mask.create(gw, gh, Graphics::PixelFormat::createFormatCLUT8());
-				// Draw on two different backgrounds, so pixels the glyph
-				// really touched can be told apart from the background
+				// Two backgrounds distinguish untouched pixels from ALL palette colors.
 				glyph.clear(0);
 				mask.clear(255);
 				font.drawChar(&glyph, '^', 1, 1, 9);
@@ -343,7 +343,7 @@ void CharacterInfo::drawStats() {
 						for (int x = left; x <= right; ++x) {
 							const byte pixel = *(const byte *)glyph.getBasePtr(x, y);
 							if (pixel != *(const byte *)mask.getBasePtr(x, y)) continue;
-							// Counterclockwise: the rightmost source column becomes the top row
+							// Counterclockwise: rightmost source column becomes top row.
 							const int dx = destX + y - top;
 							const int dy = destY + right - x;
 							if (dx >= 0 && dx < s.w && dy >= 0 && dy < s.h)
diff --git a/engines/mm/mm1/views_enh/character_inventory.cpp b/engines/mm/mm1/views_enh/character_inventory.cpp
index 1686e2a61f6..5774c3ecd3c 100644
--- a/engines/mm/mm1/views_enh/character_inventory.cpp
+++ b/engines/mm/mm1/views_enh/character_inventory.cpp
@@ -445,10 +445,11 @@ void CharacterInventory::trade(const Common::String &mode, int amount, Character
 	Character &src = *g_globals->_currCharacter;
 
 	if (mode == "GEMS") {
-		// Gems are a 16-bit value (maximum 65,535). Keep the calculation
-		// wide enough for a five-digit entered amount, and limit the
-		// transfer to both the source balance and the destination capacity
-		// so that excess gems don't disappear
+		// Gems are serialized as uint16 (maximum 65,535).  Keep the
+		// calculation wide enough for a five-digit entered amount and cap
+		// the transfer to both the source balance and destination capacity.
+		// This prevents truncation above 65,535 and prevents excess gems
+		// from disappearing when the recipient is near the cap.
 		uint32 transfer = MIN<uint32>((uint32)amount, (uint32)src._gems);
 		transfer = MIN<uint32>(transfer, 0xffffU - (uint32)destChar->_gems);
 		src._gems -= (uint16)transfer;
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index d76ef5695c0..160276d6688 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -41,6 +41,7 @@ static uint combatMessageDelayFrames() {
 	return ((uint)g_globals->_delay + 1) * FRAME_RATE / 2;
 }
 
+
 #define MONSTERS_X 120
 #define BOTTOM_Y 120
 #define LINE_H 8
@@ -65,7 +66,7 @@ void Combat::setMode(Mode newMode) {
 	}
 
 	if (_mode == MONSTER_SPELL)
-		// Make a copy of monster spell 
+		// Make a copy of monster spell
 		_monsterSpellLines = getMonsterSpellMessage();
 
 	if (_mode != MONSTER_ADVANCES && _mode != MONSTER_ATTACK &&
@@ -197,9 +198,9 @@ void Combat::draw() {
 		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTER_SPELL:
-		// Monster spell effects can cycle several messages through the same
-		// row. Rebuild the complete combat frame so the previous message (or
-		// an underlying dungeon/outdoor redraw) cannot remain on screen
+		// Monster spell effects can cycle several messages through the same row.
+		// Rebuild the complete combat frame so the previous message (or an
+		// underlying dungeon/outdoor redraw) cannot remain on screen.
 		clearSurface();
 		writeStaticContent();
 		writeHandicap();
@@ -397,9 +398,9 @@ bool Combat::msgKeypress(const KeypressMessage &msg) {
 			break;
 		}
 	} else if (_mode == SPELL_RESULT && !isDelayActive()) {
-		// Displaying a spell result that required waiting for keypress.
-		// Some results, such as Holy Word and the cast spell Done/Failed
-		// messages, have no callback and simply finish the character's turn
+		// Match timeout() semantics. Some combat spells, notably Holy Word
+		// and Turn Undead, intentionally use a result message without a
+		// callback. In that case the casting character is simply done.
 		if (_spellResult._callback)
 			_spellResult._callback();
 		else
@@ -577,7 +578,7 @@ bool Combat::msgMouseUp(const MouseUpMessage &msg) {
 		};
 
 		// Delay, Protect, Quick Reference and View Character occupy
-		// the four static command rows on the left side of combat
+		// the four static command rows on the left side of combat.
 		for (int row = 0; row < 4; ++row) {
 			Common::Rect r(0, (7 + row) * LINE_H,
 				80, (8 + row) * LINE_H);
@@ -821,10 +822,11 @@ void Combat::writeSpellTargetSelect() {
 	_attackableCount = _remainingMonsters.size();
 
 	writeBottomText(0, 1, Common::String::format(
-		"Target which (A-%c)?", 'A' + _attackableCount - 1),
-		ALIGN_MIDDLE);
+			"Target which (A-%c)?", 'A' + _attackableCount - 1),
+			ALIGN_MIDDLE);
 }
 
+
 void Combat::writeAttackOptions() {
 	writeOption(0, 0, 'A', STRING["dialogs.combat.attack"]);
 	writeOption(0, 1, 'F', STRING["dialogs.combat.fight"]);
@@ -985,7 +987,7 @@ void Combat::writeMonsterStatus(int monsterNum) {
 		int status;
 		if (statusBits == MONFLAG_DEAD) {
 			status = MON_DEAD;
-		} else {	
+		} else {
 			for (status = MON_PARALYZED; !(statusBits & 0x80);
 					++status, statusBits <<= 1) {
 			}
@@ -1056,7 +1058,7 @@ void Combat::highlightNextRound() {
 	Common::String s = Common::String::format("%s%d",
 		STRING["dialogs.combat.round"].c_str(),
 		_roundNum);
-	
+
 	for (uint i = 0; i < s.size(); ++i)
 		s.setChar(s[i] | 0x80, i);
 
@@ -1090,11 +1092,11 @@ void Combat::writeMonsterAction(bool flees) {
 	resetBottom();
 
 	const Common::String message = Common::String::format(
-		"%s %s", _monsterName.c_str(),
-		STRING[flees ?
-			"dialogs.combat.monster_flees" :
-			"dialogs.combat.monster_wanders"
-		].c_str());
+			"%s %s", _monsterName.c_str(),
+			STRING[flees ?
+				"dialogs.combat.monster_flees" :
+				"dialogs.combat.monster_wanders"
+			].c_str());
 
 	writeBottomText(0, 0, message);
 
@@ -1110,7 +1112,7 @@ void Combat::writeMonsterSpell() {
 			y = _monsterSpellLines[i].y, ++i) {
 		const Line &line = _monsterSpellLines[i];
 		writeBottomText(MAX<int>(line.x, 0), MAX<int>(line.y, 0),
-			line._text, line._align);
+				line._text, line._align);
 	}
 
 	if (beginCombatLogEvent())
@@ -1385,7 +1387,7 @@ void Combat::combatDone() {
 		g_maps->_mapId == 37 && g_maps->_mapPos == Common::Point(12, 12);
 
 	if (okrimVictory) {
-		// Map37::VAL1. Set only after victory so fleeing can't satisfy it
+		// Map37::VAL1. Set only after victory so fleeing cannot satisfy it.
 		(*g_maps->_currentMap)[232] = 1;
 		g_maps->clearSpecial();
 	}
diff --git a/engines/mm/mm1/views_enh/encounter.cpp b/engines/mm/mm1/views_enh/encounter.cpp
index ca2867ee5fe..96b918a34ce 100644
--- a/engines/mm/mm1/views_enh/encounter.cpp
+++ b/engines/mm/mm1/views_enh/encounter.cpp
@@ -198,7 +198,7 @@ void Encounter::timeout() {
 
 	case BATTLE:
 		// Finish the underlying game redraw before starting combat,
-		// and don't redraw this closed Encounter afterwards
+		// and do not redraw this closed Encounter afterward.
 		close();
 		g_events->drawElements();
 		send("Combat", GameMessage("COMBAT"));
diff --git a/engines/mm/mm1/views_enh/game.cpp b/engines/mm/mm1/views_enh/game.cpp
index 042fc69388a..85e641f94b4 100644
--- a/engines/mm/mm1/views_enh/game.cpp
+++ b/engines/mm/mm1/views_enh/game.cpp
@@ -61,7 +61,6 @@ bool Game::msgMouseDown(const MouseDownMessage &msg) {
 		_bestiaryPressed = true;
 		return true;
 	}
-
 	return TextView::msgMouseDown(msg);
 }
 
@@ -70,10 +69,8 @@ bool Game::msgMouseUp(const MouseUpMessage &msg) {
 		_bestiaryPressed = false;
 		if (isFocused() && bestiaryButtonBounds().contains(msg._pos))
 			addView("Bestiary");
-
 		return true;
 	}
-
 	return TextView::msgMouseUp(msg);
 }
 
diff --git a/engines/mm/mm1/views_enh/game_commands.cpp b/engines/mm/mm1/views_enh/game_commands.cpp
index a60f067efc2..47aa628f8f9 100644
--- a/engines/mm/mm1/views_enh/game_commands.cpp
+++ b/engines/mm/mm1/views_enh/game_commands.cpp
@@ -42,7 +42,7 @@ GameCommands::GameCommands(UIElement *owner) :
 
 	addButton(Common::Rect(235, 117, 259, 137), KEYBIND_MAP, 12, spr);
 	addButton(Common::Rect(260, 117, 284, 137), KEYBIND_QUICKREF, 16, spr);
-	// Complete the 3x3 command grid with the native Quest Log icon
+	// Complete the 3x3 command grid with a native Quest Log icon.
 	addButton(Common::Rect(286, 117, 310, 137), KEYBIND_ACTIVE_QUEST, 8, spr);
 
 	addButton(Common::Rect(109, 137, 122, 147), KEYBIND_MENU, 18, spr);
diff --git a/engines/mm/mm1/views_enh/interactions/giant.cpp b/engines/mm/mm1/views_enh/interactions/giant.cpp
index d69bee2756f..3cbfcb923be 100644
--- a/engines/mm/mm1/views_enh/interactions/giant.cpp
+++ b/engines/mm/mm1/views_enh/interactions/giant.cpp
@@ -55,8 +55,8 @@ void Giant::charSwitched(Character *priorChar) {
 	addText(line);
 	Sound::sound(SOUND_2);
 
-	// The Statue may have changed XP and a primary attribute. Repaint any
-	// visible character views now rather than at a later refresh
+	// Map30 may have changed XP and a primary attribute. Repaint any visible
+	// character/party views immediately rather than waiting for a later refresh.
 	g_events->redrawViews();
 
 	delaySeconds(5);
diff --git a/engines/mm/mm1/views_enh/interactions/hacker.cpp b/engines/mm/mm1/views_enh/interactions/hacker.cpp
index 6dbf8dde3e5..c45d3f64b8c 100644
--- a/engines/mm/mm1/views_enh/interactions/hacker.cpp
+++ b/engines/mm/mm1/views_enh/interactions/hacker.cpp
@@ -82,8 +82,10 @@ bool Hacker::msgKeypress(const KeypressMessage &msg) {
 		if (msg.keycode == Common::KEYCODE_y) {
 			map.acceptQuest();
 
-			// Asking for an eighth quest is a punishment, which
-			// leaves the quest at zero
+			// Hacker's post-quest-7 branch is a punishment, not an eighth
+			// quest. acceptQuest() intentionally leaves _quest at zero there.
+			// Do not fall through to the normal quest ingredient lookup with
+			// an invalid negative index; Classic already guards this case.
 			if (!g_globals->_party[0]._quest) {
 				close();
 				return true;
diff --git a/engines/mm/mm1/views_enh/items_view.cpp b/engines/mm/mm1/views_enh/items_view.cpp
index 2d0d70ac4b6..b1742910e0f 100644
--- a/engines/mm/mm1/views_enh/items_view.cpp
+++ b/engines/mm/mm1/views_enh/items_view.cpp
@@ -51,7 +51,7 @@ void ItemsView::addButton(int frame, const Common::String &text,
 bool ItemsView::msgFocus(const FocusMessage &msg) {
 	PartyView::msgFocus(msg);
 
-	// F1-F6 switch party members while 1-6 remain available for items
+	// F1-F6 switch party members while 1-6 remain available for items.
 
 	_selectedItem = -1;
 	return true;
diff --git a/engines/mm/mm1/views_enh/locations/training.cpp b/engines/mm/mm1/views_enh/locations/training.cpp
index 7cbe4b7bd11..d4c8775239e 100644
--- a/engines/mm/mm1/views_enh/locations/training.cpp
+++ b/engines/mm/mm1/views_enh/locations/training.cpp
@@ -79,7 +79,6 @@ void Training::draw() {
 bool Training::msgGame(const GameMessage &msg) {
 	if (msg._name == "UPDATE")
 		checkCharacter();
-
 	Location::msgGame(msg);
 	return true;
 }
diff --git a/engines/mm/mm1/views_enh/map.cpp b/engines/mm/mm1/views_enh/map.cpp
index 88a9533155c..9988fa69931 100644
--- a/engines/mm/mm1/views_enh/map.cpp
+++ b/engines/mm/mm1/views_enh/map.cpp
@@ -54,7 +54,7 @@ void Map::draw() {
 			mazeY >= yStart; yp += MAP_TILE_H, --mazeY) {
 		for (int xp = MAP_BORDER_SIZE, mazeX = xStart;
 				mazeX < (xStart + totalX); xp += MAP_TILE_W, ++mazeX) {
-			// The high bit may contain map-specific persistent state
+			// The high bit may contain map-specific persistent state.
 			byte visited = map._visited[mazeY * MAP_W + mazeX] & 0x0f;
 
 			if (!visited) {
diff --git a/engines/mm/mm1/views_enh/protect.cpp b/engines/mm/mm1/views_enh/protect.cpp
index 8cdc99eb240..76438f4f1bf 100644
--- a/engines/mm/mm1/views_enh/protect.cpp
+++ b/engines/mm/mm1/views_enh/protect.cpp
@@ -154,6 +154,7 @@ void Protect::draw() {
 			}
 		}
 	}
+
 }
 
 bool Protect::msgAction(const ActionMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/search.cpp b/engines/mm/mm1/views_enh/search.cpp
index 8c95df5dedd..d75e75e8a0e 100644
--- a/engines/mm/mm1/views_enh/search.cpp
+++ b/engines/mm/mm1/views_enh/search.cpp
@@ -206,8 +206,8 @@ void Search::timeout() {
 
 	case GET_TREASURE:
 		// Gold and gems are already awarded by drawTreasure(). If there are
-		// no item slots to show, close now rather than entering
-		// GET_ITEMS_DONE and requiring a second dismissal.
+		// no actual item slots left to show, close the Search view now rather
+		// than entering GET_ITEMS_DONE and requiring a second dismissal.
 		if (g_globals->_treasure.hasItems()) {
 			setMode(GET_ITEMS);
 			draw();
@@ -298,7 +298,8 @@ void Search::detectMagicTrap2() {
 		c._sp._current--;
 		char magic = g_globals->_treasure.hasItems() ||
 			g_globals->_treasure.getGems() ? 'Y' : 'N';
-		char trapped = g_globals->_treasure._trapType == 1 ? 'Y' : 'N';
+		char trapped =
+			g_globals->_treasure._trapType == 1 ? 'Y' : 'N';
 
 		_responseLine = 1;
 		_responseText = Common::String::format(
@@ -400,9 +401,9 @@ void Search::drawTreasure() {
 	}
 
 	Sound::sound2(SOUND_5);
-
-	// Gold/gem-only loot stays visible for longer, unless the player
-	// dismisses it early with a keypress or mouse click
+	// Preserve the normal 2-second handoff when actual items remain.
+	// Gold/gem-only loot stays visible for 7 seconds unless the player
+	// dismisses the active delay early with a keypress or mouse click.
 	delaySeconds(g_globals->_treasure.hasItems() ? 2 : 7);
 }
 
@@ -424,8 +425,8 @@ void Search::drawItem() {
 		}
 
 		if (!recipient) {
-			// Leave the item so it can be collected by Searching again
-			// once a backpack slot has been freed
+			// Keep the item in Treasure so it can be collected after the
+			// player frees a backpack slot and Searches again.
 			writeLine(_lineNum++, "Party inventory is full!", ALIGN_MIDDLE);
 			setMode(GET_ITEMS_DONE);
 			delaySeconds(7);
@@ -450,11 +451,10 @@ void Search::drawItem() {
 		}
 	}
 
-	// Finished the final item
+	// Finished the final item. Keep the existing acknowledgement timing.
 	setMode(GET_ITEMS_DONE);
 	delaySeconds(7);
 }
-
 void Search::setMode(Mode mode) {
 	_mode = mode;
 
diff --git a/engines/mm/mm1/views_enh/spells/cast_spell.cpp b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
index f8f548b10f4..7a5c4a8e7d0 100644
--- a/engines/mm/mm1/views_enh/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
@@ -74,7 +74,7 @@ void CastSpell::draw() {
 	if (spellNum >= 0 && spellNum < 47) {
 		spellName = STRING[Common::String::format("spells.cleric.%d", spellNum)];
 
-		// Cleric 2-5 gives fire protection, but the string is "Ice"
+		// MM1 Cleric 2-5 is Protection From Fire.
 		if (spellNum == 12)
 			spellName = "Protection From Fire";
 	} else if (spellNum >= 47) {
@@ -149,7 +149,8 @@ bool CastSpell::msgGame(const GameMessage &msg) {
 		close();
 		castSpell(&g_globals->_party[msg._value]);
 
-	} else if (msg._name == "MONSTER_SELECTED" && msg._value != -1) {
+	} else if (msg._name == "MONSTER_SELECTED" &&
+			msg._value != -1) {
 		castSpell();
 	}
 
@@ -229,7 +230,6 @@ void CastSpell::castSpell(Character *target) {
 			g_events->send(InfoMessage(msg));
 		}
 	};
-
 	Character &c = *g_globals->_currCharacter;
 	if (isInCombat()) {
 		Common::String spellName = _spellIndex < 47 ?
@@ -275,8 +275,8 @@ void CastSpell::spellError() {
 	Common::String msg = getSpellError();
 
 	if (isInCombat()) {
-		// A global drawElements() here can redraw the underlying
-		// dungeon/outdoor scene over the battle screen
+		// Keep spell errors inside Combat. A global drawElements() here can
+		// redraw the underlying dungeon/outdoor scene over the battle screen.
 		send("Combat", GameMessage("SPELL_RESULT", msg, 0));
 	} else {
 		g_events->drawElements();
diff --git a/engines/mm/mm1/views_enh/spells/spellbook.cpp b/engines/mm/mm1/views_enh/spells/spellbook.cpp
index 7ea7695b711..11b875eb172 100644
--- a/engines/mm/mm1/views_enh/spells/spellbook.cpp
+++ b/engines/mm/mm1/views_enh/spells/spellbook.cpp
@@ -139,7 +139,7 @@ void Spellbook::draw() {
 				spellIndex
 			)];
 
-			// Cleric 2-5 gives fire protection, but the string is "Ice"
+			// MM1 Cleric 2-5 is Protection From Fire.
 			if (!_isWizard && spellIndex == 12)
 				spellName = "Protection From Fire";
 
@@ -162,9 +162,10 @@ void Spellbook::draw() {
 }
 
 bool Spellbook::msgKeypress(const KeypressMessage &msg) {
-	// Some keyboard paths report the navigation-labelled numpad keys as
-	// KP9 and KP3. Treat them as Page Up and Page Down only while Num Lock
-	// is off, so numeric keypad input still works with it on
+	// MM1-NUMPAD-PAGE-SCROLL: ScummVM reports the navigation-labelled
+	// numpad keys as KP9/KP3 on some Windows/SDL keyboard paths. Treat them
+	// as Page Up/Page Down only while Num Lock is OFF, preserving numeric
+	// keypad input while Num Lock is ON.
 	const bool pageUpKey = msg.keycode == Common::KEYCODE_PAGEUP ||
 		(msg.keycode == Common::KEYCODE_KP9 && !(msg.flags & Common::KBD_NUM));
 	const bool pageDownKey = msg.keycode == Common::KEYCODE_PAGEDOWN ||
@@ -269,7 +270,7 @@ void Spellbook::loadFavorites() {
 	for (int slot = 0; slot < 10; ++slot) {
 		const Common::String key = favoritesKey(slot);
 
-		// Stored value is spell index + 1; zero means unused slot
+		// Stored value is spell index + 1; zero means unused slot.
 		const int value = ConfMan.hasKey(key) ? ConfMan.getInt(key) : 0;
 		_favoriteSlots[slot] = value - 1;
 
@@ -316,12 +317,14 @@ void Spellbook::assignFavoriteSlot(int slot) {
 	const int spellIndex = _favoriteAssignSpell;
 	const int knownCount = _count;
 
-	// Moving an existing favorite removes its previous assignment
+	// Moving an existing favorite removes its previous assignment.
 	for (int i = 0; i < 10; ++i) {
 		if (_favoriteSlots[i] == spellIndex)
 			_favoriteSlots[i] = -1;
 	}
 
+	// Replacing a slot simply removes the previous spell's favorite
+	// assignment and puts this spell in that shortcut position.
 	_favoriteSlots[slot] = spellIndex;
 	saveFavorites();
 
@@ -329,7 +332,7 @@ void Spellbook::assignFavoriteSlot(int slot) {
 	_favoriteAssignSpell = -1;
 	buildSpellOrder(knownCount);
 
-	// Keep the newly assigned spell highlighted
+	// Keep the newly assigned spell highlighted.
 	_selectedIndex = 0;
 	for (int i = 0; i < _count; ++i) {
 		if (_spellOrder[i] == spellIndex) {
@@ -380,7 +383,7 @@ void Spellbook::buildSpellOrder(int knownCount) {
 	int nextNormal = 0;
 
 	// The first ten visible entries correspond directly to shortcuts
-	// 1-0. A favorite assigned to a slot is fixed to that exact row
+	// 1-0. A favorite assigned to a slot is fixed to that exact row.
 	for (int slot = 0; slot < 10 &&
 			(int)_spellOrder.size() < knownCount; ++slot) {
 		const int favorite = _favoriteSlots[slot];
@@ -391,8 +394,9 @@ void Spellbook::buildSpellOrder(int knownCount) {
 			continue;
 		}
 
-		// Fill an unused slot with the next ordinary spell, keeping
-		// assigned favorites reserved for their own shortcut rows
+		// Fill an unused favorite slot with the next ordinary spell,
+		// while reserving all explicitly assigned favorites for their
+		// designated shortcut positions.
 		while (nextNormal < knownCount &&
 				(used[nextNormal] || isFavorite(nextNormal)))
 			++nextNormal;
@@ -404,7 +408,7 @@ void Spellbook::buildSpellOrder(int knownCount) {
 		}
 	}
 
-	// Append every remaining spell in normal order
+	// Append every remaining spell in normal MM1 order.
 	for (int spellIndex = 0; spellIndex < knownCount; ++spellIndex) {
 		if (!used[spellIndex])
 			_spellOrder.push_back(spellIndex);
@@ -412,7 +416,6 @@ void Spellbook::buildSpellOrder(int knownCount) {
 
 	_count = (int)_spellOrder.size();
 }
-
 void Spellbook::updateChar() {
 	// Refresh the cast spell side dialog for new character
 	send("CastSpell", GameMessage("UPDATE"));
@@ -429,8 +432,8 @@ void Spellbook::updateChar() {
 		knownCount = 0;
 	} else {
 		knownCount = (c._spellLevel._current < 5) ?
-			c._spellLevel._current * 8 :
-			32 + (c._spellLevel._current - 4) * 5;
+				c._spellLevel._current * 8 :
+				32 + (c._spellLevel._current - 4) * 5;
 	}
 
 	_favoriteAssignMode = false;
@@ -462,7 +465,7 @@ void Spellbook::spellSelected() {
 		return;
 
 	int spellIndex = (_isWizard ? CATEGORY_SPELLS_COUNT : 0) +
-		_spellOrder[_selectedIndex];
+			_spellOrder[_selectedIndex];
 
 	// Set the selected spell for the character
 	c.setSpellNumber(spellIndex);
diff --git a/engines/mm/mm1/views_enh/which_character.cpp b/engines/mm/mm1/views_enh/which_character.cpp
index 9527104a6d9..b6d2ce49759 100644
--- a/engines/mm/mm1/views_enh/which_character.cpp
+++ b/engines/mm/mm1/views_enh/which_character.cpp
@@ -66,7 +66,7 @@ bool WhichCharacter::msgAction(const ActionMessage &msg) {
 		if (charNum < g_globals->_party.size()) {
 			if (dynamic_cast<Combat *>(g_events->priorView()) != nullptr) {
 				// Apply the exchange before closing so Combat
-				// redraws the updated party order immediately
+				// redraws the updated party order immediately.
 				send("Combat", GameMessage("EXCHANGE", charNum));
 				close();
 			} else {


Commit: 1bbea618746132f0266a65ab915ac7d91495865c
    https://github.com/scummvm/scummvm/commit/1bbea618746132f0266a65ab915ac7d91495865c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:05+11:00

Commit Message:
MM: MM1: Keep the Y and N keys working in scrolling Yes/No messages

Thanks to dragoo84

Changed paths:
    engines/mm/mm1/metaengine.cpp


diff --git a/engines/mm/mm1/metaengine.cpp b/engines/mm/mm1/metaengine.cpp
index 13a55cc9bb8..c2b449a7479 100644
--- a/engines/mm/mm1/metaengine.cpp
+++ b/engines/mm/mm1/metaengine.cpp
@@ -70,6 +70,10 @@ static const KeybindingRecord QUEST_LOG_KEYS[] = {
 	{ KEYBIND_ESCAPE, "ESCAPE", _s("Escape"), "ESCAPE", nullptr },
 	// I18N: Return key
 	{ KEYBIND_SELECT, "SELECT", _s("Select"), "RETURN", nullptr },
+	// I18N: N key for No
+	{ KEYBIND_KEY_N, "KEY_N", _s("No"), "n", nullptr },
+	// I18N: Y key for Yes
+	{ KEYBIND_KEY_Y, "KEY_Y", _s("Yes"), "y", nullptr },
 	{ KEYBIND_QUEST_PREVIOUS, "QUEST_PREVIOUS", _s("Previous quest"), "UP", nullptr },
 	{ KEYBIND_QUEST_NEXT, "QUEST_NEXT", _s("Next quest"), "DOWN", nullptr },
 	{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr }


Commit: bd3a42a557919dad7ae02ac442c76c9d7ed6a3f5
    https://github.com/scummvm/scummvm/commit/bd3a42a557919dad7ae02ac442c76c9d7ed6a3f5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:06+11:00

Commit Message:
MM: MM1: Add mapText helper for text that is laid out differently when enhanced

Changed paths:
    engines/mm/mm1/utils/strings.cpp
    engines/mm/mm1/utils/strings.h


diff --git a/engines/mm/mm1/utils/strings.cpp b/engines/mm/mm1/utils/strings.cpp
index 8feb11e6015..20f5b301177 100644
--- a/engines/mm/mm1/utils/strings.cpp
+++ b/engines/mm/mm1/utils/strings.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/utils/strings.h"
+#include "mm/mm1/mm1.h"
 
 namespace MM {
 namespace MM1 {
@@ -29,5 +30,17 @@ void pad_string(Common::String &str, size_t len, char c) {
 		str += c;
 }
 
+Common::String mapText(const char *str) {
+	Common::String result = str;
+	if (g_engine->isEnhanced()) {
+		for (uint i = 0; i < result.size(); ++i) {
+			if (result[i] == '\n')
+				result.setChar(' ', i);
+		}
+	}
+
+	return result;
+}
+
 } // namespace MM1
 } // namespace MM
diff --git a/engines/mm/mm1/utils/strings.h b/engines/mm/mm1/utils/strings.h
index 033b7c6bf5f..2f8f38d674d 100644
--- a/engines/mm/mm1/utils/strings.h
+++ b/engines/mm/mm1/utils/strings.h
@@ -29,6 +29,12 @@ namespace MM1 {
 
 extern void pad_string(Common::String &str, size_t len, char c = ' ');
 
+/**
+ * Returns text laid out with newlines for the classic interface.
+ * The enhanced interface wraps text itself, so newlines become spaces.
+ */
+extern Common::String mapText(const char *str);
+
 } // namespace MM1
 } // namespace MM
 


Commit: bb1118d8add2de45e5bad0687fb2c34ec5d946fe
    https://github.com/scummvm/scummvm/commit/bb1118d8add2de45e5bad0687fb2c34ec5d946fe
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:06+11:00

Commit Message:
MM: MM1: Replace STRING lookups in game logic with literal text

Changed paths:
  A engines/mm/mm1/data/text.cpp
  A engines/mm/mm1/data/text.h
    engines/mm/mm1/console.cpp
    engines/mm/mm1/data/character.cpp
    engines/mm/mm1/game/combat.cpp
    engines/mm/mm1/game/equip_remove.cpp
    engines/mm/mm1/game/monster_touch.cpp
    engines/mm/mm1/game/rest.cpp
    engines/mm/mm1/game/spell_casting.cpp
    engines/mm/mm1/game/spells_monsters.cpp
    engines/mm/mm1/game/spells_party.cpp
    engines/mm/mm1/game/use_item.cpp
    engines/mm/mm1/game/view_base.cpp
    engines/mm/mm1/game/wheel_spin.cpp
    engines/mm/module.mk


diff --git a/engines/mm/mm1/console.cpp b/engines/mm/mm1/console.cpp
index 7321dc6ebc9..6aa047141eb 100644
--- a/engines/mm/mm1/console.cpp
+++ b/engines/mm/mm1/console.cpp
@@ -156,8 +156,8 @@ void stageEncounter() {
 
 void stageTripleBeepSpecial() {
 	g_events->send("GameMessages", InfoMessage(
-		0, 1, STRING["maps.map13.snake_pit"],
-		0, 2, STRING["maps.map13.levitation2"]
+		0, 1, "Snake pit! ",
+		0, 2, "Levitation saved you."
 	));
 	Sound::sound(SOUND_3);
 }
@@ -186,7 +186,7 @@ bool runSoundTestCase(const Common::String &testName) {
 		return stageWallBump();
 
 	} else if (!scumm_stricmp(testName.c_str(), "message")) {
-		g_events->send("GameMessages", SoundMessage(STRING["dialogs.search.nothing"]));
+		g_events->send("GameMessages", SoundMessage("nothing"));
 		return true;
 
 	} else if (!scumm_stricmp(testName.c_str(), "triple")) {
diff --git a/engines/mm/mm1/data/character.cpp b/engines/mm/mm1/data/character.cpp
index ac1a09d12f4..ab567acfc75 100644
--- a/engines/mm/mm1/data/character.cpp
+++ b/engines/mm/mm1/data/character.cpp
@@ -566,31 +566,31 @@ Common::String Character::getConditionString() const {
 	int cond = _condition;
 
 	if (cond == 0) {
-		result += STRING["stats.conditions.good"];
+		result += "Good";
 	} else if (cond == ERADICATED) {
-		result += STRING["stats.conditions.eradicated"];
+		result += "Eradicated";
 	} else {
 		if (cond & BAD_CONDITION) {
 			// Fatal conditions
 			if (cond & DEAD)
-				result += STRING["stats.conditions.dead"] + ",";
+				result += "Dead,";
 			if (cond & STONE)
-				result += STRING["stats.conditions.stone"] + ",";
+				result += "Stone,";
 		} else {
 			if (cond & UNCONSCIOUS)
-				result += STRING["stats.conditions.unconscious"] + ",";
+				result += "Unconscious,";
 			if (cond & PARALYZED)
-				result += STRING["stats.conditions.paralyzed"] + ",";
+				result += "Paralyzed,";
 			if (cond & POISONED)
-				result += STRING["stats.conditions.poisoned"] + ",";
+				result += "Poisoned,";
 			if (cond & DISEASED)
-				result += STRING["stats.conditions.diseased"] + ",";
+				result += "Diseased,";
 			if (cond & SILENCED)
-				result += STRING["stats.conditions.silenced"] + ",";
+				result += "Silenced,";
 			if (cond & BLINDED)
-				result += STRING["stats.conditions.blinded"] + ",";
+				result += "Blinded,";
 			if (cond & ASLEEP)
-				result += STRING["stats.conditions.asleep"] + ",";
+				result += "Asleep,";
 		}
 
 		result.deleteLastChar();
@@ -808,17 +808,17 @@ ConditionEnum Character::worstCondition() const {
 
 Common::String Character::getConditionString(ConditionEnum cond) {
 	switch (cond) {
-	case C_ERADICATED: return STRING["stats.conditions.eradicated"];
-	case C_DEAD: return STRING["stats.conditions.dead"];
-	case C_STONE: return STRING["stats.conditions.stone"];
-	case C_UNCONSCIOUS: return STRING["stats.conditions.unconscious"];
-	case C_PARALYZED: return STRING["stats.conditions.paralyzed"];
-	case C_POISONED: return STRING["stats.conditions.poisoned"];
-	case C_DISEASED: return STRING["stats.conditions.diseased"];
-	case C_SILENCED: return STRING["stats.conditions.silenced"];
-	case C_BLINDED: return STRING["stats.conditions.blinded"];
-	case C_ASLEEP: return STRING["stats.conditions.asleep"];
-	default: return STRING["stats.conditions.good"];
+	case C_ERADICATED: return "Eradicated";
+	case C_DEAD: return "Dead";
+	case C_STONE: return "Stone";
+	case C_UNCONSCIOUS: return "Unconscious";
+	case C_PARALYZED: return "Paralyzed";
+	case C_POISONED: return "Poisoned";
+	case C_DISEASED: return "Diseased";
+	case C_SILENCED: return "Silenced";
+	case C_BLINDED: return "Blinded";
+	case C_ASLEEP: return "Asleep";
+	default: return "Good";
 	}
 }
 
diff --git a/engines/mm/mm1/data/text.cpp b/engines/mm/mm1/data/text.cpp
new file mode 100644
index 00000000000..089df36da63
--- /dev/null
+++ b/engines/mm/mm1/data/text.cpp
@@ -0,0 +1,424 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mm/mm1/data/text.h"
+
+namespace MM {
+namespace MM1 {
+
+const char *const CLASS_NAMES[6] = {
+	"Knight",
+	"Paladin",
+	"Archer",
+	"Cleric",
+	"Sorcerer",
+	"Robber"
+};
+
+const char *const RACE_NAMES[5] = {
+	"Human",
+	"Elf",
+	"Dwarf",
+	"Gnome",
+	"H-Orc"
+};
+
+const char *const ALIGNMENT_NAMES[3] = {
+	"Good",
+	"Neut",
+	"Evil"
+};
+
+const char *const SEX_NAMES[3] = {
+	"Male",
+	"Female",
+	"Yes Please"
+};
+
+const char *const TOWN_NAMES[5] = {
+	"Sorpigal",
+	"Portsmith",
+	"Algary",
+	"Dusk",
+	"Erliquin"
+};
+
+const char *const COLOR_NAMES[8] = {
+	"Red Thorac",
+	"Blue Ogram",
+	"Green Bagar",
+	"Yellow Limra",
+	"Purple Sagran",
+	"Orange Oolak",
+	"Black Dresidion",
+	"White Dilithium"
+};
+
+const char *const CLERIC_SPELL_NAMES[47] = {
+	"Awaken",
+	"Bless",
+	"Blind",
+	"First Aid",
+	"Light",
+	"Power Cure",
+	"Prot. From Fear",
+	"Turn Undead",
+	"Cure Wounds",
+	"Heroism",
+	"Pain",
+	"Prot. From Cold",
+	"Prot. From Ice",
+	"Prot. From Poison",
+	"Silence",
+	"Suggestion",
+	"Create Food",
+	"Cure Blindness",
+	"Cure Paralysis",
+	"Lasting Light",
+	"Produce Flame",
+	"Produce Frost",
+	"Remove Quest",
+	"Walk on Water",
+	"Cure Disease",
+	"Neutralize Poison",
+	"Prot. From Acid",
+	"Prot. From Electricity",
+	"Restore Alignment",
+	"Summon Lightning",
+	"Super Heroism",
+	"Surface",
+	"Deadly Swarm",
+	"Dispel Magic",
+	"Paralyze",
+	"Remove Condition",
+	"Restore Energy",
+	"Moon Ray",
+	"Raise Dead",
+	"Rejuvinate",
+	"Stone to Flesh",
+	"Town Portal",
+	"Divine Intervention",
+	"Holy Word",
+	"Prot. From Elements",
+	"Ressurection",
+	"Sun Ray"
+};
+
+const char *const WIZARD_SPELL_NAMES[47] = {
+	"Awaken",
+	"Detect Magic",
+	"Energy Blast",
+	"Flame Arrow",
+	"Leather Skin",
+	"Light",
+	"Location",
+	"Sleep",
+	"Electric Arrow",
+	"Hypnotize",
+	"identify Monster",
+	"Jump",
+	"Levitate",
+	"Power",
+	"Quickness",
+	"Scare",
+	"Fireball",
+	"Fly",
+	"Invisibility",
+	"Lightning Bolt",
+	"Make Room",
+	"Slow",
+	"Weaken",
+	"Web",
+	"Acid Arrow",
+	"Cold Beam",
+	"Feeble Mind",
+	"Freeze",
+	"Guard Dog",
+	"Psychic Protection",
+	"Shield",
+	"Time Distortion",
+	"Acid Rain",
+	"Dispel Magic",
+	"Finger of Death",
+	"Shelter",
+	"Teleport",
+	"Dancing Sword",
+	"Disintegration",
+	"Etherialize",
+	"Prot. From Magic",
+	"Recharge Item",
+	"Astral Spell",
+	"Duplication",
+	"Meteor Shower",
+	"Power Shield",
+	"Prismatic Light"
+};
+
+const char *const MONSTER_EFFECTS[9] = {
+	"is scared",
+	"is blinded",
+	"is silenced",
+	"is mindless",
+	"is slept",
+	"is held",
+	"is webbed",
+	"is paralyzed",
+	"is affected"
+};
+
+const char *const CHAR_EFFECTS[11] = {
+	"is slept",
+	"is blinded",
+	"is silenced",
+	"is diseased",
+	"is poisoned",
+	"is paralyzed",
+	"is rendered unconscious",
+	"is killed",
+	"is turned to stone",
+	"is eradicated",
+	"is affected"
+};
+
+const char *const LOCATION_TITLES[5] = {
+	"--training--",
+	"--market--",
+	"--temple--",
+	"--blacksmith--",
+	"--tavern--"
+};
+
+const char *const OBSTRUCTED_MESSAGES[6] = {
+	"   solid!   ",
+	"   locked!  ",
+	" too dense! ",
+	" impassable ",
+	" rough seas ",
+	" too windy! "
+};
+
+const char *const COMBAT_STATUS[9] = {
+	"(paralyze)",
+	"(webbed)  ",
+	"(held)    ",
+	"(asleep)  ",
+	"(mindless)",
+	"(silenced)",
+	"(blinded) ",
+	"(afraid)  ",
+	"(dead)    "
+};
+
+const char *const ATTACK_TYPES[15] = {
+	"attacks",
+	"fights",
+	"charges",
+	"assaults",
+	"battles",
+	"stabs at",
+	"flails at",
+	"lunges at",
+	"swings at",
+	"chops at",
+	"hacks at",
+	"thrusts at",
+	"slashes at",
+	"strikes at",
+	"thrashes at"
+};
+
+const char *const GAME_COMMANDS[16] = {
+	" commands",
+	" ",
+	"[ forward",
+	"\\ back",
+	"] turn",
+	"   left",
+	"^ turn",
+	"   right",
+	"o order",
+	"p protect",
+	"r rest",
+	"s search",
+	"b bash",
+	"u unlock",
+	"q quikref",
+	"# view ch"
+};
+
+const char *const PROTECTION_NAMES[18] = {
+	"Fear",
+	"Cold",
+	"Fire",
+	"Poison",
+	"Acid",
+	"Elec",
+	"Magic",
+	"Light (",
+	"Leather Skin",
+	"Levitate",
+	"Walk on Water",
+	"Guard Dog",
+	"Psychic Protection",
+	"Bless",
+	"Invisibility",
+	"Shield",
+	"Power Shield",
+	"*** Cursed -"
+};
+
+const char *const SEARCH_CONTAINERS[11] = {
+	" Cloth Sack ",
+	"Leather Sack",
+	" Wooden Box ",
+	"Wooden Chest",
+	"  Iron Box  ",
+	" Iron Chest ",
+	" Silver Box ",
+	"Silver Chest",
+	"  Gold Box  ",
+	" Gold Chest ",
+	" Black Box  "
+};
+
+const char *const STATUE_NAMES[8] = {
+	"a human knight",
+	"an elven wizard",
+	"a gnome robber",
+	"a dwarf paladin\npainted a black & white checkered motif",
+	"a h-orc archer",
+	"a human cleric",
+	"a blue dragon",
+	"a gray minotaur"
+};
+
+const char *const STATUE_MESSAGES[8] = {
+	"Services rendered, secrets unfold\nthe brothers together\nlead to treasures untold\n\nfive towns you must travel\nfor this quest to unravel",
+	"Seek the wizard Ranalou\nin his lair at the Korin Bluffs\nsix castles he will send you to\nbut Doom will be quite tough!\ncruelty and kindness\nmeasured throughout\njudgement day is then sought out",
+	"One by water, one by land\none by air, and one by sand\nthe wheel of luck\nwill favorably pay, the more of these\nmenacing beasts you slay!\nAlthough wishes may come true\nall the beasts will become anew",
+	"There are many dungeons like me\nfind the right pair\nand you'll discover the key\nthe ancient seer Og has lost his sight\nthe idols will help to end his plight",
+	"In honor of Corak...\n\nfor his mapping expedition\nof the land of Varn and rediscovery\nof the lost town of Dusk",
+	"In honor of Gala...\n\nfor her brave attempt to work with the\nsavages of the Volcanic Isles",
+	"In memory of a time long ago...\n\nbefore the days when the towns\nmoved underground, dragons were\nfew and far between",
+	"This beast once roamed the\nEnchanted Forest and now rules\na great fortress there"
+};
+
+const char *const TAVERN_RUMORS[17] = {
+	"All portals are connected",
+	"Sorpigal has 8 statues",
+	"The brothers live by docks",
+	"Some caves have more than one exit",
+	"The ice princess has the key",
+	"The desert has many oases",
+	"The swamp was once a city of gold",
+	"Portsmith and men don't mix",
+	"Most towns have caverns below",
+	"Varn is not what it appears to be",
+	"Kilburn is near the wyvern peaks",
+	"A dragons breath is only as strong\x8b""as it's condition",
+	"The king is in seclusion",
+	"",
+	"The inner sanctum is a myth",
+	"The canine has the key",
+	"ScummVM is totally awesome!"
+};
+
+const char *const INGREDIENTS[7] = {
+	"garlic",
+	"wolfsbane",
+	"belladonna",
+	"the head of a medusa",
+	"an eye of a wyvern",
+	"a dragons tooth",
+	"the ring of Okrim"
+};
+
+const char *const INSPECTRON_QUESTS[7] = {
+	"Find the ancient ruins in the\nQuivering Forest",
+	"Visit Blithes Peak, and report",
+	"The people of the desert have much to\ntrade,bring me a sample of their goods",
+	"Find the Shrine of Okzar in the\ncaves below Dusk",
+	"Find the Fabled Fountain in Dragadune",
+	"Solve the Riddle of the Ruby",
+	"Defeat the stronghold\nin the Enchanted Forest"
+};
+
+const char *const IRONFIST_QUESTS[7] = {
+	"Find the stronghold in Ravens Wood",
+	"Find Lord Kilburn",
+	"Discover the secret of Portsmith",
+	"Find the pirates secret cove",
+	"Find the shipwreck of the Jolly Raven",
+	"Defeat the Pirate Ghost Ship Anarchist",
+	"Defeat the stronghold in Ravens Wood"
+};
+
+const char *const TRIVIA_QUESTIONS[5] = {
+	"Who rules Castle W.W.?",
+	"Who is the voluptuous one?",
+	"Who's lost sight?",
+	"Where's the very latest?",
+	"Who be ye?"
+};
+
+const char *const TRIVIA_ANSWERS[5] = {
+	"Lord Ironfist",
+	"Laura",
+	"Og",
+	"Erliquin",
+	"I be me"
+};
+
+const char *const TAVERN_TIPS[5][4] = {
+	{
+		"See man in cave below (1,2)",
+		"Check walls near (12,3)",
+		"Statue at (2,4) is your first job",
+		"Similar pieces of a puzzle may not\\8dbelong to the same puzzle"
+	},
+	{
+		"Seek quests behind moons",
+		"The queen can be helpful",
+		"The beasts are on the map",
+		"Islands like colors are unknown\x8duntil discovered"
+	},
+	{
+		"Attacks should be concentrated",
+		"Due north is the cave of square magic",
+		"Secret doors are abundant",
+		"The demon and the maze are one"
+	},
+	{
+		"Telgoran is in s.e. maze",
+		"Some items give protection",
+		"Dragadune holds many gems",
+		"Donations help on judgement day"
+	},
+	{
+		"Agar lives behind the inn",
+		"Find the prisoner",
+		"Barriers guard wealth",
+		"The magic total is 34"
+	}
+};
+
+} // namespace MM1
+} // namespace MM
diff --git a/engines/mm/mm1/data/text.h b/engines/mm/mm1/data/text.h
new file mode 100644
index 00000000000..ef5c8cc9b93
--- /dev/null
+++ b/engines/mm/mm1/data/text.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MM1_DATA_TEXT_H
+#define MM1_DATA_TEXT_H
+
+namespace MM {
+namespace MM1 {
+
+extern const char *const CLASS_NAMES[6];
+extern const char *const RACE_NAMES[5];
+extern const char *const ALIGNMENT_NAMES[3];
+extern const char *const SEX_NAMES[3];
+extern const char *const TOWN_NAMES[5];
+extern const char *const COLOR_NAMES[8];
+extern const char *const CLERIC_SPELL_NAMES[47];
+extern const char *const WIZARD_SPELL_NAMES[47];
+extern const char *const MONSTER_EFFECTS[9];
+extern const char *const CHAR_EFFECTS[11];
+extern const char *const LOCATION_TITLES[5];
+extern const char *const OBSTRUCTED_MESSAGES[6];
+extern const char *const COMBAT_STATUS[9];
+extern const char *const ATTACK_TYPES[15];
+extern const char *const GAME_COMMANDS[16];
+extern const char *const PROTECTION_NAMES[18];
+extern const char *const SEARCH_CONTAINERS[11];
+extern const char *const STATUE_NAMES[8];
+extern const char *const STATUE_MESSAGES[8];
+extern const char *const TAVERN_RUMORS[17];
+extern const char *const INGREDIENTS[7];
+extern const char *const INSPECTRON_QUESTS[7];
+extern const char *const IRONFIST_QUESTS[7];
+extern const char *const TRIVIA_QUESTIONS[5];
+extern const char *const TRIVIA_ANSWERS[5];
+extern const char *const TAVERN_TIPS[5][4];
+
+} // namespace MM1
+} // namespace MM
+
+#endif
diff --git a/engines/mm/mm1/game/combat.cpp b/engines/mm/mm1/game/combat.cpp
index 1b69e7732d6..4730f6444de 100644
--- a/engines/mm/mm1/game/combat.cpp
+++ b/engines/mm/mm1/game/combat.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/game/combat.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/game/spell_casting.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
@@ -958,9 +959,8 @@ void Combat::iterateSelectedMonster1() {
 
 
 void Combat::iterateMonsters1Inner() {
-	Common::String line1 = Common::String::format("%s %s",
-		g_globals->_currCharacter->_name,
-		STRING["spells.casts_spell"].c_str());
+	Common::String line1 = Common::String::format("%s casts a spell:",
+		g_globals->_currCharacter->_name);
 
 	_monsterP = _remainingMonsters[_destMonsterNum];
 	const Common::String monsterName = _monsterP->_name;
@@ -985,7 +985,7 @@ void Combat::iterateMonsters1Inner() {
 
 	Common::String effect;
 	if (!affects) {
-		effect = STRING["monster_spells.not_affected"];
+		effect = "is not affected!";
 
 	} else {
 		_monsterP->_status |= g_globals->_spellsState._damage;
@@ -997,8 +997,7 @@ void Combat::iterateMonsters1Inner() {
 				break;
 		}
 
-		effect = STRING[Common::String::format(
-			"spells.monster_effects.%d", effectNum)];
+		effect = MONSTER_EFFECTS[effectNum];
 	}
 
 	InfoMessage msg(
@@ -1048,9 +1047,8 @@ void Combat::iterateSelectedMonster2() {
 
 
 void Combat::iterateMonsters2Inner() {
-	Common::String line1 = Common::String::format("%s %s",
-		g_globals->_currCharacter->_name,
-		STRING["spells.casts_spell"].c_str());
+	Common::String line1 = Common::String::format("%s casts a spell:",
+		g_globals->_currCharacter->_name);
 
 	_monsterP = _remainingMonsters[_destMonsterNum];
 	const Common::String monsterName = _monsterP->_name;
@@ -1084,25 +1082,20 @@ void Combat::iterateMonsters2Inner() {
 
 	if (!affects) {
 		msg._lines.push_back(Line(
-			0, 1, Common::String::format("%s %s",
-				monsterName.c_str(),
-				STRING["monster_spells.not_affected"].c_str()
-			)
+			0, 1, Common::String::format("%s is not affected!",
+				monsterName.c_str())
 		));
 
 	} else {
-		Common::String line2 = Common::String::format("%s %s %d %s %s",
+		Common::String line2 = Common::String::format("%s takes %d %s of damage!",
 			monsterName.c_str(),
-			STRING["dialogs.combat.takes"].c_str(),
 			_damage,
-			STRING[_damage == 1 ? "dialogs.combat.point" : "dialogs.combat.points"].c_str(),
-			STRING["dialogs.combat.of_damage"].c_str()
-		);
+			(_damage == 1 ? "point" : "points"));
 
 		msg._lines.push_back(Line(0, 1, line2));
 
 		if (_damage >= _monsterP->_hp) {
-			msg._lines.push_back(Line(0, 2, STRING["dialogs.combat.and_goes_down"]));
+			msg._lines.push_back(Line(0, 2, "and goes down!!!"));
 		}
 	}
 
@@ -1169,7 +1162,7 @@ void Combat::spellFailed() {
 	g_globals->_combatParty[_currentChar]->_checked = true;
 	Sound::sound(SOUND_2);
 
-	SoundMessage msg(10, 2, SpellCasting::spellResultMessage(STRING["spells.failed"]));
+	SoundMessage msg(10, 2, SpellCasting::spellResultMessage("Spell failed"));
 	msg._delaySeconds = 3;
 	displaySpellResult(msg);
 }
@@ -1182,7 +1175,7 @@ bool Combat::monsterLevelThreshold() const {
 
 void Combat::turnUndead() {
 	// It can only be used once per combat. Further casts have no effect
-	Common::String result = STRING["spells.no_effect"];
+	Common::String result = "No effect!";
 	int resultX = 15;
 
 	if (!_turnUndeadUsed) {
@@ -1201,7 +1194,7 @@ void Combat::turnUndead() {
 		}
 
 		if (_monstersDestroyedCtr) {
-			result = STRING["spells.monsters_destroyed"];
+			result = "Some monsters were destroyed!";
 			resultX = 5;
 		}
 	}
@@ -1223,9 +1216,8 @@ void Combat::summonLightning() {
 	SpellsState &ss = g_globals->_spellsState;
 
 	if (_destMonsterNum < _attackersCount) {
-		Common::String line1 = Common::String::format("%s %s",
-			g_globals->_currCharacter->_name,
-			STRING["spells.casts_spell"].c_str());
+		Common::String line1 = Common::String::format("%s casts a spell:",
+			g_globals->_currCharacter->_name);
 
 		ss._damage = g_globals->_currCharacter->_level * 2 + 4;
 		ss._mmVal1++;
@@ -1333,7 +1325,7 @@ void Combat::holyWord() {
 
 	// Holy Word result must be timed, like the existing Turn Undead result.
 	InfoMessage msg(_monstersDestroyedCtr ? 5 : 15, 1,
-		STRING[_monstersDestroyedCtr ? "spells.monsters_destroyed" : "spells.no_effect"]);
+		_monstersDestroyedCtr ? "Some monsters were destroyed!" : "No effect!");
 	msg._delaySeconds = 3;
 	msg._callback = []() {
 		g_globals->_combat->characterDone();
@@ -1352,35 +1344,35 @@ void Combat::identifyMonster() {
 	line += ':';
 	while (line.size() < COL2)
 		line += ' ';
-	line += STRING["spells.info.hp"];
+	line += "hp = ";
 	line += Common::String::format("%d", _monsterP->_hp);
 	line += "  ";
-	line += STRING["spells.info.ac"];
+	line += "ac = ";
 	line += Common::String::format("%d", _monsterP->_ac);
 	msg._lines.push_back(Line(0, 0, line));
 
-	line = STRING["spells.info.speed"];
+	line = "speed = ";
 	line += Common::String::format("%d", _monsterP->_speed);
 	while (line.size() < COL2)
 		line += ' ';
-	line += STRING["spells.info.bonus_on_touch"];
+	line += "bonus on touch   ";
 	line += _monsterP->_bonusOnTouch ? 'Y' : 'N';
 	msg._lines.push_back(Line(0, 1, line));
 
-	line = STRING["spells.info.num_attacks"];
+	line = "# of attacks = ";
 	line += Common::String::format("%d", _monsterP->_numberOfAttacks);
 	while (line.size() < COL2)
 		line += ' ';
-	line += STRING["spells.info.special_ability"];
+	line += "special ability ";
 	line += (_monsterP->_specialAbility & 0x80) ||
 		!_monsterP->_specialAbility ? 'N' : 'Y';
 	msg._lines.push_back(Line(0, 2, line));
 
-	line = STRING["spells.info.max_damage"];
+	line = "maximum damage = ";
 	line += Common::String::format("%d", _monsterP->_maxDamage);
 	while (line.size() < COL2)
 		line += ' ';
-	line += STRING["spells.info.magic_resistance"];
+	line += "magic resistance ";
 	line += Common::String::format("%d",
 		_monsterP->_resistUndead & MAGIC_RESISTANCE);
 	msg._lines.push_back(Line(0, 3, line));
@@ -1528,9 +1520,8 @@ bool Combat::acidRain() {
 }
 
 void Combat::fingerOfDeath() {
-	Common::String line1 = Common::String::format("%s %s",
-		g_globals->_currCharacter->_name,
-		STRING["spells.casts_spell"].c_str());
+	Common::String line1 = Common::String::format("%s casts a spell:",
+		g_globals->_currCharacter->_name);
 
 	const Common::String monsterName = _monsterP->_name;
 	bool kills = !(_monsterP->_resistUndead & IS_UNDEAD) &&
@@ -1546,8 +1537,8 @@ void Combat::fingerOfDeath() {
 
 	Common::String line2 = Common::String::format("%s %s",
 		monsterName.c_str(),
-		kills ? STRING["spells.char_effects.7"].c_str() :
-		STRING["monster_spells.not_affected"].c_str()
+		kills ? "is killed" :
+		"is not affected!"
 	);
 
 	InfoMessage msg(0, 0, line1, 0, 2, line2);
@@ -1560,9 +1551,8 @@ void Combat::fingerOfDeath() {
 }
 
 void Combat::disintegration() {
-	Common::String line1 = Common::String::format("%s %s",
-		g_globals->_currCharacter->_name,
-		STRING["spells.casts_spell"].c_str());
+	Common::String line1 = Common::String::format("%s casts a spell:",
+		g_globals->_currCharacter->_name);
 
 	const Common::String monsterName = _monsterP->_name;
 	bool kills = !monsterLevelThreshold();
@@ -1577,8 +1567,8 @@ void Combat::disintegration() {
 
 	Common::String line2 = Common::String::format("%s %s",
 		monsterName.c_str(),
-		kills ? STRING["spells.char_effects.disintegrated"].c_str() :
-		STRING["monster_spells.not_affected"].c_str()
+		kills ? "is disintegrated!" :
+		"is not affected!"
 	);
 
 	InfoMessage msg(0, 0, line1, 0, 2, line2);
@@ -1786,16 +1776,14 @@ Common::String Combat::subtractDamageFromChar() {
 		if (!(c._condition & (BAD_CONDITION | UNCONSCIOUS))) {
 			c._condition |= UNCONSCIOUS;
 
-			result = Common::String::format("%s %s", c._name,
-				STRING["dialogs.combat.goes_down"].c_str());
+			result = Common::String::format("%s goes down!!!", c._name);
 			Sound::sound2(SOUND_8);
 
 		} else {
 			if (c._condition & BAD_CONDITION)
 				c._condition = BAD_CONDITION | DEAD;
 
-			result = Common::String::format("%s %s", c._name,
-				STRING["dialogs.combat.dies"].c_str());
+			result = Common::String::format("%s dies!", c._name);
 			Sound::sound2(SOUND_8);
 		}
 	}
diff --git a/engines/mm/mm1/game/equip_remove.cpp b/engines/mm/mm1/game/equip_remove.cpp
index a6c121a010e..cfd14121e10 100644
--- a/engines/mm/mm1/game/equip_remove.cpp
+++ b/engines/mm/mm1/game/equip_remove.cpp
@@ -54,7 +54,7 @@ bool EquipRemove::equipItem(int index, Common::Point &textPos, Common::String &e
 		classBit = ROBBER_BIT;
 		break;
 	default:
-		equipError = STRING["dialogs.character.wrong_class"];
+		equipError = "*** wrong class ***";
 		break;
 	}
 
@@ -62,7 +62,7 @@ bool EquipRemove::equipItem(int index, Common::Point &textPos, Common::String &e
 	const Item &item = g_globals->_currItem;
 
 	if (equipError.empty() && (item._disablements & classBit))
-		equipError = STRING["dialogs.character.wrong_class"];
+		equipError = "*** wrong class ***";
 	if (equipError.empty()) {
 		int alignBit = 0;
 		switch (c._alignment) {
@@ -76,17 +76,17 @@ bool EquipRemove::equipItem(int index, Common::Point &textPos, Common::String &e
 			alignBit = EVIL_BIT;
 			break;
 		default:
-			equipError = STRING["dialogs.character.wrong_alignment"];
+			equipError = "*** wrong alignment ***";
 			break;
 		}
 
 		if ((item._disablements & alignBit) && alignBit != NEUTRAL_BIT)
-			equipError = STRING["dialogs.character.wrong_alignment"];
+			equipError = "*** wrong alignment ***";
 	}
 
 	if (equipError.empty()) {
 		if (item._constBonus_id == IS_EQUIPPABLE) {
-			equipError = STRING["dialogs.character.not_equipped"];
+			equipError = "*** not equipped ***";
 			textPos.x = 10;
 		}
 	}
@@ -94,38 +94,38 @@ bool EquipRemove::equipItem(int index, Common::Point &textPos, Common::String &e
 	if (equipError.empty()) {
 		if (isWeapon(itemId)) {
 			if (c._equipped.hasWeapon() || c._equipped.hasTwoHanded())
-				equipError = STRING["dialogs.character.have_weapon"];
+				equipError = "*** already have weapon ***";
 		} else if (isMissile(itemId)) {
 			if (c._equipped.hasMissile()) {
-				equipError = STRING["dialogs.character.have_missile"];
+				equipError = "*** aready have missile weapon ***";
 				textPos.x = 3;
 			}
 		} else if (isTwoHanded(itemId)) {
 			if (c._equipped.hasShield()) {
-				equipError = STRING["dialogs.character.cannot_with_shield"];
+				equipError = "*** cannot with shield ***";
 				textPos.x = 7;
 			} else if (c._equipped.hasWeapon()) {
-				equipError = STRING["dialogs.character.have_weapon"];
+				equipError = "*** already have weapon ***";
 
 			}
 		} else if (isArmor(itemId)) {
 			if (c._equipped.hasArmor()) {
-				equipError = STRING["dialogs.character.have_armor"];
+				equipError = "*** already wearing armor ***";
 				textPos.x = 5;
 			}
 		} else if (isShield(itemId)) {
 			if (c._equipped.hasTwoHanded()) {
-				equipError = STRING["dialogs.character.cannot_two_handed"];
+				equipError = "*** cannot with two-handed weapon ***";
 				textPos.x = 1;
 			}
 		} else if (itemId == 255) {
-			equipError = STRING["dialogs.character.not_equipped"];
+			equipError = "*** not equipped ***";
 			textPos.x = 10;
 		}
 	}
 
 	if (equipError.empty() && c._equipped.full()) {
-		equipError = STRING["dialogs.character.full"];
+		equipError = "*** full ***";
 		textPos.x = 14;
 	}
 
@@ -136,7 +136,7 @@ bool EquipRemove::equipItem(int index, Common::Point &textPos, Common::String &e
 
 		if (item._constBonus_id != NO_EQUIP_BONUS) {
 			if (item._constBonus_id == IS_EQUIPPABLE) {
-				equipError = STRING["dialogs.character.not_equipped"];
+				equipError = "*** not equipped ***";
 				textPos.x = 10;
 			} else if (item._constBonus_id == EQUIP_CURSED) {
 				c._equipped[freeIndex]._charges += item._constBonus_value;
@@ -182,10 +182,10 @@ bool EquipRemove::removeItem(int index, Common::Point &textPos, Common::String &
 	g_globals->_items.getItem(itemId);
 	const Item &item = g_globals->_currItem;
 	if (item._constBonus_id == EQUIP_CURSED) {
-		removeError = STRING["dialogs.character.cursed"];
+		removeError = "*** cursed ***";
 		textPos.x = 13;
 	} else if (c._backpack.full()) {
-		removeError = STRING["dialogs.character.full"];
+		removeError = "*** full ***";
 		textPos.x = 14;
 	}
 
diff --git a/engines/mm/mm1/game/monster_touch.cpp b/engines/mm/mm1/game/monster_touch.cpp
index 396448aaa9d..e089af687de 100644
--- a/engines/mm/mm1/game/monster_touch.cpp
+++ b/engines/mm/mm1/game/monster_touch.cpp
@@ -57,15 +57,10 @@ const MonsterTouchAction MonsterTouch::ACTIONS[25] = {
 };
 
 static Common::String formatMonsterAction(const Common::String &action) {
-	Common::String conjunction = STRING["monster_actions.and"];
-	if (!conjunction.empty() && conjunction.firstChar() == '!')
-		conjunction.deleteChar(0);
-
-	return Common::String::format("    %s%s!",
-		conjunction.c_str(), action.c_str());
+	return Common::String::format("    and %s!", action.c_str());
 }
 
-#define LINE(ACTION) line = formatMonsterAction(STRING[ACTION])
+#define LINE(ACTION) line = formatMonsterAction(ACTION)
 
 bool MonsterTouch::monsterTouch(uint index, Common::String &line) {
 	line = "";
@@ -84,7 +79,7 @@ bool MonsterTouch::canPerform(int level) const {
 bool MonsterTouch::action00(Common::String &line) {
 	if (canPerform(3)) {
 		g_globals->_currCharacter->_food = 0;
-		LINE("monster_actions.takes_food");
+		LINE("takes food");
 		return true;
 
 	} else {
@@ -95,7 +90,7 @@ bool MonsterTouch::action00(Common::String &line) {
 bool MonsterTouch::action01(Common::String &line) {
 	if (canPerform(20)) {
 		setCondition(DISEASED);
-		LINE("monster_actions.inflicts_disease");
+		LINE("inflicts disease");
 		return true;
 	}
 
@@ -125,7 +120,7 @@ bool MonsterTouch::action04(Common::String &line) {
 
 	if (canPerform(2) && c._gems != 0) {
 		c._gems /= 2;
-		LINE("monster_actions.steals_gems");
+		LINE("steals some gems");
 		return true;
 
 	} else {
@@ -140,14 +135,14 @@ bool MonsterTouch::action05(Common::String &line) {
 		setCondition(BAD_CONDITION | DEAD);
 	_damage += 3;
 
-	LINE("monster_actions.reduces_endurance");
+	LINE("reduces endurance");
 	return true;
 }
 
 bool MonsterTouch::action06(Common::String &line) {
 	if (damageType7()) {
 		setCondition(ASLEEP);
-		LINE("monster_actions.induces_sleep");
+		LINE("induces sleep");
 		return true;
 	}
 
@@ -157,7 +152,7 @@ bool MonsterTouch::action06(Common::String &line) {
 bool MonsterTouch::action07(Common::String &line) {
 	if (damageType5()) {
 		setCondition(3);
-		LINE("monster_actions.causes_paralysis");
+		LINE("causes paralysis");
 		return true;
 	}
 
@@ -167,7 +162,7 @@ bool MonsterTouch::action07(Common::String &line) {
 bool MonsterTouch::action08(Common::String &line) {
 	if (canPerform(4)) {
 		setCondition(DISEASED);
-		LINE("monster_actions.inflicts_disease");
+		LINE("inflicts disease");
 		return true;
 
 	} else {
@@ -180,7 +175,7 @@ bool MonsterTouch::action09(Common::String &line) {
 
 	if (canPerform(2) && c._gold != 0) {
 		c._gold /= 2;
-		LINE("monster_actions.steals_gold");
+		LINE("steals some gold");
 		return true;
 
 	} else {
@@ -195,7 +190,7 @@ bool MonsterTouch::action10(Common::String &line) {
 
 	if (canPerform(2) && !c._backpack.empty()) {
 		c._backpack.removeAt(c._backpack.size() - 1);
-		LINE("monster_actions.steals_something");
+		LINE("steals something");
 		return true;
 
 	} else {
@@ -206,7 +201,7 @@ bool MonsterTouch::action10(Common::String &line) {
 bool MonsterTouch::action11(Common::String &line) {
 	if (damageType6()) {
 		setCondition(16);
-		LINE("monster_actions.induces_poison");
+		LINE("induces poison");
 		return true;
 	}
 
@@ -216,7 +211,7 @@ bool MonsterTouch::action11(Common::String &line) {
 bool MonsterTouch::action12(Common::String &line) {
 	if (canPerform(3)) {
 		setCondition(BLINDED);
-		LINE("monster_actions.causes_blindness");
+		LINE("causes blindness");
 		return true;
 
 	} else {
@@ -231,7 +226,7 @@ bool MonsterTouch::action13(Common::String &line) {
 		setCondition(BAD_CONDITION | DEAD);
 
 	_damage += 10;
-	LINE("monster_actions.drains_lifeforce");
+	LINE("drains lifeforce");
 	return true;
 }
 
@@ -240,10 +235,8 @@ bool MonsterTouch::action14(Common::String &line) {
 
 	if (canPerform(3) && isCharAffected()) {
 		setCondition(BAD_CONDITION | STONE);
-		line = Common::String::format("%s %s",
-			c._name,
-			STRING["monster_actions.turned_to_stone"].c_str()
-		);
+		line = Common::String::format("%s is turned to stone",
+			c._name);
 		return true;
 
 	} else {
@@ -263,7 +256,7 @@ bool MonsterTouch::action15(Common::String &line) {
 			setCondition(ERADICATED);
 		}
 
-		LINE("monster_actions.causes_aging");
+		LINE("causes rapid aging");
 		return true;
 
 	} else {
@@ -283,7 +276,7 @@ bool MonsterTouch::action16(Common::String &line) {
 	}
 
 	_damage += 20;
-	LINE("monster_actions.drains_lifeforce");
+	LINE("drains lifeforce");
 	return true;
 }
 
@@ -292,10 +285,8 @@ bool MonsterTouch::action17(Common::String &line) {
 
 	if (canPerform(3) && isCharAffected()) {
 		setCondition(BAD_CONDITION | DEAD);
-		line = Common::String::format("%s %s",
-			c._name,
-			STRING["monster_actions.is_killed"].c_str()
-		);
+		line = Common::String::format("%s is killed",
+			c._name);
 		return true;
 
 	} else {
@@ -306,7 +297,7 @@ bool MonsterTouch::action17(Common::String &line) {
 bool MonsterTouch::action18(Common::String &line) {
 	if (canPerform(3) && isCharAffected()) {
 		setCondition(UNCONSCIOUS);
-		LINE("monster_actions.induces_unconsciousness");
+		LINE("induces unconsciousness");
 		return true;
 
 	} else {
@@ -325,7 +316,7 @@ bool MonsterTouch::action19(Common::String &line) {
 		c._might._current -= 3;
 	}
 
-	LINE("monster_actions.drains_might");
+	LINE("drains might");
 	return true;
 }
 
@@ -344,7 +335,7 @@ bool MonsterTouch::action20(Common::String &line) {
 		}
 	}
 
-	LINE("monster_actions.drains_ability");
+	LINE("drains ability");
 	return true;
 }
 
@@ -368,7 +359,7 @@ bool MonsterTouch::action22(Common::String &line) {
 		c._gold = 0;
 		c._gems = 0;
 
-		LINE("monster_actions.steals_gold_and_gems");
+		LINE("steals gold and gems");
 		return true;
 
 	} else {
@@ -380,10 +371,8 @@ bool MonsterTouch::action23(Common::String &line) {
 	Character &c = *g_globals->_currCharacter;
 
 	setCondition(ERADICATED);
-	line = Common::String::format("%s %s",
-		c._name,
-		STRING["monster_actions.is_eradicated"].c_str()
-	);
+	line = Common::String::format("%s is eradicated",
+		c._name);
 
 	return true;
 }
@@ -392,7 +381,7 @@ bool MonsterTouch::action24(Common::String &line) {
 	Character &c = *g_globals->_currCharacter;
 
 	c._sp._current = 0;
-	LINE("monster_actions.drains_sp");
+	LINE("drains spell points");
 	return true;
 }
 
diff --git a/engines/mm/mm1/game/rest.cpp b/engines/mm/mm1/game/rest.cpp
index 60dc8cd7a7b..b7caea6bdf7 100644
--- a/engines/mm/mm1/game/rest.cpp
+++ b/engines/mm/mm1/game/rest.cpp
@@ -63,7 +63,7 @@ void Rest::execute() {
 	}
 
 	g_events->send(InfoMessage(
-		STRING["dialogs.game.rest.rest_complete"]
+		"Rest complete: no encounters!"
 	));
 }
 
diff --git a/engines/mm/mm1/game/spell_casting.cpp b/engines/mm/mm1/game/spell_casting.cpp
index 91dd370db2f..669b2165b0a 100644
--- a/engines/mm/mm1/game/spell_casting.cpp
+++ b/engines/mm/mm1/game/spell_casting.cpp
@@ -172,22 +172,22 @@ Common::String SpellCasting::getSpellError() const {
 	Common::String msg;
 	switch (_spellState) {
 	case SS_NOT_ENOUGH_SP:
-		msg = STRING["dialogs.misc.not_enough_sp"];
+		msg = "Not enough spell points";
 		break;
 	case SS_NOT_ENOUGH_GEMS:
-		msg = STRING["dialogs.misc.not_enough_gems"];
+		msg = "Not enough gems";
 		break;
 	case SS_COMBAT_ONLY:
-		msg = STRING["spells.combat_only"];
+		msg = "Combat only";
 		break;
 	case SS_NONCOMBAT_ONLY:
-		msg = STRING["spells.noncombat_only"];
+		msg = "Non combat only";
 		break;
 	case SS_OUTDOORS_ONLY:
-		msg = STRING["spells.outdoors_only"];
+		msg = "Outdoor only";
 		break;
 	default:
-		msg = STRING["spells.done"];
+		msg = "Done";
 		break;
 	}
 
diff --git a/engines/mm/mm1/game/spells_monsters.cpp b/engines/mm/mm1/game/spells_monsters.cpp
index 3873cf435e4..44632615e15 100644
--- a/engines/mm/mm1/game/spells_monsters.cpp
+++ b/engines/mm/mm1/game/spells_monsters.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/game/spells_monsters.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -82,7 +83,7 @@ void SpellsMonsters::castMonsterSpell(const Common::String &monsterName, int spe
 
 void SpellsMonsters::spell01_curse() {
 	if (casts()) {
-		add(STRING["monster_spells.a_curse"]);
+		add("a curse");
 		g_globals->_activeSpells._s.cursed = MIN(255,
 			(int)g_globals->_activeSpells._s.cursed + 1);
 	}
@@ -90,7 +91,7 @@ void SpellsMonsters::spell01_curse() {
 
 void SpellsMonsters::spell02_energyBlast() {
 	if (casts()) {		
-		add(STRING["monster_spells.energy_blast"]);
+		add("energy blast");
 		++g_globals->_spellsState._mmVal1;
 		g_globals->_spellsState._damage = getRandomNumber(16) + 4;
 		damageRandomChar();
@@ -98,9 +99,7 @@ void SpellsMonsters::spell02_energyBlast() {
 }
 
 void SpellsMonsters::spell03_fire() {
-	add(Common::String::format("%s %s",
-		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.fire"].c_str()));
+	add("breathes  fire");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
 
@@ -122,7 +121,7 @@ void SpellsMonsters::spell04_blindness() {
 }
 
 void SpellsMonsters::spell05_sprayPoison() {
-	add(STRING["monster_spells.sprays_poison"]);
+	add("sprays poison");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
 	g_globals->_spellsState._damage = POISONED;
@@ -130,7 +129,7 @@ void SpellsMonsters::spell05_sprayPoison() {
 }
 
 void SpellsMonsters::spell06_sprayAcid() {
-	add(STRING["monster_spells.sprays_acid"]);
+	add("sprays acid");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ACID;
 	g_globals->_spellsState._damage = getRandomNumber((int)POISONED);
@@ -141,7 +140,7 @@ void SpellsMonsters::spell06_sprayAcid() {
 
 void SpellsMonsters::spell07_sleep() {
 	if (casts()) {
-		add(STRING["monster_spells.sleep"]);
+		add("sleep");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_PSYCHIC;
@@ -152,7 +151,7 @@ void SpellsMonsters::spell07_sleep() {
 
 void SpellsMonsters::spell08_paralyze() {
 	if (casts()) {
-		add(STRING["monster_spells.paralyze"]);
+		add("paralyze");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FEAR;
@@ -169,14 +168,14 @@ void SpellsMonsters::spell08_paralyze() {
 
 void SpellsMonsters::spell09_dispel() {
 	if (casts()) {
-		add(STRING["monster_spells.dispel"]);
+		add("dispel");
 		dispelParty();
 	}
 }
 
 void SpellsMonsters::spell10_lightningBolt() {
 	if (casts()) {
-		add(STRING["monster_spells.lightning_bolt"]);
+		add("lightning bolt");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
@@ -186,9 +185,7 @@ void SpellsMonsters::spell10_lightningBolt() {
 }
 
 void SpellsMonsters::spell11_strangeGas() {
-	add(Common::String::format("%s%s",
-		STRING["monster_spells.breathes"].c_str(),
-		"strange gas"));
+	add("breathes strange gas");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
 	g_globals->_spellsState._damage = BAD_CONDITION | STONE;
@@ -197,7 +194,7 @@ void SpellsMonsters::spell11_strangeGas() {
 }
 
 void SpellsMonsters::spell12_explode() {
-	add(STRING["monster_spells.explode"]);
+	add("explode");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
 	g_globals->_spellsState._damage = getRandomNumber(
@@ -212,7 +209,7 @@ void SpellsMonsters::spell12_explode() {
 
 void SpellsMonsters::spell13_fireball() {
 	if (casts()) {
-		add(STRING["monster_spells.fireball"]);
+		add("fireball");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
@@ -228,9 +225,7 @@ void SpellsMonsters::spell13_fireball() {
 }
 
 void SpellsMonsters::spell14_fireBreath() {
-	add(Common::String::format("%s %s",
-		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.fire"].c_str()));
+	add("breathes  fire");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
 
@@ -244,7 +239,7 @@ void SpellsMonsters::spell14_fireBreath() {
 }
 
 void SpellsMonsters::spell15_gazes() {
-	add(STRING["monster_spells.gazes"]);
+	add("gazes");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._damage = BAD_CONDITION | STONE;
 
@@ -253,7 +248,7 @@ void SpellsMonsters::spell15_gazes() {
 }
 
 void SpellsMonsters::spell16_acidArrow() {
-	add(STRING["monster_spells.acid_arrow"]);
+	add("acid arrow");
 	++g_globals->_spellsState._mmVal1;
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ACID;
@@ -272,7 +267,7 @@ void SpellsMonsters::spell17_elements() {
 
 void SpellsMonsters::spell18_coldBeam() {
 	if (casts()) {
-		add(STRING["monster_spells.cold_beam"]);
+		add("cold beam");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_COLD;
@@ -284,7 +279,7 @@ void SpellsMonsters::spell18_coldBeam() {
 
 void SpellsMonsters::spell19_dancingSword() {
 	if (casts()) {
-		add(STRING["monster_spells.dancing_sword"]);
+		add("dancing sword");
 		++g_globals->_spellsState._mmVal1;
 		g_globals->_spellsState._damage = getRandomNumber(14) + 16;
 
@@ -294,7 +289,7 @@ void SpellsMonsters::spell19_dancingSword() {
 }
 
 void SpellsMonsters::spell20_magicDrain() {
-	add(STRING["monster_spells.magic_drain"]);
+	add("magic drain");
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
 		Character &c = g_globals->_party[i];
@@ -307,7 +302,7 @@ void SpellsMonsters::spell20_magicDrain() {
 
 void SpellsMonsters::spell21_fingerOfDeath() {
 	if (casts()) {
-		add(STRING["monster_spells.finger_of_death"]);
+		add("finger of death");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._damage = BAD_CONDITION | DEAD;
@@ -319,7 +314,7 @@ void SpellsMonsters::spell21_fingerOfDeath() {
 
 void SpellsMonsters::spell22_sunRay() {
 	if (casts()) {
-		add(STRING["monster_spells.sun_ray"]);
+		add("sun ray");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._damage = getRandomNumber(51) + 49;
@@ -330,7 +325,7 @@ void SpellsMonsters::spell22_sunRay() {
 
 void SpellsMonsters::spell23_disintegration() {
 	if (casts()) {
-		add(STRING["monster_spells.disintegration"]);
+		add("disintegration");
 		++g_globals->_spellsState._mmVal1;
 		++g_globals->_spellsState._resistanceIndex;
 		g_globals->_spellsState._damage = ERADICATED;
@@ -341,15 +336,13 @@ void SpellsMonsters::spell23_disintegration() {
 }
 
 void SpellsMonsters::spell24_commandsEnergy() {
-	add(STRING["monster_spells.commands_energy"]);
+	add("commands energy");
 	g_globals->_spellsState._damage = SILENCED | PARALYZED | UNCONSCIOUS;
 	damageRandomChar();
 }
 
 void SpellsMonsters::spell25_poison() {
-	add(Common::String::format("%s %s",
-		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.poison"].c_str()));
+	add("breathes  poison");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -359,9 +352,7 @@ void SpellsMonsters::spell25_poison() {
 }
 
 void SpellsMonsters::spell26_lightning() {
-	add(Common::String::format("%s %s",
-		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.lightning"].c_str()));
+	add("breathes  lightning");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -371,9 +362,7 @@ void SpellsMonsters::spell26_lightning() {
 }
 
 void SpellsMonsters::spell27_frost() {
-	add(Common::String::format("%s %s",
-		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.frost"].c_str()));
+	add("breathes  frost");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_COLD;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -383,9 +372,7 @@ void SpellsMonsters::spell27_frost() {
 }
 
 void SpellsMonsters::spell28_spikes() {
-	add(Common::String::format("%s %s",
-		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.spikes"].c_str()));
+	add("breathes  spikes");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -395,9 +382,7 @@ void SpellsMonsters::spell28_spikes() {
 }
 
 void SpellsMonsters::spell29_acid() {
-	add(Common::String::format("%s %s",
-		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.acid"].c_str()));
+	add("breathes  acid");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ACID;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -407,9 +392,7 @@ void SpellsMonsters::spell29_acid() {
 }
 
 void SpellsMonsters::spell30_fire() {
-	add(Common::String::format("%s %s",
-		STRING["monster_spells.breathes"].c_str(),
-		STRING["monster_spells.fire"].c_str()));
+	add("breathes  fire");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -419,7 +402,7 @@ void SpellsMonsters::spell30_fire() {
 }
 
 void SpellsMonsters::spell31_energy() {
-	add(STRING["monster_spells.energy"]);
+	add("energy");
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
 
@@ -428,7 +411,7 @@ void SpellsMonsters::spell31_energy() {
 }
 
 void SpellsMonsters::spell32_swarm() {
-	add(STRING["monster_spells.swarm"]);
+	add("swarm");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._damage = getRandomNumber(12);
 
@@ -438,10 +421,10 @@ void SpellsMonsters::spell32_swarm() {
 
 bool SpellsMonsters::casts() {
 	if (canMonsterCast()) {
-		add(STRING["monster_spells.casts"]);
+		add("casts ");
 		return true;
 	} else {
-		add(STRING["monster_spells.fails_to_cast"]);
+		add("fails to cast a spell");
 		return false;
 	}
 }
@@ -505,7 +488,7 @@ bool SpellsMonsters::charAffected() {
 	add(' ');
 
 	if (g_globals->_spellsState._mmVal1 && !isCharAffected()) {
-		_lines.back()._text += STRING["monster_spells.not_affected"];
+		_lines.back()._text += "is not affected!";
 		return false;
 	}
 
@@ -520,7 +503,7 @@ bool SpellsMonsters::isEffective() {
 			if (g_globals->_spellsState._mmVal5) {
 				_damage >>= 1;
 			} else {
-				_lines.back()._text += STRING["monster_spells.not_affected"];
+				_lines.back()._text += "is not affected!";
 				return false;
 			}
 		}
@@ -555,7 +538,7 @@ bool SpellsMonsters::testElementalResistance() {
 		if (g_globals->_spellsState._mmVal5) {
 			_damage >>= 2;
 		} else {
-			add(STRING["monster_spells.not_affected"]);
+			add("is not affected!");
 			return false;
 		}
 	}
@@ -564,15 +547,15 @@ bool SpellsMonsters::testElementalResistance() {
 }
 
 void SpellsMonsters::writeDamage() {
-	add(STRING["monster_spells.takes"]);
+	add("takes ");
 	add(Common::String::format("%d ", _damage));
-	add(STRING[_damage > 1 ? "dialogs.combat.points" : "dialogs.combat.point"]);
+	add(_damage > 1 ? "points" : "point");
 	add(' ');
 
 	if (_lines.back()._text.size() >= 30)
 		add('!');
 	else
-		add(STRING["dialogs.combat.of_damage"]);
+		add("of damage!");
 }
 
 void SpellsMonsters::proc9() {
@@ -661,8 +644,7 @@ void SpellsMonsters::writeConditionEffect() {
 		}
 	}
 
-	add(STRING[Common::String::format("spells.char_effects.%d",
-			effectNum)]);
+	add(CHAR_EFFECTS[effectNum]);
 	add('!');
 }
 
diff --git a/engines/mm/mm1/game/spells_party.cpp b/engines/mm/mm1/game/spells_party.cpp
index 1753e63f807..cc713d98d5d 100644
--- a/engines/mm/mm1/game/spells_party.cpp
+++ b/engines/mm/mm1/game/spells_party.cpp
@@ -563,7 +563,7 @@ SpellResult SpellsParty::cleric64_stoneToFlesh() {
 SpellResult SpellsParty::cleric65_townPortal() {
 	Sound::sound(SOUND_2);
 	InfoMessage msg(
-		STRING["spells.which_town"],
+		"Which town (1-5)?",
 		[](const Common::KeyState &keyState) {
 			switch (keyState.keycode) {
 			case Common::KEYCODE_ESCAPE:
@@ -596,12 +596,9 @@ SpellResult SpellsParty::cleric65_townPortal() {
 	);
 
 	msg._largeMessage = true;
-	msg._lines.push_back(Line(Common::String::format("1) %s  2) %s",
-		STRING["stats.towns.1"].c_str(), STRING["stats.towns.2"].c_str())));
-	msg._lines.push_back(Line(Common::String::format("3) %s  4) %s",
-		STRING["stats.towns.3"].c_str(), STRING["stats.towns.4"].c_str())));
-	msg._lines.push_back(Line(Common::String::format("5) %s",
-		STRING["stats.towns.5"].c_str())));
+	msg._lines.push_back(Line("1) Sorpigal  2) Portsmith"));
+	msg._lines.push_back(Line("3) Algary  4) Dusk"));
+	msg._lines.push_back(Line("5) Erliquin"));
 	g_events->send(msg);
 
 	return SR_SUCCESS_SILENT;
diff --git a/engines/mm/mm1/game/use_item.cpp b/engines/mm/mm1/game/use_item.cpp
index 5dd73eff4b3..309f8f0d039 100644
--- a/engines/mm/mm1/game/use_item.cpp
+++ b/engines/mm/mm1/game/use_item.cpp
@@ -36,7 +36,7 @@ Common::String UseItem::combatUseItem(Inventory &inv, Inventory::Entry &invEntry
 	Item *item = g_globals->_items.getItem(invEntry._id);
 
 	if (!item->_tempBonus_id) {	
-		msg = STRING["dialogs.character.use_combat.no_special_power"];
+		msg = "No special power";
 
 	} else if (item->_constBonus_id == IS_EQUIPPABLE || isEquipped) {
 		if (invEntry._charges) {
@@ -58,14 +58,14 @@ Common::String UseItem::combatUseItem(Inventory &inv, Inventory::Entry &invEntry
 						&g_globals->_currCharacter->_backpack)->removeCharge(&invEntry);
 
 				g_globals->_party.updateAC();
-				msg = STRING["dialogs.character.use_combat.done"];
+				msg = "Done";
 				return "";
 			}
 		} else {
-			msg = STRING["dialogs.character.use_combat.no_charges_left"];
+			msg = "No charges left";
 		}
 	} else {
-		msg = STRING["dialogs.character.use_combat.not_equipped"];
+		msg = "Not equipped";
 	}
 
 	g_events->send("Combat", GameMessage("DISABLE_ATTACKS"));
@@ -79,7 +79,7 @@ Common::String UseItem::nonCombatUseItem(Inventory &inv, Inventory::Entry &invEn
 	Item *item = g_globals->_items.getItem(invEntry._id);
 
 	if (!item->_tempBonus_id) {
-		msg = STRING["dialogs.character.use_noncombat.no_special_power"];
+		msg = "*** No special power ***";
 
 	} else if (item->_constBonus_id == IS_EQUIPPABLE || isEquipped) {
 		if (invEntry._charges) {
@@ -91,10 +91,10 @@ Common::String UseItem::nonCombatUseItem(Inventory &inv, Inventory::Entry &invEn
 				SpellResult result = Game::SpellsParty::cast(_spellIndex, g_globals->_currCharacter);
 				switch (result) {
 				case SR_SUCCESS_DONE:
-					msg = STRING["spells.done"];
+					msg = "Done";
 					break;
 				case SR_FAILED:
-					msg = STRING["spells.failed"];
+					msg = "Spell failed";
 					break;
 				case SR_SUCCESS_SILENT:
 					break;
@@ -108,13 +108,13 @@ Common::String UseItem::nonCombatUseItem(Inventory &inv, Inventory::Entry &invEn
 						&g_globals->_currCharacter->_backpack)->removeCharge(&invEntry);
 
 				g_globals->_party.updateAC();
-				msg = STRING["spells.done"];
+				msg = "Done";
 			}
 		} else {
-			msg = STRING["dialogs.character.use_noncombat.no_charges_left"];
+			msg = "*** No charges left ***";
 		}
 	} else {
-		msg = STRING["dialogs.character.use_noncombat.not_equipped"];
+		msg = "*** Not equipped ***";
 	}
 
 	return msg;
diff --git a/engines/mm/mm1/game/view_base.cpp b/engines/mm/mm1/game/view_base.cpp
index 120af0b4f6f..5d387f4c773 100644
--- a/engines/mm/mm1/game/view_base.cpp
+++ b/engines/mm/mm1/game/view_base.cpp
@@ -21,6 +21,7 @@
 
 #include "common/util.h"
 #include "mm/mm1/game/view_base.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
 #include "mm/mm1/mm1.h"
@@ -114,8 +115,7 @@ bool ViewBase::msgGame(const GameMessage &msg) {
 		update();
 		return true;
 	} else if (msg._name == "LOCATION") {
-		_descriptionLine = STRING[Common::String::format(
-			"dialogs.location.titles.%d", msg._value)];
+		_descriptionLine = LOCATION_TITLES[msg._value];
 		draw();
 		return true;
 	}
@@ -176,7 +176,7 @@ void ViewBase::forward(KeybindingAction action) {
 			if (map.dataByte(Maps::MAP_30 + offset) == 4 &&
 				!g_globals->_activeSpells._s.walk_on_water) {
 				Sound::sound(SOUND_1);
-				_dialogMessage = STRING["movement.obstructed.cant_swim"];
+				_dialogMessage = " can't swim";
 				redraw();
 				return;
 			}
@@ -214,12 +214,11 @@ void ViewBase::obstructed(byte mask) {
 	else if (!(maps._currentWalls & mask & 0xaa))
 		index = 30;
 
-	_dialogMessage = STRING[Common::String::format(
-		"movement.obstructed.%d", map.dataByte(index))];
+	_dialogMessage = OBSTRUCTED_MESSAGES[map.dataByte(index)];
 }
 
 void ViewBase::barrier() {
-	_dialogMessage = STRING["movement.obstructed.barrier"];
+	_dialogMessage = "  barrier!  ";
 	Sound::sound(SOUND_1);
 }
 
diff --git a/engines/mm/mm1/game/wheel_spin.cpp b/engines/mm/mm1/game/wheel_spin.cpp
index 024d467f381..174ee55262c 100644
--- a/engines/mm/mm1/game/wheel_spin.cpp
+++ b/engines/mm/mm1/game/wheel_spin.cpp
@@ -22,6 +22,7 @@
 #include "mm/mm1/game/wheel_spin.h"
 #include "mm/mm1/maps/map16.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -52,7 +53,7 @@ void WheelSpin::spin() {
 		Common::String line;
 
 		if (map[VAL4] == 0) {
-			line = STRING["maps.map16.loser"];
+			line = mapText("Loser!\n");
 		} else {
 			c._flags[2] |= CHARFLAG2_80;
 			int val;
@@ -63,7 +64,7 @@ void WheelSpin::spin() {
 				WRITE_LE_UINT16(&map[VAL3], val);
 				c._exp += val;
 				line = Common::String::format("+%d %s", val,
-					STRING["maps.map16.exp"].c_str());
+					mapText("Exp!\n").c_str());
 				break;
 
 			case 2:
@@ -71,7 +72,7 @@ void WheelSpin::spin() {
 				WRITE_LE_UINT16(&map[VAL3], val);
 				c._gold += val;
 				line = Common::String::format("+%d %s", val,
-					STRING["maps.map16.gold"].c_str());
+					mapText("Gold!\n").c_str());
 				break;
 
 			case 3:
@@ -79,11 +80,11 @@ void WheelSpin::spin() {
 				WRITE_LE_UINT16(&map[VAL3], val);
 				c._gems = MIN((int)c._gems + val, 0xffff);
 				line = Common::String::format("+%d %s", val,
-					STRING["maps.map16.gems"].c_str());
+					mapText("Gems!\n").c_str());
 				break;
 
 			default:
-				line = STRING["maps.map16.loser"];
+				line = mapText("Loser!\n");
 				break;
 			}
 		}
diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index 1962bcda0d7..25246518e35 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -34,6 +34,7 @@ MODULE_OBJS += \
 	mm1/data/monsters.o \
 	mm1/data/party.o \
 	mm1/data/roster.o \
+	mm1/data/text.o \
 	mm1/data/text_parser.o \
 	mm1/data/trap.o \
 	mm1/data/treasure.o \


Commit: a4f2c2957660123fff76d708c8ac4909bc73fded
    https://github.com/scummvm/scummvm/commit/a4f2c2957660123fff76d708c8ac4909bc73fded
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:08+11:00

Commit Message:
MM: MM1: Replace STRING lookups in maps with literal text

Changed paths:
    engines/mm/mm1/maps/map00.cpp
    engines/mm/mm1/maps/map01.cpp
    engines/mm/mm1/maps/map02.cpp
    engines/mm/mm1/maps/map03.cpp
    engines/mm/mm1/maps/map04.cpp
    engines/mm/mm1/maps/map05.cpp
    engines/mm/mm1/maps/map06.cpp
    engines/mm/mm1/maps/map07.cpp
    engines/mm/mm1/maps/map08.cpp
    engines/mm/mm1/maps/map09.cpp
    engines/mm/mm1/maps/map10.cpp
    engines/mm/mm1/maps/map11.cpp
    engines/mm/mm1/maps/map12.cpp
    engines/mm/mm1/maps/map13.cpp
    engines/mm/mm1/maps/map14.cpp
    engines/mm/mm1/maps/map15.cpp
    engines/mm/mm1/maps/map16.cpp
    engines/mm/mm1/maps/map17.cpp
    engines/mm/mm1/maps/map18.cpp
    engines/mm/mm1/maps/map19.cpp
    engines/mm/mm1/maps/map20.cpp
    engines/mm/mm1/maps/map21.cpp
    engines/mm/mm1/maps/map22.cpp
    engines/mm/mm1/maps/map23.cpp
    engines/mm/mm1/maps/map24.cpp
    engines/mm/mm1/maps/map25.cpp
    engines/mm/mm1/maps/map26.cpp
    engines/mm/mm1/maps/map27.cpp
    engines/mm/mm1/maps/map28.cpp
    engines/mm/mm1/maps/map29.cpp
    engines/mm/mm1/maps/map30.cpp
    engines/mm/mm1/maps/map31.cpp
    engines/mm/mm1/maps/map32.cpp
    engines/mm/mm1/maps/map33.cpp
    engines/mm/mm1/maps/map34.cpp
    engines/mm/mm1/maps/map35.cpp
    engines/mm/mm1/maps/map36.cpp
    engines/mm/mm1/maps/map37.cpp
    engines/mm/mm1/maps/map38.cpp
    engines/mm/mm1/maps/map39.cpp
    engines/mm/mm1/maps/map40.cpp
    engines/mm/mm1/maps/map41.cpp
    engines/mm/mm1/maps/map42.cpp
    engines/mm/mm1/maps/map43.cpp
    engines/mm/mm1/maps/map44.cpp
    engines/mm/mm1/maps/map45.cpp
    engines/mm/mm1/maps/map46.cpp
    engines/mm/mm1/maps/map47.cpp
    engines/mm/mm1/maps/map48.cpp
    engines/mm/mm1/maps/map49.cpp
    engines/mm/mm1/maps/map50.cpp
    engines/mm/mm1/maps/map51.cpp
    engines/mm/mm1/maps/map52.cpp
    engines/mm/mm1/maps/map53.cpp
    engines/mm/mm1/maps/map54.cpp
    engines/mm/mm1/maps/map_desert.cpp
    engines/mm/mm1/maps/map_town.cpp


diff --git a/engines/mm/mm1/maps/map00.cpp b/engines/mm/mm1/maps/map00.cpp
index b9b76bfb24b..b5a6575d409 100644
--- a/engines/mm/mm1/maps/map00.cpp
+++ b/engines/mm/mm1/maps/map00.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -61,19 +62,19 @@ void Map00::special01() {
 
 	switch (g_maps->_forwardMask) {
 	case DIRMASK_E:
-		line2 = STRING["maps.map00.market"];
+		line2 = "\"Eulards Fine Foods\"";
 		x = 6;
 		break;
 	case DIRMASK_W:
-		line2 = STRING["maps.map00.blacksmith"];
+		line2 = "\"B and B Blacksmithing\"";
 		break;
 	default:
-		line2 = STRING["maps.map00.inn"];
+		line2 = "\"The Inn of Sorpigal\"";
 		break;
 	}
 
 	send(SoundMessage(
-		2, 0, STRING["maps.sign"],
+		2, 0, mapText("A sign above the door reads:\n"),
 		x, 1, line2
 	));
 }
@@ -89,7 +90,7 @@ void Map00::special03() {
 void Map00::special04() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.passage_outside1"],
+		"A passage leads outside, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(10, 10);
 			g_maps->changeMapFromExit(0xa11, 2);
@@ -116,7 +117,7 @@ void Map00::special08() {
 void Map00::special09() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xa11, 1);
 		}
@@ -173,28 +174,28 @@ void Map00::special17() {
 
 void Map00::special18() {
 	send(SoundMessage(
-		2, 0, STRING["maps.sign"],
-		6, 1, STRING["maps.map00.temple"]
+		2, 0, mapText("A sign above the door reads:\n"),
+		6, 1, "Temple Moonshadow"
 	));
 }
 
 void Map00::special19() {
 	send(SoundMessage(
-		2, 0, STRING["maps.sign"],
-		6, 1, STRING["maps.map00.jail"]
+		2, 0, mapText("A sign above the door reads:\n"),
+		6, 1, "Jail. Keep Out!"
 	));
 }
 
 void Map00::special20() {
 	send(SoundMessage(
-		2, 0, STRING["maps.sign"],
-		6, 1, STRING["maps.map00.tavern"]
+		2, 0, mapText("A sign above the door reads:\n"),
+		6, 1, "Ye Olde Hogge Tavern"
 	));
 }
 void Map00::special21() {
 	send(SoundMessage(
-		2, 0, STRING["maps.sign"],
-		6, 1, STRING["maps.map00.training"]
+		2, 0, mapText("A sign above the door reads:\n"),
+		6, 1, "Otto's Training"
 	));
 }
 
@@ -207,9 +208,9 @@ void Map00::special22() {
 
 void Map00::special23() {
 	visitedExit();
-	Common::String msg = STRING["maps.map00.trapdoor"];
+	Common::String msg = "Trap door!";
 	if (g_globals->_activeSpells._s.levitate)
-		msg += STRING["maps.map00.levitate"];
+		msg += ", levitation saves you!";
 
 	send(SoundMessage(msg,
 		[](const Common::KeyState &keyState) {
@@ -222,7 +223,7 @@ void Map00::special23() {
 
 void Map00::searchStatue() {
 	send(SoundMessage(
-		STRING["maps.map00.statue"],
+		"There is a statue here. Search it (Y/N)?",
 		[]() {
 			Map &map = *g_maps->_currentMap;
 			g_events->send("Statue", GameMessage("STATUE", map[STATUE_VAL]));
diff --git a/engines/mm/mm1/maps/map01.cpp b/engines/mm/mm1/maps/map01.cpp
index bbff2c58aca..90ed41bc878 100644
--- a/engines/mm/mm1/maps/map01.cpp
+++ b/engines/mm/mm1/maps/map01.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -54,8 +55,9 @@ void Map01::special00() {
 }
 
 void Map01::special01() {
-	Common::String line1 = STRING["maps.map01.zam1"];
-	Common::String line2 = STRING["maps.map01.zam3"];
+	Common::String line1 = mapText("\"I am Zam, astral brother of Zom and\n"
+		"only living male residing in Portsmith\n");
+	Common::String line2 = "You're not the couriers!\"";
 
 	bool hasCourier = false;
 	for (uint i = 0; i < g_globals->_party.size() && !hasCourier; ++i) {
@@ -68,7 +70,7 @@ void Map01::special01() {
 			c._flags[0] |= CHARFLAG0_COURIER3 | CHARFLAG0_ZAM_CLUE;
 		}
 
-		line2 = STRING["maps.map01.zam2"];
+		line2 = "My clue is c-15\"";
 	}
 
 	send(InfoMessage(
@@ -88,7 +90,7 @@ void Map01::special03() {
 void Map01::special04() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.passage_outside2"],
+		"Passage outside, exit (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(3, 3);
 			g_maps->changeMapFromExit(0x101, 2);
@@ -120,7 +122,9 @@ void Map01::special08() {
 	
 	// Show the message and wait for a keypress
 	send(SoundMessage(
-		STRING["maps.map01.secret"],
+		mapText("A sensuous female voice speaks from\n"
+			"within an intoxicating mist,\n"
+			"\"You've discovered my secret!\""),
 		[](const Common::KeyState &) {
 			g_events->close();
 
@@ -154,7 +158,7 @@ void Map01::special08() {
 void Map01::special09() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 0);
 			g_maps->changeMapFromExit(0xc01, 1);
@@ -163,33 +167,33 @@ void Map01::special09() {
 }
 
 void Map01::special10() {
-	showSign(STRING["maps.map01.market"]);
+	showSign("\"Maid Marion's Market\"");
 }
 
 void Map01::special11() {
-	showSign(STRING["maps.map01.blacksmith"]);
+	showSign("\"B. Smith's Workshop\"");
 }
 
 void Map01::special12() {
-	showSign(STRING["maps.map01.inn"]);
+	showSign("\"Inn of Portsmith\"");
 }
 
 void Map01::special13() {
-	showSign(STRING["maps.map01.temple"]);
+	showSign("\"Temple Succubon\"");
 }
 
 void Map01::special14() {
 	send(SoundMessage(
-		0, 1, STRING["maps.map01.zam0"]
+		0, 1, "Note: Zam 12,2"
 	));
 }
 
 void Map01::special15() {
-	showSign(STRING["maps.map01.tavern"]);
+	showSign("\"Zorelda's Watering Hole\"");
 }
 
 void Map01::special16() {
-	showSign(STRING["maps.map01.training"]);
+	showSign("\"Amazonia's Training\"");
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map02.cpp b/engines/mm/mm1/maps/map02.cpp
index a3e85b2a6a4..46899b3dac8 100644
--- a/engines/mm/mm1/maps/map02.cpp
+++ b/engines/mm/mm1/maps/map02.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -59,8 +60,8 @@ void Map02::special00() {
 }
 
 void Map02::special01() {
-	Common::String line1 = STRING["maps.map02.zom1"];
-	Common::String line2 = STRING["maps.map02.zom3"];
+	Common::String line1 = mapText("\"I am Zom, astral brother of Zam.\n");
+	Common::String line2 = "You're not the couriers!\"";
 
 	bool hasCourier = false;
 	for (uint i = 0; i < g_globals->_party.size() && !hasCourier; ++i) {
@@ -73,7 +74,7 @@ void Map02::special01() {
 			c._flags[0] |= CHARFLAG0_COURIER3 | CHARFLAG0_ZOM_CLUE;
 		}
 
-		line2 = STRING["maps.map02.zom2"];
+		line2 = "My clue is 1-15.\"";
 	}
 
 	send(InfoMessage(
@@ -94,7 +95,7 @@ void Map02::special03() {
 void Map02::special04() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.passage_outside2"],
+		"Passage outside, exit (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 7);
 			g_maps->changeMapFromExit(0x801, 2);
@@ -119,28 +120,28 @@ void Map02::special08() {
 }
 
 void Map02::special09() {
-	showSign(STRING["maps.map02.docks"]);
+	showSign("\"Ye Old Docks\"");
 }
 
 void Map02::special10() {
-	showSign(STRING["maps.map02.market"]);
+	showSign("\"Arcon's Slop\"");
 }
 
 void Map02::special11() {
-	showSign(STRING["maps.map02.blacksmith"]);
+	showSign("\"Swampside Supplies\"");
 }
 
 void Map02::special12() {
-	showSign(STRING["maps.map02.inn"]);
+	showSign("\"The Inn of Algary\"");
 }
 
 void Map02::special13() {
-	send(SoundMessage(0, 1, STRING["maps.map02.zom0"]));
+	send(SoundMessage(0, 1, "Note: Zom 1,1"));
 }
 
 void Map02::special14() {
 	send(SoundMessage(
-		STRING["maps.map02.pit"],
+		"Swaze pit! jump in (Y/N)?",
 		[]() {
 			g_maps->_mapPos.x++;
 			g_globals->_encounters.execute();
@@ -151,7 +152,7 @@ void Map02::special14() {
 void Map02::special15() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map02.portal"],
+		"A magic portal! enter (Y/N)?",
 		[]() {
 			int index = g_maps->_mapPos.x - 9;
 			if (index < 0 || index >= 4)
@@ -167,15 +168,15 @@ void Map02::special15() {
 }
 
 void Map02::special18() {
-	showSign(STRING["maps.map02.temple"]);
+	showSign("\"Temple Half-Dead\"");
 }
 
 void Map02::special20() {
-	showSign(STRING["maps.map02.tavern"]);
+	showSign("\"Jolly Jester Tavern\"");
 }
 
 void Map02::special21() {
-	showSign(STRING["maps.map02.training"]);
+	showSign("\"Dragons Claw Training\"");
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map03.cpp b/engines/mm/mm1/maps/map03.cpp
index e14e55318d0..3c51e2ee089 100644
--- a/engines/mm/mm1/maps/map03.cpp
+++ b/engines/mm/mm1/maps/map03.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -86,11 +87,15 @@ void Map03::special01() {
 		}
 
 		InfoMessage info1(
-			0, 0, STRING["maps.map03.telgoran1"],
-			0, 1, STRING["maps.map03.telgoran2"],
+			0, 0, mapText("An elf dressed in robes approaches...\n\n"),
+			0, 1, mapText("\"Friends, I am Telgoran. This scroll you\n"
+				"have brought me will be very helpful\n"
+				"(+2500 exp) Here's 1500 gold for your\n"
+				"services. I'll tell you a secret, if\n"
+				"treasure is what you want...\""),
 			[](const Common::KeyState &) {
 				InfoMessage info2(
-					0, 1, STRING["maps.map03.telgoran3"],
+					0, 1, mapText("\"The brothers you must find...\nPortsmith and Algary are combined.\""),
 					[](const Common::KeyState &) {
 						g_events->close();
 					}
@@ -105,8 +110,8 @@ void Map03::special01() {
 
 	} else {
 		send(InfoMessage(
-			0, 0, STRING["maps.map03.telgoran1"],
-			0, 1, STRING["maps.map03.telgoran4"]
+			0, 0, mapText("An elf dressed in robes approaches...\n\n"),
+			0, 1, "\"You're not the couriers!\""
 		));
 	}
 
@@ -124,7 +129,7 @@ void Map03::special03() {
 void Map03::special04() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.passage_outside1"],
+		"A passage leads outside, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(9, 11);
 			g_maps->changeMapFromExit(0x112, 2);
@@ -154,7 +159,7 @@ void Map03::special08() {
 void Map03::special09() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 0);
 			g_maps->changeMapFromExit(5, 1);
@@ -163,15 +168,15 @@ void Map03::special09() {
 }
 
 void Map03::special10() {
-	showSign(STRING["maps.map03.market"]);
+	showSign("\"Fewlerd's Feed\"");
 }
 
 void Map03::special11() {
-	showSign(STRING["maps.map03.blacksmith"]);
+	showSign("\"Mystical Metal Works\"");
 }
 
 void Map03::special12() {
-	showSign(STRING["maps.map03.inn"]);
+	showSign("\"The Inn of Dusk\"");
 }
 
 void Map03::special13() {
@@ -204,23 +209,23 @@ void Map03::special15() {
 }
 
 void Map03::special18() {
-	showSign(STRING["maps.map03.temple"]);
+	showSign("\"Temple Geist\"");
 }
 
 void Map03::special20() {
-	showSign(STRING["maps.map03.tavern"]);
+	showSign("\"Casper's Fine Spirits\"");
 }
 
 void Map03::special21() {
-	showSign(STRING["maps.map03.training"]);
+	showSign("\"Clearman's Training\"");
 }
 
 void Map03::special27() {
-	showSign(STRING["maps.map03.forbidden_crypt"]);
+	showSign("\"Forbidden Crypt\"");
 }
 
 void Map03::special28() {
-	showSign(STRING["maps.map03.eternal_rest"]);
+	showSign("\"Eternal Rest Chamber\"");
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map04.cpp b/engines/mm/mm1/maps/map04.cpp
index d8ca11ac260..03dd8f3779a 100644
--- a/engines/mm/mm1/maps/map04.cpp
+++ b/engines/mm/mm1/maps/map04.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -46,7 +47,7 @@ void Map04::special() {
 
 	// Stealable treasure
 	send(SoundMessage(
-		STRING["maps.map04.treasure"],
+		"Town treasure, steal (Y/N)?",
 		[]() {
 			g_maps->clearSpecialAndRememberReward();
 			if ((*g_maps->_currentMap)[MAP04_TREASURE_STOLEN] < 255)
@@ -85,14 +86,16 @@ void Map04::special01() {
 		}
 
 		send(InfoMessage(
-			0, 0, STRING["maps.map04.agar1"],
-			0, 1, STRING["maps.map04.agar2"],
+			0, 0, mapText("The wizard Agar speaks:\n\n"),
+			0, 1, mapText("\"You have done well to bring me this\n"
+				"scroll. (+1000 exp) Now take it to my\n"
+				"protege Telgoran in Dusk.\""),
 			callback	
 		));
 	} else {
 		send(InfoMessage(
-			0, 0, STRING["maps.map04.agar1"],
-			0, 1, STRING["maps.map04.agar3"],
+			0, 0, mapText("The wizard Agar speaks:\n\n"),
+			0, 1, "\"I have no business with you. Begone!\"",
 			callback
 		));
 	}
@@ -110,7 +113,7 @@ void Map04::special04() {
 	visitedExit();
 	if (_data[MAP04_PASSAGE_OVERRIDE] || _data[MAP04_TREASURE_STOLEN] == 0) {
 		send(SoundMessage(
-			STRING["maps.passage_outside2"],
+			"Passage outside, exit (Y/N)?",
 			[]() {
 				g_maps->_mapPos.x = 13;
 				g_maps->_mapPos.y = 1;
@@ -141,14 +144,14 @@ void Map04::special08() {
 	g_maps->_mapPos.y = getRandomNumber(15);
 	redrawGame();
 
-	send(InfoMessage(0, 1, STRING["maps.poof"]));
+	send(InfoMessage(0, 1, "Poof!"));
 }
 
 void Map04::special09() {
 	visitedExit();
 	if (_data[MAP04_STAIRS_OVERRIDE] || _data[MAP04_TREASURE_STOLEN] == 0) {
 		send(SoundMessage(
-			STRING["maps.stairs_down"],
+			"Stairs going down! Take them (Y/N)?",
 			[]() {
 				g_maps->_mapPos.x = 0;
 				g_maps->_mapPos.y = 7;
@@ -163,15 +166,15 @@ void Map04::special09() {
 }
 
 void Map04::special10() {
-	showSign(STRING["maps.map04.market"]);
+	showSign("\"Four Star Foods\"");
 }
 
 void Map04::special11() {
-	showSign(STRING["maps.map04.blacksmith"]);
+	showSign("\"Current Trends Ironworks\"");
 }
 
 void Map04::special12() {
-	showSign(STRING["maps.map04.inn"]);
+	showSign("\"The Inn of Erliquin\"");
 }
 
 void Map04::special13() {
@@ -184,15 +187,15 @@ void Map04::special13() {
 }
 
 void Map04::special18() {
-	showSign(STRING["maps.map04.temple"]);
+	showSign("\"Temple Gauche\"");
 }
 
 void Map04::special20() {
-	showSign(STRING["maps.map04.tavern"]);
+	showSign("\"Tavern of Tall Tales\"");
 }
 
 void Map04::special21() {
-	showSign(STRING["maps.map04.training"]);
+	showSign("\"Superior Training\"");
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map05.cpp b/engines/mm/mm1/maps/map05.cpp
index b18b59bd25f..8dd1c64c1f4 100644
--- a/engines/mm/mm1/maps/map05.cpp
+++ b/engines/mm/mm1/maps/map05.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -56,7 +57,7 @@ void Map05::special() {
 void Map05::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x604, 1);
 		}
@@ -95,12 +96,19 @@ void Map05::special05() {
 void Map05::special06() {
 	if (!g_globals->_party.hasItem(VELLUM_SCROLL_ID)) {
 		send(SoundMessage(
-			0, 1, STRING[!hasFlag() ? "maps.map05.man1" : "maps.map05.man2"],
+			0, 1, !hasFlag() ?
+				mapText("An elderly man behind a desk speaks:\n"
+					"\"i am in need of courier service\n"
+					"are you interested (Y/N)?\"") :
+				mapText("\"I see you haven't delivered my message,\n"
+					"Care to try again (Y/N)?\""),
 			[]() {
 				Map05 &map = *static_cast<Map05 *>(g_maps->_currentMap);
 				if (map.addScroll()) {
 					map.addFlag();
-					g_events->send(InfoMessage(0, 1, STRING["maps.map05.man3"]));
+					g_events->send(InfoMessage(0, 1, mapText("\"Good! take this scroll to the\n"
+						"wizard Agar in Erliquin and you shall\n"
+						"be rewarded.\"")));
 				}
 			}
 		));
@@ -110,7 +118,7 @@ void Map05::special06() {
 void Map05::special07() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map05.portal"],
+		mapText("A shimmering blue and white portal\nappears! Enter (Y/N)?"),
 		[]() {
 			g_maps->_mapPos.x = 8;
 			g_maps->_mapPos.y = 8;
@@ -121,14 +129,16 @@ void Map05::special07() {
 
 void Map05::special08() {
 	send(SoundMessage(
-		2, 1, STRING["maps.sign"],
-		10, 2, STRING["maps.map05.arena"]
+		2, 1, mapText("A sign above the door reads:\n"),
+		10, 2, "\"The Arena\""
 	));
 }
 
 void Map05::special09() {
 	send(SoundMessage(
-		0, 1, STRING["maps.map05.arena_inside"],
+		0, 1, mapText("Around the room there are several\n"
+			"balconies filled with cheering peasants\n"
+			"A man asks \"Will you participate (Y/N)?\""),
 		[]() {
 			Map05 &map = *static_cast<Map05 *>(g_maps->_currentMap);
 			map[MAP_47] = 2;
@@ -148,7 +158,7 @@ void Map05::special09() {
 }
 
 void Map05::special10() {
-	showMessage(STRING["maps.map05.message2"]);
+	showMessage("The jail above has many cells.");
 }
 
 void Map05::special11() {
@@ -156,7 +166,7 @@ void Map05::special11() {
 		g_maps->clearSpecial();
 		g_globals->_encounters.execute();
 	} else {
-		showMessage(STRING["maps.map05.message3"]);
+		showMessage("Don't turn around!");
 	}
 }
 
@@ -183,7 +193,7 @@ void Map05::encounter(int monsterId) {
 
 void Map05::showMessage(const Common::String &msg) {
 	send(SoundMessage(
-		0, 1, STRING["maps.map05.message1"],
+		0, 1, mapText("Scrawled on the wall, a message reads:\n"),
 		0, 2, msg
 	));
 }
@@ -199,7 +209,7 @@ bool Map05::addScroll() {
 		}
 	}
 
-	g_events->send(SoundMessage(8, 2, STRING["maps.map05.backpacks_full"]));
+	g_events->send(SoundMessage(8, 2, "*** Backpacks full ***"));
 	return false;
 }
 
diff --git a/engines/mm/mm1/maps/map06.cpp b/engines/mm/mm1/maps/map06.cpp
index e1b7127ebe3..d8a8ddc3aaa 100644
--- a/engines/mm/mm1/maps/map06.cpp
+++ b/engines/mm/mm1/maps/map06.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -95,7 +96,7 @@ void Map06::special() {
 void Map06::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.passage_outside2"],
+		"Passage outside, exit (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 11);
 			g_maps->changeMapFromExit(0xa11, 2);
@@ -106,7 +107,7 @@ void Map06::special00() {
 void Map06::special01() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map06.portal"],
+		mapText("Shimmering blue and white portal,\nenter (Y/N)?"),
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 0);
 			g_maps->changeMapFromExit(0xc01, 1);
@@ -135,7 +136,7 @@ void Map06::special03() {
 void Map06::special04() {
 	if (_data[VAL2]) {
 		_data[VAL2] = 0;
-		send(SoundMessage(0, 1, STRING["maps.map06.acid"]));
+		send(SoundMessage(0, 1, "Splash! A pool of acid!"));
 
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
 			Character &c = g_globals->_party[i];
@@ -150,7 +151,7 @@ void Map06::special04() {
 
 void Map06::special06() {
 	send(SoundMessage(
-		STRING["maps.map06.button"],
+		"Button on the wall, press it (Y/N)?",
 		[]() {
 			Map06 &map = *static_cast<Map06 *>(g_maps->_currentMap);
 			byte &val1 = map[VAL1];
@@ -174,11 +175,11 @@ void Map06::special07() {
 	g_maps->_mapPos.x = 10;
 	g_maps->_mapPos.y = 11;
 	updateGame();
-	send(SoundMessage(17, 1, STRING["maps.poof"]));
+	send(SoundMessage(17, 1, "Poof!"));
 }
 
 void Map06::special09() {
-	send(SoundMessage(0, 1, STRING["maps.map06.banner"]));
+	send(SoundMessage(0, 1, mapText("A banner reads:\nCorridor of Endless Encounters!")));
 }
 
 void Map06::special10() {
@@ -223,7 +224,7 @@ void Map06::special26() {
 	if (completedEncounters() < 13) {
 		// Talking to the wizard starts the tracked side quest
 		_visited[QUEST_ACTIVE_OFFSET] |= ENCOUNTER_COMPLETE;
-		send(SoundMessage(STRING["maps.map06.wizard"]));
+		send(SoundMessage(mapText("A crazed wizard exclaims,\n\"Encounter the 13, win the prize!\"")));
 	} else {
 		g_globals->_treasure.setGold(12000);
 		g_globals->_treasure._items[1] = BRONZE_KEY_ID;
@@ -275,7 +276,7 @@ void Map06::restoreVisitState() {
 void Map06::slide() {
 	_data[VAL2]++;
 
-	SoundMessage msg(16, 1, STRING["maps.map06.slide"]);
+	SoundMessage msg(16, 1, "A slide!");
 	msg._delaySeconds = 2;
 	msg._callback = []() {
 		g_maps->_currentMap->updateGame();
diff --git a/engines/mm/mm1/maps/map07.cpp b/engines/mm/mm1/maps/map07.cpp
index 68efc7a2f72..d11f5444d13 100644
--- a/engines/mm/mm1/maps/map07.cpp
+++ b/engines/mm/mm1/maps/map07.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -58,7 +59,7 @@ void Map07::special() {
 void Map07::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 15);
 			g_maps->changeMapFromExit(0xc03, 1);
@@ -69,7 +70,7 @@ void Map07::special00() {
 void Map07::special01() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map07.portal"],
+		"Blue and white portal, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(2, 7);
 			g_maps->changeMapFromExit(0x202, 1);
@@ -93,11 +94,11 @@ void Map07::special02() {
 
 	g_maps->_mapPos.y--;
 	updateGame();
-	send(SoundMessage(0, 1, STRING["maps.map07.gate"]));
+	send(SoundMessage(0, 1, "Bronze grate repels you!"));
 }
 
 void Map07::special03() {
-	send(SoundMessage(0, 1, STRING["maps.map07.do_not_disturb"]));
+	send(SoundMessage(0, 1, mapText("\n\"Demons in conference, do not disturb!\"")));
 }
 
 void Map07::special04() {
@@ -164,7 +165,7 @@ void Map07::special13() {
 				c.loadFaceSprites();
 			}
 
-			g_events->send(SoundMessage(0, 1, STRING["maps.map07.reversal"]));
+			g_events->send(SoundMessage(0, 1, "Reversal!"));
 			Sound::sound(SOUND_3);
 		}
 	);
@@ -186,7 +187,7 @@ void Map07::special15() {
 		}
 	}
 
-	g_events->send(SoundMessage(0, 1, STRING["maps.map07.might"]));
+	g_events->send(SoundMessage(0, 1, "Those who are worthy +4 might!"));
 	Sound::sound(SOUND_3);
 }
 
@@ -214,7 +215,7 @@ void Map07::setMonsters(int id1, int id2) {
 }
 
 void Map07::poolYN(YNCallback callback) {
-	send(SoundMessage(STRING["maps.map07.pool"], callback));
+	send(SoundMessage("Glowing pool, step in (Y/N)?", callback));
 }
 
 void Map07::applyCondition(Condition cond) {
@@ -225,7 +226,7 @@ void Map07::applyCondition(Condition cond) {
 	}
 
 	Sound::sound(SOUND_3);
-	send(InfoMessage(0, 1, STRING["maps.map07.toxic"]));
+	send(InfoMessage(0, 1, "Toxic!"));
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map08.cpp b/engines/mm/mm1/maps/map08.cpp
index edead47ebd3..fab5b4169bb 100644
--- a/engines/mm/mm1/maps/map08.cpp
+++ b/engines/mm/mm1/maps/map08.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -64,7 +65,7 @@ void Map08::special() {
 void Map08::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 7);
 			g_maps->changeMapFromExit(0xb1a, 1);
@@ -75,7 +76,7 @@ void Map08::special00() {
 void Map08::special01() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 1);
 			g_maps->changeMapFromExit(0xa00, 2);
@@ -105,7 +106,7 @@ void Map08::special06() {
 	if (_data[CODE_ARMED]) {
 		reduceHP();
 
-		send(InfoMessage(18, 2, STRING["maps.map08.zap"]));
+		send(InfoMessage(18, 2, "Zap!"));
 		Sound::sound(SOUND_3);
 	} else {
 		checkPartyDead();
@@ -114,7 +115,7 @@ void Map08::special06() {
 
 void Map08::special08() {
 	if (_data[CODE_ARMED]) {
-		send(InfoMessage(0, 1, STRING["maps.map08.dancing_lights"]));
+		send(InfoMessage(0, 1, mapText("Dancing lights flicker and crackel in\nthe area in front of you!")));
 		Sound::sound(SOUND_3);
 	} else {
 		checkPartyDead();
@@ -157,12 +158,12 @@ void Map08::correctCode() {
 	_states[119] ^= 0x10;
 	_states[120] ^= 1;
 
-	send(SoundMessage(STRING["maps.map08.good_code"]));
+	send(SoundMessage("\"Thank you, fields deactivated.\""));
 }
 
 void Map08::incorrectCode() {
 	SoundMessage msg(
-		STRING["maps.map08.bad_code"],
+		"\"Improper access code!\"",
 		[]() {
 			MM1::Maps::Map &map = *g_maps->_currentMap;
 			map[CODE_FAILURES]++;
@@ -176,8 +177,8 @@ void Map08::incorrectCode() {
 			}
 
 			InfoMessage msg2(
-				0, 1, STRING["maps.map08.bad_code"],
-				17, 2, STRING["maps.map08.alarm"],
+				0, 1, "\"Improper access code!\"",
+				17, 2, "Alarm!",
 				[]() {
 					g_globals->_encounters.execute();
 				}
diff --git a/engines/mm/mm1/maps/map09.cpp b/engines/mm/mm1/maps/map09.cpp
index ed5348d5b9d..9e0e034568f 100644
--- a/engines/mm/mm1/maps/map09.cpp
+++ b/engines/mm/mm1/maps/map09.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -56,7 +57,7 @@ void Map09::special() {
 void Map09::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x802, 1);
 		}
@@ -66,7 +67,7 @@ void Map09::special00() {
 void Map09::special01() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map09.passage_outside"],
+		"A cavernous passage, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 0);
 			g_maps->changeMapFromExit(0xf05, 3);
@@ -102,7 +103,8 @@ void Map09::special06() {
 		}
 	}
 
-	g_events->send(SoundMessage(0, 1, STRING["maps.map09.accuracy"]));
+	g_events->send(SoundMessage(0, 1, mapText("The Prism of Precision grants those\n"
+		"who are worthy +4 accuracy!")));
 }
 
 void Map09::special07() {
@@ -117,7 +119,8 @@ void Map09::special07() {
 		}
 	}
 
-	g_events->send(SoundMessage(0, 1, STRING["maps.map09.agility"]));
+	g_events->send(SoundMessage(0, 1, mapText("The Flame of Agility grants those who\n"
+		"are worthy +4 speed!")));
 }
 
 void Map09::special08() {
@@ -127,13 +130,13 @@ void Map09::special08() {
 	}
 
 	send(SoundMessage(
-		0, 1, STRING["maps.map09.shrine1"],
+		0, 1, "The Shrine of Okzar, pray (Y/N)?",
 		[]() {
 			const Character &leader = g_globals->_party[0];
 
 			if (leader._alignment == leader._alignmentInitial) {
 				SoundMessage msg(
-					STRING["maps.map09.shrine2"],
+					"Your leader is of clear mind.",
 					[]() {
 						g_globals->_treasure._items[2] = g_events->getRandomNumber(26) + 120;
 						g_globals->_treasure.setGold(120);
@@ -145,7 +148,7 @@ void Map09::special08() {
 
 			} else {
 				SoundMessage msg(
-					STRING["maps.map09.shrine3"],
+					"Your leader is inconsistent. Begone!",
 					[]() {
 						// TODO: Check this is right. Original set y twice
 						g_maps->_mapPos = Common::Point(5, 13);
@@ -160,7 +163,7 @@ void Map09::special08() {
 }
 
 void Map09::special09() {
-	send(SoundMessage(0, 1, STRING["maps.map09.stalactites"]));
+	send(SoundMessage(0, 1, "Stalactites shower the party!"));
 	reduceHP();
 }
 
@@ -169,8 +172,8 @@ void Map09::special14() {
 
 	if (g_globals->_activeSpells._s.levitate) {
 		send(SoundMessage(
-			0, 1, STRING["maps.map09.pit"],
-			0, 2, STRING["maps.map09.levitation"]
+			0, 1, mapText("A pit of poisonous spikes!\n"),
+			0, 2, "Levitation saved you."
 		));
 	} else {
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
@@ -182,7 +185,7 @@ void Map09::special14() {
 		}
 
 		SoundMessage msg(
-			STRING["maps.map09.pit"],
+			mapText("A pit of poisonous spikes!\n"),
 			[]() {
 				g_globals->_encounters.execute();
 			}
@@ -195,8 +198,8 @@ void Map09::special14() {
 void Map09::special18() {
 	if (g_globals->_activeSpells._s.psychic_protection) {
 		send(SoundMessage(
-			0, 1, STRING["maps.map09.psychic_blast"],
-			0, 2, STRING["maps.map09.protection"]
+			0, 1, mapText("Psychic blast!\n"),
+			0, 2, "Protection saved you."
 		));
 	} else {
 		// getRandomNumber() is inclusive, so pass the last valid index.
@@ -224,7 +227,7 @@ void Map09::special18() {
 			"Psychic blast! %s", effectLine.c_str());
 
 		SoundMessage msg(
-			0, 1, STRING["maps.map09.psychic_blast"],
+			0, 1, mapText("Psychic blast!\n"),
 			0, 2, effectLine,
 			[]() {
 				Common::String preCombatLog =
@@ -238,15 +241,17 @@ void Map09::special18() {
 }
 
 void Map09::special25() {
-	send(SoundMessage(0, 1, STRING["maps.map09.scrawl"]));
+	send(SoundMessage(0, 1, mapText("Scrawled in stone:\n"
+		"Character #1 leads the way.\n"
+		"Beware, traps abound!")));
 }
 
 void Map09::special26() {
-	send(SoundMessage(0, 1, STRING["maps.map09.corak_was_here"]));
+	send(SoundMessage(0, 1, "Corak was here"));
 }
 
 void Map09::special27() {
-	send(SoundMessage(0, 1, STRING["maps.map09.message"]));
+	send(SoundMessage(0, 1, mapText("Behind an old tapestry, a message\netched in gold reads: YICU2ME3")));
 }
 
 void Map09::portal(int index) {
@@ -254,7 +259,7 @@ void Map09::portal(int index) {
 	_portalIndex = index;
 
 	send(SoundMessage(
-		STRING["maps.map09.portal"],
+		"Blue and white portal, enter (Y/N)?",
 		[]() {
 			int idx = static_cast<Map09 *>(g_maps->_currentMap)->_portalIndex;
 			g_maps->_mapPos = Common::Point(MAP_DEST_X[idx], MAP_DEST_Y[idx]);
diff --git a/engines/mm/mm1/maps/map10.cpp b/engines/mm/mm1/maps/map10.cpp
index 088d741907a..8bcf44f9c8f 100644
--- a/engines/mm/mm1/maps/map10.cpp
+++ b/engines/mm/mm1/maps/map10.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -66,7 +67,7 @@ void Map10::special() {
 void Map10::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.passage_outside1"],
+		"A passage leads outside, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 7);
 			g_maps->changeMapFromExit(0x101, 2);
@@ -79,9 +80,13 @@ void Map10::special01() {
 	// Enhanced GameMessages view will provide vertical scrolling if wrapping
 	// still produces more lines than can fit in that frame.
 	SoundMessage msg(
-		STRING["maps.map10.ranalou1"],
+		mapText("\"I'm the wizard Ranalou. Are you here to\nuse the portals (Y/N)?\""),
 		[]() {
-			InfoMessage info(STRING["maps.map10.ranalou2"]);
+			InfoMessage info(mapText("\"Good! Inside this room are 6 portals,\n"
+				"each of which leads to a castle. The\n"
+				"Statue of Judgement requires you to find\n"
+				"one prisoner from each of these castles\n"
+				"before you may be found worthy.\""));
 			info._largeMessage = true;
 			g_events->send(info);
 			Sound::sound(SOUND_3);
@@ -92,7 +97,7 @@ void Map10::special01() {
 			g_maps->clearSpecial();
 		},
 		[]() {
-			g_events->send(SoundMessage(STRING["maps.map10.ranalou3"]));
+			g_events->send(SoundMessage("\"Then begone!\""));
 			g_maps->_mapPos = Common::Point(15, 0);
 			g_maps->_currentMap->updateGame();
 		}
@@ -105,7 +110,7 @@ void Map10::special02() {
 	int idx = g_maps->_mapPos.x;
 	g_maps->_mapPos = Common::Point(MAP_DEST_X[idx], MAP_DEST_Y[idx]);
 	g_maps->changeMap(MAP_DEST_ID[idx], MAP_DEST_SECTION[idx]);
-	send(SoundMessage(STRING["maps.map10.poof"]));
+	send(SoundMessage("Poof! you've been teleported..."));
 }
 
 void Map10::special17() {
@@ -135,9 +140,7 @@ void Map10::special23() {
 	g_maps->clearSpecial();
 
 	if (g_globals->_activeSpells._s.levitate) {
-		Common::String msg = Common::String::format("%s %s",
-			STRING["maps.map10.pit"].c_str(),
-			STRING["maps.map10.levitation"].c_str());
+		Common::String msg = "Jagged slime pit! Levitation saved you.";
 		send(SoundMessage(msg));
 		Sound::sound(SOUND_3);
 
@@ -146,7 +149,7 @@ void Map10::special23() {
 
 		if (getRandomNumber(4) == 4) {
 			SoundMessage msg(
-				0, 1, STRING["maps.map10.pit"],
+				0, 1, "Jagged slime pit!",
 				[]() {
 					g_globals->_encounters.execute();
 				}
@@ -156,14 +159,14 @@ void Map10::special23() {
 			Sound::sound(SOUND_3);
 
 		} else {
-			send(SoundMessage(STRING["maps.map10.pit"]));
+			send(SoundMessage("Jagged slime pit!"));
 			Sound::sound(SOUND_3);
 		}
 	}
 }
 
 void Map10::special29() {
-	send(SoundMessage(STRING["maps.map10.sign1"]));
+	send(SoundMessage(mapText("A sign:\n\nExperiment in progress, do not enter!")));
 }
 
 void Map10::special30() {
@@ -182,7 +185,7 @@ void Map10::special30() {
 }
 
 void Map10::special31() {
-	send(SoundMessage(STRING["maps.map10.sign2"]));
+	send(SoundMessage(mapText("A sign:\n\nAuthorized personnel only! Keep out.")));
 }
 
 void Map10::special32() {
diff --git a/engines/mm/mm1/maps/map11.cpp b/engines/mm/mm1/maps/map11.cpp
index a775ddb74cc..292161f84a7 100644
--- a/engines/mm/mm1/maps/map11.cpp
+++ b/engines/mm/mm1/maps/map11.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -85,13 +86,13 @@ void Map11::special() {
 		}
 
 		updateGame();
-		send(SoundMessage(STRING["maps.map11.poof"]));
+		send(SoundMessage("Poof! You've been teleported..."));
 	}
 }
 void Map11::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map11.ladder"],
+		"A ladder going up, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 2);
 			g_maps->changeMapFromExit(0xf04, 2);
@@ -115,7 +116,9 @@ void Map11::special02() {
 }
 
 void Map11::special03() {
-	send(SoundMessage(STRING["maps.map11.sign"]));
+	send(SoundMessage(mapText("Painted in a black and white pattern,\n"
+		"a sign reads: The second part is the\n"
+		"most valuable.")));
 }
 
 void Map11::special04() {
@@ -124,7 +127,7 @@ void Map11::special04() {
 }
 
 void Map11::special05() {
-	send(SoundMessage(STRING["maps.map11.tip2"]));
+	send(SoundMessage(mapText("This cavern is random until the dials\nare set.")));
 }
 
 void Map11::special06() {
@@ -142,16 +145,14 @@ void Map11::special08() {
 
 void Map11::pit() {
 	if (g_globals->_activeSpells._s.levitate) {
-		Common::String msg = Common::String::format("%s %s",
-			STRING["maps.map10.pit"].c_str(),
-			STRING["maps.map10.levitation"].c_str());
+		Common::String msg = "Jagged slime pit! Levitation saved you.";
 		send(SoundMessage(msg));
 		Sound::sound(SOUND_3);
 
 	} else {
 		reduceHP();
 		reduceHP();
-		send(SoundMessage(STRING["maps.map10.pit"]));
+		send(SoundMessage("Jagged slime pit!"));
 		Sound::sound(SOUND_3);
 	}
 }
@@ -159,7 +160,7 @@ void Map11::pit() {
 void Map11::selectDial(int dialIndex) {
 	_dialIndex = dialIndex;
 	Common::String msg = Common::String::format(
-		STRING["maps.map11.dial"].c_str(), '1' + dialIndex);
+		"Stabilization dial #%c set (A-Z)?", '1' + dialIndex);
 
 	// Show the dial's current setting under the prompt
 	byte current = _data[VAL2 + dialIndex];
@@ -217,7 +218,7 @@ void Map11::setDialChar(char c) {
 	// Echo the chosen value below the original A-Z instruction so there is
 	// unambiguous visual feedback about what the engine recorded.
 	Common::String line1 = Common::String::format(
-		STRING["maps.map11.dial"].c_str(), '1' + _dialIndex);
+		"Stabilization dial #%c set (A-Z)?", '1' + _dialIndex);
 	Common::String line2 = Common::String::format("Selected: %c", c);
 	send(InfoMessage(0, 1, line1, 0, 2, line2));
 }
diff --git a/engines/mm/mm1/maps/map12.cpp b/engines/mm/mm1/maps/map12.cpp
index ee45675c68e..98b05bd6864 100644
--- a/engines/mm/mm1/maps/map12.cpp
+++ b/engines/mm/mm1/maps/map12.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -56,7 +57,7 @@ void Map12::special() {
 void Map12::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map12.ladder_up"],
+		"Ladder up, climb (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 13);
 			g_maps->changeMapFromExit(0x106, 2);
@@ -65,12 +66,12 @@ void Map12::special00() {
 }
 
 void Map12::special01() {
-	Common::String line1 = STRING["maps.map12.lever"];
+	Common::String line1 = "Platinum lever protrudes from the wall.";
 	Common::String line2;
 
 	for (int i = 0; i < 9; ++i) {
 		if (!_data[SELECTIONS + i]) {
-			line2 = STRING["maps.map12.wont_budge"];
+			line2 = "It won't budge!";
 			break;
 		}
 	}
@@ -78,7 +79,7 @@ void Map12::special01() {
 	if (!line2.empty()) {
 		send(SoundMessage(0, 1, line1, 0, 2, line2));
 	} else {
-		line2 = STRING["maps.map12.pull_it"];
+		line2 = "Pull it (Y/N)?";
 		send(SoundMessage(
 			0, 1, line1,
 			0, 2, line2,
@@ -91,7 +92,7 @@ void Map12::special01() {
 				}
 
 				if (i < 9) {
-					g_events->send(SoundMessage(STRING["maps.map12.incorrect"]));
+					g_events->send(SoundMessage("Incorrect settings!"));
 
 				} else {
 					// Reward the party
@@ -107,7 +108,8 @@ void Map12::special01() {
 						c._exp += 2000;
 					}
 
-					g_events->send(SoundMessage(STRING["maps.map12.correct"]));
+					g_events->send(SoundMessage(mapText("You have mastered the magic square!\n"
+						"+2 int, +20 gems, +200 gold, +2000 exp")));
 				}
 			}
 		));
@@ -180,7 +182,7 @@ void Map12::special17() {
 
 void Map12::polyhedron(unsigned char side1, unsigned char side2) {
 	Common::String msg = Common::String::format(
-		STRING["maps.map12.polyhedron3"].c_str(), side1, side2);
+		"A glowing polyhedron is set on side %c%c", side1, side2);
 	send(SoundMessage(msg));
 }
 
@@ -203,11 +205,12 @@ void Map12::setPolyhedron(int polyIndex) {
 
 	if (_data[SELECTIONS + polyIndex]) {
 		Common::String msg = Common::String::format(
-			STRING["maps.map12.polyhedron2"].c_str(),
+			mapText("The polyhedron is set on %c\nSpin it (Y/N)?").c_str(),
 			_data[SELECTIONS + polyIndex]);
 		send(SoundMessage(msg, keyCallbackSpinPolyhedronTwo));
 	} else {
-		send(SoundMessage(STRING["maps.map12.polyhedron1"], keyCallbackSpinPolyhedronOne));
+		send(SoundMessage(mapText("Atop a crystal pyramid spins a glowing\n"
+			"polyhedron. Stop it on side (1-9)?"), keyCallbackSpinPolyhedronOne));
 	}
 }
 
diff --git a/engines/mm/mm1/maps/map13.cpp b/engines/mm/mm1/maps/map13.cpp
index 49a44b6fcf5..80267c2e55d 100644
--- a/engines/mm/mm1/maps/map13.cpp
+++ b/engines/mm/mm1/maps/map13.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -78,7 +80,7 @@ void Map13::special00() {
 	visitedExit();
 	_data[VAL1] = 0;
 	send(SoundMessage(
-		STRING["maps.map13.passage_outside"],
+		"A passage leads outdoors, exit (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 4);
 			g_maps->changeMapFromExit(0x703, 2);
@@ -102,8 +104,10 @@ void Map13::special02() {
 
 	if (g_globals->_activeSpells._s.levitate) {
 		send(SoundMessage(
-			0, 1, STRING["maps.map13.spike_pit"],
-			0, 2, STRING["maps.map13.levitation1"]
+			0, 1, mapText("A trap! A pit full of poisonous spikes\nopens below the party!\n"),
+			0, 2, g_engine->isEnhanced() ?
+				"Luckily, your levitation spell saved you!" :
+				"Luckily,your levitation spell saved you!"
 		));
 	} else if (!g_globals->_activeSpells._s.poison) {
 		reduceHP();
@@ -115,12 +119,12 @@ void Map13::special02() {
 				c._condition = POISONED;
 		}
 
-		send(SoundMessage(STRING["maps.map13.snake_pit"]));
+		send(SoundMessage("Snake pit! "));
 	}
 }
 
 void Map13::special06() {
-	send(SoundMessage(STRING["maps.map13.remains"]));
+	send(SoundMessage(mapText("Smashed remains of stone statues\nseem suspiciously lifelike.")));
 }
 
 void Map13::special10() {
@@ -130,8 +134,8 @@ void Map13::special10() {
 
 	if (g_globals->_activeSpells._s.levitate) {
 		send(InfoMessage(
-			0, 1, STRING["maps.map13.snake_pit"],
-			0, 2, STRING["maps.map13.levitation2"]
+			0, 1, "Snake pit! ",
+			0, 2, "Levitation saved you."
 		));
 		Sound::sound(SOUND_3);
 
@@ -145,7 +149,7 @@ void Map13::special10() {
 		}
 
 		InfoMessage msg(
-			0, 1, STRING["maps.map13.snake_pit"],
+			0, 1, "Snake pit! ",
 			[]() {
 				static_cast<Map13 *>(g_maps->_currentMap)->encounter(
 					g_events->getRandomNumber(3) + 10, 14, 1);
diff --git a/engines/mm/mm1/maps/map14.cpp b/engines/mm/mm1/maps/map14.cpp
index 8ea8857dd3a..62fef075736 100644
--- a/engines/mm/mm1/maps/map14.cpp
+++ b/engines/mm/mm1/maps/map14.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -79,7 +80,8 @@ void Map14::special00() {
 			}
 		}
 
-		send(SoundMessage(STRING["maps.map14.surrounded"],
+		send(SoundMessage(mapText("You've been surrounded by the Dark Rider\n"
+			"and his men! He challenges you to a duel... accept (Y/N)?"),
 			[]() {
 				Map14 &map = *static_cast<Map14 *>(g_maps->_currentMap);
 				map.encounter();
@@ -129,7 +131,7 @@ void Map14::special02() {
 		send("View", DrawGraphicMessage(65 + 6));
 
 		send(SoundMessage(
-			STRING["maps.map14.castle"],
+			mapText("The fabeled Castle Doom!\nWill you enter (Y/N)?"),
 			[]() {
 				Map14 &map = *static_cast<Map14 *>(g_maps->_currentMap);
 				map[VAL3] = 0xff;
@@ -145,14 +147,14 @@ void Map14::special02() {
 }
 
 void Map14::special03() {
-	send(SoundMessage(STRING["maps.map14.words"]));
+	send(SoundMessage(mapText("Carved on a block of ice are the words:\nStart at 15-7 and walk to Doom!")));
 }
 
 void Map14::special04() {
 	visitedExit();
 	if (_data[VAL2]) {
 		send(SoundMessage(
-			STRING["maps.map14.passage"],
+			"A secret passage to Doom, take it (Y/N)?",
 			[]() {
 				g_maps->_mapPos = Common::Point(4, 4);
 				g_maps->changeMapFromExit(0x706, 3);
@@ -175,7 +177,7 @@ void Map14::special05() {
 		}
 	}
 
-	send(SoundMessage(STRING["maps.map14.pool"]));
+	send(SoundMessage(mapText("The pool of health grants those who are\nworthy +4 endurance!")));
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map15.cpp b/engines/mm/mm1/maps/map15.cpp
index 6d2c376bd7c..f2d04f99457 100644
--- a/engines/mm/mm1/maps/map15.cpp
+++ b/engines/mm/mm1/maps/map15.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -67,7 +69,7 @@ void Map15::special() {
 			enc.execute();
 
 		} else {
-			send(SoundMessage(16, 1, STRING["maps.map15.its_hot"]));
+			send(SoundMessage(16, 1, "It's hot!"));
 
 			if (!g_globals->_activeSpells._s.fire) {
 				for (uint i = 0; i < g_globals->_party.size(); ++i) {
@@ -101,7 +103,7 @@ void Map15::special() {
 }
 
 void Map15::special00() {
-	send(SoundMessage(STRING["maps.map15.lava"]));
+	send(SoundMessage("Lava fills the Valley of Fire!"));
 }
 
 void Map15::special01() {
@@ -109,7 +111,10 @@ void Map15::special01() {
 
 	if (_data[VAL2]) {
 		SoundMessage msg(
-			STRING["maps.map15.body"],
+			g_engine->isEnhanced() ?
+				"The dragon's body disappears, except for a shiny tooth!" :
+				"The dragons body disappears, except\n"
+					"for a shiny tooth!",
 			[]() {
 				g_globals->_treasure._items[2] = DRAGONS_TOOTH_ID;
 				g_events->addAction(KEYBIND_SEARCH);
@@ -171,14 +176,16 @@ void Map15::special05() {
 
 void Map15::special06() {
 	g_maps->clearSpecial();
-	send(SoundMessage(STRING["maps.map15.percella1"],
+	send(SoundMessage(mapText("I am Percella the druid and i have what\n"
+		"you need. But promise me that you won't\n"
+		"help that demonic menace. Accept (Y/N)?"),
 		[]() {
 			g_globals->_treasure._items[2] = KINGS_PASS_ID;
 			g_events->addAction(KEYBIND_SEARCH);
 		},
 		[]() {
 			SoundMessage msg(
-				STRING["maps.map15.percella2"],
+				"Then meet my pets!",
 				[]() {
 					g_maps->_mapPos = Common::Point(14, 2);
 					g_maps->_currentMap->updateGame();
@@ -196,7 +203,7 @@ void Map15::special08() {
 }
 
 void Map15::cove() {
-	send(SoundMessage(STRING["maps.map15.cove"],
+	send(SoundMessage("A pirates secret cove, search (Y/N)?",
 		[]() {
 			Map15 &map = *static_cast<Map15 *>(g_maps->_currentMap);
 			g_maps->clearSpecial();
diff --git a/engines/mm/mm1/maps/map16.cpp b/engines/mm/mm1/maps/map16.cpp
index 474d8fbb07c..97cba2a1c05 100644
--- a/engines/mm/mm1/maps/map16.cpp
+++ b/engines/mm/mm1/maps/map16.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -91,7 +92,7 @@ void Map16::special00() {
 		}
 
 		_data[VAL1]++;
-		SoundMessage msg(STRING["maps.map16.water"],
+		SoundMessage msg(mapText("The water erupts violently,\nrevealing a tremendous serpent"),
 			[]() {
 				Game::Encounter &enc = g_globals->_encounters;
 
@@ -111,7 +112,9 @@ void Map16::special00() {
 }
 
 void Map16::special01() {
-	send(SoundMessage(STRING["maps.map16.wheel"],
+	send(SoundMessage(mapText("Embedded in the side of the mountain is\n"
+		"a large wheel covered in strange and\n"
+		"archaic symbols. Spin the wheel (Y/N)?"),
 		[]() {
 			for (int i = 0; i < 20; ++i)
 				Sound::sound(SOUND_1);
diff --git a/engines/mm/mm1/maps/map17.cpp b/engines/mm/mm1/maps/map17.cpp
index bc2f53c7453..b58b3a799a2 100644
--- a/engines/mm/mm1/maps/map17.cpp
+++ b/engines/mm/mm1/maps/map17.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -68,7 +69,7 @@ void Map17::special() {
 	} else if (getRandomNumber(30) == 10) {
 		g_maps->_mapPos = Common::Point(15, 15);
 		updateGame();
-		send(SoundMessage(STRING["maps.map17.wave"]));
+		send(SoundMessage("A tidal wave sweeps the party away!"));
 
 	} else {
 		none160();
@@ -76,7 +77,7 @@ void Map17::special() {
 }
 
 void Map17::special00() {
-	send(SoundMessage(STRING["maps.map17.islands"]));
+	send(SoundMessage(mapText("Atop this peak 5 islands can be seen\nto the southeast")));
 }
 
 void Map17::special01() {
@@ -108,7 +109,10 @@ void Map17::special01() {
 	// All current party members know their Gypsy signs. Keep the original
 	// retryable bridge-prompt behavior: declining does not consume the special.
 	SoundMessage msg(
-		STRING["maps.map17.bridge"],
+		mapText("A wooden bridge extendes across the\n"
+			"ocean into the mist. A hooded figure\n"
+			"materializes, \"To cross you must each\n"
+			"answer a question, Accept (Y/N)?\""),
 		[]() {
 			g_events->addView("ColorQuestions");
 		}
@@ -149,8 +153,11 @@ void Map17::askQuestion(uint partyIndex) {
 
 	if (!(g_globals->_currCharacter->_condition & BAD_CONDITION)) {
 		InfoMessage msg(
-			0, 0, STRING["maps.map17.color"],
-			0, 2, STRING["maps.map17.options"],
+			0, 0, "What is your color (1-8)?",
+			0, 2, mapText("1) Red Thorac       2) Blue Ogram\n"
+				"3) Green Bagar      4) Yellow Limra\n"
+				"5) Purple Sagran    6) Orange Oolak\n"
+				"7) Black Dresidion  8) White Dilithium"),
 			[](const Common::KeyState &ks) {
 				Map17 &map = *static_cast<Map17 *>(g_maps->_currentMap);
 				if (ks.keycode >= Common::KEYCODE_1 &&
@@ -165,19 +172,22 @@ void Map17::askQuestion(uint partyIndex) {
 					// or it has but the wrong color is selected, eradicate them
 					if (!color || (color & 7) != map[COLOR]) {
 						c._condition = ERADICATED;
-						line = STRING["maps.map17.wrong"];
+						line = "Wrong!";
 					} else {
 						map[CORRECT_ANSWERS]++;
 						c._flags[4] |= CHARFLAG4_80;
-						line = STRING["maps.map17.correct"];
+						line = "Correct!";
 					}
 
 					Sound::sound(SOUND_3);
 					InfoMessage msg2;
 					msg2._largeMessage = true;
 					msg2._delaySeconds = 1;
-					msg2._lines.push_back(Line(0, 0, STRING["maps.map17.color"]));
-					msg2._lines.push_back(Line(0, 2, STRING["maps.map17.options"]));
+					msg2._lines.push_back(Line(0, 0, "What is your color (1-8)?"));
+					msg2._lines.push_back(Line(0, 2, mapText("1) Red Thorac       2) Blue Ogram\n"
+						"3) Green Bagar      4) Yellow Limra\n"
+						"5) Purple Sagran    6) Orange Oolak\n"
+						"7) Black Dresidion  8) White Dilithium")));
 					msg2._lines.push_back(Line(16, 6, line));
 					msg2._callback = []() {
 						Map17 &map17 = *static_cast<Map17 *>(g_maps->_currentMap);
diff --git a/engines/mm/mm1/maps/map18.cpp b/engines/mm/mm1/maps/map18.cpp
index 1eade86a4ec..a7178b0fd5d 100644
--- a/engines/mm/mm1/maps/map18.cpp
+++ b/engines/mm/mm1/maps/map18.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -55,7 +57,7 @@ void Map18::special() {
 void Map18::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map18.passage"],
+		mapText("Cavernous passage to Erliquin,\ntake it (Y/N)?"),
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 7);
 			g_maps->changeMapFromExit(0xb1a, 1);
@@ -75,7 +77,7 @@ void Map18::special01() {
 	} else {
 		send("View", DrawGraphicMessage(65 + 6));
 		send(SoundMessage(
-			STRING["maps.map18.castle_south"],
+			"Castle Blackridge South, enter (Y/N)?",
 			[]() {
 				Map18 &map = *static_cast<Map18 *>(g_maps->_currentMap);
 				map[VAL1] = 0xff;
@@ -102,7 +104,7 @@ void Map18::special02() {
 	} else {
 		send("View", DrawGraphicMessage(65 + 6));
 		send(SoundMessage(
-			STRING["maps.map18.castle_north"],
+			"Castle Blackridge North, enter (Y/N)?",
 			[]() {
 				Map18 &map = *static_cast<Map18 *>(g_maps->_currentMap);
 				map[VAL2] = 0xff;
@@ -119,7 +121,10 @@ void Map18::special02() {
 
 void Map18::special03() {
 	visitedExit();
-	send(SoundMessage(STRING["maps.map18.ruins"],
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Ancient ruins of a deserted wizard's lair show signs of recent use, enter (Y/N)?" :
+		"Ancient ruins of a deserted wizards lair\n"
+			"show signs of recent use, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(2, 2);
 			g_maps->changeMapFromExit(0xf03, 3);
@@ -128,15 +133,21 @@ void Map18::special03() {
 }
 
 void Map18::special04() {
-	send(SoundMessage(STRING["maps.map18.sign1"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"A sign pointing S. reads:\n"
+			"Blackridge N." :
+		"A sign pointing S. reads: Blackridge N."));
 }
 
 void Map18::special05() {
-	send(SoundMessage(STRING["maps.map18.sign2"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"A sign pointing E. reads:\n"
+			"Blackridge S." :
+		"A sign pointing E. reads: Blackridge S."));
 }
 
 void Map18::special06() {
-	send(SoundMessage(STRING["maps.map18.sign3"]));
+	send(SoundMessage("A sign pointing E. reads: Erliquin"));
 }
 
 void Map18::special07() {
@@ -149,13 +160,13 @@ void Map18::special07() {
 	if (hasWonGame)
 		g_events->addView("WonGame");
 	else
-		send(SoundMessage(STRING["maps.map18.gates"]));
+		send(SoundMessage(mapText("The Gates to Another World!\n")));
 }
 
 void Map18::special08() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map18.cave"],
+		"A cave, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 7);
 			g_maps->changeMapFromExit(0x202, 1);
diff --git a/engines/mm/mm1/maps/map19.cpp b/engines/mm/mm1/maps/map19.cpp
index 995bab01726..234fbed6acc 100644
--- a/engines/mm/mm1/maps/map19.cpp
+++ b/engines/mm/mm1/maps/map19.cpp
@@ -23,6 +23,7 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
 
 namespace MM {
@@ -65,7 +66,7 @@ void Map19::special00() {
 void Map19::special01() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map19.cave"],
+		"There's a cave here, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 0);
 			g_maps->changeMapFromExit(0xa00, 1);
@@ -76,7 +77,10 @@ void Map19::special01() {
 void Map19::special02() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map19.stairs_down"],
+		g_engine->isEnhanced() ?
+			"A descending staircase is the entrance to a warrior's stronghold, go in (Y/N)?" :
+			"A descending staircase is the entrance\n"
+				"to a warriors stronghold, go in (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 1);
 			g_maps->changeMapFromExit(0xf02, 3);
@@ -85,7 +89,7 @@ void Map19::special02() {
 }
 
 void Map19::special03() {
-	send(SoundMessage(STRING["maps.map19.carving"]));
+	send(SoundMessage("Carved on a tree: \"9-9 Raven's Lair\""));
 }
 
 void Map19::special04() {
@@ -135,7 +139,7 @@ void Map19::riddleAnswer(const Common::String &answer) {
 
 	if (answer.equalsIgnoreCase(properAnswer)) {
 		InfoMessage msg(
-			16, 2, STRING["maps.map19.correct"],
+			16, 2, "Correct!",
 			[]() {
 				g_maps->clearSpecial();
 
@@ -158,7 +162,7 @@ void Map19::riddleAnswer(const Common::String &answer) {
 	} else {
 		g_maps->_mapPos.x = 15;
 		updateGame();
-		send(SoundMessage(STRING["maps.map19.incorrect"]));
+		send(SoundMessage("Wrong, you're too young to understand."));
 	}
 }
 
diff --git a/engines/mm/mm1/maps/map20.cpp b/engines/mm/mm1/maps/map20.cpp
index b909513c127..23c1fde0334 100644
--- a/engines/mm/mm1/maps/map20.cpp
+++ b/engines/mm/mm1/maps/map20.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -72,7 +73,7 @@ void Map20::special() {
 void Map20::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map20.stairs_down"],
+		"Stairs going down, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(1, 15);
 			g_maps->changeMapFromExit(0xc03, 1);
@@ -83,7 +84,7 @@ void Map20::special00() {
 void Map20::special01() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map20.cave"],
+		"A cave, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 0);
 			g_maps->changeMapFromExit(0x51b, 1);
@@ -103,7 +104,7 @@ void Map20::special02() {
 		send("View", DrawGraphicMessage(6 + 65));
 
 		send(SoundMessage(
-			STRING["maps.map20.castle"],
+			"Castle White Wolf, enter (Y/N)?",
 			[]() {
 				Map20 &map = *static_cast<Map20 *>(g_maps->_currentMap);
 				map[CASTLE_STATE] = 0xff;
@@ -122,7 +123,7 @@ void Map20::special03() {
 	bool hasWhistle = g_globals->_party.hasItem(RUBY_WHISTLE_ID);
 
 	if (!hasWhistle) {
-		send(SoundMessage(STRING["maps.map20.temple"],
+		send(SoundMessage(mapText("An ancient temple converted to a\nstronghold has no apparent entrance."),
 			[](const Common::KeyState &) {
 				g_events->focusedView()->close();
 			}
@@ -131,11 +132,11 @@ void Map20::special03() {
 	}
 
 	send(
-		SoundMessage(STRING["maps.map20.temple"],
+		SoundMessage(mapText("An ancient temple converted to a\nstronghold has no apparent entrance."),
 		[](const Common::KeyState &) {
 			g_events->focusedView()->close();
 			g_events->send(SoundMessage(
-				STRING["maps.map20.whistle"],
+				mapText("Your ruby whistle begins to glow,\nblow it (0-9 times)?"),
 				[](const Common::KeyState &ks) {
 					if (ks.keycode == Common::KEYCODE_0) {
 						g_events->focusedView()->close();
@@ -143,7 +144,7 @@ void Map20::special03() {
 					} else if (ks.keycode == Common::KEYCODE_2) {
 						g_events->focusedView()->close();
 						g_events->send(SoundMessage(
-							STRING["maps.map20.stairs_down"],
+							"Stairs going down, enter (Y/N)?",
 							[]() {
 								for (uint i = 0; i < g_globals->_party.size(); ++i) {
 									g_globals->_currCharacter = &g_globals->_party[i];
@@ -168,11 +169,11 @@ void Map20::special03() {
 }
 
 void Map20::special04() {
-	send(SoundMessage(STRING["maps.map20.sign1"]));
+	send(SoundMessage("A sign pointing N. reads: White Wolf"));
 }
 
 void Map20::special05() {
-	send(SoundMessage(STRING["maps.map20.sign2"]));
+	send(SoundMessage("A sign pointing S. reads: Portsmith"));
 }
 
 void Map20::special06() {
@@ -181,7 +182,12 @@ void Map20::special06() {
 		g_globals->_currCharacter->_flags[5] |= CHARFLAG5_2;
 	}
 
-	SoundMessage info(0, 0, STRING["maps.map20.peak"]);
+	SoundMessage info(0, 0, "Atop this peak looking:\n"
+		"\n"
+		"N= A cave beyond White Wolf.\n"
+		"E= Large beasts above Wyvern Peaks!\n"
+		"S= Distant uncharted isles.\n"
+		"W= A pirate ship beyond Korin Bluffs.");
 	info._largeMessage = true;
 	send(info);
 }
diff --git a/engines/mm/mm1/maps/map21.cpp b/engines/mm/mm1/maps/map21.cpp
index 518bf09840b..d108f33f32c 100644
--- a/engines/mm/mm1/maps/map21.cpp
+++ b/engines/mm/mm1/maps/map21.cpp
@@ -82,7 +82,7 @@ void Map21::special00() {
 	}
 
 	SoundMessage msg(
-		STRING["maps.map21.ghostship"],
+		"Pirate Ghostship Anarchist!",
 		[]() {
 			Game::Encounter &enc = g_globals->_encounters;
 			enc.clearMonsters();
@@ -104,7 +104,7 @@ void Map21::special00() {
 
 void Map21::special01() {
 	send(SoundMessage(
-		STRING["maps.map21.free_trivia"],
+		"Free trivia chance, pull branch (Y/N)?",
 		[]() {
 			Map21 &map = *static_cast<Map21 *>(g_maps->_currentMap);
 			map[TRIVIA_ENABLED]++;
@@ -120,7 +120,7 @@ void Map21::special02() {
 		return;
 
 	send(SoundMessage(
-		STRING["maps.map21.trivia_island"],
+		"Trivia Island! 500 gold, enter (Y/N)?",
 		[]() {
 			MM1::Maps::Map &map = *g_maps->_currentMap;
 
@@ -137,7 +137,7 @@ void Map21::special02() {
 
 			g_maps->_mapPos.y++;
 			g_maps->_currentMap->updateGame();
-			g_events->send(SoundMessage(STRING["maps.map21.not_enough_gold"]));
+			g_events->send(SoundMessage("Not enough gold!"));
 		},
 		[]() {
 			g_maps->_mapPos.y++;
diff --git a/engines/mm/mm1/maps/map22.cpp b/engines/mm/mm1/maps/map22.cpp
index 34665dd63b2..ad2991d273a 100644
--- a/engines/mm/mm1/maps/map22.cpp
+++ b/engines/mm/mm1/maps/map22.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -69,7 +70,12 @@ void Map22::special00() {
 	}
 
 	Sound::sound(SOUND_3);
-	InfoMessage msg(STRING["maps.map22.chest"]);
+	InfoMessage msg(mapText("You found a chest upon which an\n"
+		"inscription reads: Property of Xx!xx!\n"
+		"Inside you found a ruby whistle, 2000\n"
+		"gold and a note:\n"
+		"The stronghold lies at B-3,14-2\n"
+		"Blow twice to enter."));
 	msg._largeMessage = true;
 	send(msg);
 
@@ -100,12 +106,12 @@ void Map22::special00() {
 }
 
 void Map22::special01() {
-	send(SoundMessage(STRING["maps.map22.roadsign"]));
+	send(SoundMessage("Roadsign: Sorpigal S.17, W.1, N.2"));
 }
 
 void Map22::special02() {
 	send(SoundMessage(
-		STRING["maps.map22.wagons"],
+		"Deserted merchant wagons, search (Y/N)?",
 		[]() {
 			if (g_maps->_mapPos.x == 5) {
 				g_maps->clearSpecialAndRememberReward();
@@ -115,7 +121,7 @@ void Map22::special02() {
 			} else {
 				g_maps->clearSpecial();
 				InfoMessage msg1(
-					16, 2, STRING["maps.map22.ambush"],
+					16, 2, "Ambush!",
 					[]() {
 						Game::Encounter &enc = g_globals->_encounters;
 						int monsterCount = g_events->getRandomNumber(3);
@@ -142,7 +148,7 @@ void Map22::special02() {
 
 void Map22::special08() {
 	send(SoundMessage(
-		STRING["maps.map22.fountain"],
+		mapText("A fountain speaks:\n\"Will the party drink (Y/N)?\""),
 		[]() {
 			switch (g_maps->_mapPos.x) {
 			case 8:
@@ -151,7 +157,7 @@ void Map22::special08() {
 					g_globals->_party[i]._might._current += 50;
 
 				Sound::sound(SOUND_3);
-				g_events->send(SoundMessage(STRING["maps.map22.today_might"]));
+				g_events->send(SoundMessage("Today might!"));
 				break;
 
 			case 9:
@@ -160,7 +166,7 @@ void Map22::special08() {
 					g_globals->_party[i]._spellLevel._current += 7;
 
 				Sound::sound(SOUND_3);
-				g_events->send(SoundMessage(STRING["maps.map22.today_spells"]));
+				g_events->send(SoundMessage("Today spells!"));
 				break;
 
 			default:
@@ -172,7 +178,7 @@ void Map22::special08() {
 				}
 
 				Sound::sound(SOUND_3);
-				g_events->send(SoundMessage(STRING["maps.map22.poison"]));
+				g_events->send(SoundMessage("Arghhh... poison!"));
 				break;
 			}
 		}
diff --git a/engines/mm/mm1/maps/map23.cpp b/engines/mm/mm1/maps/map23.cpp
index d72201b73c4..df764eb06b7 100644
--- a/engines/mm/mm1/maps/map23.cpp
+++ b/engines/mm/mm1/maps/map23.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -51,7 +53,7 @@ void Map23::special() {
 
 	g_maps->clearSpecial();
 	InfoMessage msg(
-		14, 2, STRING["maps.map23.look_out"],
+		14, 2, "Look out!",
 		[]() {
 			g_globals->_encounters.execute();
 		}
@@ -63,7 +65,7 @@ void Map23::special() {
 void Map23::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map23.passage"],
+		mapText("A cavernous passage leads to Sorpigal,\ntake it (Y/N)?"),
 		[]() {
 			g_maps->_mapPos = Common::Point(12, 0);
 			g_maps->changeMapFromExit(0x604, 1);
@@ -74,7 +76,7 @@ void Map23::special00() {
 void Map23::special01() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map23.cave"],
+		"There's a cave here, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 0);
 			g_maps->changeMapFromExit(1, 1);
@@ -91,20 +93,14 @@ void Map23::special03() {
 	g_maps->clearSpecial();
 
 	if (g_globals->_activeSpells._s.levitate) {
-		Common::String line1 = Common::String::format("%s %s",
-			STRING["maps.map23.pit"].c_str(),
-			STRING["maps.map23.levitation"].c_str()
-		);
+		Common::String line1 = "A pit!.... Your levitation saved you!";
 
 		send(SoundMessage(line1));
 
 	} else {
 		reduceHP();
 
-		Common::String line1 = Common::String::format("%s %s",
-			STRING["maps.map23.pit"].c_str(),
-			STRING["maps.map23.ambush"].c_str()
-		);
+		Common::String line1 = "A pit!.... An ambush!";
 		SoundMessage msg(line1,
 			[]() {
 				g_globals->_encounters.execute();
@@ -117,7 +113,7 @@ void Map23::special03() {
 
 void Map23::special08() {
 	send(SoundMessage(
-		STRING["maps.map23.column"],
+		"A glowing white column, touch it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(
 				g_events->getRandomNumber(15),
@@ -129,7 +125,9 @@ void Map23::special08() {
 
 void Map23::special09() {
 	send(SoundMessage(
-		STRING["maps.map23.statues"],
+		g_engine->isEnhanced() ?
+			"Statues block the path, smash them (Y/N)?" :
+			"Statues block the path,smash them (Y/N)?",
 		[]() {
 			Map23 &map = *static_cast<Map23 *>(g_maps->_currentMap);
 			Game::Encounter &enc = g_globals->_encounters;
@@ -176,7 +174,7 @@ void Map23::special11() {
 }
 
 void Map23::special12() {
-	send(SoundMessage(14, 2, STRING["maps.map23.avalanche"]));
+	send(SoundMessage(14, 2, "Avalanche!"));
 	g_maps->_currentState = 209;
 	_states[g_maps->_mapOffset] = 0xff;
 	_walls[g_maps->_mapOffset] = 162;
@@ -199,7 +197,7 @@ void Map23::special13() {
 
 void Map23::fountain() {
 	send(SoundMessage(
-		STRING["maps.map23.fountain"],
+		"A strange fountain, drink (Y/N)?",
 		[]() {
 			for (uint i = 0; i < g_globals->_party.size(); ++i) {
 				Character &c = g_globals->_party[i];
@@ -207,7 +205,7 @@ void Map23::fountain() {
 				c.getAttribute(attrNum)._current = 30;
 			}
 
-			g_events->send(InfoMessage(16, 2, STRING["maps.map23.cheers"]));
+			g_events->send(InfoMessage(16, 2, "Cheers!"));
 		}
 	));
 }
diff --git a/engines/mm/mm1/maps/map24.cpp b/engines/mm/mm1/maps/map24.cpp
index 37194fcf725..7c7b88401d1 100644
--- a/engines/mm/mm1/maps/map24.cpp
+++ b/engines/mm/mm1/maps/map24.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -51,12 +53,18 @@ void Map24::special() {
 }
 
 void Map24::special00() {
-	send(SoundMessage(STRING["maps.map24.roadsign"]));
+	send(SoundMessage("Roadsign: Sorpigal N.22, W.2, N.2"));
 }
 
 void Map24::special01() {
 	SoundMessage msg(
-		STRING["maps.map24.kilburn"],
+		g_engine->isEnhanced() ?
+			"The exiled Lord Kilburn speaks: \"Take this map and explore the desert. Report your "
+				"findings to the other lords. Then they will understand!\" Accept (Y/N)?" :
+			"The exiled Lord Kilburn speaks:\n"
+				"\"Take this map and explore the desert.\n"
+				"Report your findings to the other lords.\n"
+				"Then they will understand!\" Accept(Y/N)?",
 		[]() {
 			Map24 &map = *static_cast<Map24 *>(g_maps->_currentMap);
 			// Meeting Kilburn satisfies Lord Ironfist's second quest.
@@ -76,7 +84,7 @@ void Map24::special01() {
 void Map24::special02() {
 	if (_data[VAL1]) {
 		send(SoundMessage(
-			STRING["maps.map24.wyvern_eye"],
+			mapText("One of the wyvern's eyes begins to glow!\nTake it (Y/N)?"),
 			[]() {
 				Map24 &map = *static_cast<Map24 *>(g_maps->_currentMap);
 				map.addItem(WYVERN_EYE_ID);
@@ -84,7 +92,7 @@ void Map24::special02() {
 		));
 	} else {
 		send(SoundMessage(
-			STRING["maps.map24.lair"],
+			"A wyvern's lair, investigate (Y/N)?",
 			[]() {
 				Map24 &map = *static_cast<Map24 *>(g_maps->_currentMap);
 				Game::Encounter &enc = g_globals->_encounters;
@@ -109,7 +117,7 @@ void Map24::special03() {
 	g_maps->clearSpecial();
 
 	SoundMessage msg(
-		STRING["maps.map24.wyverns"],
+		"Wyverns attack from above!",
 		[]() {
 			Game::Encounter &enc = g_globals->_encounters;
 			int monsterCount = g_events->getRandomNumber(4) + 3;
@@ -128,12 +136,13 @@ void Map24::special03() {
 }
 
 void Map24::special08() {
-	send(SoundMessage(STRING["maps.map24.sign"]));
+	send(SoundMessage(mapText("Painted in a black and white pattern,\n"
+		"a sign reads: The first part is female.")));
 }
 
 void Map24::special09() {
 	send(SoundMessage(
-		STRING["maps.map24.hermit"],
+		"The hermit says: \"Trade wares (Y/N)?\"",
 		[]() {
 			for (uint i = 0; i < g_globals->_party.size(); ++i) {
 				Character &c = g_globals->_party[i];
@@ -164,7 +173,7 @@ bool Map24::addItem(byte itemId) {
 		}
 	}
 
-	g_events->send(SoundMessage(STRING["maps.map24.backpacks_full"]));
+	g_events->send(SoundMessage("*** Backpacks full!"));
 	Sound::sound(SOUND_3);
 	return true;
 }
diff --git a/engines/mm/mm1/maps/map25.cpp b/engines/mm/mm1/maps/map25.cpp
index 57a59172c7e..2bc6d6ba2de 100644
--- a/engines/mm/mm1/maps/map25.cpp
+++ b/engines/mm/mm1/maps/map25.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -64,14 +65,14 @@ void Map25::special() {
 		} else {
 			switch (getRandomNumber(100)) {
 			case 99:
-				send(SoundMessage(STRING["maps.map25.volcano"]));
+				send(SoundMessage("The volcano erupts!"));
 				reduceHP();
 				Sound::sound(SOUND_3);
 				break;
 
 			case 100: {
 				SoundMessage msg(
-					STRING["maps.map25.pirates"],
+					"Pirates attack!",
 					[]() {
 						Game::Encounter &enc = g_globals->_encounters;
 						int monsterCount = g_events->getRandomNumber(8) + 4;
@@ -101,7 +102,7 @@ void Map25::special() {
 			nativesAttack();
 		} else {
 			if (g_maps->_mapPos.x >= 10) {
-				SoundMessage msg(STRING["maps.map25.weeping"]);
+				SoundMessage msg("Mysterious weeping echoes throughout!");
 				if (getRandomNumber(100) >= 99) {
 					msg._delaySeconds = 3;
 					msg._callback = []() {
@@ -136,11 +137,11 @@ void Map25::special() {
 		}
 	}
 		send(SoundMessage(
-			STRING["maps.map25.ship"],
+			mapText("At the waters edge are the decaying\nremains of a large ship. Search (Y/N)?"),
 			[]() {
 				if (g_maps->_mapOffset == 216) {
 					g_maps->clearSpecialAndRememberReward();
-					g_events->send(SoundMessage(STRING["maps.map25.jolly_raven"]));
+					g_events->send(SoundMessage("The Jolly Raven!"));
 
 					for (uint i = 0; i < g_globals->_party.size(); ++i) {
 						g_globals->_currCharacter = &g_globals->_party[i];
@@ -162,7 +163,7 @@ void Map25::special() {
 void Map25::special00() {
 	visitedExit();
 	send(SoundMessage(
-		STRING["maps.map25.portal"],
+		"Portal of power, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(12, 4);
 			g_maps->changeMapFromExit(0x201, 3);
@@ -175,7 +176,7 @@ void Map25::special01() {
 
 	if (g_globals->_party.hasItem(CORAL_KEY_ID)) {
 		send(InfoMessage(
-			STRING["maps.map25.key"],
+			mapText("Your coral key fits perfectly\nin the lock, enter (Y/N)?"),
 			[]() {
 				g_maps->_mapPos = Common::Point(7, 5);
 				g_maps->changeMapFromExit(0x212, 1);
@@ -190,7 +191,7 @@ void Map25::special02() {
 
 void Map25::nativesAttack() {
 	SoundMessage msg(
-		STRING["maps.map25.natives"],
+		"Crazed natives attack!",
 		[]() {
 			Game::Encounter &enc = g_globals->_encounters;
 			int monsterCount = g_events->getRandomNumber(6) + 7;
diff --git a/engines/mm/mm1/maps/map26.cpp b/engines/mm/mm1/maps/map26.cpp
index 332a9638cab..df8409fd98d 100644
--- a/engines/mm/mm1/maps/map26.cpp
+++ b/engines/mm/mm1/maps/map26.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -63,7 +64,7 @@ void Map26::special00() {
 		}
 
 		SoundMessage msg(
-			STRING["maps.map26.scorpion"],
+			"Suddenly, a gargantuan scorpion attacks!",
 			[]() {
 				Map26 &map = *static_cast<Map26 *>(g_maps->_currentMap);
 				Game::Encounter &enc = g_globals->_encounters;
@@ -92,11 +93,11 @@ void Map26::special01() {
 
 void Map26::special02() {
 	send(SoundMessage(
-		STRING["maps.map26.trading_post"],
+		mapText("A small tribe of desert nomads have\nestablished a trading post here.\nTrade item (Y/N)?"),
 		[]() {
 			Character &c = g_globals->_party[0];
 			if (c._backpack.empty()) {
-				g_events->send(SoundMessage(STRING["maps.map26.nothing_to_trade"]));
+				g_events->send(SoundMessage("Your leader has nothing to trade!"));
 			} else {
 				c._backpack[0]._id = CACTUS_NECTAR_ID;
 				c._backpack[0]._charges = 10;
@@ -106,7 +107,7 @@ void Map26::special02() {
 }
 
 void Map26::special03() {
-	send(SoundMessage(STRING["maps.map26.kilburn"]));
+	send(SoundMessage("Kilburn, C-3 6-14"));
 }
 
 void Map26::addFlag() {
diff --git a/engines/mm/mm1/maps/map27.cpp b/engines/mm/mm1/maps/map27.cpp
index b55d2fcedef..d25b0050081 100644
--- a/engines/mm/mm1/maps/map27.cpp
+++ b/engines/mm/mm1/maps/map27.cpp
@@ -25,6 +25,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -54,7 +55,7 @@ void Map27::special() {
 }
 
 void Map27::special00() {
-	send(SoundMessage(STRING["maps.map27.pool"]));
+	send(SoundMessage(mapText("The pool of wisdom grants those who are\nworthy +4 personality.")));
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
 		Character &c = g_globals->_party[i];
@@ -69,13 +70,13 @@ void Map27::special00() {
 }
 
 void Map27::special01() {
-	send(SoundMessage(STRING["maps.map27.retreat"]));
+	send(SoundMessage("Clerical Retreat"));
 }
 
 void Map27::special02() {
 	send(SoundMessage(
 		0, 1, clerics('N'),
-		0, 2, STRING["maps.map27.cured"]
+		0, 2, "Your party is cured!"
 	));
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
@@ -92,7 +93,7 @@ void Map27::special02() {
 void Map27::special03() {
 	send(SoundMessage(
 		0, 1, clerics('E'),
-		0, 2, STRING["maps.map27.alignment"]
+		0, 2, "Your alignment is restored!"
 	));
 
 	TempleData alignmentData;
@@ -106,7 +107,7 @@ void Map27::special03() {
 void Map27::special04() {
 	send(SoundMessage(
 		0, 1, clerics('W'),
-		0, 2, STRING["maps.map27.curses"]
+		0, 2, "All curses are removed!"
 	));
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
@@ -129,12 +130,11 @@ void Map27::special04() {
 }
 
 void Map27::special05() {
-	send(SoundMessage(STRING["maps.map27.sign"]));
+	send(SoundMessage(mapText("The clerics of the S. shall deem you\nworthy, if you can find them!")));
 }
 
 Common::String Map27::clerics(char name) {
-	return Common::String::format("%s %c.",
-		STRING["maps.map27.clerics"].c_str(), name);
+	return Common::String::format("We are the clerics of the %c.", name);
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map28.cpp b/engines/mm/mm1/maps/map28.cpp
index d134d862b67..622798b706c 100644
--- a/engines/mm/mm1/maps/map28.cpp
+++ b/engines/mm/mm1/maps/map28.cpp
@@ -146,12 +146,12 @@ void Map28::special() {
 
 	} else {
 		send(SoundMessage(
-			STRING["maps.map28.tree"],
+			"Climb tree (Y/N)?",
 			[]() {
 				Map28 &map = *static_cast<Map28 *>(g_maps->_currentMap);
 
 				if (!map[VAL2]) {
-					g_events->send(SoundMessage(STRING["maps.map28.nothing"]));
+					g_events->send(SoundMessage("Nothing here."));
 
 				} else {
 					g_maps->clearSpecial();
@@ -160,7 +160,7 @@ void Map28::special() {
 					int val = g_events->getRandomNumber(10);
 					switch (val) {
 					case 1:
-						g_events->send(SoundMessage(STRING["maps.map28.nothing"]));
+						g_events->send(SoundMessage("Nothing here."));
 						break;
 
 					case 2:
@@ -168,33 +168,33 @@ void Map28::special() {
 							Character &c = g_globals->_party[i];
 							c._food = MIN((int)c._food + 5, MAX_FOOD);
 						}
-						g_events->send(SoundMessage(STRING["maps.map28.food"]));
+						g_events->send(SoundMessage("+5 food"));
 						break;
 
 					case 3:
 						map.setCondition(POISONED);
 						map.reduceHpBase();
-						g_events->send(SoundMessage(STRING["maps.map28.thorns"]));
+						g_events->send(SoundMessage("Poison thorns"));
 						break;
 
 					case 4:
 						if (g_globals->_activeSpells._s.levitate) {
-							g_events->send(SoundMessage(STRING["maps.map28.nothing"]));
+							g_events->send(SoundMessage("Nothing here."));
 						} else {
 							map.reduceHpBase();
 							map.reduceHpBase();
-							g_events->send(SoundMessage(STRING["maps.map28.you_fell"]));
+							g_events->send(SoundMessage("You fell"));
 						}
 						break;
 
 					case 5:
-						g_events->send(SoundMessage(STRING["maps.map28.lightning"]));
+						g_events->send(SoundMessage("Lightning"));
 						break;
 
 					case 6:
 						map.setCondition(DISEASED);
 						map.reduceHpBase();
-						g_events->send(SoundMessage(STRING["maps.map28.lightning"]));
+						g_events->send(SoundMessage("Lightning"));
 						break;
 
 					case 7:
@@ -205,11 +205,11 @@ void Map28::special() {
 							}
 						}
 
-						g_events->send(SoundMessage(STRING["maps.map28.flash"]));
+						g_events->send(SoundMessage("Flash"));
 						break;
 
 					case 8:
-						g_events->send(SoundMessage(STRING["maps.map28.poof"]));
+						g_events->send(SoundMessage("Poof!"));
 						g_maps->_mapPos = Common::Point(
 							g_events->getRandomNumber(15),
 							g_events->getRandomNumber(15));
@@ -218,7 +218,7 @@ void Map28::special() {
 
 					default:
 						g_globals->_activeSpells._s.cursed = val;
-						g_events->send(SoundMessage(STRING["maps.map28.cursed"]));
+						g_events->send(SoundMessage("Cursed"));
 						break;
 					}
 				}
@@ -235,7 +235,7 @@ void Map28::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map28.cave"],
+		"A cave, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 8);
 			g_maps->changeMapFromExit(0x601, 1);
@@ -244,7 +244,7 @@ void Map28::special01() {
 }
 
 void Map28::special02() {
-	send(SoundMessage(STRING["maps.map28.sign"]));
+	send(SoundMessage("Roadsign: Algary S.9"));
 }
 
 void Map28::setCondition(byte condition) {
diff --git a/engines/mm/mm1/maps/map29.cpp b/engines/mm/mm1/maps/map29.cpp
index 5b5e20a99ca..e8b87029f35 100644
--- a/engines/mm/mm1/maps/map29.cpp
+++ b/engines/mm/mm1/maps/map29.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -96,7 +97,7 @@ void Map29::special00() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map29.algary"],
+		"Algary, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(8, 15);
 			g_maps->changeMapFromExit(0x203, 1);
@@ -119,7 +120,9 @@ void Map29::special01() {
 		}
 
 		send(SoundMessage(
-			STRING["maps.map29.beast"],
+			mapText("A huge winged beast exclaims,\n"
+				"\"This swamp is quite dangerous. Fly you\n"
+				"to safety (Y/N)?\""),
 			[]() {
 				g_maps->_mapPos = Common::Point(12, 12);
 				g_maps->_currentMap->updateGame();
@@ -155,7 +158,7 @@ void Map29::special02() {
 
 void Map29::special03() {
 	SoundMessage msg(
-		STRING["maps.map29.attack"],
+		"Paul Pead and his men attack!",
 		[]() {
 			Game::Encounter &enc = g_globals->_encounters;
 			int monsterCount = g_events->getRandomNumber(5) + 6;
@@ -238,7 +241,7 @@ void Map29::chessAnswer(const Common::String &answer) {
 
 void Map29::begone() {
 	g_maps->_mapPos.y = 7;
-	SoundMessage msg(STRING["maps.map29.begone"],
+	SoundMessage msg("Og says, \"Begone!\"",
 		[]() {
 			g_maps->_currentMap->updateGame();
 		}
diff --git a/engines/mm/mm1/maps/map30.cpp b/engines/mm/mm1/maps/map30.cpp
index 1cbd8910099..24cc4eae754 100644
--- a/engines/mm/mm1/maps/map30.cpp
+++ b/engines/mm/mm1/maps/map30.cpp
@@ -23,12 +23,24 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
 namespace Maps {
 
+static const char *const ATTRIBUTES[7] = {
+	"intellect",
+	"might",
+	"personality",
+	"endurance",
+	"speed",
+	"accuracy",
+	"luck"
+};
+
 static const byte MONSTER_ID1[8] = { 2, 3, 8, 10, 14, 16, 17, 7 };
 static const byte MONSTER_ID2[8] = { 5, 6, 3, 8, 10, 12, 12, 12 };
 
@@ -73,7 +85,7 @@ void Map30::special00() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map30.passage"],
+		"Passage to Dusk, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 7);
 			g_maps->changeMapFromExit(0x802, 1);
@@ -85,7 +97,7 @@ void Map30::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map30.ruins"],
+		"Ruins of Castle Dragadune, enter (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 15);
 			g_maps->changeMapFromExit(0x107, 3);
@@ -99,7 +111,10 @@ void Map30::special02() {
 
 void Map30::special03() {
 	send(SoundMessage(
-		STRING["maps.map30.hourglass"],
+		g_engine->isEnhanced() ?
+			"At the center of the land that time forgot stands an hourglass, turn it (Y/N)?" :
+			"At the center of the land that time\n"
+				"forgot stands an hourglass,turn it(Y/N)?",
 		[]() {
 			g_maps->clearSpecial();
 			Sound::sound(SOUND_3);
@@ -128,14 +143,14 @@ Common::String Map30::worthy() {
 	c._exp += val;
 
 	Common::String line = Common::String::format(
-		STRING["maps.map30.worthy"].c_str(), c._worthiness / 32);
-	line = Common::String::format("%s%d %s",
-		line.c_str(), val, STRING["maps.map30.experience"].c_str());
+		mapText("Your actions reflect your views %d of 6\n+").c_str(), c._worthiness / 32);
+	line = Common::String::format("%s%d experience",
+		line.c_str(), val);
 
 	if (c._worthiness & 0x80) {
 		int attrNum = getRandomNumber(7) - 1;
 		const Common::String attrName =
-			STRING[Common::String::format("maps.map30.attributes.%d", attrNum)];
+			ATTRIBUTES[attrNum];
 		AttributePair &attrib = c.getAttribute(attrNum);
 
 		// The Statue uses the same rule for all seven attributes. Apply the
@@ -161,7 +176,7 @@ Common::String Map30::worthy() {
 }
 
 Common::String Map30::unworthy() {
-	return STRING["maps.map30.unworthy"];
+	return "Not worthy!";
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map31.cpp b/engines/mm/mm1/maps/map31.cpp
index 4d22ac0cc93..3462e8b9dea 100644
--- a/engines/mm/mm1/maps/map31.cpp
+++ b/engines/mm/mm1/maps/map31.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -51,7 +52,7 @@ void Map31::special() {
 			g_maps->clearSpecial();
 			g_globals->_encounters.execute();
 		} else {
-			send(SoundMessage(STRING["maps.map31.poof"]));
+			send(SoundMessage("Poof!"));
 			g_maps->_mapPos = Common::Point(
 				getRandomNumber(15), getRandomNumber(15));
 			updateGame();
@@ -64,7 +65,7 @@ void Map31::special() {
 }
 
 void Map31::special00() {
-	send(SoundMessage(STRING["maps.map31.device"]));
+	send(SoundMessage(mapText("Strange alien device grants those who\nare worthy +4 intellect!")));
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
 		Character &c = g_globals->_party[i];
@@ -115,7 +116,7 @@ void Map31::encounter() {
 
 void Map31::hostile() {
 	SoundMessage msg(
-		STRING["maps.map31.flash"],
+		"A bright flash!",
 		[]() {
 			for (uint i = 0; i < g_globals->_party.size(); ++i) {
 				g_globals->_party[i]._condition = ERADICATED;
@@ -133,7 +134,9 @@ void Map31::neutral() {
 }
 
 void Map31::friendly() {
-	send(SoundMessage(STRING["maps.map31.varnlings"]));
+	send(SoundMessage(mapText("\"Varnlings take heed! our prisoner has\n"
+		"escaped and may be disguised as a noble.\n"
+		"Find his counterpart to expose him!\"")));
 	g_maps->clearSpecial();
 }
 
diff --git a/engines/mm/mm1/maps/map32.cpp b/engines/mm/mm1/maps/map32.cpp
index a57e8ab36d5..7bc1f212f2e 100644
--- a/engines/mm/mm1/maps/map32.cpp
+++ b/engines/mm/mm1/maps/map32.cpp
@@ -24,11 +24,22 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
 namespace Maps {
 
+static const char *const PASSWORDS[7] = {
+	"Musical",
+	"Tones",
+	"Together",
+	"Clerics",
+	"South",
+	"Deem",
+	"Worthy"
+};
+
 #define VAL1 75
 #define VAL2 111
 #define PASSWORD_INDEX 393
@@ -49,7 +60,7 @@ void Map32::special() {
 	}
 
 	if (_walls[g_maps->_mapOffset] == 0x55 && g_maps->_mapPos.x < 13) {
-		send(SoundMessage(STRING["maps.map32.music"]));
+		send(SoundMessage("Distant harp music..."));
 	} else {
 		g_maps->clearSpecial();
 		g_globals->_encounters.execute();
@@ -60,11 +71,11 @@ void Map32::special00() {
 	visitedExit();
 
 	if (!g_globals->_party.hasItem(DIAMOND_KEY_ID)) {
-		send(SoundMessage(STRING["maps.map32.door"]));
+		send(SoundMessage("A diamond door!"));
 	} else {
 		send(SoundMessage(
-			0, 1, STRING["maps.map32.door"],
-			0, 2, STRING["maps.map32.key"],
+			0, 1, "A diamond door!",
+			0, 2, "Use your key (Y/N)?",
 			[]() {
 				g_maps->_mapPos = Common::Point(7, 0);
 				g_maps->changeMapFromExit(0xb1a, 3);
@@ -90,7 +101,7 @@ void Map32::special02() {
 	} else {
 		send("View", DrawGraphicMessage(65 + 6));
 		send(SoundMessage(
-			STRING["maps.map32.castle"],
+			"King Alamar's castle, enter (Y/N)?",
 			[]() {
 				Map32 &map = *static_cast<Map32 *>(g_maps->_currentMap);
 				map[VAL2] = 0xff;
@@ -111,13 +122,12 @@ void Map32::special04() {
 	}
 
 	Common::String line2 = Common::String::format("\"%s %s",
-		STRING[Common::String::format("maps.map32.passwords.%d",
-			_data[PASSWORD_INDEX])].c_str(),
-		STRING["maps.map32.password"].c_str()
+		PASSWORDS[_data[PASSWORD_INDEX]],
+		mapText("is the password for today,\nleave the area and change it i may!\"").c_str()
 	);
 
 	send(SoundMessage(
-		0, 1, STRING["maps.map32.heratio"],
+		0, 1, "Heratio Harper sings:",
 		0, 2, line2
 	));
 }
@@ -131,13 +141,12 @@ void Map32::special05() {
 
 void Map32::passwordEntered(const Common::String &password) {
 	if ((_data[PASSWORD_INDEX] & 0x80) ||
-		!password.equalsIgnoreCase(STRING[Common::String::format("maps.map32.passwords.%d",
-			_data[PASSWORD_INDEX])])) {
+		!password.equalsIgnoreCase(PASSWORDS[_data[PASSWORD_INDEX]])) {
 		g_maps->_mapPos.x--;
 		updateGame();
 
 	} else {
-		g_events->send(SoundMessage(STRING["maps.map32.correct"]));
+		g_events->send(SoundMessage("Correct! you may pass."));
 		_data[VAL1]++;
 		g_maps->clearSpecial();
 	}
diff --git a/engines/mm/mm1/maps/map33.cpp b/engines/mm/mm1/maps/map33.cpp
index 188db48842c..4827e9e7a20 100644
--- a/engines/mm/mm1/maps/map33.cpp
+++ b/engines/mm/mm1/maps/map33.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -68,7 +69,7 @@ void Map33::special() {
 			if (g_maps->_mapPos.y < 5 && g_maps->_forwardMask != DIRMASK_N) {
 				g_events->addKeypress((Common::KeyCode)149);
 			} else {
-				InfoMessage msg(0, 1, STRING["maps.map33.slime"]);
+				InfoMessage msg(0, 1, mapText("Ectoplasmic slime slushes beneath you!\n"));
 				Sound::sound(SOUND_2);
 
 				switch (getRandomNumber(200)) {
@@ -79,7 +80,7 @@ void Map33::special() {
 					if (!(c._condition & BAD_CONDITION)) {
 						c._condition |= SILENCED | PARALYZED | UNCONSCIOUS;
 						c._hpCurrent = 0;
-						msg._lines.push_back(Line(0, 2, STRING["maps.map33.quicksand"]));
+						msg._lines.push_back(Line(0, 2, "Quicksand!"));
 						Sound::sound(SOUND_3);
 					}
 					break;
@@ -105,7 +106,7 @@ void Map33::special() {
 				}
 			}
 		} else {
-			send(SoundMessage(STRING["maps.map33.tombstones"]));
+			send(SoundMessage("Toppled tombstones abound!"));
 		}
 	} else {
 		if (g_maps->_mapPos.y < 5) {
@@ -129,7 +130,7 @@ void Map33::special() {
 
 void Map33::special00() {
 	send(SoundMessage(
-		STRING["maps.map33.meeting"],
+		mapText("Dragon City, town meeting...\nDisrupt (Y/N)?"),
 		[]() {
 			Game::Encounter &enc = g_globals->_encounters;
 			g_maps->clearSpecial();
@@ -149,7 +150,7 @@ void Map33::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map33.building"],
+		mapText("The fabled building of gold,\nenter (Y/N)?"),
 		[]() {
 			g_maps->_mapPos = Common::Point(0, 0);
 			g_maps->changeMapFromExit(0xf01, 3);
@@ -160,23 +161,23 @@ void Map33::special01() {
 void Map33::special02() {
 	_data[VAL1] = 12;
 	_data[VAL2] = 8;
-	search(STRING["maps.map33.coffin"]);
+	search("Unearthed coffin! Examine (Y/N)?");
 }
 
 void Map33::special03() {
 	_data[VAL1] = 10;
 	_data[VAL2] = 10;
-	search(STRING["maps.map33.crypt"]);
+	search("Open crypt! Examine (Y/N)?");
 }
 
 void Map33::special04() {
 	g_maps->clearSpecialAndRememberReward();
 
 	send(SoundMessage(
-		STRING["maps.map33.corpse"],
+		mapText("Half buried corpse asks for help!\nAssist (Y/N)?"),
 		[]() {
 			SoundMessage msg(
-				STRING["maps.map33.thanks"],
+				mapText("Thanks! It ain't easy being a corpse!\nTake these..."),
 				[](const Common::KeyState &) {
 					g_events->close();
 					g_globals->_treasure.setGems(50);
diff --git a/engines/mm/mm1/maps/map34.cpp b/engines/mm/mm1/maps/map34.cpp
index 4b85ff80f0a..31fd92758fd 100644
--- a/engines/mm/mm1/maps/map34.cpp
+++ b/engines/mm/mm1/maps/map34.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -59,7 +60,7 @@ void Map34::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.passage_outside1"],
+		"A passage leads outside, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(7, 15);
 			g_maps->changeMapFromExit(0xf01, 2);
@@ -79,7 +80,13 @@ void Map34::special02() {
 
 	Sound::sound2(SOUND_2);
 
-	InfoMessage msg(0, 0, STRING["maps.map34.alamar"]);
+	InfoMessage msg(0, 0, mapText("Suspended in a forcefield is the body\n"
+		"of a man enclosed in an iron suit.\n"
+		"Telepathic communication: \"I am the true\n"
+		"King Alamar, imprisoned by a demonic\n"
+		"alien being! Confront him with this Eye\n"
+		"of Goros and you shall see his true\n"
+		"self. The fate of the realm is at stake\""));
 	msg._largeMessage = true;
 	msg._keyCallback = [](const Common::KeyState &) {
 		g_events->close();
@@ -91,37 +98,37 @@ void Map34::special02() {
 }
 
 void Map34::special03() {
-	send(SoundMessage(STRING["maps.map34.message3"]));
+	send(SoundMessage(mapText("Etched in silver, message D reads:\n//sv/21;-22r,;du1rs0")));
 }
 
 void Map34::special04() {
-	send(SoundMessage(STRING["maps.map34.message4"]));
+	send(SoundMessage(mapText("Written in blue blood:\nThe canine has the key!")));
 }
 
 void Map34::special05() {
-	send(SoundMessage(STRING["maps.map34.message5"]));
+	send(SoundMessage(mapText("Etched in silver:\nMessage interleave 'fedbac'")));
 }
 
 void Map34::special06() {
-	send(SoundMessage(STRING["maps.map34.message6"]));
+	send(SoundMessage(mapText("Etched in gold:\nMessage interleave '8,5,3,9,1,4,6,2,7'")));
 }
 
 void Map34::special07() {
 	if (g_globals->_party.hasItem(GOLD_KEY_ID)) {
 		checkPartyDead();
 	} else {
-		send(SoundMessage(STRING["maps.map34.door"]));
+		send(SoundMessage("A golden door repels you!"));
 		g_maps->_mapPos.y--;
 		updateGame();
 	}
 }
 
 void Map34::special08() {
-	send(SoundMessage(STRING["maps.map34.statue"]));
+	send(SoundMessage(mapText("A gruesome statue says:\n\"Start at 15-7 and walk to Doom!\"")));
 }
 
 void Map34::special09() {
-	send(SoundMessage(STRING["maps.map34.banner"]));
+	send(SoundMessage(mapText("A banner reads:\nThe endless spiral!!!")));
 }
 
 void Map34::special10() {
@@ -137,25 +144,27 @@ void Map34::special12() {
 }
 
 void Map34::special13() {
-	send(SoundMessage(STRING["maps.map34.sign"]));
+	send(SoundMessage(mapText("A sign above the door reads:\nMonster breeding grounds, keep out!")));
 }
 
 void Map34::special16() {
-	send(SoundMessage(STRING["maps.map34.machine"]));
+	send(SoundMessage("A loud machine pulsates rhythmically."));
 }
 
 void Map34::special17() {
-	send(SoundMessage(STRING["maps.map34.box"]));
+	send(SoundMessage(mapText("A metalic box echoes:\n\"Intruder alert!\"")));
 	_data[MAP_47] = 9;
 	_data[VAL1] = 9;
 }
 
 void Map34::special18() {
-	send(SoundMessage(STRING["maps.map34.message1"]));
+	send(SoundMessage(mapText("Scratched on the wall:\nSearch after the canine rewards you!")));
 }
 
 void Map34::special19() {
-	send(SoundMessage(STRING["maps.map34.message2"]));
+	send(SoundMessage(mapText("Scrawled on the wall:\n"
+		"For the world is hollow and i have\n"
+		"touched the sky!")));
 }
 
 void Map34::special20() {
@@ -168,7 +177,7 @@ void Map34::special20() {
 }
 
 void Map34::special21() {
-	send(SoundMessage(STRING["maps.map34.message7"]));
+	send(SoundMessage(mapText("Written on the wall:\nJump three times to reach center.")));
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map35.cpp b/engines/mm/mm1/maps/map35.cpp
index 83bc2376bf5..960c17d2724 100644
--- a/engines/mm/mm1/maps/map35.cpp
+++ b/engines/mm/mm1/maps/map35.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -71,7 +72,7 @@ void Map35::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map35.exit"],
+		"Exit castle (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 10);
 			g_maps->changeMapFromExit(0xa00, 2);
@@ -84,18 +85,18 @@ void Map35::special02() {
 
 	g_maps->_mapPos = Common::Point(14, 9);
 	g_maps->changeMapFromExit(0xa00, 2);
-	send(SoundMessage(STRING["maps.map35.slide"]));
+	send(SoundMessage("A slide!"));
 }
 
 void Map35::special03() {
-	send(SoundMessage(STRING["maps.map35.message"]));
+	send(SoundMessage(mapText("Etched in silver, message A reads:\natis-19-31ud54aeupi1")));
 }
 
 void Map35::special04() {
 	if (!g_globals->_party.hasItem(MERCHANTS_PASS_ID)) {
 		g_maps->_mapPos.y++;
 		updateGame();
-		send(SoundMessage(STRING["maps.map35.merchant_pass"]));
+		send(SoundMessage(mapText("Castle guards exclaim\n\"No merchants pass! Begone peasants.\"")));
 	}
 }
 
@@ -112,7 +113,7 @@ void Map35::special06() {
 }
 
 void Map35::special07() {
-	send(SoundMessage(STRING["maps.map35.vault"]));
+	send(SoundMessage("Empty vault, alarm!"));
 	_data[VAL1] = 30;
 	_data[VAL2] = 7;
 }
@@ -192,11 +193,11 @@ Common::String Map35::checkQuestComplete() {
 		}
 
 		return Common::String::format(
-			STRING["maps.map35.inspectron5"].c_str(),
+			"Well done, quest complete! +%u exp",
 			QUEST_EXPERIENCE[qIndex]);
 	} else {
 		// The quest isn't yet complete
-		return STRING["maps.map35.inspectron3"];
+		return "\"Return not until thy quest is complete\"";
 	}
 }
 
diff --git a/engines/mm/mm1/maps/map36.cpp b/engines/mm/mm1/maps/map36.cpp
index 7755a532d6f..1dd971eb21d 100644
--- a/engines/mm/mm1/maps/map36.cpp
+++ b/engines/mm/mm1/maps/map36.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -68,7 +70,7 @@ void Map36::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map36.exit"],
+		"Exit castle (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(11, 2);
 			g_maps->changeMapFromExit(0xa00, 2);
@@ -81,18 +83,20 @@ void Map36::special02() {
 
 	g_maps->_mapPos = Common::Point(12, 2);
 	g_maps->changeMapFromExit(0xa00, 2);
-	send(SoundMessage(STRING["maps.map36.slide"]));
+	send(SoundMessage("Slide!"));
 }
 
 void Map36::special03() {
-	send(SoundMessage(STRING["maps.map36.message"]));
+	send(SoundMessage(mapText("Etched in silver, message C reads:\niaci1;-2;0nu--g,not2")));
 }
 
 void Map36::special04() {
 	if (!g_globals->_party.hasItem(MERCHANTS_PASS_ID)) {
 		g_maps->_mapPos.x--;
 		updateGame();
-		send(SoundMessage(STRING["maps.map36.begone"]));
+		send(SoundMessage(g_engine->isEnhanced() ?
+			"Castle guards exclaim, \"Begone peasants!\"" :
+			"Castle guards exclaim,\"Begone peasants!\""));
 	}
 }
 
@@ -107,11 +111,11 @@ void Map36::special06() {
 
 	g_maps->_mapPos = Common::Point(7, 4);
 	g_maps->changeMapFromExit(0x705, 3);
-	send(SoundMessage(STRING["maps.map36.pit"]));
+	send(SoundMessage("The pit of peril..."));
 }
 
 void Map36::special07() {
-	send(SoundMessage(STRING["maps.map36.vault"]));
+	send(SoundMessage("Empty vault, alarm!"));
 	_data[MAP_47] = 7;
 	_data[MAP_29] = 30;
 	for (int i = 0; i < 3; ++i)
@@ -163,7 +167,10 @@ void Map36::acceptQuest() {
 		}
 
 		send(SoundMessage(
-			STRING["maps.map36.hacker7"],
+			g_engine->isEnhanced() ?
+				"My brew is complete. Guards take their items and send them to the pit!" :
+				"My brew is complete, guards take their\n"
+					"items and send them to the pit!",
 			[](const Common::KeyState &) {
 				g_maps->_mapPos = Common::Point(11, 15);
 				g_maps->_currentMap->updateGame();
@@ -200,11 +207,11 @@ Common::String Map36::checkQuestComplete() {
 		}
 
 		return Common::String::format(
-			STRING["maps.map36.hacker6"].c_str(),
+			"Well done, quest complete! +%u exp",
 			QUEST_EXPERIENCE[qIndex]);
 	} else {
 		// The quest isn't yet complete
-		return STRING["maps.map36.hacker5"];
+		return mapText("\"Return not until thy quest is complete\"\n(leader should present items)");
 	}
 }
 
@@ -218,7 +225,7 @@ void Map36::brewComplete() {
 		c._backpack.clear();
 	}
 
-	send(SoundMessage(STRING["maps.map36.hacker6"]));
+	send(SoundMessage("Well done, quest complete! +%u exp"));
 	g_maps->_mapPos = Common::Point(12, 5);
 	g_maps->_mapPos.x--;
 	redrawGame();
diff --git a/engines/mm/mm1/maps/map37.cpp b/engines/mm/mm1/maps/map37.cpp
index b751afdfb3c..06f791e3eed 100644
--- a/engines/mm/mm1/maps/map37.cpp
+++ b/engines/mm/mm1/maps/map37.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -51,11 +52,13 @@ void Map37::special() {
 	for (int i = 0; i < count; ++i)
 		g_maps->turnLeft();
 
-	send(SoundMessage(STRING["maps.map37.spins"]));
+	send(SoundMessage("The floor spins!"));
 }
 
 void Map37::special00() {
-	SoundMessage msg(STRING["maps.map37.message1"]);
+	SoundMessage msg(mapText("Etched in gold, message 1 reads:\n"
+		"Completion-must-each-kings-of-astral-\n"
+		"with-9th-sanctum-and-wondrous"));
 	msg._fontReduced = true;
 	send(msg);
 }
@@ -71,7 +74,7 @@ void Map37::special01() {
 		}
 
 		send(SoundMessage(
-			STRING["maps.map37.opening"],
+			mapText("An opening above leads outside,\nClimb out (Y/N)?"),
 			[]() {
 				g_maps->_mapPos = Common::Point(13, 5);
 				g_maps->changeMapFromExit(0xa00, 2);
@@ -84,7 +87,7 @@ void Map37::special02() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos.x = 15;
 			g_maps->changeMapFromExit(0x703, 3);
@@ -161,7 +164,9 @@ void Map37::special14() {
 }
 
 void Map37::special15() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 
 	if (!g_globals->_party.hasItem(B_QUEEN_IDOL_ID)) {
 		g_globals->_treasure._items[2] = B_QUEEN_IDOL_ID;
@@ -176,7 +181,7 @@ void Map37::special16() {
 }
 
 void Map37::special19() {
-	send(SoundMessage(STRING["maps.map37.archway"]));
+	send(SoundMessage(mapText("A gem encrusted archway reads:\nLair of the omnipotent wizard Okrim")));
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map38.cpp b/engines/mm/mm1/maps/map38.cpp
index e8e8e753fb3..633133c570f 100644
--- a/engines/mm/mm1/maps/map38.cpp
+++ b/engines/mm/mm1/maps/map38.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -48,15 +50,23 @@ void Map38::special() {
 		getRandomNumber(16) - 1);
 	// This random relocation bypasses updateGame()/redrawGame().
 	g_maps->visitedTile();
-	send(SoundMessage(STRING["maps.map38.ringing"]));
+	send(SoundMessage(mapText("A strange ringing sound startles\nthe party. You are teleported!")));
 }
 
 void Map38::special00() {
-	send(SoundMessage(STRING["maps.map38.message4"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Etched in gold, message 4 reads:\n"
+			"Of-be-has-true-knowledge-plane-\n"
+			"a-level-to-reality-it" :
+		"Etched in gold, message 4 reads:'\n"
+			"of-be-has-true-knowledge-plane-a-level-'\n"
+			"to-reality-it"));
 }
 
 void Map38::special01() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map38::special02() {
@@ -67,7 +77,7 @@ void Map38::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos.x = 0;
 			g_maps->changeMapFromExit(0xf03, 3);
@@ -76,7 +86,9 @@ void Map38::special03() {
 }
 
 void Map38::special04() {
-	send(SoundMessage(STRING["maps.map38.banner"]));
+	send(SoundMessage(mapText("A banner reads:\n"
+		"the Labyrinth of Lazzeruth...\n"
+		"Few have entered, none have returned!")));
 }
 
 void Map38::special05() {
@@ -120,19 +132,25 @@ void Map38::special14() {
 }
 
 void Map38::special15() {
-	send(SoundMessage(STRING["maps.map38.face1"]));
+	send(SoundMessage(mapText("Inset in the wall, a stone face\nyells: \"My brain hurts!\"")));
 }
 
 void Map38::special16() {
-	send(SoundMessage(STRING["maps.map38.face2"]));
+	send(SoundMessage(mapText("Inset in the wall, a stone face\n"
+		"speaks: \"I would pay highly for a green\n"
+		"handled, pearl encrusted abelnuski.\"")));
 }
 
 void Map38::special17() {
-	send(SoundMessage(STRING["maps.map38.face3"]));
+	send(SoundMessage(mapText("Inset in the wall, a stone face\n"
+		"speaks: \"I see all, hear all,\n"
+		"know nothing...\"")));
 }
 
 void Map38::special18() {
-	send(SoundMessage(STRING["maps.map38.face4"]));
+	send(SoundMessage(mapText("Inset in the wall, a stone face\n"
+		"proclaims: \"Okrim is watching.\n"
+		"He studies your weaknesses!\"")));
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map39.cpp b/engines/mm/mm1/maps/map39.cpp
index c8e1e1b7648..7c4d6e6d2d0 100644
--- a/engines/mm/mm1/maps/map39.cpp
+++ b/engines/mm/mm1/maps/map39.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -53,7 +55,13 @@ void Map39::special() {
 }
 
 void Map39::special00() {
-	send(SoundMessage(STRING["maps.map39.message6"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Etched in gold, message 6 reads:\n"
+			"Book-done.-A-self-that-from-\n"
+			"key-you-be-to-seems" :
+		"Etched in gold, message 6 reads:\n"
+			"book-done.-a-self-that-from-key-you-\n"
+			"be-to-seems"));
 }
 
 void Map39::special01() {
@@ -64,7 +72,7 @@ void Map39::special01() {
 	}
 
 	send(SoundMessage(
-		STRING["maps.map39.stairs_up"],
+		"Stairs to surface, take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(9, 9);
 			g_maps->changeMapFromExit(0x703, 2);
@@ -76,7 +84,7 @@ void Map39::special02() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x702, 3);
 		}
@@ -89,11 +97,11 @@ void Map39::special03() {
 
 void Map39::special04() {
 	if (g_globals->_party.hasItem(SILVER_KEY_ID)) {
-		send(SoundMessage(STRING["maps.map39.door_glows"]));
+		send(SoundMessage("A silver door, your silver key glows!"));
 	} else {
 		g_maps->_mapPos.y++;
 		updateGame();
-		send(SoundMessage(STRING["maps.map39.door_repels"]));
+		send(SoundMessage("A silver door repels you!"));
 	}
 }
 
@@ -138,7 +146,9 @@ void Map39::special14() {
 }
 
 void Map39::special18() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map39::riddleAnswered(const Common::String &answer) {
@@ -163,7 +173,7 @@ void Map39::riddleAnswered(const Common::String &answer) {
 		g_maps->_mapPos.x = 9;
 		updateGame();
 
-		send(InfoMessage(STRING["maps.map39.ruby2"]));
+		send(InfoMessage("Wrong!  A bright flash..."));
 	}
 }
 
diff --git a/engines/mm/mm1/maps/map40.cpp b/engines/mm/mm1/maps/map40.cpp
index 65f0a35fece..583f2e306be 100644
--- a/engines/mm/mm1/maps/map40.cpp
+++ b/engines/mm/mm1/maps/map40.cpp
@@ -25,6 +25,7 @@
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -57,7 +58,7 @@ void Map40::special() {
 		g_maps->_mapPos.y++;
 		updateGame();
 
-		SoundMessage msg(STRING["maps.map40.conveyor_belt"]);
+		SoundMessage msg("Conveyor belt...");
 		if (g_engine->isEnhanced()) {
 			// Enhanced messages have focus and normally forward the key used
 			// to dismiss them back to Game. On the conveyor that immediately
@@ -72,7 +73,13 @@ void Map40::special() {
 }
 
 void Map40::special00() {
-	send(SoundMessage(STRING["maps.map40.message2"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Etched in gold, message 2 reads:\n"
+			"One,-riddles-value-you-you-\n"
+			"5-card-must-claimed.-Dreams,-..." :
+		"Etched in gold, message 2 reads:\n"
+			"One,-riddles-value-you-you-5-card-must-\n"
+			"claimed.-dreams,-..."));
 }
 
 void Map40::special01() {
@@ -87,14 +94,14 @@ void Map40::special01() {
 
 void Map40::special02() {
 	reduceHP();
-	send(SoundMessage(STRING["maps.map40.giants"]));
+	send(SoundMessage("Giants hurl boulders!"));
 }
 
 void Map40::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xf02, 3);
 		}
@@ -103,7 +110,7 @@ void Map40::special03() {
 
 void Map40::special04() {
 	send(SoundMessage(
-		STRING["maps.map40.button"],
+		"A button on the wall, push it (Y/N)?",
 		[]() {
 			Map40 &map = *static_cast<Map40 *>(g_maps->_currentMap);
 			map[VAL1]++;
@@ -166,7 +173,7 @@ void Map40::special15() {
 		_data[VAL2] = 0;
 		reduceHP();
 		reduceHP();
-		send(SoundMessage(STRING["maps.map40.squish"]));
+		send(SoundMessage(mapText("Squish! The party is impaled on a wall\nof spears!")));
 
 	} else {
 		none160();
@@ -174,23 +181,25 @@ void Map40::special15() {
 }
 
 void Map40::special16() {
-	send(SoundMessage(STRING["maps.map40.boulder"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"A boulder says: \"There should be 5 tests\"" :
+		"A boulder says:\"There should be 5 tests\""));
 }
 
 void Map40::special17() {
-	send(SoundMessage(STRING["maps.map40.test1"]));
+	send(SoundMessage("The First Test"));
 }
 
 void Map40::special18() {
-	send(SoundMessage(STRING["maps.map40.test2"]));
+	send(SoundMessage("The Second Test"));
 }
 
 void Map40::special19() {
-	send(SoundMessage(STRING["maps.map40.test3"]));
+	send(SoundMessage("The Third Test"));
 }
 
 void Map40::special20() {
-	send(SoundMessage(STRING["maps.map40.test4"]));
+	send(SoundMessage("The Fourth Test"));
 }
 
 void Map40::archerResist() {
diff --git a/engines/mm/mm1/maps/map41.cpp b/engines/mm/mm1/maps/map41.cpp
index 3e4acd41d76..e0b11d44e25 100644
--- a/engines/mm/mm1/maps/map41.cpp
+++ b/engines/mm/mm1/maps/map41.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -48,14 +50,22 @@ void Map41::special() {
 }
 
 void Map41::special00() {
-	send(SoundMessage(STRING["maps.map41.message3"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Etched in gold, message 3 reads:\n"
+			"For-tasks-and-rating.-Be-from-\n"
+			"6th-frustrations!-The-to-in" :
+		"Etched in gold, message 3 reads:\n"
+			"For-tasks-and-rating.-be-from-6th-\n"
+			"frustrations!-the-to-in"));
 }
 
 void Map41::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map41.stairs_up"],
+		g_engine->isEnhanced() ?
+			"Spiralling stairs go up, take them (Y/N)?" :
+			"Spiralling stairs go up,take them (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 2);
 			g_maps->changeMapFromExit(0x101, 2);
@@ -67,7 +77,7 @@ void Map41::special02() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x704, 3);
 		}
@@ -79,7 +89,7 @@ void Map41::special03() {
 }
 
 void Map41::special04() {
-	send(SoundMessage(STRING["maps.map41.dung"]));
+	send(SoundMessage("Minotaur dung slushes beneath you!"));
 }
 
 void Map41::special05() {
@@ -123,40 +133,43 @@ void Map41::special14() {
 }
 
 void Map41::special16() {
-	showSign(STRING["maps.map41.sign1"]);
+	showSign("The Enchanted Chamber");
 }
 
 void Map41::special17() {
-	showSign(STRING["maps.map41.sign2"]);
+	showSign("Cleptomian Guild, do not disturb!");
 }
 
 void Map41::special18() {
-	showSign(STRING["maps.map41.sign3"]);
+	showSign("The Council of Strength is in session");
 }
 
 void Map41::special19() {
-	showSign(STRING["maps.map41.sign4"]);
+	showSign("The Uglies Club - Members only!");
 }
 
 void Map41::special20() {
-	showSign(STRING["maps.map41.sign5"]);
+	showSign("Specimen Tank - keep closed");
 }
 
 void Map41::special21() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map41::special22() {
-	send(SoundMessage(STRING["maps.map41.tapestry1"]));
+	send(SoundMessage(mapText("A tapestry on the wall depicts a gray\n"
+		"minotaur stomping on a group of knights.")));
 }
 
 void Map41::special23() {
-	send(SoundMessage(STRING["maps.map41.tapestry2"]));
+	send(SoundMessage(mapText("A tapestry on the wall depicts\na kingdom ruled by minotaurs")));
 }
 
 void Map41::showSign(const Common::String &line) {
 	send(SoundMessage(
-		0, 1, STRING["maps.sign"],
+		0, 1, mapText("A sign above the door reads:\n"),
 		0, 2, line
 	));
 }
diff --git a/engines/mm/mm1/maps/map42.cpp b/engines/mm/mm1/maps/map42.cpp
index 7a79f2ce857..6569fb3273f 100644
--- a/engines/mm/mm1/maps/map42.cpp
+++ b/engines/mm/mm1/maps/map42.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -56,14 +58,21 @@ void Map42::special00() {
 }
 
 void Map42::special01() {
-	send(SoundMessage(STRING["maps.map42.message9"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Etched in gold, message 9 reads:\n"
+			"Successful-that-training,-the-\n"
+			"worthy-the-visit-yet-inner-\n"
+			"reality,-order," :
+		"Etched in gold, message 9 reads:\n"
+			"Successful-that-training,-the-worthy-\n"
+			"the-visit-yet-inner-reality,-order,"));
 }
 
 void Map42::special02() {
 	Game::Encounter &enc = g_globals->_encounters;
 
 	if (_data[VAL1]) {
-		send(SoundMessage(STRING["maps.map42.defeated"]));
+		send(SoundMessage(mapText("The master of this stronghold\nhas been defeated!")));
 
 		for (uint i = 0; i < g_globals->_party.size(); ++i) {
 			g_globals->_party[i]._flags[5] |= CHARFLAG5_40;
@@ -83,7 +92,7 @@ void Map42::special02() {
 }
 
 void Map42::special03() {
-	send(SoundMessage(STRING["maps.map42.sign1"]));
+	send(SoundMessage(mapText("Enter the Maze of the Minotaur...\nIf you dare!")));
 }
 
 void Map42::special04() {
@@ -137,7 +146,7 @@ void Map42::special15() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xf04, 3);
 		}
diff --git a/engines/mm/mm1/maps/map43.cpp b/engines/mm/mm1/maps/map43.cpp
index 8205a96fec4..36e6df4b052 100644
--- a/engines/mm/mm1/maps/map43.cpp
+++ b/engines/mm/mm1/maps/map43.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -73,7 +74,7 @@ void Map43::special01() {
 		visitedExit();
 
 		send(SoundMessage(
-			STRING["maps.map43.exit"],
+			"Exit castle, (Y/N)?",
 			[]() {
 				g_maps->_mapPos = Common::Point(9, 13);
 				g_maps->changeMapFromExit(0x101, 2);
@@ -86,7 +87,7 @@ void Map43::special01() {
 
 void Map43::special02() {
 	send(SoundMessage(
-		STRING["maps.map43.button"],
+		"Button on the wall, push it (Y/N)?",
 		[]() {
 			Map43 &map = *static_cast<Map43 *>(g_maps->_currentMap);
 			map._states[101] = 17;
@@ -96,13 +97,13 @@ void Map43::special02() {
 }
 
 void Map43::special03() {
-	send(SoundMessage(STRING["maps.map43.message_b"]));
+	send(SoundMessage(mapText("Etched in silver, message B reads:\nrstia-,e1,;/11rn;/m-")));
 }
 
 void Map43::special04() {
 	if (!g_globals->_party.hasItem(MERCHANTS_PASS_ID)) {
 		send(SoundMessage(
-			STRING["maps.map43.guards"],
+			mapText("Castle guards exclaim,\n\"No merchants pass! Begone peasants.\""),
 			[](const Common::KeyState &) {
 				g_events->focusedView()->close();
 				g_maps->_mapPos = Common::Point(9, 13);
@@ -138,11 +139,11 @@ void Map43::special06() {
 }
 
 void Map43::special07() {
-	send(SoundMessage(STRING["maps.map43.tower"]));
+	send(SoundMessage("The Tower"));
 }
 
 void Map43::special08() {
-	send(SoundMessage(STRING["maps.map43.throne_room"]));
+	send(SoundMessage("Throne Room"));
 }
 
 void Map43::updateFlags() {
@@ -215,11 +216,11 @@ Common::String Map43::checkQuestComplete() {
 		}
 
 		return Common::String::format(
-			STRING["maps.map43.ironfist5"].c_str(),
+			"Well done, quest complete! +%u exp",
 			QUEST_EXPERIENCE[qIndex]);
 	} else {
 		// The quest isn't yet complete
-		return STRING["maps.map43.ironfist3"];
+		return "\"Return not until thy quest is complete\"";
 	}
 }
 
diff --git a/engines/mm/mm1/maps/map44.cpp b/engines/mm/mm1/maps/map44.cpp
index 234261eec3b..9bf94bb6c3c 100644
--- a/engines/mm/mm1/maps/map44.cpp
+++ b/engines/mm/mm1/maps/map44.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -57,7 +58,7 @@ void Map44::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.passage_outside1"],
+		"A passage leads outside, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(12, 12);
 			g_maps->changeMapFromExit(0x112, 2);
@@ -69,7 +70,7 @@ void Map44::special02() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xf05, 3);
 		}
@@ -77,11 +78,11 @@ void Map44::special02() {
 }
 
 void Map44::special03() {
-	send(SoundMessage(STRING["maps.map44.message_f"]));
+	send(SoundMessage(mapText("Etched in silver, message F reads:\ntstst,e1,d-ds15a1drh")));
 }
 
 void Map44::special04() {
-	send(SoundMessage(STRING["maps.map44.clover"]));
+	send(SoundMessage(mapText("The green clover grants those who\nare worthy +4 luck!")));
 
 	for (uint i = 0; i < g_globals->_party.size(); ++i) {
 		Character &c = g_globals->_party[i];
@@ -102,7 +103,9 @@ void Map44::special05() {
 	}
 
 	send(SoundMessage(
-		STRING["maps.map44.fountain"],
+		mapText("The Fabulous Fabled Fountain of\n"
+			"Dragadune converts all gold into\n"
+			"experience, accept (Y/N)?"),
 		[]() {
 			Map44 &map = *static_cast<Map44 *>(g_maps->_currentMap);
 
@@ -118,11 +121,11 @@ void Map44::special05() {
 }
 
 void Map44::special06() {
-	send(SoundMessage(STRING["maps.map44.message"]));
+	send(SoundMessage(mapText("Carved in stone, a trivial message\nreads: i be me")));
 }
 
 void Map44::special07() {
-	send(SoundMessage(STRING["maps.map44.bones"]));
+	send(SoundMessage("Decayed bones crunch beneath you!"));
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map45.cpp b/engines/mm/mm1/maps/map45.cpp
index 27325ba20ff..3b6815e2808 100644
--- a/engines/mm/mm1/maps/map45.cpp
+++ b/engines/mm/mm1/maps/map45.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -50,14 +52,21 @@ void Map45::special() {
 }
 
 void Map45::special00() {
-	send(SoundMessage(STRING["maps.map45.message8"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Etched in gold, message 8 reads:\n"
+			"The-are-quests,-increases-\n"
+			"identify,-apply.-locations,-end-\n"
+			"attained-my-sequel" :
+		"etched in gold, message 8 reads:\n"
+			"The-are-quests,-increases-identify,-\n"
+			"apply.-locations,-end-attained-my-sequel"));
 }
 
 void Map45::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map45.passage"],
+		"A cavernous passage, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(15, 12);
 			g_maps->changeMapFromExit(5, 1);
@@ -73,7 +82,7 @@ void Map45::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x107, 1);
 		}
@@ -84,7 +93,7 @@ void Map45::special04() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xa00, 3);
 		}
@@ -132,32 +141,37 @@ void Map45::special14() {
 }
 
 void Map45::special15() {
-	send(SoundMessage(STRING["maps.map45.sign1"]));
+	send(SoundMessage(mapText("painted in a black and white pattern,\n"
+		"a sign reads: The last part is the first")));
 }
 
 void Map45::special16() {
-	showSign(STRING["maps.map45.sign2"]);
+	showSign("\"Temple Wolf Pass\"");
 }
 
 void Map45::special18() {
-	showSign(STRING["maps.map45.sign3"]);
+	showSign("\"Cult of the New Order\"");
 }
 
 void Map45::special19() {
-	send(SoundMessage(STRING["maps.map45.walls"]));
+	send(SoundMessage("The Walls Glow Ominously"));
 }
 
 void Map45::special20() {
-	send(SoundMessage(STRING["maps.map45.message"]));
+	send(SoundMessage(mapText("Etched in stone:\n"
+		"\"28 days out. No food, low on supplies.\n"
+		"We fear the worst yet we continue-Corak")));
 }
 
 void Map45::special21() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map45::showSign(const Common::String &line) {
 	send(SoundMessage(
-		0, 1, STRING["maps.sign"],
+		0, 1, mapText("A sign above the door reads:\n"),
 		0, 2, line
 	));
 }
diff --git a/engines/mm/mm1/maps/map46.cpp b/engines/mm/mm1/maps/map46.cpp
index 30e7b926416..454a5c6f160 100644
--- a/engines/mm/mm1/maps/map46.cpp
+++ b/engines/mm/mm1/maps/map46.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -50,18 +52,20 @@ void Map46::special() {
 }
 
 void Map46::special00() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map46::special01() {
-	send(SoundMessage(STRING["maps.map46.shakes"]));
+	send(SoundMessage("The ground shakes!"));
 }
 
 void Map46::special02() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x705, 3);
 		}
@@ -72,7 +76,7 @@ void Map46::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xf05, 3);
 		}
@@ -89,7 +93,9 @@ void Map46::special05() {
 }
 
 void Map46::special06() {
-	send(SoundMessage(STRING["maps.map46.clerics"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Clerics of the South just below. Corak" :
+		"Clerics of the South just below-Corak"));
 }
 
 } // namespace Maps
diff --git a/engines/mm/mm1/maps/map47.cpp b/engines/mm/mm1/maps/map47.cpp
index 6ec444a8acc..11efa3d21b4 100644
--- a/engines/mm/mm1/maps/map47.cpp
+++ b/engines/mm/mm1/maps/map47.cpp
@@ -23,12 +23,21 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
 namespace Maps {
 
+static const char *const TONES[3] = {
+	"A loud tone resounds\n"
+		"throughout the room!",
+	"A sharp tone assaults your senses!",
+	"A mellow tone relaxes you."
+};
+
 #define ARRAY1 250
 #define QUEST_CLERICS_TONE_OFFSET (15 * MAP_W + 10)
 #define QUEST_CLERICS_COMPLETE_OFFSET (15 * MAP_W + 13)
@@ -57,17 +66,25 @@ void Map47::special() {
 		g_maps->_mapPos = Common::Point(
 			getRandomNumber(16) - 1, getRandomNumber(16) - 1);
 		updateGame();
-		send(SoundMessage(STRING["maps.map47.poof"]));
+		send(SoundMessage("Poof!"));
 	}
 }
 
 void Map47::special00() {
-	send(SoundMessage(STRING["maps.map47.message5"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Etched in gold, message 5 reads:\n"
+			"Scoop:-the-discoveries-your-to-\n"
+			"return-the-your-for-dreams-is" :
+		"Etched in gold, message 5 reads:\n"
+			"Scoop:-the-discoveries-your-to-return-\n"
+			"the-your-for-dreams-is"));
 }
 
 void Map47::special01() {
 	send(SoundMessage(
-		STRING["maps.map47.gong"],
+		mapText("Covered with cryptic symbols, a large\n"
+			"brass gong hangs from the ceiling.\n"
+			"Strike it (Y/N)?"),
 		[]() {
 			Map47 &map = *static_cast<Map47 *>(g_maps->_currentMap);
 			int toneNum = g_events->getRandomNumber(6);
@@ -76,8 +93,7 @@ void Map47::special01() {
 				map._data[ARRAY1 + toneNum - 1] = 1;
 				map._visited[QUEST_CLERICS_TONE_OFFSET +
 					toneNum - 1] |= QUEST_LOG_FLAG;
-				g_events->send(SoundMessage(STRING[
-					Common::String::format("maps.map47.tones.%d", toneNum)]));
+				g_events->send(SoundMessage(mapText(TONES[toneNum - 1])));
 			} else {
 				map.poof();
 			}
@@ -95,7 +111,7 @@ void Map47::special02() {
 	}
 
 	if (heard[0] && heard[1] && heard[2]) {
-		send(SoundMessage(STRING["maps.map47.clerics3"]));
+		send(SoundMessage("\"We deem you worthy of advancement!\""));
 		_visited[QUEST_CLERICS_COMPLETE_OFFSET] |= QUEST_LOG_FLAG;
 		_visited[QUEST_CLERICS_ACTIVE_OFFSET] &= ~QUEST_LOG_FLAG;
 
@@ -118,8 +134,8 @@ void Map47::special02() {
 		// Speaking to the Clerics starts the optional advancement quest.
 		_visited[QUEST_CLERICS_ACTIVE_OFFSET] |= QUEST_LOG_FLAG;
 		send(SoundMessage(
-			0, 1, STRING["maps.map47.clerics1"],
-			0, 2, STRING["maps.map47.clerics2"]
+			0, 1, mapText("The Clerics of the South speak:\n"),
+			0, 2, "\"Hear the 3 tones and return!\""
 		));
 	}
 }
@@ -128,7 +144,7 @@ void Map47::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xa00, 3);
 		}
@@ -136,7 +152,10 @@ void Map47::special03() {
 }
 
 void Map47::special04() {
-	send(SoundMessage(STRING["maps.map47.painting"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"On the right wall is a painting of a box, on the left a painting of a curtain." :
+		"On the right wall is a painting of a\n"
+			"box,on the left a painting of a curtain."));
 }
 
 void Map47::special05() {
@@ -193,7 +212,7 @@ void Map47::special17() {
 
 void Map47::special23() {
 	Common::String line = Common::String::format(
-		STRING["maps.map47.door_number"].c_str(),
+		"Door number %c",
 		'0' + (g_maps->_mapPos.x - 5)
 	);
 	send(SoundMessage(line));
@@ -208,7 +227,7 @@ void Map47::poof() {
 			getRandomNumber(16) - 1, getRandomNumber(16) - 1);
 		updateGame();
 
-		send(SoundMessage(STRING["maps.map47.poof"]));
+		send(SoundMessage("Poof!"));
 	}
 }
 
diff --git a/engines/mm/mm1/maps/map48.cpp b/engines/mm/mm1/maps/map48.cpp
index bcf4d669673..4ef8c46804f 100644
--- a/engines/mm/mm1/maps/map48.cpp
+++ b/engines/mm/mm1/maps/map48.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -49,14 +50,20 @@ void Map48::orangoAnswer(const Common::String &answer) {
 		g_maps->_mapPos = Common::Point(8, 5);
 		g_maps->changeMap(0x604, 1);
 
-		SoundMessage msg(STRING["maps.map48.orango3"]);
+		SoundMessage msg(mapText("Agent Orango Seventeen reporting:\n"
+			"Imposter xx21a7-3 voided!\n"
+			"Starphase 5281.6 at 120-varn-161 pod #41\n"
+			"You are now rank 1 and eligible for\n"
+			"transfer. Find Inner Sanctum for new\n"
+			"assignment. The walls begin to fade...\n"
+			"(press space)"));
 		msg._largeMessage = true;
 		send(msg);
 
 	} else {
 		g_maps->_mapPos.x++;
 		updateGame();
-		send(SoundMessage(13, 2, STRING["maps.map48.orango2"]));
+		send(SoundMessage(13, 2, "Keep looking!"));
 	}
 }
 
diff --git a/engines/mm/mm1/maps/map49.cpp b/engines/mm/mm1/maps/map49.cpp
index 8abc3109f80..3da99ebec6d 100644
--- a/engines/mm/mm1/maps/map49.cpp
+++ b/engines/mm/mm1/maps/map49.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -60,7 +61,7 @@ void Map49::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.passage_outside1"],
+		"A passage leads outside, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(14, 7);
 			g_maps->changeMapFromExit(0xb1a, 2);
@@ -73,17 +74,17 @@ void Map49::special02() {
 
 	g_maps->_mapPos = Common::Point(15, 7);
 	g_maps->changeMapFromExit(0xb1a, 2);
-	send(SoundMessage(STRING["maps.map49.chute"]));
+	send(SoundMessage("A chute..."));
 }
 
 void Map49::special03() {
-	send(SoundMessage(STRING["maps.map49.message_e"]));
+	send(SoundMessage(mapText("Etched in silver, message E reads:\noei/:1-33-1ek5;d-et,")));
 }
 
 void Map49::special04() {
 	if (!g_globals->_party.hasItem(MERCHANTS_PASS_ID)) {
 		send(SoundMessage(
-			STRING["maps.map49.guards2"],
+			mapText("Kings guards approach.\n\"No King's Pass, no admittance!\""),
 			[](const Common::KeyState &) {
 				Map49 &map = *static_cast<Map49 *>(g_maps->_currentMap);
 				g_maps->_mapPos = Common::Point(
@@ -100,7 +101,7 @@ void Map49::special05() {
 	if (!g_globals->_party.hasItem(KINGS_PASS_ID)) {
 		g_maps->_mapPos.x++;
 		updateGame();
-		send(SoundMessage(STRING["maps.map49.guards2"]));
+		send(SoundMessage(mapText("Kings guards approach.\n\"No King's Pass, no admittance!\"")));
 
 	} else {
 		checkPartyDead();
@@ -126,7 +127,7 @@ void Map49::special07() {
 	g_maps->_mapPos = Common::Point(4, 7);
 	g_maps->changeMapFromExit(0xb1a, 2);
 
-	send(SoundMessage(STRING["maps.map49.catapult"]));
+	send(SoundMessage("A catapult ejects you from the tower!"));
 }
 
 void Map49::special08() {
@@ -135,7 +136,7 @@ void Map49::special08() {
 		reduceHP();
 	reduceHP();
 
-	messageEncounter(STRING["maps.map49.trap"]);
+	messageEncounter("Singe! Acid trap.");
 }
 
 void Map49::special09() {
@@ -144,11 +145,11 @@ void Map49::special09() {
 		reduceHP();
 	reduceHP();
 
-	messageEncounter(STRING["maps.map49.explosion"]);
+	messageEncounter("Boom! A fiery explosion.");
 }
 
 void Map49::special16() {
-	send(SoundMessage(STRING["maps.map49.scream"]));
+	send(SoundMessage("A loud scream from behind the door!"));
 }
 
 void Map49::special20() {
@@ -158,7 +159,7 @@ void Map49::special20() {
 }
 
 void Map49::special22() {
-	send(SoundMessage(STRING["maps.map49.throne_room"]));
+	send(SoundMessage("Throne Room"));
 }
 
 void Map49::messageEncounter(const Common::String &line) {
diff --git a/engines/mm/mm1/maps/map50.cpp b/engines/mm/mm1/maps/map50.cpp
index c063082ea82..15e96a6e618 100644
--- a/engines/mm/mm1/maps/map50.cpp
+++ b/engines/mm/mm1/maps/map50.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -50,14 +52,20 @@ void Map50::special() {
 }
 
 void Map50::special00() {
-	send(SoundMessage(STRING["maps.map50.message7"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"Etched in gold, message 7 reads:\n"
+			"These-and-that-must-can-\n"
+			"different-shall-have-from-a-jvc" :
+		"Etched in gold, message 7 reads:\n"
+			"These-and-that-must-can-different-\n"
+			"shall-have-from-a-jvc"));
 }
 
 void Map50::special01() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.passage_outside1"],
+		"A passage leads outside, take it (Y/N)?",
 		[]() {
 			g_maps->_mapPos = Common::Point(10, 5);
 			g_maps->changeMapFromExit(0x11b, 2);
@@ -69,7 +77,7 @@ void Map50::special02() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x701, 3);
 		}
@@ -80,7 +88,7 @@ void Map50::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xe00, 3);
 		}
@@ -91,7 +99,7 @@ void Map50::special04() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x201, 3);
 		}
@@ -143,31 +151,33 @@ void Map50::special15() {
 }
 
 void Map50::special16() {
-	send(SoundMessage(STRING["maps.map50.sign1"]));
+	send(SoundMessage(mapText("Club Dead...\nThe antidote to humanization!")));
 }
 
 void Map50::special17() {
-	send(SoundMessage(STRING["maps.map50.sign2"]));
+	send(SoundMessage("Temple of the Old Order"));
 }
 
 void Map50::special18() {
-	send(SoundMessage(STRING["maps.map50.sign3"]));
+	send(SoundMessage("Glass Room"));
 }
 
 void Map50::special19() {
-	send(SoundMessage(STRING["maps.map50.sign4"]));
+	send(SoundMessage("Ruby Room"));
 }
 
 void Map50::special20() {
-	send(SoundMessage(STRING["maps.map50.sign5"]));
+	send(SoundMessage("Twinkle Room"));
 }
 
 void Map50::special21() {
-	send(SoundMessage(STRING["maps.map50.sign6"]));
+	send(SoundMessage("Rainbow Room"));
 }
 
 void Map50::special22() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map50::special23() {
@@ -175,7 +185,7 @@ void Map50::special23() {
 		g_maps->_mapPos.y--;
 		updateGame();
 
-		send(SoundMessage(STRING["maps.map50.grate"]));
+		send(SoundMessage("The crystal grate repels you!"));
 	}
 }
 
diff --git a/engines/mm/mm1/maps/map51.cpp b/engines/mm/mm1/maps/map51.cpp
index 7465696110b..9cc11fa5c71 100644
--- a/engines/mm/mm1/maps/map51.cpp
+++ b/engines/mm/mm1/maps/map51.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -50,7 +52,9 @@ void Map51::special() {
 }
 
 void Map51::special00() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map51::special01() {
@@ -58,7 +62,10 @@ void Map51::special01() {
 		g_maps->_mapPos = Common::Point(
 			getRandomNumber(14), getRandomNumber(14));
 		updateGame();
-		send(SoundMessage(STRING["maps.map51.substance"]));
+		send(SoundMessage(g_engine->isEnhanced() ?
+			"Without experimental substance there is no hope... Poof!" :
+			"Without experimental substance\n"
+				"there is no hope...poof!"));
 	}
 }
 
@@ -66,7 +73,7 @@ void Map51::special02() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_down"],
+		"Stairs going down! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xe00, 3);
 		}
@@ -77,7 +84,7 @@ void Map51::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xf01, 3);
 		}
diff --git a/engines/mm/mm1/maps/map52.cpp b/engines/mm/mm1/maps/map52.cpp
index c854c2af5a7..2e75f199b80 100644
--- a/engines/mm/mm1/maps/map52.cpp
+++ b/engines/mm/mm1/maps/map52.cpp
@@ -23,7 +23,9 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -50,7 +52,9 @@ void Map52::special() {
 }
 
 void Map52::special00() {
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map52::special01() {
@@ -58,14 +62,16 @@ void Map52::special01() {
 	g_maps->_mapPos = Common::Point(8, 1);
 	updateGame();
 
-	send(SoundMessage(STRING["maps.map52.summoned"]));
+	send(SoundMessage(g_engine->isEnhanced() ?
+		"You have been summoned... Poof!" :
+		"You have been summoned...poof!"));
 }
 
 void Map52::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0x701, 3);
 		}
@@ -76,7 +82,7 @@ void Map52::special04() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.stairs_up"],
+		"Stairs going up! Take them (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xf01, 3);
 		}
diff --git a/engines/mm/mm1/maps/map53.cpp b/engines/mm/mm1/maps/map53.cpp
index cfd0e988297..af7d6c3197d 100644
--- a/engines/mm/mm1/maps/map53.cpp
+++ b/engines/mm/mm1/maps/map53.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -56,18 +57,20 @@ void Map53::special00() {
 	if (!g_globals->_party.hasItem(W_QUEEN_IDOL_ID))
 		g_globals->_treasure._items[2] = W_QUEEN_IDOL_ID;
 
-	send(SoundMessage(STRING["maps.wall_painted"]));
+	send(SoundMessage(mapText("The wall is painted from ceiling to\n"
+		"floor in a black and white checkered\n"
+		"pattern!")));
 }
 
 void Map53::special01() {
-	send(SoundMessage(STRING["maps.map53.sign"]));
+	send(SoundMessage("Danger! Dungeon under construction."));
 }
 
 void Map53::special03() {
 	visitedExit();
 
 	send(SoundMessage(
-		STRING["maps.map53.ladder"],
+		"A ladder going up, climb it (Y/N)?",
 		[]() {
 			g_maps->changeMapFromExit(0xf01, 3);
 		}
diff --git a/engines/mm/mm1/maps/map54.cpp b/engines/mm/mm1/maps/map54.cpp
index cac146f0879..84cb84b5a95 100644
--- a/engines/mm/mm1/maps/map54.cpp
+++ b/engines/mm/mm1/maps/map54.cpp
@@ -24,6 +24,7 @@
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -58,8 +59,8 @@ void Map54::special00() {
 		updateGame();
 
 		send(SoundMessage(
-			0, 1, STRING["maps.map54.slot1"],
-			0, 2, STRING["maps.map54.slot2"]
+			0, 1, mapText("There's a small slot in the door.\n"),
+			0, 2, "No admittance!"
 		));
 		return;
 	}
@@ -73,16 +74,16 @@ void Map54::special00() {
 			updateGame();
 
 			send(SoundMessage(
-				0, 1, STRING["maps.map54.slot1"],
-				0, 2, STRING["maps.map54.slot3"]
+				0, 1, mapText("There's a small slot in the door.\n"),
+				0, 2, "Key card is rejected!"
 			));
 			return;
 		}
 	}
 
 	send(SoundMessage(
-		0, 1, STRING["maps.map54.slot1"],
-		0, 2, STRING["maps.map54.slot4"]
+		0, 1, mapText("There's a small slot in the door.\n"),
+		0, 2, "Key card is accepted!"
 	));
 }
 
@@ -107,7 +108,7 @@ void Map54::special05() {
 }
 
 void Map54::special06() {
-	send(SoundMessage(STRING["maps.map54.glow"]));
+	send(SoundMessage(mapText("The metallic room is dimly lit by\na pulsating white glow!")));
 }
 
 void Map54::special07() {
@@ -123,7 +124,7 @@ void Map54::projector(int index) {
 	sorpigalInn();
 
 	Common::String line = Common::String::format(
-		STRING["maps.map54.projector"].c_str(),
+		"Astral Projector # %c, zap!",
 		'1' + index);
 	send(SoundMessage(line));
 }
diff --git a/engines/mm/mm1/maps/map_desert.cpp b/engines/mm/mm1/maps/map_desert.cpp
index f392fed6dc1..8b13f7777c0 100644
--- a/engines/mm/mm1/maps/map_desert.cpp
+++ b/engines/mm/mm1/maps/map_desert.cpp
@@ -30,12 +30,12 @@ namespace Maps {
 
 void MapDesert::desert() {
 	SoundMessage msg;
-	msg._lines.push_back(Line(0, 1, STRING["maps.desert.its_hot"]));
+	msg._lines.push_back(Line(0, 1, "It's hot... "));
 
 	// Check whether party has the desert map
 	bool hasMap = g_globals->_party.hasItem(MAP_OF_DESERT_ID);
 	if (!hasMap) {
-		msg._lines.push_back(Line(0, 2, STRING["maps.desert.lost"]));
+		msg._lines.push_back(Line(0, 2, "You're lost!!!"));
 		lost();
 	}
 
@@ -81,7 +81,7 @@ void MapDesert::desert() {
 		switch (getRandomNumber(200)) {
 		case 20:
 			msg._lines.push_back(Line(0, msg._lines.back().y + 1,
-				STRING["maps.desert.whirlwind"]));
+				"A whirlwind swoops the party away!"));
 			Sound::sound(SOUND_3);
 			g_maps->_mapPos = Common::Point(
 				getRandomNumber(15), getRandomNumber(15));
@@ -92,7 +92,7 @@ void MapDesert::desert() {
 
 		case 30:
 			msg._lines.push_back(Line(0, msg._lines.back().y + 1,
-				STRING["maps.desert.sandstorm"]));
+				"A violent sandstorm blasts the party!"));
 			Sound::sound(SOUND_3);
 			reduceHP();
 			break;
diff --git a/engines/mm/mm1/maps/map_town.cpp b/engines/mm/mm1/maps/map_town.cpp
index 88fd3ec4d38..6c03efc2f75 100644
--- a/engines/mm/mm1/maps/map_town.cpp
+++ b/engines/mm/mm1/maps/map_town.cpp
@@ -23,17 +23,188 @@
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
+#include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
 namespace Maps {
 
+struct TownText {
+	const char *_classic;
+	const char *_enhanced;
+};
+
+static const TownText BLACKSMITH_TEXT[5] = {
+	{
+		"A man wearing a leather apron speaks:\n"
+			"\"Distinguished travelers, you've come to\n"
+			"the right place. Can I help you (Y/N)?\"",
+		nullptr
+	},
+	{
+		"Busily forging a long sword,an extremely\n"
+			"beautiful young girl says with a\n"
+			"seductive smile, \"Can i help you (Y/N)?\"",
+		"Busily forging a long sword, an extremely beautiful young girl says with a seductive "
+			"smile, \"Can i help you (Y/N)?\""
+	},
+	{
+		"A man enclosed in a steel cage says,\n"
+			"\"Can i help you find anything (Y/N)?\"",
+		nullptr
+	},
+	{
+		"A young gnome materializes, asking,\n"
+			"\"Would you be wanting my services(Y/N)?\"",
+		"A young gnome materializes, asking,\n"
+			"\"Would you be wanting my services (Y/N)?\""
+	},
+	{
+		"A boistrous half-orc proclaims,\n"
+			"\"We carry the very latest,\n"
+			"browse (Y/N)?\"",
+		nullptr
+	}
+};
+
+static const TownText INN_TEXT[5] = {
+	{
+		"The innekeeper asks: \"Would you like to sign in (Y/N)?\"",
+		nullptr
+	},
+	{
+		"Lara, the inkeepers voluptuous\n"
+			"daughter, asks if you will be spending\n"
+			"the night... sign in (Y/N)?",
+		nullptr
+	},
+	{
+		"Standing behind a large desk, a tall\n"
+			"bearded half-orc grunts, \"If you don't\n"
+			"sign in, you can't stay! Well (Y/N)?\"",
+		nullptr
+	},
+	{
+		"An eerie silence pervades the deserted\n"
+			"inn. suddenly, a ghostly apparition\n"
+			"appears, asking, \"Sign in (Y/N)?\"",
+		nullptr
+	},
+	{
+		"Adjusting his spectacles, an elderly\n"
+			"gnome eyes the party and asks,\n"
+			"\"Signing in (Y/N)?\"",
+		nullptr
+	}
+};
+
+static const TownText MARKET_TEXT[5] = {
+	{
+		"Behind the counter, an overweight dwarf\n"
+			"exclaims: \"You look like a hungry bunch!\n"
+			"Would you like to buy some food (Y/N)?\"",
+		nullptr
+	},
+	{
+		"From behind the fruit stand\n"
+			"a sultry voice asks, \"See anything\n"
+			"you like (Y/N)?\"",
+		nullptr
+	},
+	{
+		"A putrid smell pervades the room...\n"
+			"look for some food (Y/N)?",
+		nullptr
+	},
+	{
+		"A faint outline of a man appears,\n"
+			"saying, \"Need some feed (Y/N)?\"",
+		nullptr
+	},
+	{
+		"The chef says,\"I only serve the best!\n"
+			"Hungry (Y/N)?\"",
+		nullptr
+	}
+};
+
+static const TownText TEMPLE_TEXT[5] = {
+	{
+		"Several ornately robed clerics approach\n"
+			"the party and ask, \"Do you seek our help (Y/N)?\"",
+		nullptr
+	},
+	{
+		"The once magnificent temple shows\n"
+			"signs of neglect and decay. The high\n"
+			"priestess asks, \"Need help (Y/N)?\"",
+		nullptr
+	},
+	{
+		"An ancient looking cleric staggers up to\n"
+			"the party gasping, \"I can help,\n"
+			"really i can!\" Accept the offer (Y/N)?",
+		nullptr
+	},
+	{
+		"Bright lights swirl throughout the room.A horrifying voice shrieks,\n"
+			"\"Need help (Y/N)?\"",
+		"Bright lights swirl throughout the room. A horrifying voice shrieks,\n"
+			"\"Need help (Y/N)?\""
+	},
+	{
+		"Several fat clerics welcome you.\n"
+			"Need help (Y/N)?",
+		nullptr
+	}
+};
+
+static const TownText TRAINING_TEXT[5] = {
+	{
+		"Before you are various groups engaged in\n"
+			"training exercises. Worg,the guildmaster\n"
+			"asks, \"Do you require training (Y/N)?\"",
+		"Before you are various groups engaged in training exercises. Worg, the guildmaster asks, "
+			"\"Do you require training (Y/N)?\""
+	},
+	{
+		"A tall, muscular female clad in leather\n"
+			"armor approaches asking, \"Strangers,\n"
+			"are you here for training (Y/N)?\"",
+		nullptr
+	},
+	{
+		"several adventurers armored in dragon\n"
+			"scales, address the party,\n"
+			"\"Looking for training (Y/N)?\"",
+		nullptr
+	},
+	{
+		"Several empty suits of armor approach\n"
+			"asking, \"Need training (Y/N)?\"",
+		nullptr
+	},
+	{
+		"Three huge armor clad knights bark in\n"
+			"unison, \"Want a good workout (Y/N)?\"",
+		nullptr
+	}
+};
+
+static Common::String townText(const TownText &text) {
+	if (g_engine->isEnhanced() && text._enhanced)
+		return text._enhanced;
+
+	return mapText(text._classic);
+}
+
 void MapTown::blacksmith() {
 	visitedBusiness();
 
 	send(SoundMessage(
-		STRING[Common::String::format("maps.map%.2u.blacksmith_inside", _mapIndex)],
+		townText(BLACKSMITH_TEXT[_mapIndex]),
 		[]() {
 			g_events->send("Blacksmith", GameMessage("DISPLAY"));
 		}
@@ -44,7 +215,7 @@ void MapTown::inn() {
 	visitedBusiness();
 
 	send(SoundMessage(
-		STRING[Common::String::format("maps.map%.2u.inn_inside", _mapIndex)],
+		townText(INN_TEXT[_mapIndex]),
 		[]() {
 			g_events->replaceView("Inn", true);
 		}
@@ -55,7 +226,7 @@ void MapTown::market() {
 	visitedBusiness();
 
 	send(SoundMessage(
-		STRING[Common::String::format("maps.map%.2u.market_inside", _mapIndex)],
+		townText(MARKET_TEXT[_mapIndex]),
 		[]() {
 			g_events->send("Market", GameMessage("DISPLAY"));
 		}
@@ -66,7 +237,7 @@ void MapTown::tavern() {
 	visitedBusiness();
 
 	send(SoundMessage(
-		STRING["maps.tavern_inside"],
+		"Step up to the bar (Y/N)?",
 		[]() {
 			g_events->send("Tavern", GameMessage("DISPLAY"));
 		}
@@ -77,7 +248,7 @@ void MapTown::temple() {
 	visitedBusiness();
 
 	send(SoundMessage(
-		STRING[Common::String::format("maps.map%.2u.temple_inside", _mapIndex)],
+		townText(TEMPLE_TEXT[_mapIndex]),
 		[]() {
 			g_events->send("Temple", GameMessage("DISPLAY"));
 		}
@@ -88,7 +259,7 @@ void MapTown::training() {
 	visitedBusiness();
 
 	send(SoundMessage(
-		STRING[Common::String::format("maps.map%.2u.training_inside", _mapIndex)],
+		townText(TRAINING_TEXT[_mapIndex]),
 		[]() {
 			g_events->send("Training", GameMessage("DISPLAY"));
 		}
@@ -97,7 +268,7 @@ void MapTown::training() {
 
 void MapTown::showSign(const Common::String &msg) {
 	send(SoundMessage(
-		2, 1, STRING["maps.sign"],
+		2, 1, mapText("A sign above the door reads:\n"),
 		6, 2, msg
 	));
 }


Commit: faf5923a50829ffac901b960612329be39b44466
    https://github.com/scummvm/scummvm/commit/faf5923a50829ffac901b960612329be39b44466
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:09+11:00

Commit Message:
MM: MM1: Replace STRING lookups in classic views with literal text

Changed paths:
    engines/mm/mm1/views/are_you_ready.cpp
    engines/mm/mm1/views/character_base.cpp
    engines/mm/mm1/views/character_info.cpp
    engines/mm/mm1/views/character_manage.cpp
    engines/mm/mm1/views/character_view_combat.cpp
    engines/mm/mm1/views/characters.cpp
    engines/mm/mm1/views/color_questions.cpp
    engines/mm/mm1/views/combat.cpp
    engines/mm/mm1/views/create_characters.cpp
    engines/mm/mm1/views/dead.cpp
    engines/mm/mm1/views/encounter.cpp
    engines/mm/mm1/views/game_commands.cpp
    engines/mm/mm1/views/game_view.cpp
    engines/mm/mm1/views/interactions/access_code.cpp
    engines/mm/mm1/views/interactions/alamar.cpp
    engines/mm/mm1/views/interactions/alien.cpp
    engines/mm/mm1/views/interactions/arenko.cpp
    engines/mm/mm1/views/interactions/arrested.cpp
    engines/mm/mm1/views/interactions/chess.cpp
    engines/mm/mm1/views/interactions/dog_statue.cpp
    engines/mm/mm1/views/interactions/ghost.cpp
    engines/mm/mm1/views/interactions/gypsy.cpp
    engines/mm/mm1/views/interactions/hacker.cpp
    engines/mm/mm1/views/interactions/ice_princess.cpp
    engines/mm/mm1/views/interactions/inspectron.cpp
    engines/mm/mm1/views/interactions/keeper.cpp
    engines/mm/mm1/views/interactions/leprechaun.cpp
    engines/mm/mm1/views/interactions/lion.cpp
    engines/mm/mm1/views/interactions/lord_archer.cpp
    engines/mm/mm1/views/interactions/lord_ironfist.cpp
    engines/mm/mm1/views/interactions/orango.cpp
    engines/mm/mm1/views/interactions/prisoners.cpp
    engines/mm/mm1/views/interactions/resistances.cpp
    engines/mm/mm1/views/interactions/ruby.cpp
    engines/mm/mm1/views/interactions/trivia.cpp
    engines/mm/mm1/views/interactions/volcano_god.cpp
    engines/mm/mm1/views/interactions/won_game.cpp
    engines/mm/mm1/views/locations/blacksmith.cpp
    engines/mm/mm1/views/locations/blacksmith_buy_armor.cpp
    engines/mm/mm1/views/locations/blacksmith_buy_misc.cpp
    engines/mm/mm1/views/locations/blacksmith_buy_weapons.cpp
    engines/mm/mm1/views/locations/blacksmith_sell_item.cpp
    engines/mm/mm1/views/locations/blacksmith_subview.cpp
    engines/mm/mm1/views/locations/inn.cpp
    engines/mm/mm1/views/locations/location.cpp
    engines/mm/mm1/views/locations/market.cpp
    engines/mm/mm1/views/locations/statue.cpp
    engines/mm/mm1/views/locations/tavern.cpp
    engines/mm/mm1/views/locations/temple.cpp
    engines/mm/mm1/views/locations/training.cpp
    engines/mm/mm1/views/main_menu.cpp
    engines/mm/mm1/views/order.cpp
    engines/mm/mm1/views/protect.cpp
    engines/mm/mm1/views/quick_ref.cpp
    engines/mm/mm1/views/rest.cpp
    engines/mm/mm1/views/search.cpp
    engines/mm/mm1/views/spells/cast_spell.cpp
    engines/mm/mm1/views/spells/detect_magic.cpp
    engines/mm/mm1/views/spells/duplication.cpp
    engines/mm/mm1/views/spells/fly.cpp
    engines/mm/mm1/views/spells/location.cpp
    engines/mm/mm1/views/spells/recharge_item.cpp
    engines/mm/mm1/views/spells/spell_view.cpp
    engines/mm/mm1/views/spells/teleport.cpp
    engines/mm/mm1/views/text_view.cpp
    engines/mm/mm1/views/trap.cpp
    engines/mm/mm1/views/unlock.cpp


diff --git a/engines/mm/mm1/views/are_you_ready.cpp b/engines/mm/mm1/views/are_you_ready.cpp
index 3c2066b0a23..1fc20bd9146 100644
--- a/engines/mm/mm1/views/are_you_ready.cpp
+++ b/engines/mm/mm1/views/are_you_ready.cpp
@@ -28,8 +28,8 @@ namespace Views {
 
 void AreYouReady::draw() {
 	drawTextBorder();
-	writeString(11, 11, STRING["dialogs.ready.1"]);
-	writeString(11, 13, STRING["dialogs.ready.2"]);
+	writeString(11, 11, "  Are you ready? ");
+	writeString(11, 13, "Then press Enter!");
 }
 
 bool AreYouReady::msgAction(const ActionMessage &msg) {
diff --git a/engines/mm/mm1/views/character_base.cpp b/engines/mm/mm1/views/character_base.cpp
index d0549fc6e9e..beccc6122fe 100644
--- a/engines/mm/mm1/views/character_base.cpp
+++ b/engines/mm/mm1/views/character_base.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/character_base.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/utils/strings.h"
 #include "mm/mm1/globals.h"
 
@@ -31,27 +32,27 @@ void CharacterBase::printStats() {
 	Character &re = *g_globals->_currCharacter;
 	printSummary();
 
-	writeString(0, 2, STRING["stats.attributes.int"]);
+	writeString(0, 2, "Int=");
 	writeNumber(re._intelligence);
 	_textPos.x = 8;
-	writeString(STRING["stats.attributes.level"]);
+	writeString("Level=");
 	writeNumber(re._level);
 	_textPos.x = 18;
-	writeString(STRING["stats.attributes.age"]);
+	writeString("Age=");
 	writeNumber(re._level);
 	_textPos.x = 27;
-	writeString(STRING["stats.attributes.exp"]);
+	writeString("Exp=");
 	writeNumber(re._exp);
 
 	newLine();
-	writeString(STRING["stats.attributes.mgt"]);
+	writeString("Mgt=");
 	writeNumber(re._might);
 
 	newLine();
-	writeString(STRING["stats.attributes.per"]);
+	writeString("Per=");
 	writeNumber(re._personality);
 	_textPos.x = 8;
-	writeString(STRING["stats.attributes.sp"]);
+	writeString("SP=");
 	writeNumber(re._sp._current);
 	_textPos.x = 16;
 	writeChar('/');
@@ -61,38 +62,38 @@ void CharacterBase::printStats() {
 	writeNumber(re._spellLevel._current);
 	writeChar(')');
 	_textPos.x = 26;
-	writeString(STRING["stats.attributes.gems"]);
+	writeString("Gems=");
 	writeNumber(re._gems);
 
 	newLine();
-	writeString(STRING["stats.attributes.end"]);
+	writeString("End=");
 	writeNumber(re._endurance);
 
 	newLine();
-	writeString(STRING["stats.attributes.spd"]);
+	writeString("Spd=");
 	writeNumber(re._speed);
 	_textPos.x = 8;
-	writeString(STRING["stats.attributes.hp"]);
+	writeString("HP=");
 	writeNumber(re._hpCurrent);
 	_textPos.x = 16;
 	writeChar('/');
 	writeNumber(re._hpMax);
 	_textPos.x = 26;
-	writeString(STRING["stats.attributes.gold"]);
+	writeString("Gold=");
 	writeNumber(re._gold);
 
 	newLine();
-	writeString(STRING["stats.attributes.acy"]);
+	writeString("Acy=");
 	writeNumber(re._accuracy);
 
 	newLine();
-	writeString(STRING["stats.attributes.luc"]);
+	writeString("Luc=");
 	writeNumber(re._luck);
 	_textPos.x = 8;
-	writeString(STRING["stats.attributes.ac"]);
+	writeString("AC=");
 	writeNumber(re._ac);
 	_textPos.x = 26;
-	writeString(STRING["stats.attributes.food"]);
+	writeString("Food=");
 	writeNumber(re._food);
 
 	newLine();
@@ -112,24 +113,24 @@ void CharacterBase::printSummary() {
 
 	_textPos.x++;
 	writeString((re._alignment >= GOOD && re._alignment <= EVIL) ?
-		STRING[Common::String::format("stats.alignments.%d", re._alignment)] :
-		STRING["stats.none"]
+		ALIGNMENT_NAMES[re._alignment - 1] :
+		"None"
 	);
 
 	if (re._race >= HUMAN && re._race <= HALF_ORC)
-		writeString(26, 0, STRING[Common::String::format("stats.races.%d", re._race)]);
+		writeString(26, 0, RACE_NAMES[re._race - 1]);
 	else
-		writeString(26, 0, STRING["stats.none"]);
+		writeString(26, 0, "None");
 
 	if (re._class >= KNIGHT && re._class <= ROBBER)
-		writeString(32, 0, STRING[Common::String::format("stats.classes.%d", re._class)]);
+		writeString(32, 0, CLASS_NAMES[re._class - 1]);
 	else
-		writeString(32, 0, STRING["stats.none"]);
+		writeString(32, 0, "None");
 }
 
 void CharacterBase::printCondition() {
 	Character &c = *g_globals->_currCharacter;
-	writeString(STRING["stats.attributes.cond"]);
+	writeString("Cond=");
 	_textPos.x++;
 
 	writeString(c.getConditionString());
@@ -137,7 +138,7 @@ void CharacterBase::printCondition() {
 
 void CharacterBase::printInventory() {
 	Character &re = *g_globals->_currCharacter;
-	writeString(0, 12, STRING["stats.inventory"]);
+	writeString(0, 12, "-----<Equipped>----------<Backpack>----");
 
 	// Print the equipped and backpack items
 	for (uint i = 0; i < INVENTORY_COUNT; ++i) {
diff --git a/engines/mm/mm1/views/character_info.cpp b/engines/mm/mm1/views/character_info.cpp
index af8448582d4..b5939cb865d 100644
--- a/engines/mm/mm1/views/character_info.cpp
+++ b/engines/mm/mm1/views/character_info.cpp
@@ -41,29 +41,29 @@ void CharacterInfo::draw() {
 
 	switch (_state) {
 	case DISPLAY:
-		writeString(0, 21, STRING["dialogs.character.legend1"]);
-		writeString(0, 22, STRING["dialogs.character.legend2"]);
-		writeString(0, 23, STRING["dialogs.character.legend3"]);
-		writeString(0, 24, STRING["dialogs.character.legend4"]);
+		writeString(0, 21, "'q' quick ref     'c' cast    'r' remove");
+		writeString(0, 22, " # view other     'd' discard 's' share");
+		writeString(0, 23, "                  'e' equip   't' trade");
+		writeString(0, 24, "'esc' to go back  'g' gather  'u' use");
 		break;
 
 	case DISCARD:
-		writeString(0, 20, STRING["dialogs.character.discard"]);
+		writeString(0, 20, "discard which item: 'a'-'f'?");
 		escToGoBack(0);
 		break;
 
 	case EQUIP:
-		writeString(0, 20, STRING["dialogs.character.equip"]);
+		writeString(0, 20, "equip which item: 'a'-'f'?");
 		escToGoBack(0);
 		break;
 
 	case REMOVE:
-		writeString(0, 20, STRING["dialogs.character.remove"]);
+		writeString(0, 20, "remove which item: '1'-'6'?");
 		escToGoBack(0);
 		break;
 
 	case SHARE:
-		writeString(8, 20, STRING["dialogs.character.share_all"]);
+		writeString(8, 20, "share all:");
 		drawGemsGoldFood();
 		break;
 
@@ -85,21 +85,21 @@ void CharacterInfo::draw() {
 		break;
 
 	case TRADE_KIND:
-		writeString(6, 20, STRING["dialogs.character.trade_which"]);
+		writeString(6, 20, "trade which:");
 		drawGemsGoldFood();
-		writeString(20, 23, STRING["dialogs.character.item"]);
+		writeString(20, 23, "4) Item");
 		escToGoBack(0);
 		break;
 
 	case TRADE_ITEM:
-		writeString(10, 20, STRING["dialogs.character.which"]);
+		writeString(10, 20, "which item: 'a'-'f'?");
 		escToGoBack(0);
 		break;
 
 	case USE:
 		g_globals->_combatEffectCtr = 0;
 		g_globals->_nonCombatEffectCtr = 0;
-		writeString(7, 20, STRING["dialogs.character.use_what"]);
+		writeString(7, 20, "use which 'a'-'f','1'-'6'?");
 		escToGoBack(0);
 		break;
 
@@ -214,7 +214,7 @@ bool CharacterInfo::msgKeypress(const KeypressMessage &msg) {
 
 		} else if (msg.keycode == Common::KEYCODE_4) {
 			if (g_globals->_party[_tradeWith]._backpack.full()) {
-				writeString(14, 21, STRING["dialogs.character.full"]);
+				writeString(14, 21, "*** full ***");
 				Sound::sound(SOUND_2);
 				_state = DISPLAY;
 				delaySeconds(3);
@@ -231,7 +231,7 @@ bool CharacterInfo::msgKeypress(const KeypressMessage &msg) {
 			switch (g_globals->_currCharacter->trade(_tradeWith,
 				msg.keycode - Common::KEYCODE_a)) {
 			case Character::TRADE_FULL:
-				writeString(14, 21, STRING["dialogs.character.full"]);
+				writeString(14, 21, "*** full ***");
 				_state = DISPLAY;
 				delaySeconds(3);
 				break;
@@ -356,9 +356,9 @@ void CharacterInfo::removeItem(uint index) {
 }
 
 void CharacterInfo::drawGemsGoldFood() {
-	writeString(20, 20, STRING["dialogs.character.gems"]);
-	writeString(20, 21, STRING["dialogs.character.gold"]);
-	writeString(20, 22, STRING["dialogs.character.food"]);
+	writeString(20, 20, "1) gems");
+	writeString(20, 21, "2) gold");
+	writeString(20, 22, "3) Food");
 }
 
 void CharacterInfo::howMuchAborted() {
@@ -417,7 +417,7 @@ void CharacterInfo::enterFunc(const Common::String &text) {
 void CharacterInfo::tradeHowMuch() {
 	clearLines(20, 24);
 	escToGoBack(0);
-	writeString(10, 20, STRING["dialogs.character.how_much"]);
+	writeString(10, 20, "how much:");
 
 	_textEntry.display(20, 20, _tradeKind == TK_GOLD ? 8 : 5, true,
 		abortFunc, enterFunc);
diff --git a/engines/mm/mm1/views/character_manage.cpp b/engines/mm/mm1/views/character_manage.cpp
index 6e03f4c4752..053291304d9 100644
--- a/engines/mm/mm1/views/character_manage.cpp
+++ b/engines/mm/mm1/views/character_manage.cpp
@@ -33,19 +33,19 @@ void CharacterManage::draw() {
 
 	switch (_state) {
 	case DISPLAY:
-		writeString(6, 21, STRING["dialogs.view_character.rename"]);
-		writeString(6, 22, STRING["dialogs.view_character.delete"]);
+		writeString(6, 21, "(Ctrl)-'N' Re-name character");
+		writeString(6, 22, "(Ctrl)-'D' Delete character");
 		escToGoBack();
 		break;
 
 	case RENAME:
-		writeString(6, 21, STRING["dialogs.view_character.name"]);
+		writeString(6, 21, "Name: ");
 		writeString(_newName);
 		writeChar('_');
 		break;
 
 	case DELETE:
-		writeString(6, 21, STRING["dialogs.view_character.are_you_sure"]);
+		writeString(6, 21, "    Are you sure (Y/N)?          ");
 		break;
 	}
 }
diff --git a/engines/mm/mm1/views/character_view_combat.cpp b/engines/mm/mm1/views/character_view_combat.cpp
index 108b318dcd9..0d94efd0036 100644
--- a/engines/mm/mm1/views/character_view_combat.cpp
+++ b/engines/mm/mm1/views/character_view_combat.cpp
@@ -40,8 +40,8 @@ bool CharacterViewCombat::msgUnfocus(const UnfocusMessage &msg) {
 void CharacterViewCombat::draw() {
 	CharacterBase::draw();
 
-	writeString(12, 22, Common::String::format("F1-F%d %s",
-		(int)g_globals->_combatParty.size(), STRING["dialogs.quick_ref.to_view"].c_str()));
+	writeString(12, 22, Common::String::format("F1-F%d to view",
+		(int)g_globals->_combatParty.size()));
 	escToGoBack();
 }
 
diff --git a/engines/mm/mm1/views/characters.cpp b/engines/mm/mm1/views/characters.cpp
index 89e0d4dfb48..de9d6bd119b 100644
--- a/engines/mm/mm1/views/characters.cpp
+++ b/engines/mm/mm1/views/characters.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/characters.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/utils/strings.h"
 #include "mm/mm1/globals.h"
 
@@ -31,12 +32,12 @@ void Characters::draw() {
 	drawTextBorder();
 
 	Roster &roster = g_globals->_roster;
-	writeString(11, 0, STRING["dialogs.view_characters.title"]);
+	writeString(11, 0, "View all characters");
 	int lineNum = 0;
 	_charIndexes.clear();
 
 	if (g_globals->_roster.empty()) {
-		writeString(8, 5, STRING["dialogs.misc.no_characters"]);
+		writeString(8, 5, "No available characters");
 		escToGoBack();
 		return;
 	}
@@ -53,8 +54,8 @@ void Characters::draw() {
 			pad_string(level, 7);
 
 			Common::String className = (re._class >= KNIGHT && re._class <= ROBBER) ?
-				STRING[Common::String::format("stats.classes.%d", (int)re._class)] :
-				STRING["stats.none"];
+				CLASS_NAMES[(int)re._class - 1] :
+				"None";
 
 			// Form line like: A) charName...(1)L1  Knight
 			Common::String line = Common::String::format("(%c) %s%s%s",
@@ -67,9 +68,9 @@ void Characters::draw() {
 
 	// Print legend at the bottom
 	writeString(6, 22, Common::String::format(
-		STRING["dialogs.view_characters.legend1"].c_str(),
+		"'a'-'%c' to view a character",
 		'A' + (int)_charIndexes.size() - 1));
-	writeString(12, 24, STRING["dialogs.misc.go_back"]);
+	writeString(12, 24, "'Esc' to go back");
 }
 
 bool Characters::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/color_questions.cpp b/engines/mm/mm1/views/color_questions.cpp
index 52fd37c193c..6c4d7bbf9ff 100644
--- a/engines/mm/mm1/views/color_questions.cpp
+++ b/engines/mm/mm1/views/color_questions.cpp
@@ -60,12 +60,14 @@ void ColorQuestions::draw() {
 	g_globals->_currCharacter = &g_globals->_party[_charIndex];
 
 	if (!_showingResponse) {
-		writeString(0, 0, STRING["maps.map17.color"]);
-		writeString(0, 2, STRING["maps.map17.options"]);
+		writeString(0, 0, "What is your color (1-8)?");
+		writeString(0, 2, "1) Red Thorac       2) Blue Ogram\n"
+			"3) Green Bagar      4) Yellow Limra\n"
+			"5) Purple Sagran    6) Orange Oolak\n"
+			"7) Black Dresidion  8) White Dilithium");
 	} else {
 		const Character &c = g_globals->_party[_charIndex];
-		Common::String result = STRING[c.hasBadCondition() ?
-			"maps.map17.wrong" : "maps.map17.correct"];
+		Common::String result = (c.hasBadCondition() ? "Wrong!" : "Correct!");
 		writeString(16, 2, result);
 	}
 }
diff --git a/engines/mm/mm1/views/combat.cpp b/engines/mm/mm1/views/combat.cpp
index c66eb077ce2..caeea715319 100644
--- a/engines/mm/mm1/views/combat.cpp
+++ b/engines/mm/mm1/views/combat.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/combat.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/views/character_view_combat.h"
 #include "mm/mm1/game/encounter.h"
 #include "mm/mm1/globals.h"
@@ -126,7 +127,7 @@ void Combat::draw() {
 		return;
 	case MONSTER_ADVANCES:
 		writeString(0, 20, _monsterName);
-		writeString(STRING["dialogs.combat.advances"]);
+		writeString(" advances!");
 		writeSpaces(30);
 		writeRound();
 		writeMonsters();
@@ -393,7 +394,7 @@ void Combat::writeAllOptions() {
 	assert(g_globals->_currCharacter &&
 		g_globals->_currCharacter == g_globals->_combatParty[_currentChar]);
 	const Character &c = *g_globals->_currCharacter;
-	writeString(0, 20, STRING["dialogs.combat.options_for"]);
+	writeString(0, 20, "Options for: ");
 	writeString(0, 22, c._name);
 
 	// Highlight the currently active character
@@ -421,15 +422,15 @@ void Combat::writeAllOptions() {
 		_allowCast = true;
 	}
 
-	writeString(16, 22, STRING["dialogs.combat.exchange_use"]);
-	writeString(16, 23, STRING["dialogs.combat.retreat_block"]);
+	writeString(16, 22, "'E' Exchange  'U' Use");
+	writeString(16, 23, "'R' Retreat   'B' Block");
 }
 
 void Combat::writeDelaySelect() {
 	resetBottom();
-	writeString(0, 20, STRING["dialogs.combat.set_delay"]);
+	writeString(0, 20, "Set delay (0-9):");
 	writeString(0, 23, Common::String::format(
-		STRING["dialogs.combat.delay_currently"].c_str(),
+		"(currently= %d)",
 		g_globals->_delay));
 	escToGoBack(0, 23);
 }
@@ -446,7 +447,7 @@ void Combat::writeFightSelect() {
 	_attackableCount = MIN(_attackersCount, (int)_remainingMonsters.size());
 
 	writeString(10, 20, Common::String::format(
-		STRING["dialogs.combat.fight_which"].c_str(), 'A' + _attackableCount - 1));
+		"Fight which 'A'-'%c'?", 'A' + _attackableCount - 1));
 	escToGoBack(12, 23);
 }
 
@@ -454,21 +455,21 @@ void Combat::writeShootSelect() {
 	_attackableCount = MIN(_attackersCount, (int)_remainingMonsters.size());
 
 	writeString(10, 20, Common::String::format(
-		STRING["dialogs.combat.shoot_which"].c_str(), 'A' + _attackableCount - 1));
+		"Shoot which 'A'-'%c'?", 'A' + _attackableCount - 1));
 	escToGoBack(12, 23);
 }
 
 void Combat::writeAttackOptions() {
-	writeString(16, 20, STRING["dialogs.combat.attack"]);
-	writeString(16, 21, STRING["dialogs.combat.fight"]);
+	writeString(16, 20, "'A' Attack(a)");
+	writeString(16, 21, "'F' Fight(+)");
 }
 
 void Combat::writeCastOption() {
-	writeString(30, 21, STRING["dialogs.combat.cast"]);
+	writeString(30, 21, "'C' Cast");
 }
 
 void Combat::writeShootOption() {
-	writeString(30, 20, STRING["dialogs.combat.shoot"]);
+	writeString(30, 20, "'S' Shoot");
 }
 
 void Combat::resetBottom() {
@@ -478,13 +479,13 @@ void Combat::resetBottom() {
 }
 
 void Combat::writeStaticContent() {
-	writeString(0, 0, STRING["dialogs.combat.combat"]);
-	writeString(0, 1, STRING["dialogs.combat.round"]);
-	writeString(0, 7, STRING["dialogs.combat.delay"]);
-	writeString(0, 8, STRING["dialogs.combat.protect"]);
-	writeString(0, 9, STRING["dialogs.combat.quickref"]);
-	writeString(0, 10, STRING["dialogs.combat.view_char"]);
-	writeString(0, 12, STRING["dialogs.combat.handicap"]);
+	writeString(0, 0, "Combat");
+	writeString(0, 1, "Round #");
+	writeString(0, 7, "D Delay");
+	writeString(0, 8, "P Protect");
+	writeString(0, 9, "Q QuickRef");
+	writeString(0, 10, "# View Ch");
+	writeString(0, 12, "Handicap");
 
 	_textPos = Common::Point(0, 15);
 	for (int i = 0; i < 40; ++i)
@@ -497,14 +498,14 @@ void Combat::writeHandicap() {
 
 	switch (_handicap) {
 	case HANDICAP_EVEN:
-		writeString(STRING["dialogs.combat.even"]);
+		writeString("Even");
 		break;
 	case HANDICAP_PARTY:
-		writeString(STRING["dialogs.combat.party_plus"]);
+		writeString("Party +");
 		writeNumber(_handicapDelta);
 		break;
 	case HANDICAP_MONSTER:
-		writeString(STRING["dialogs.combat.monster_plus"]);
+		writeString("Monster +");
 		writeNumber(_handicapDelta);
 		break;
 	}
@@ -566,11 +567,10 @@ void Combat::writeMonsterStatus(int monsterNum) {
 			}
 		}
 
-		writeString(STRING[Common::String::format("dialogs.combat.status.%d",
-			status)]);
+		writeString(COMBAT_STATUS[status]);
 	} else if (_monsterP->_hp != _monsterP->_defaultHP) {
 		writeDots();
-		writeString(STRING["dialogs.combat.status.wounded"]);
+		writeString("(wounded) ");
 	} else {
 		writeSpaces(40 - _textPos.x);
 	}
@@ -608,16 +608,15 @@ void Combat::writeDefeat() {
 		writeString(10, y, "!                            !");
 	writeString(10, 8, "+----------------------------+");
 
-	writeString(10, 2, STRING["dialogs.combat.defeating1"]);
-	writeString(10, 4, STRING["dialogs.combat.defeating2"]);
+	writeString(10, 2, "! For defeating the monsters !");
+	writeString(10, 4, "!   each survivor receives   !");
 	writeNumber(14, 6, _totalExperience);
 	_textPos.x++;
-	writeString(STRING["dialogs.combat.xp"]);
+	writeString("experience points.");
 }
 
 void Combat::highlightNextRound() {
-	Common::String s = Common::String::format("%s%d",
-		STRING["dialogs.combat.round"].c_str(),
+	Common::String s = Common::String::format("Round #%d",
 		_roundNum);
 	
 	for (uint i = 0; i < s.size(); ++i)
@@ -628,13 +627,13 @@ void Combat::highlightNextRound() {
 
 void Combat::writeMonsterEffects() {
 	if (_monstersRegenerate)
-		writeString(0, 21, STRING["dialogs.combat.regenerate"]);
+		writeString(0, 21, "some monsters regenerate!");
 
 	if (_monstersResistSpells) {
 		if (_textPos.y != 21)
 			_textPos.y = 20;
 
-		writeString(0, _textPos.y + 1, STRING["dialogs.combat.overcome"]);
+		writeString(0, _textPos.y + 1, "some monsters overcome spells!");
 	}
 
 	writeMonsters();
@@ -644,9 +643,7 @@ void Combat::writeMonsterAction(bool flees) {
 	resetBottom();
 	writeString(0, 20, _monsterName);
 	writeChar(' ');
-	writeString(STRING[flees ?
-		"dialogs.combat.monster_flees" : "dialogs.combat.monster_wanders"
-	]);
+	writeString(flees ? "runs away..." : "wanders aimlessly");
 }
 
 void Combat::writeMonsterSpell() {
@@ -666,8 +663,8 @@ void Combat::writeMonsterSpell() {
 
 void Combat::writeMonsterAttack() {
 	Common::String monsterName = _monsterP->_name;
-	Common::String attackStyle = STRING[Common::String::format(
-		"dialogs.combat.attack_types.%d", _monsterAttackStyle)];
+	Common::String attackStyle = _monsterAttackStyle == 99 ?
+		"shoots at" : ATTACK_TYPES[_monsterAttackStyle - 1];
 	Character &c = *g_globals->_currCharacter;
 
 	Common::String line = Common::String::format("%s %s %s",
@@ -698,9 +695,8 @@ void Combat::writeMonsterAttack() {
 }
 
 void Combat::writeInfiltration() {
-	Common::String line = Common::String::format("%s %s",
-		_monsterP->getDisplayName().c_str(),
-		STRING["dialogs.combat.infiltration"].c_str());
+	Common::String line = Common::String::format("%s infiltrates the ranks!",
+		_monsterP->getDisplayName().c_str());
 
 	resetBottom();
 	writeString(0, 20, line);
@@ -709,10 +705,8 @@ void Combat::writeInfiltration() {
 }
 
 void Combat::writeWaitsForOpening() {
-	Common::String line = Common::String::format("%s %s",
-		_monsterP->getDisplayName().c_str(),
-		STRING["dialogs.combat.infiltration"].c_str()
-	);
+	Common::String line = Common::String::format("%s infiltrates the ranks!",
+		_monsterP->getDisplayName().c_str());
 
 	resetBottom();
 	writeString(0, 20, line);
@@ -780,8 +774,7 @@ void Combat::writeCharAttackDamage() {
 
 	writeString(0, 20, Common::String::format("%s %s %s",
 		g_globals->_currCharacter->_name,
-		STRING[_isShooting ? "dialogs.combat.shoots" :
-		"dialogs.combat.attacks"].c_str(),
+		_isShooting ? "shoots" : "attacks",
 		_monsterP->_name.c_str()
 	));
 	_isShooting = false;
@@ -789,9 +782,8 @@ void Combat::writeCharAttackDamage() {
 	writeString(0, 21, getAttackString());
 
 	if (_monsterP->_status == MONFLAG_DEAD) {
-		writeString(0, 22, Common::String::format("%s %s",
-			_monsterP->_name.c_str(),
-			STRING["dialogs.combat.goes_down"].c_str()));
+		writeString(0, 22, Common::String::format("%s goes down!!!",
+			_monsterP->_name.c_str()));
 	}
 }
 
@@ -800,49 +792,45 @@ void Combat::writeCharAttackNoEffect() {
 
 	writeString(0, 20, Common::String::format("%s %s %s",
 		g_globals->_currCharacter->_name,
-		STRING[_isShooting ? "dialogs.combat.shoots" :
-		"dialogs.combat.attacks"].c_str(),
+		_isShooting ? "shoots" : "attacks",
 		_monsterP->_name.c_str()
 	));
 	_isShooting = false;
 
-	writeString(0, 21, STRING["dialogs.combat.weapon_no_effect"]);
+	writeString(0, 21, "Weapon has no effect!");
 }
 
 Common::String Combat::getAttackString() {
 	Common::String line1;
 	if (_numberOfTimes == 1) {
-		line1 = STRING["dialogs.combat.once"];
+		line1 = "once";
 	} else {
-		line1 = Common::String::format("%d %s", _numberOfTimes,
-			STRING["dialogs.combat.times"].c_str());
+		line1 = Common::String::format("%d times", _numberOfTimes);
 	}
 
-	line1 += Common::String::format(" %s ", STRING["dialogs.combat.and"].c_str());
+	line1 += " and ";
 
 	if (_displayedDamage == 0) {
-		line1 += STRING["dialogs.combat.misses"];
+		line1 += "misses";
 	} else {
-		line1 += STRING["dialogs.combat.hit"];
+		line1 += "hit";
 
 		if (_numberOfTimes > 1) {
 			line1 += ' ';
 
 			if (_timesHit == 1) {
-				line1 += STRING["dialogs.combat.once"];
+				line1 += "once";
 			} else {
-				line1 += Common::String::format("%d %s", _timesHit,
-					STRING["dialogs.combat.times"].c_str());
+				line1 += Common::String::format("%d times", _timesHit);
 			}
 		}
 
-		line1 += Common::String::format(" %s %d %s",
-			STRING["dialogs.combat.for"].c_str(), _displayedDamage,
-			STRING[_damage == 1 ? "dialogs.combat.point" : "dialogs.combat.points"].c_str());
+		line1 += Common::String::format(" for %d %s", _displayedDamage,
+			_damage == 1 ? "point" : "points");
 
 		if (line1.size() < 30) {
 			line1 += ' ';
-			line1 += STRING["dialogs.combat.of_damage"];
+			line1 += "of damage!";
 		} else {
 			line1 += '!';
 		}
diff --git a/engines/mm/mm1/views/create_characters.cpp b/engines/mm/mm1/views/create_characters.cpp
index 997dcd0d829..84a3d5b7e11 100644
--- a/engines/mm/mm1/views/create_characters.cpp
+++ b/engines/mm/mm1/views/create_characters.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/create_characters.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
 
@@ -184,21 +185,21 @@ void CreateCharacters::NewCharacter::setSP(int amount) {
 void CreateCharacters::draw() {
 	drawTextBorder();
 
-	writeString(10, 0, STRING["dialogs.create_characters.title"]);
-	writeString(3, 5, STRING["dialogs.create_characters.intellect"]);
-	writeString(3, 7, STRING["dialogs.create_characters.might"]);
-	writeString(3, 9, STRING["dialogs.create_characters.personality"]);
-	writeString(3, 11, STRING["dialogs.create_characters.endurance"]);
-	writeString(3, 13, STRING["dialogs.create_characters.speed"]);
-	writeString(3, 15, STRING["dialogs.create_characters.accuracy"]);
-	writeString(3, 17, STRING["dialogs.create_characters.luck"]);
+	writeString(10, 0, "Create New Characters");
+	writeString(3, 5, "intellect...=");
+	writeString(3, 7, "might.......=");
+	writeString(3, 9, "personality.=");
+	writeString(3, 11, "endurance...=");
+	writeString(3, 13, "speed.......=");
+	writeString(3, 15, "accuracy....=");
+	writeString(3, 17, "luck........=");
 	printAttributes();
 
 	switch (_state) {
 	case SELECT_CLASS:
 		printClasses();
 		if (g_globals->_roster.full())
-			writeString(9, 21, STRING["dialogs.create_characters.full"]);
+			writeString(9, 21, "*** Roster is Full ***");
 		escToGoBack();
 		break;
 
@@ -238,107 +239,107 @@ void CreateCharacters::printAttributes() {
 void CreateCharacters::printClasses() {
 	if (_newChar._classesAllowed[KNIGHT]) {
 		writeString(23, 5, "1) ");
-		writeString(STRING["stats.classes.1"]);
+		writeString("Knight");
 	}
 	if (_newChar._classesAllowed[PALADIN]) {
 		writeString(23, 6, "2) ");
-		writeString(STRING["stats.classes.2"]);
+		writeString("Paladin");
 	}
 	if (_newChar._classesAllowed[ARCHER]) {
 		writeString(23, 7, "3) ");
-		writeString(STRING["stats.classes.3"]);
+		writeString("Archer");
 	}
 	if (_newChar._classesAllowed[CLERIC]) {
 		writeString(23, 8, "4) ");
-		writeString(STRING["stats.classes.4"]);
+		writeString("Cleric");
 	}
 	if (_newChar._classesAllowed[SORCERER]) {
 		writeString(23, 9, "5) ");
-		writeString(STRING["stats.classes.5"]);
+		writeString("Sorcerer");
 	}
 	writeString(23, 10, "6) ");
-	writeString(STRING["stats.classes.6"]);
+	writeString("Robber");
 
-	writeString(22, 13, STRING["dialogs.create_characters.select_class"]);
+	writeString(22, 13, "Select class");
 	writeString(26, 15, "(1-6)");
-	writeString(21, 17, STRING["dialogs.create_characters.reroll"]);
+	writeString(21, 17, "'ent' to re-roll");
 }
 
 void CreateCharacters::printRaces() {
-	writeString(22, 5, STRING["dialogs.create_characters.class"]);
-	writeString(STRING[Common::String::format("stats.classes.%d", _newChar._class)]);
+	writeString(22, 5, "class.=");
+	writeString(CLASS_NAMES[_newChar._class - 1]);
 
 	writeString(23, 7, "1) ");
-	writeString(STRING["stats.races.1"]);
+	writeString("Human");
 	writeString(23, 8, "2) ");
-	writeString(STRING["stats.races.2"]);
+	writeString("Elf");
 	writeString(23, 9, "3) ");
-	writeString(STRING["stats.races.3"]);
+	writeString("Dwarf");
 	writeString(23, 10, "4) ");
-	writeString(STRING["stats.races.4"]);
+	writeString("Gnome");
 	writeString(23, 11, "5) ");
-	writeString(STRING["stats.races.5"]);
+	writeString("H-Orc");
 
-	writeString(22, 13, STRING["dialogs.create_characters.select_race"]);
+	writeString(22, 13, "Select a race");
 	writeString(26, 15, "(1-5)");
-	writeString(21, 17, STRING["dialogs.create_characters.start_over"]);
+	writeString(21, 17, "'esc' start over");
 }
 
 void CreateCharacters::printAlignments() {
-	writeString(22, 5, STRING["dialogs.create_characters.class"]);
-	writeString(STRING[Common::String::format("stats.classes.%d", _newChar._class)]);
-	writeString(22, 6, STRING["dialogs.create_characters.race"]);
-	writeString(STRING[Common::String::format("stats.races.%d", _newChar._race)]);
+	writeString(22, 5, "class.=");
+	writeString(CLASS_NAMES[_newChar._class - 1]);
+	writeString(22, 6, "race..=");
+	writeString(RACE_NAMES[_newChar._race - 1]);
 
 	writeString(23, 8, "1) ");
-	writeString(STRING["stats.alignments.1"]);
+	writeString("Good");
 	writeString(23, 9, "2) ");
-	writeString(STRING["stats.alignments.2"]);
+	writeString("Neut");
 	writeString(23, 10, "3) ");
-	writeString(STRING["stats.alignments.3"]);
+	writeString("Evil");
 
-	writeString(21, 13, STRING["dialogs.create_characters.select_alignment"]);
+	writeString(21, 13, "Select alignment");
 	writeString(26, 15, "(1-3)");
-	writeString(21, 17, STRING["dialogs.create_characters.start_over"]);
+	writeString(21, 17, "'esc' start over");
 }
 
 void CreateCharacters::printSexes() {
-	writeString(22, 5, STRING["dialogs.create_characters.class"]);
-	writeString(STRING[Common::String::format("stats.classes.%d", _newChar._class)]);
-	writeString(22, 6, STRING["dialogs.create_characters.race"]);
-	writeString(STRING[Common::String::format("stats.races.%d", _newChar._race)]);
-	writeString(22, 7, STRING["dialogs.create_characters.alignment"]);
-	writeString(STRING[Common::String::format("stats.alignments.%d", _newChar._alignment)]);
+	writeString(22, 5, "class.=");
+	writeString(CLASS_NAMES[_newChar._class - 1]);
+	writeString(22, 6, "race..=");
+	writeString(RACE_NAMES[_newChar._race - 1]);
+	writeString(22, 7, "algn..=");
+	writeString(ALIGNMENT_NAMES[_newChar._alignment - 1]);
 
 	writeString(23, 9, "1) ");
-	writeString(STRING["stats.sex.1"]);
+	writeString("Male");
 	writeString(23, 10, "2) ");
-	writeString(STRING["stats.sex.2"]);
+	writeString("Female");
 
-	writeString(23, 13, STRING["dialogs.create_characters.select_sex"]);
+	writeString(23, 13, "Select a sex");
 	writeString(26, 15, "(1-2)");
-	writeString(21, 17, STRING["dialogs.create_characters.start_over"]);
+	writeString(21, 17, "'esc' start over");
 }
 
 void CreateCharacters::printSummary(bool promptToSave) {
-	writeString(22, 5, STRING["dialogs.create_characters.class"]);
-	writeString(STRING[Common::String::format("stats.classes.%d", _newChar._class)]);
-	writeString(22, 6, STRING["dialogs.create_characters.race"]);
-	writeString(STRING[Common::String::format("stats.races.%d", _newChar._race)]);
-	writeString(22, 7, STRING["dialogs.create_characters.alignment"]);
-	writeString(STRING[Common::String::format("stats.alignments.%d", _newChar._alignment)]);
-	writeString(22, 8, STRING["dialogs.create_characters.sex"]);
-	writeString(STRING[Common::String::format("stats.sex.%d", _newChar._sex)]);
-
-	writeString(22, 11, STRING["dialogs.create_characters.name"]);
+	writeString(22, 5, "class.=");
+	writeString(CLASS_NAMES[_newChar._class - 1]);
+	writeString(22, 6, "race..=");
+	writeString(RACE_NAMES[_newChar._race - 1]);
+	writeString(22, 7, "algn..=");
+	writeString(ALIGNMENT_NAMES[_newChar._alignment - 1]);
+	writeString(22, 8, "sex...=");
+	writeString(SEX_NAMES[_newChar._sex - 1]);
+
+	writeString(22, 11, "name:");
 	writeString(22, 13, _newChar._name);
 
 	if (promptToSave) {
-		writeString(22, 15, STRING["dialogs.create_characters.save_character"]);
+		writeString(22, 15, "Save character");
 		writeString(26, 17, "(Y/N)?");
 	} else {
 		writeChar('_');
-		writeString(21, 17, STRING["dialogs.create_characters.start_over"]);
+		writeString(21, 17, "'esc' start over");
 	}
 }
 
diff --git a/engines/mm/mm1/views/dead.cpp b/engines/mm/mm1/views/dead.cpp
index ad0faeb6bea..1773b8efcf5 100644
--- a/engines/mm/mm1/views/dead.cpp
+++ b/engines/mm/mm1/views/dead.cpp
@@ -41,12 +41,12 @@ bool Dead::msgFocus(const FocusMessage &msg) {
 void Dead::draw() {
 	clearSurface();
 	writeLine(4);
-	writeString(11, 6, STRING["dialogs.dead.1"]);
-	writeString(7, 8, STRING["dialogs.dead.2"]);
-	writeString(7, 10, STRING["dialogs.dead.3"]);
-	writeString(8, 12, STRING["dialogs.dead.4"]);
-	writeString(8, 14, STRING["dialogs.dead.5"]);
-	writeString(10, 16, STRING["dialogs.dead.6"]);
+	writeString(11, 6, "The shadow of death");
+	writeString(7, 8, "has fallen upon your party!");
+	writeString(7, 10, "Fortunately, you may renew");
+	writeString(8, 12, "your quest from the last");
+	writeString(8, 14, "inn in which you stayed");
+	writeString(10, 16, "by pressing 'Enter'");
 	writeLine(18);
 }
 
diff --git a/engines/mm/mm1/views/encounter.cpp b/engines/mm/mm1/views/encounter.cpp
index 9086fff5ed2..9bec5b7b1a8 100644
--- a/engines/mm/mm1/views/encounter.cpp
+++ b/engines/mm/mm1/views/encounter.cpp
@@ -48,52 +48,52 @@ void Encounter::draw() {
 	switch (_mode) {
 	case ALERT:
 		writeString(9, 6, "            ");
-		writeString(9, 7, STRING["dialogs.encounter.title"]);
+		writeString(9, 7, " Encounter! ");
 		writeString(9, 8, "            ");
 
 		delaySeconds(2);
 		break;
 
 	case SURPRISED_BY_MONSTERS:
-		writeString(6, 21, STRING["dialogs.encounter.surprised"]);
+		writeString(6, 21, "The monsters surprised you!");
 		enc._encounterType = Game::FORCE_SURPRISED;
 		delaySeconds(2);
 		break;
 
 	case SURPRISED_MONSTERS:
-		writeString(2, 21, STRING["dialogs.encounter.surprise"]);
-		writeString(1, 22, STRING["dialogs.encounter.approach"]);
+		writeString(2, 21, "You surprised a group of monsters,");
+		writeString(1, 22, "Approach (Y/N)?");
 		break;
 
 	case ENCOUNTER_OPTIONS: {
 		// Write the encounter options
 		clearLines(20, 24);
-		writeString(0, 21, STRING["dialogs.encounter.options1"]);
-		writeString(10, 22, STRING["dialogs.encounter.options2"]);
+		writeString(0, 21, "Options:  'A' Attack  'R' Retreat");
+		writeString(10, 22, "'B' Bribe   'S' Surrender");
 		break;
 	}
 
 	case NOWHERE_TO_RUN:
 		clearLines(20, 24);
-		writeString(11, 21, STRING["dialogs.encounter.nowhere_to_run"]);
+		writeString(11, 21, "Nowhere to run");
 		delaySeconds(2);
 		break;
 
 	case SURROUNDED:
 		clearLines(20, 24);
-		writeString(5, 21, STRING["dialogs.encounter.surround"]);
+		writeString(5, 21, "The monsters surround you!");
 		delaySeconds(2);
 		break;
 
 	case SURRENDER_FAILED:
 		clearLines(20, 24);
-		writeString(2, 21, STRING["dialogs.encounter.surrender_failed"]);
+		writeString(2, 21, "The monsters don't take prisoners!");
 		delaySeconds(2);
 		break;
 
 	case NO_RESPONSE:
 		clearLines(20, 24);
-		writeString(12, 21, STRING["dialogs.encounter.no_response"]);
+		writeString(12, 21, "No response");
 		delaySeconds(2);
 		break;
 
@@ -101,19 +101,19 @@ void Encounter::draw() {
 		enc._bribeFleeCtr++;
 		enc._bribeAlignmentCtr++;
 		writeString(5, 21, Common::String::format(
-			STRING["dialogs.encounter.give_up"].c_str(),
+			"Give up all your %s (Y/N)?",
 			_bribeTypeStr.c_str()));
 		break;
 
 	case NOT_ENOUGH:
 		clearLines(20, 24);
-		writeString(14, 21, STRING["dialogs.encounter.not_enough"]);
+		writeString(14, 21, "Not enough");
 		delaySeconds(2);
 		break;
 
 	case COMBAT:
 		clearLines(20, 24);
-		writeString(16, 21, STRING["dialogs.encounter.combat"]);
+		writeString(16, 21, "Combat!");
 		delaySeconds(2);
 		break;
 
@@ -138,7 +138,7 @@ void Encounter::draw() {
 			_mode == NOWHERE_TO_RUN || _mode == SURRENDER_FAILED ||
 			_mode == SURPRISED_BY_MONSTERS) {
 		if (enc._alignmentsChanged) {
-			writeString(8, 23, STRING["dialogs.encounter.alignment_slips"]);
+			writeString(8, 23, "*** Alignment slips ***");
 			Sound::sound(SOUND_2);
 		}
 
@@ -284,13 +284,13 @@ void Encounter::bribe() {
 		int val = getRandomNumber(100);
 		if (val < 6) {
 			_bribeType = BRIBE_GEMS;
-			_bribeTypeStr = STRING["dialogs.encounter.gems"];
+			_bribeTypeStr = "gems";
 		} else if (val < 16) {
 			_bribeType = BRIBE_FOOD;
-			_bribeTypeStr = STRING["dialogs.encounter.food"];
+			_bribeTypeStr = "food";
 		} else {
 			_bribeType = BRIBE_GOLD;
-			_bribeTypeStr = STRING["dialogs.encounter.gold"];
+			_bribeTypeStr = "gold";
 		}
 
 		redraw();
diff --git a/engines/mm/mm1/views/game_commands.cpp b/engines/mm/mm1/views/game_commands.cpp
index de8cf0e9a04..0bd4fb03f64 100644
--- a/engines/mm/mm1/views/game_commands.cpp
+++ b/engines/mm/mm1/views/game_commands.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/game_commands.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 
 namespace MM {
@@ -28,8 +29,7 @@ namespace Views {
 
 void GameCommands::draw() {
 	for (int i = 1; i < 17; ++i)
-		writeString(31, i - 1, STRING[Common::String::format(
-			"dialogs.game.commands.%d", i)]);
+		writeString(31, i - 1, GAME_COMMANDS[i - 1]);
 }
 
 } // namespace Views
diff --git a/engines/mm/mm1/views/game_view.cpp b/engines/mm/mm1/views/game_view.cpp
index 579b5af0323..2f70ce0b193 100644
--- a/engines/mm/mm1/views/game_view.cpp
+++ b/engines/mm/mm1/views/game_view.cpp
@@ -61,7 +61,7 @@ void GameView::draw() {
 		drawDialogMessage();
 		_dialogMessage.clear();
 	} else if (_isDark) {
-		writeString(9, 7, STRING["view.darkness"]);
+		writeString(9, 7, "  darkness");
 	}
 
 	if (!_descriptionLine.empty())
diff --git a/engines/mm/mm1/views/interactions/access_code.cpp b/engines/mm/mm1/views/interactions/access_code.cpp
index cbd7f0eb88d..05ca6e59069 100644
--- a/engines/mm/mm1/views/interactions/access_code.cpp
+++ b/engines/mm/mm1/views/interactions/access_code.cpp
@@ -36,7 +36,7 @@ AccessCode::AccessCode() :
 
 void AccessCode::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map08.enter_code"]);
+	writeString(0, 1, "Covered with flashing lights the wall\nspeaks: \"Please enter access code:\"\n");
 	AnswerEntry::draw();
 }
 
diff --git a/engines/mm/mm1/views/interactions/alamar.cpp b/engines/mm/mm1/views/interactions/alamar.cpp
index 9241d99adc7..93d6f158437 100644
--- a/engines/mm/mm1/views/interactions/alamar.cpp
+++ b/engines/mm/mm1/views/interactions/alamar.cpp
@@ -60,22 +60,24 @@ void Alamar::draw() {
 	clearSurface();
 
 	if (_succeeded) {
-		writeString(0, 0, STRING["maps.map49.alamar1"]);
-		writeString(STRING["maps.map49.alamar3"]);
+		writeString(0, 0, "The omnipotent King Alamar speaks,\n\n");
+		writeString("\"My saviors, you're always welcome here!\n"
+			"You should find the Inner Sanctum...\n"
+			"Live long and prosper.\"");
 
 	} else if (map[HAS_EYE]) {
 		send("View", DrawGraphicMessage(65 + 7));
 		send("View", DrawGraphicMessage(65 + 8));
 
-		writeString(0, 0, STRING["maps.map49.alamar1"]);
-		writeString(STRING["maps.map49.alamar4"]);
+		writeString(0, 0, "The omnipotent King Alamar speaks,\n\n");
+		writeString("\"You've discovered my true identity!\nInto the soul maze you go...\"");
 
 		for (int i = 0; i < 6; ++i)
 			Sound::sound(SOUND_2);
 
 	} else {
-		writeString(0, 0, STRING["maps.map49.alamar1"]);
-		writeString(STRING["maps.map49.alamar2"]);
+		writeString(0, 0, "The omnipotent King Alamar speaks,\n\n");
+		writeString("\"Varnlings, I quest thee to find the\nCrypt of Carmenca. Away with you...\"");
 	}
 }
 
diff --git a/engines/mm/mm1/views/interactions/alien.cpp b/engines/mm/mm1/views/interactions/alien.cpp
index dba9d46b08b..fa43627ad74 100644
--- a/engines/mm/mm1/views/interactions/alien.cpp
+++ b/engines/mm/mm1/views/interactions/alien.cpp
@@ -34,7 +34,9 @@ Alien::Alien() : TextView("Alien") {
 
 void Alien::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map31.alien"]);
+	writeString(0, 1, "Scattered remains of a metallic craft!\n"
+		"An alien grunts, \"Eep op oop!\"\n"
+		"Reaction: a)Hostile b)Neutral c)Friendly");
 }
 
 bool Alien::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/interactions/arenko.cpp b/engines/mm/mm1/views/interactions/arenko.cpp
index f64d7302229..b534cb7621a 100644
--- a/engines/mm/mm1/views/interactions/arenko.cpp
+++ b/engines/mm/mm1/views/interactions/arenko.cpp
@@ -39,14 +39,16 @@ bool Arenko::msgFocus(const FocusMessage &msg) {
 	MM1::Maps::Map28 &map = *static_cast<MM1::Maps::Map28 *>(g_maps->_currentMap);
 
 	if (!map.arenkoQuestActive()) {
-		_line = STRING["maps.map28.arenko"];
+		_line = "I am Arenko Guire and this is my grove.\n"
+			"Climb all the trees, without leaving\n"
+			"the area, and return.";
 		map.startArenkoQuest();
 
 	} else if (map[VAL1] < 19) {
-		_line = STRING["maps.map28.keep_climbing"];
+		_line = "Keep climbing.";
 
 	} else {
-		_line = STRING["maps.map28.well_done"];
+		_line = "Well done! What is your pleasure?\na) Gold  b) Gems  c) Item";
 	}
 
 	return true;
diff --git a/engines/mm/mm1/views/interactions/arrested.cpp b/engines/mm/mm1/views/interactions/arrested.cpp
index f669f4c97b5..f296980f41a 100644
--- a/engines/mm/mm1/views/interactions/arrested.cpp
+++ b/engines/mm/mm1/views/interactions/arrested.cpp
@@ -41,7 +41,7 @@ void Arrested::draw() {
 	clearSurface();
 	Sound::sound(SOUND_2);
 
-	writeString(0, 1, STRING["maps.map04.guards"]);
+	writeString(0, 1, "Town guards: you're under arrest!\n\na)Attack b)Bribe c)Run d)Surrender");
 }
 
 bool Arrested::msgKeypress(const KeypressMessage &msg) {
@@ -74,7 +74,7 @@ void Arrested::surrender(int numYears) {
 	// Display sentence
 	clearSurface();
 	Sound::sound(SOUND_2);
-	writeString(0, 1, STRING["maps.map04.sentence"]);
+	writeString(0, 1, "High court sentence: Years= ");
 	writeNumber(numYears);
 
 	delaySeconds(3);
diff --git a/engines/mm/mm1/views/interactions/chess.cpp b/engines/mm/mm1/views/interactions/chess.cpp
index 50022469bce..918dac4936b 100644
--- a/engines/mm/mm1/views/interactions/chess.cpp
+++ b/engines/mm/mm1/views/interactions/chess.cpp
@@ -41,7 +41,13 @@ bool Chess::msgFocus(const FocusMessage &msg) {
 
 void Chess::draw() {
 	clearSurface();
-	writeString(0, 0, STRING["maps.map29.chess"]);
+	writeString(0, 0, "Painted on these grounds is a black &\n"
+		"white checkered motif covered with\n"
+		"idols similar to the ones you carry.\n"
+		"a large being approaches...\n"
+		"Og speaks, \"Queen to queens level 3\"\n"
+		"\n"
+		"Response: ");
 	AnswerEntry::draw();
 }
 
diff --git a/engines/mm/mm1/views/interactions/dog_statue.cpp b/engines/mm/mm1/views/interactions/dog_statue.cpp
index 5388fb7c20f..42b802c237b 100644
--- a/engines/mm/mm1/views/interactions/dog_statue.cpp
+++ b/engines/mm/mm1/views/interactions/dog_statue.cpp
@@ -65,14 +65,19 @@ void DogStatue::draw() {
 	MM1::Maps::Map42 &map = *static_cast<MM1::Maps::Map42 *>(g_maps->_currentMap);
 
 	clearSurface();
-	writeString(0, 0, STRING["maps.map42.statue1"]);
+	writeString(0, 0, "Standing atop a pedestal in the center\n"
+		"of the room is a statue of a small sable\n"
+		"and white dog. ");
 
 	if (_completedQuests) {
-		writeString(0, 2, STRING["maps.map42.statue2"]);
+		writeString(0, 2, "It comes to life and\n"
+			"speaks,\"Congratulations, you've\n"
+			"completed a major quest (+10000 exp)\n"
+			"Remember B.J.!\"");
 		map.dogSuccess();
 
 	} else {
-		writeString(0, 3, STRING["maps.map42.statue3"]);
+		writeString(0, 3, "Desecrate it (Y/N)?");
 	}
 }
 
diff --git a/engines/mm/mm1/views/interactions/ghost.cpp b/engines/mm/mm1/views/interactions/ghost.cpp
index 7981db3f2a2..4ced3f57a42 100644
--- a/engines/mm/mm1/views/interactions/ghost.cpp
+++ b/engines/mm/mm1/views/interactions/ghost.cpp
@@ -35,7 +35,9 @@ Ghost::Ghost() : TextView("Ghost") {
 
 void Ghost::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map37.okrim1"]);
+	writeString(0, 1, "A ghostlike figure rises from the body\n"
+		"of Okrim and speaks,\"In exchange for\n"
+		"my ring, forfeit a life. Accept (Y/N)?\"");
 }
 
 bool Ghost::msgKeypress(const KeypressMessage &msg) {
@@ -46,7 +48,7 @@ bool Ghost::msgKeypress(const KeypressMessage &msg) {
 			g_globals->_party[0]._condition = ERADICATED;
 		} else {
 			clearSurface();
-			writeString(0, 1, STRING["maps.map37.okrim2"]);
+			writeString(0, 1, "\"May the shadow of death greet you\nwith open arms!\"");
 
 			map[MM1::Maps::MAP_29] = 32;
 			map[MM1::Maps::MAP_47] = 8;
diff --git a/engines/mm/mm1/views/interactions/gypsy.cpp b/engines/mm/mm1/views/interactions/gypsy.cpp
index 8ee6a43bdbf..332f568c5cc 100644
--- a/engines/mm/mm1/views/interactions/gypsy.cpp
+++ b/engines/mm/mm1/views/interactions/gypsy.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/interactions/gypsy.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 
 namespace MM {
@@ -74,9 +75,8 @@ void Gypsy::draw() {
 
 	} else {
 		Common::String line = Common::String::format(
-			STRING["maps.map23.your_sign_is"].c_str(),
-			STRING[Common::String::format("colors.%d",
-				_character->_flags[4] & CHARFLAG4_SIGN)].c_str()
+			"\"Your sign is the %s\"",
+			COLOR_NAMES[_character->_flags[4] & CHARFLAG4_SIGN]
 		);
 
 		writeString(0, 1, line);
diff --git a/engines/mm/mm1/views/interactions/hacker.cpp b/engines/mm/mm1/views/interactions/hacker.cpp
index 5cf257dd9b0..a0bf592d1e8 100644
--- a/engines/mm/mm1/views/interactions/hacker.cpp
+++ b/engines/mm/mm1/views/interactions/hacker.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/interactions/hacker.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map36.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -54,14 +55,14 @@ bool Hacker::msgGame(const GameMessage &msg) {
 		if (questNum >= 15 && questNum <= 21)
 			line = map.checkQuestComplete();
 		else
-			line = STRING["maps.map36.hacker4"];
+			line = "\"Sorry, you're already quested.\"";
 
 		g_maps->_mapPos.x--;
 		map.redrawGame();
 
 		Sound::sound2(SOUND_2);
 		send(SoundMessage(
-			0, 1, STRING["maps.map36.hacker1"],
+			0, 1, "Lord Hacker speaks:\n",
 			0, 2, line
 		));
 	}
@@ -71,8 +72,8 @@ bool Hacker::msgGame(const GameMessage &msg) {
 
 void Hacker::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map36.hacker1"]);
-	writeString(0, 2, STRING["maps.map36.hacker2"]);
+	writeString(0, 1, "Lord Hacker speaks:\n");
+	writeString(0, 2, "\"Your services are needed!\"Accept (Y/N)?");
 }
 
 bool Hacker::msgKeypress(const KeypressMessage &msg) {
@@ -84,15 +85,12 @@ bool Hacker::msgKeypress(const KeypressMessage &msg) {
 
 		Character &c = g_globals->_party[0];
 		if (c._quest) {
-			Common::String line = Common::String::format("%s %s",
-				STRING["maps.map36.hacker3"].c_str(),
-				STRING[Common::String::format(
-					"maps.map36.ingredients.%d",
-					g_globals->_party[0]._quest - 15)].c_str()
+			Common::String line = Common::String::format("Bring me %s",
+				INGREDIENTS[g_globals->_party[0]._quest - 15]
 			);
 
 			send(InfoMessage(
-				0, 1, STRING["maps.map36.hacker1"],
+				0, 1, "Lord Hacker speaks:\n",
 				0, 2, line
 			));
 		}
diff --git a/engines/mm/mm1/views/interactions/ice_princess.cpp b/engines/mm/mm1/views/interactions/ice_princess.cpp
index 697be5806ab..a9dc70bf2dd 100644
--- a/engines/mm/mm1/views/interactions/ice_princess.cpp
+++ b/engines/mm/mm1/views/interactions/ice_princess.cpp
@@ -36,7 +36,13 @@ IcePrincess::IcePrincess() :
 
 void IcePrincess::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map19.ice_princess"]);
+	writeString(0, 1, "On a throne adorned with precious gems\n"
+		"the ice princess speaks,\n"
+		"\"Conqueror of worlds, maker of dreams\n"
+		"The greatest force of all, yet elusive\n"
+		"it seems.\"\n"
+		"\n"
+		"Answer:> ");
 	AnswerEntry::draw();
 }
 
diff --git a/engines/mm/mm1/views/interactions/inspectron.cpp b/engines/mm/mm1/views/interactions/inspectron.cpp
index d18251f51e3..89ae3927d03 100644
--- a/engines/mm/mm1/views/interactions/inspectron.cpp
+++ b/engines/mm/mm1/views/interactions/inspectron.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/interactions/inspectron.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map35.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -59,14 +60,14 @@ bool Inspectron::msgGame(const GameMessage &msg) {
 		if (questNum >= 8 && questNum <= 14)
 			line = map.checkQuestComplete();
 		else
-			line = STRING["maps.map35.inspectron4"];
+			line = "\"Sorry, but since you are currently\nquested, i can't engage your services.\"";
 
 		g_maps->_mapPos.y++;
 		map.redrawGame();
 
 		Sound::sound2(SOUND_2);
 		send(SoundMessage(
-			0, 1, STRING["maps.map35.inspectron1"],
+			0, 1, "Lord Inspectron speaks:\n",
 			0, 2, line
 		));
 	}
@@ -76,8 +77,8 @@ bool Inspectron::msgGame(const GameMessage &msg) {
 
 void Inspectron::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map35.inspectron1"]);
-	writeString(0, 2, STRING["maps.map35.inspectron2"]);
+	writeString(0, 1, "Lord Inspectron speaks:\n");
+	writeString(0, 2, "\"Your services are needed!\"Accept (Y/N)?");
 }
 
 bool Inspectron::msgKeypress(const KeypressMessage &msg) {
@@ -88,11 +89,8 @@ bool Inspectron::msgKeypress(const KeypressMessage &msg) {
 		close();
 
 		send(InfoMessage(
-			0, 1, STRING["maps.map35.inspectron1"],
-			0, 2, STRING[Common::String::format(
-				"maps.map35.quests.%d",
-				g_globals->_party[0]._quest - 8
-			)]
+			0, 1, "Lord Inspectron speaks:\n",
+			0, 2, INSPECTRON_QUESTS[g_globals->_party[0]._quest - 8]
 		));
 
 	} else if (msg.keycode == Common::KEYCODE_n) {
diff --git a/engines/mm/mm1/views/interactions/keeper.cpp b/engines/mm/mm1/views/interactions/keeper.cpp
index 2bbac2aaee8..49c6f62b0bd 100644
--- a/engines/mm/mm1/views/interactions/keeper.cpp
+++ b/engines/mm/mm1/views/interactions/keeper.cpp
@@ -49,7 +49,9 @@ void Keeper::draw() {
 	case 0:
 		_bounds = getLineBounds(20, 24);
 		clearSurface();
-		writeString(0, 1, STRING["maps.map54.keeper1"]);
+		writeString(0, 1, "A metallic panel slides open, revealing\n"
+			"a mysterious man in a white coat...\n"
+			"(Press Space to communicate)");
 		break;
 
 	case 1:
@@ -57,11 +59,30 @@ void Keeper::draw() {
 		isWorthy = map.isWorthy(perfTotal);
 
 		clearSurface();
-		writeString(10, 0, STRING["maps.map54.keeper2"]);
+		writeString(10, 0, ":::Inner Sanctum:::");
 		writeString(0, 2, Common::String::format(
-			STRING["maps.map54.keeper3"].c_str(), perfTotal));
-		writeString(0, 3, STRING[isWorthy ? "maps.map54.keeper5" :
-			"maps.map54.keeper4"]);
+			"In a serene voice, the data keeper says,\n"
+				"\"Welcome to your inner sanctum\n"
+				"Varnlings! I am very pleased that you've\n"
+				"made it this far. You are to be\n"
+				"commended. I've been monitoring your\n"
+				"progress.\" Turning to the strange\n"
+				"mechanical device, he inserts a flat\n"
+				"object into a slot. Your party's current\n"
+				"performance total=%u", perfTotal));
+		writeString(0, 3, isWorthy ?
+			"Excellent rating! This is a rare        occasion, for only a privileged few are given the "
+				"opportunity for transfer to\n"
+				"another V.A.R.N. (Vehicular Astropod\n"
+				"Research Nacelle). Return now to the\n"
+				"Inn of Sorpigal for rest and celebration\n"
+				"then continue on to your new assignment at the Gates To Another World...\"       (+50000 "
+				"exp for your accomplishment!)\n"
+				"----------------------------------------\n"
+				"Please send your performance total to\n"
+				"New World Computing, Inc.\n"
+				"  P. O. Box 2068  Van Nuys, CA  91404" :
+			"\"You are not yet worthy for transfer.   Return after defeating the imposter.\"");
 		break;
 
 	default:
diff --git a/engines/mm/mm1/views/interactions/leprechaun.cpp b/engines/mm/mm1/views/interactions/leprechaun.cpp
index 39382a6a34a..d307f2ee474 100644
--- a/engines/mm/mm1/views/interactions/leprechaun.cpp
+++ b/engines/mm/mm1/views/interactions/leprechaun.cpp
@@ -39,7 +39,13 @@ bool Leprechaun::msgFocus(const FocusMessage &msg) {
 
 void Leprechaun::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map00.leprechaun"]);
+	writeString(0, 1, "A tenacious leprechaun appears saying,\n"
+		"\"Traveling the roads is quite dangerous\n"
+		"save for the strong and courageous,\n"
+		"only 1 gem you lose and i'll send you\n"
+		"to the town you choose.\"\n"
+		"\n"
+		"'ESC' to go back      Which town (1-5)?");
 }
 
 bool Leprechaun::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/interactions/lion.cpp b/engines/mm/mm1/views/interactions/lion.cpp
index 3bd7d51174c..b3c4c460750 100644
--- a/engines/mm/mm1/views/interactions/lion.cpp
+++ b/engines/mm/mm1/views/interactions/lion.cpp
@@ -36,7 +36,7 @@ Lion::Lion() : AnswerEntry("Lion", Common::Point(11, 2), 10) {
 
 void Lion::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map32.statue"]);
+	writeString(0, 1, "Statue of a lion growls, \"Password?\"\nResponse:> ..........");
 	AnswerEntry::draw();
 }
 
diff --git a/engines/mm/mm1/views/interactions/lord_archer.cpp b/engines/mm/mm1/views/interactions/lord_archer.cpp
index a0c902f2152..d809f078617 100644
--- a/engines/mm/mm1/views/interactions/lord_archer.cpp
+++ b/engines/mm/mm1/views/interactions/lord_archer.cpp
@@ -40,7 +40,9 @@ bool LordArcher::msgFocus(const FocusMessage &msg) {
 
 void LordArcher::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map40.archer"]);
+	writeString(0, 1, "\"I am Lord Archer, the Raven,\n"
+		"I take from those who have and give to\n"
+		"those who have not! Submit (Y/N)?\"");
 }
 
 bool LordArcher::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/interactions/lord_ironfist.cpp b/engines/mm/mm1/views/interactions/lord_ironfist.cpp
index 427f07cb957..80abd947815 100644
--- a/engines/mm/mm1/views/interactions/lord_ironfist.cpp
+++ b/engines/mm/mm1/views/interactions/lord_ironfist.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/interactions/lord_ironfist.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map43.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -47,8 +48,8 @@ void LordIronfist::draw() {
 	clearSurface();
 
 	if (_canAccept) {
-		writeString(0, 1, STRING["maps.map43.ironfist1"]);
-		writeString(0, 2, STRING["maps.map43.ironfist2"]);
+		writeString(0, 1, "Lord Ironfist speaks:\n");
+		writeString(0, 2, "\"Your services are needed!\" Accept(Y/N)?");
 
 	} else {
 		int questNum = g_globals->_party[0]._quest;
@@ -57,14 +58,14 @@ void LordIronfist::draw() {
 		if (questNum < 8)
 			line = map.checkQuestComplete();
 		else
-			line = STRING["maps.map43.ironfist4"];
+			line = "\"Sorry, but since you are currently\nquested, i can't engage your services.\"";
 
 		g_maps->_mapPos.x++;
 		map.redrawGame();
 
 		clearSurface();
 		send(SoundMessage(
-			0, 1, STRING["maps.map43.ironfist1"],
+			0, 1, "Lord Ironfist speaks:\n",
 			0, 2, line
 		));
 		close();
@@ -80,10 +81,8 @@ bool LordIronfist::msgKeypress(const KeypressMessage &msg) {
 			map.acceptQuest();
 
 			send(InfoMessage(
-				0, 1, STRING["maps.map43.ironfist1"],
-				0, 2, STRING[Common::String::format(
-					"maps.map43.quests.%d",
-					g_globals->_party[0]._quest)]
+				0, 1, "Lord Ironfist speaks:\n",
+				0, 2, IRONFIST_QUESTS[g_globals->_party[0]._quest - 1]
 			));
 
 		} else if (msg.keycode == Common::KEYCODE_n) {
diff --git a/engines/mm/mm1/views/interactions/orango.cpp b/engines/mm/mm1/views/interactions/orango.cpp
index fb6946c306a..9da9af40dc5 100644
--- a/engines/mm/mm1/views/interactions/orango.cpp
+++ b/engines/mm/mm1/views/interactions/orango.cpp
@@ -36,7 +36,13 @@ Orango::Orango() :
 
 void Orango::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map48.orango1"]);
+	writeString(0, 1, "A strange alien being in a shimmering\n"
+		"silver jumpsuit proclaims, \"This is a\n"
+		"soul maze and you are its prisoner!\n"
+		"To escape, you must find your captor's\n"
+		"name hidden within these walls.\"\n"
+		"\n"
+		"answer:>");
 	AnswerEntry::draw();
 }
 
diff --git a/engines/mm/mm1/views/interactions/prisoners.cpp b/engines/mm/mm1/views/interactions/prisoners.cpp
index 3d7b30eed17..59e0646a14c 100644
--- a/engines/mm/mm1/views/interactions/prisoners.cpp
+++ b/engines/mm/mm1/views/interactions/prisoners.cpp
@@ -39,11 +39,11 @@ Prisoner::Prisoner(const Common::String &name, const Common::String &line1,
 void Prisoner::draw() {
 	clearSurface();
 	writeString(0, 0, _line1);
-	writeString(0, 3, STRING["maps.prisoners.options1"]);
+	writeString(0, 3, "Options:  1) Set the prisoner free.\n");
 	_textPos.x = 10;
-	writeString(STRING["maps.prisoners.options2"]);
+	writeString("2) Torment the prisoner.\n");
 	_textPos.x = 10;
-	writeString(STRING["maps.prisoners.options3"]);
+	writeString("3) Leave without disturbing.");
 }
 
 bool Prisoner::msgFocus(const FocusMessage &msg) {
@@ -62,14 +62,14 @@ bool Prisoner::msgKeypress(const KeypressMessage &msg) {
 	int align;
 	switch (msg.keycode) {
 	case Common::KEYCODE_1:
-		line = STRING["maps.prisoners.flees"];
+		line = "The prisoner flees!";
 		align = _freeAlignment;
 		g_maps->clearSpecial();
 		flee();
 		break;
 
 	case Common::KEYCODE_2:
-		line = STRING["maps.prisoners.cowers"];
+		line = "The prisoner cowers!";
 		align = _leaveAlignment;
 		break;
 
@@ -108,32 +108,36 @@ void Prisoner::timeout() {
 /*------------------------------------------------------------------------*/
 
 ChildPrisoner::ChildPrisoner() :
-		Prisoner("ChildPrisoner", STRING["maps.prisoners.child"],
+		Prisoner("ChildPrisoner", "A small child shivers fearfully as you\napproach.\n\n",
 		CHARFLAG1_4, GOOD, EVIL) {
 }
 
 ManPrisoner::ManPrisoner() :
-	Prisoner("ManPrisoner", STRING["maps.prisoners.man"],
+	Prisoner("ManPrisoner", "Man in shackles moans in agony!\n\n",
 	CHARFLAG1_20, EVIL, GOOD) {
 }
 
 CloakedPrisoner::CloakedPrisoner() :
-	Prisoner("CloakedPrisoner", STRING["maps.prisoners.cloaked"],
+	Prisoner("CloakedPrisoner", "A mysterious cloaked figure, bound and\n"
+		"gagged, lies motionless in the corner.\n"
+		"\n",
 		CHARFLAG1_40, EVIL, GOOD) {
 }
 
 DemonPrisoner::DemonPrisoner() :
-	Prisoner("DemonPrisoner", STRING["maps.prisoners.demon"],
+	Prisoner("DemonPrisoner", "A vicious demon lunges at the party,\n"
+		"restrained only by a collar and chain!\n"
+		"\n",
 		CHARFLAG1_10, EVIL, GOOD) {
 }
 
 MutatedPrisoner::MutatedPrisoner() :
-	Prisoner("MutatedPrisoner", STRING["maps.prisoners.mutated"],
+	Prisoner("MutatedPrisoner", "Mutated creature chained to the floor\ngrunts as you approach.\n\n",
 		CHARFLAG1_2, GOOD, EVIL) {
 }
 
 MaidenPrisoner::MaidenPrisoner() :
-	Prisoner("MaidenPrisoner", STRING["maps.prisoners.maiden"],
+	Prisoner("MaidenPrisoner", "Inside a steel cage,a fair maiden weeps.\n\n",
 		CHARFLAG1_8, GOOD, EVIL) {
 }
 
@@ -150,14 +154,14 @@ VirginPrisoner::VirginPrisoner() : TextView("VirginPrisoner") {
 
 void VirginPrisoner::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map11.virgin"]);
+	writeString(0, 1, "A luscious virgin shackled to the wall!\na) Kiss her  b) Release her  c) Leave");
 }
 
 bool VirginPrisoner::msgKeypress(const KeypressMessage &msg) {
 	switch (msg.keycode) {
 	case Common::KEYCODE_a:
 		g_events->close();
-		g_events->send(SoundMessage(STRING["maps.map11.tip1"]));
+		g_events->send(SoundMessage("Try setting 1B,2J"));
 		break;
 	case Common::KEYCODE_b:
 		g_events->close();
diff --git a/engines/mm/mm1/views/interactions/resistances.cpp b/engines/mm/mm1/views/interactions/resistances.cpp
index 8d3d43f3220..fbda039c804 100644
--- a/engines/mm/mm1/views/interactions/resistances.cpp
+++ b/engines/mm/mm1/views/interactions/resistances.cpp
@@ -52,10 +52,10 @@ void Resistances::draw() {
 
 	} else {
 		Character &c = *g_globals->_currCharacter;
-		writeString(0, 1, STRING["maps.map02.resistances1"]);
+		writeString(0, 1, "% resistance for ");
 		writeString(c._name);
 		newLine();
-		writeString(0, 1, STRING["maps.map02.resistances2"]);
+		writeString(0, 1, "Magic     Fire     Cold       Elec\nAcid      Fear     Poison     Sleep");
 
 		writeNumber(6, 1, c._resistances._s._magic);
 		writeNumber(15, 1, c._resistances._s._fire);
diff --git a/engines/mm/mm1/views/interactions/ruby.cpp b/engines/mm/mm1/views/interactions/ruby.cpp
index c116e659bb0..44c34e53297 100644
--- a/engines/mm/mm1/views/interactions/ruby.cpp
+++ b/engines/mm/mm1/views/interactions/ruby.cpp
@@ -35,7 +35,13 @@ Ruby::Ruby() : AnswerEntry("Ruby", Common::Point(14, 7), 12) {
 
 void Ruby::draw() {
 	clearSurface();
-	writeString(0, 1, STRING["maps.map39.ruby1"]);
+	writeString(0, 1, "In the center of the room, a huge ruby\n"
+		"stands atop a pedestal. hexagonally\n"
+		"shaped, it glows rhythmically!\n"
+		"Mesmerized by its light, you hear:\n"
+		"Glass that glitters, rubies that glow\n"
+		"When i twinkle, I cast a rainbow\n"
+		"What am i? :> ...............");
 	AnswerEntry::draw();
 }
 
diff --git a/engines/mm/mm1/views/interactions/trivia.cpp b/engines/mm/mm1/views/interactions/trivia.cpp
index 857618d3399..f4f98d7d543 100644
--- a/engines/mm/mm1/views/interactions/trivia.cpp
+++ b/engines/mm/mm1/views/interactions/trivia.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/interactions/trivia.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map21.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -36,10 +37,8 @@ Trivia::Trivia() :
 
 bool Trivia::msgGame(const GameMessage &msg) {
 	if (msg._name == "TRIVIA") {
-		_question = STRING[Common::String::format(
-			"maps.map21.questions.%d", msg._value)];
-		_correctAnswer = STRING[Common::String::format(
-			"maps.map21.answers.%d", msg._value)];
+		_question = TRIVIA_QUESTIONS[msg._value];
+		_correctAnswer = TRIVIA_ANSWERS[msg._value];
 
 		open();
 		return true;
@@ -51,7 +50,7 @@ bool Trivia::msgGame(const GameMessage &msg) {
 void Trivia::draw() {
 	clearSurface();
 	writeString(0, 1, _question);
-	writeString(0, 7, STRING["maps.map21.answer"]);
+	writeString(0, 7, "Answer:> ..............");
 	AnswerEntry::draw();
 }
 
@@ -60,7 +59,7 @@ void Trivia::answerEntered() {
 
 	if (_answer.equalsIgnoreCase(_correctAnswer)) {
 		g_maps->clearSpecialAndRememberReward();
-		send(InfoMessage(STRING["maps.map21.correct"]));
+		send(InfoMessage("Correct! +50 gems"));
 		g_globals->_party[0]._gems = MIN(
 			(int)g_globals->_party[0]._gems + 50, 0xffff);
 		Sound::sound(SOUND_3);
@@ -68,7 +67,7 @@ void Trivia::answerEntered() {
 	} else {
 		g_maps->_mapPos.x = 15;
 		g_events->send("Game", GameMessage("UPDATE"));
-		send(InfoMessage(STRING["maps.map21.incorrect"]));
+		send(InfoMessage("Wrong!"));
 	}
 }
 
diff --git a/engines/mm/mm1/views/interactions/volcano_god.cpp b/engines/mm/mm1/views/interactions/volcano_god.cpp
index d5ee0a62f1e..1d6ac574f79 100644
--- a/engines/mm/mm1/views/interactions/volcano_god.cpp
+++ b/engines/mm/mm1/views/interactions/volcano_god.cpp
@@ -38,11 +38,13 @@ void VolcanoGod::draw() {
 
 	switch (_mode) {
 	case CHOOSE_OPTION:
-		writeString(0, 1, STRING["maps.map11.volcano_god"]);
+		writeString(0, 1, "The Volcano God bellows,\n"
+			"\"What do you seek here?\"\n"
+			"a) A challenge  b) A riddle  c) A clue");
 		break;
 
 	case ENTER_RESPONSE:
-		writeString(0, 1, STRING["maps.map11.question"]);
+		writeString(0, 1, "Who was brave yet failed?\n\nanswer:> ..........");
 		AnswerEntry::draw();
 		break;
 
diff --git a/engines/mm/mm1/views/interactions/won_game.cpp b/engines/mm/mm1/views/interactions/won_game.cpp
index 0d721581eb8..5d7b1a6ac62 100644
--- a/engines/mm/mm1/views/interactions/won_game.cpp
+++ b/engines/mm/mm1/views/interactions/won_game.cpp
@@ -36,8 +36,12 @@ WonGame::WonGame() : TextView("WonGame") {
 
 void WonGame::draw() {
 	clearSurface();
-	writeString(0, 0, STRING["maps.map18.gates"]);
-	writeString(0, 1, STRING["maps.map18.congratulations"]);
+	writeString(0, 0, "The Gates to Another World!\n");
+	writeString(0, 1, "Congratulations distinguished \n"
+		"travelers! The gates are now\n"
+		"open to you, in order to pass\n"
+		"through you must first find\n"
+		"Might and Magic Book Two !!!");
 	Sound::sound(SOUND_3);
 }
 
diff --git a/engines/mm/mm1/views/locations/blacksmith.cpp b/engines/mm/mm1/views/locations/blacksmith.cpp
index 97c37cac608..7c29f18b6ca 100644
--- a/engines/mm/mm1/views/locations/blacksmith.cpp
+++ b/engines/mm/mm1/views/locations/blacksmith.cpp
@@ -43,10 +43,10 @@ bool Blacksmith::msgFocus(const FocusMessage &msg) {
 void Blacksmith::draw() {
 	Location::draw();
 
-	writeString(22, 1, STRING["dialogs.blacksmith.a"]);
-	writeString(22, 2, STRING["dialogs.blacksmith.b"]);
-	writeString(22, 3, STRING["dialogs.blacksmith.c"]);
-	writeString(22, 4, STRING["dialogs.blacksmith.d"]);
+	writeString(22, 1, "a) buy weapons");
+	writeString(22, 2, "b) buy armor");
+	writeString(22, 3, "c) buy misc");
+	writeString(22, 4, "d) sell item");
 }
 
 bool Blacksmith::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/locations/blacksmith_buy_armor.cpp b/engines/mm/mm1/views/locations/blacksmith_buy_armor.cpp
index b0e89c8ef83..d2fe3abb49d 100644
--- a/engines/mm/mm1/views/locations/blacksmith_buy_armor.cpp
+++ b/engines/mm/mm1/views/locations/blacksmith_buy_armor.cpp
@@ -32,7 +32,7 @@ namespace Locations {
 
 BlacksmithBuyArmor::BlacksmithBuyArmor() :
 		BlacksmithSubview("BlacksmithBuyArmor") {
-	_modeString = STRING["dialogs.location.buy"];
+	_modeString = "'a'-'f' to buy\n(- = cant use)";
 }
 
 bool BlacksmithBuyArmor::msgFocus(const FocusMessage &msg) {
@@ -47,7 +47,7 @@ bool BlacksmithBuyArmor::msgFocus(const FocusMessage &msg) {
 void BlacksmithBuyArmor::draw() {
 	Location::draw();
 
-	writeString(23, 0, STRING["dialogs.blacksmith.armor"]);
+	writeString(23, 0, "armor      price");
 	drawItems();
 }
 
diff --git a/engines/mm/mm1/views/locations/blacksmith_buy_misc.cpp b/engines/mm/mm1/views/locations/blacksmith_buy_misc.cpp
index c6832939595..fbd25e0c333 100644
--- a/engines/mm/mm1/views/locations/blacksmith_buy_misc.cpp
+++ b/engines/mm/mm1/views/locations/blacksmith_buy_misc.cpp
@@ -32,7 +32,7 @@ namespace Locations {
 
 BlacksmithBuyMisc::BlacksmithBuyMisc() :
 	BlacksmithSubview("BlacksmithBuyMisc") {
-	_modeString = STRING["dialogs.location.buy"];
+	_modeString = "'a'-'f' to buy\n(- = cant use)";
 }
 
 bool BlacksmithBuyMisc::msgFocus(const FocusMessage &msg) {
@@ -47,7 +47,7 @@ bool BlacksmithBuyMisc::msgFocus(const FocusMessage &msg) {
 void BlacksmithBuyMisc::draw() {
 	Location::draw();
 
-	writeString(23, 0, STRING["dialogs.blacksmith.misc"]);
+	writeString(23, 0, "misc       price");
 	drawItems();
 }
 
diff --git a/engines/mm/mm1/views/locations/blacksmith_buy_weapons.cpp b/engines/mm/mm1/views/locations/blacksmith_buy_weapons.cpp
index f77a4a45aeb..23ba9de84a8 100644
--- a/engines/mm/mm1/views/locations/blacksmith_buy_weapons.cpp
+++ b/engines/mm/mm1/views/locations/blacksmith_buy_weapons.cpp
@@ -32,7 +32,7 @@ namespace Locations {
 
 BlacksmithBuyWeapons::BlacksmithBuyWeapons() :
 		BlacksmithSubview("BlacksmithBuyWeapons") {
-	_modeString = STRING["dialogs.location.buy"];
+	_modeString = "'a'-'f' to buy\n(- = cant use)";
 }
 
 bool BlacksmithBuyWeapons::msgFocus(const FocusMessage &msg) {
@@ -47,7 +47,7 @@ bool BlacksmithBuyWeapons::msgFocus(const FocusMessage &msg) {
 void BlacksmithBuyWeapons::draw() {
 	Location::draw();
 
-	writeString(23, 0, STRING["dialogs.blacksmith.weapons"]);
+	writeString(23, 0, "weapons     price");
 	drawItems();
 }
 
diff --git a/engines/mm/mm1/views/locations/blacksmith_sell_item.cpp b/engines/mm/mm1/views/locations/blacksmith_sell_item.cpp
index 5cefc659425..3178bf9b7ca 100644
--- a/engines/mm/mm1/views/locations/blacksmith_sell_item.cpp
+++ b/engines/mm/mm1/views/locations/blacksmith_sell_item.cpp
@@ -32,13 +32,13 @@ namespace Locations {
 
 BlacksmithSellItem::BlacksmithSellItem() :
 	BlacksmithSubview("BlacksmithSellItem") {
-	_modeString = STRING["dialogs.location.sell"];
+	_modeString = "'a'-'f' to sell";
 }
 
 void BlacksmithSellItem::draw() {
 	Location::draw();
 
-	writeString(22, 0, STRING["dialogs.blacksmith.backpack"]);
+	writeString(22, 0, "backpack     price");
 	drawItems();
 }
 
diff --git a/engines/mm/mm1/views/locations/blacksmith_subview.cpp b/engines/mm/mm1/views/locations/blacksmith_subview.cpp
index 903b933d570..5c34052f852 100644
--- a/engines/mm/mm1/views/locations/blacksmith_subview.cpp
+++ b/engines/mm/mm1/views/locations/blacksmith_subview.cpp
@@ -94,7 +94,7 @@ void BlacksmithSubview::selectItem(uint index) {
 		break;
 	default:
 		// Purchased successfully
-		displayMessage(15, STRING["dialogs.blacksmith.thankyou"]);
+		displayMessage(15, "Thank you!");
 		break;
 	}
 }
diff --git a/engines/mm/mm1/views/locations/inn.cpp b/engines/mm/mm1/views/locations/inn.cpp
index 3ce905ac6d9..d939f760918 100644
--- a/engines/mm/mm1/views/locations/inn.cpp
+++ b/engines/mm/mm1/views/locations/inn.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/locations/inn.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 
 namespace MM {
@@ -62,16 +63,15 @@ void Inn::draw() {
 	drawTextBorder();
 
 	writeString(10, 0, Common::String::format(
-		STRING["dialogs.inn.title"].c_str(),
+		"(%c) Inn of ",
 		'0' + g_globals->_startingTown));
-	writeString(STRING[Common::String::format(
-		"stats.towns.%d", g_globals->_startingTown)]);
+	writeString(TOWN_NAMES[g_globals->_startingTown - 1]);
 	escToGoBack();
 
 	if (_charNums.empty()) {
-		writeString(8, 5, STRING["dialogs.misc.no_characters"]);
+		writeString(8, 5, "No available characters");
 	} else {
-		writeString(10, 3, STRING["dialogs.misc.some_characters"]);
+		writeString(10, 3, "Available characters");
 
 		for (uint idx = 0; idx < _charNums.size(); ++idx) {
 			uint charNum = _charNums[idx];
@@ -90,15 +90,15 @@ void Inn::draw() {
 		Common::String range = Common::String::format("'A'-'%c' ",
 			'A' + _charNums.size() - 1);
 		writeString(13, 19, range);
-		writeString(STRING["dialogs.inn.to_view"]);
-		writeString(6, 20, STRING["dialogs.inn.ctrl"]);
+		writeString("to view");
+		writeString(6, 20, "(Ctrl)-");
 		writeString(range);
-		writeString(STRING["dialogs.inn.add_remove"]);
+		writeString("Add/Remove");
 
 		if (!_partyChars.empty())
-			writeString(13, 22, STRING["dialogs.inn.exit"]);
+			writeString(13, 22, "'x' Exit inn");
 		if (_partyChars.size() == 6)
-			writeString(10, 16, STRING["dialogs.inn.full"]);
+			writeString(10, 16, "*** Party is full ***");
 	}
 }
 
diff --git a/engines/mm/mm1/views/locations/location.cpp b/engines/mm/mm1/views/locations/location.cpp
index 467991c8d19..529d3f6ce8e 100644
--- a/engines/mm/mm1/views/locations/location.cpp
+++ b/engines/mm/mm1/views/locations/location.cpp
@@ -33,7 +33,7 @@ namespace Locations {
 
 Location::Location(const Common::String &name) : TextView(name) {
 	_bounds = getLineBounds(17, 24);
-	_modeString = STRING["dialogs.location.gather"];
+	_modeString = "'g' gather gold\n'#' other char";
 }
 
 bool Location::msgGame(const GameMessage &msg) {
@@ -49,12 +49,12 @@ void Location::draw() {
 	clearSurface();
 	writeString(0, 0, g_globals->_currCharacter->_name);
 	newLine();
-	writeString(STRING["dialogs.location.gold"]);
+	writeString("gold= ");
 	writeNumber(g_globals->_currCharacter->_gold);
 	newLine();
 	newLine();
 	writeString(_modeString);
-	writeString(0, 6, STRING["dialogs.misc.go_back"]);
+	writeString(0, 6, "'Esc' to go back");
 }
 
 void Location::displayMessage(int x, const Common::String &msg) {
@@ -81,12 +81,12 @@ bool Location::subtractGold(uint amount) {
 
 void Location::notEnoughGold() {
 	Sound::sound(SOUND_2);
-	displayMessage(STRING["dialogs.misc.not_enough_gold"]);
+	displayMessage("Not enough gold");
 }
 
 void Location::backpackFull() {
 	Sound::sound(SOUND_2);
-	displayMessage(STRING["dialogs.misc.backpack_full"]);
+	displayMessage("Backpack is full");
 }
 
 void Location::changeCharacter(uint index) {
diff --git a/engines/mm/mm1/views/locations/market.cpp b/engines/mm/mm1/views/locations/market.cpp
index c4823ec22e7..166867d1972 100644
--- a/engines/mm/mm1/views/locations/market.cpp
+++ b/engines/mm/mm1/views/locations/market.cpp
@@ -82,10 +82,10 @@ bool Market::msgAction(const ActionMessage &msg) {
 void Market::draw() {
 	clearSurface();
 
-	writeString(0, 0, STRING["dialogs.market.special"]);
+	writeString(0, 0, "Special today,all you can eat! ");
 	writeNumber(_foodCost);
-	writeString(STRING["dialogs.market.gp"]);
-	writeString(10, 2, STRING["dialogs.market.will_you_pay"]);
+	writeString("GP/ea");
+	writeString(10, 2, "Will you pay (Y/N)?");
 }
 
 void Market::buyFood() {
@@ -97,8 +97,8 @@ void Market::buyFood() {
 
 	clearSurface();
 	writeString(10, 2, foodPurchased ?
-		STRING["dialogs.market.thankyou"] :
-		STRING["dialogs.market.no_gold"]
+		"Thank you, come again!" :
+		"No gold, no food!"
 	);
 
 	delaySeconds(3);
diff --git a/engines/mm/mm1/views/locations/statue.cpp b/engines/mm/mm1/views/locations/statue.cpp
index bf089a3b464..aa5b879e5f8 100644
--- a/engines/mm/mm1/views/locations/statue.cpp
+++ b/engines/mm/mm1/views/locations/statue.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/locations/statue.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
@@ -71,11 +72,10 @@ void Statue::draw() {
 		_bounds = getLineBounds(20, 24);
 		clearSurface();
 
-		writeString(0, 1, STRING["dialogs.statues.stone"]);
-		writeString(STRING[Common::String::format(
-			"dialogs.statues.names.%d", _statueNum)]);
+		writeString(0, 1, "On this stone statue of ");
+		writeString(STATUE_NAMES[_statueNum]);
 		newLine();
-		writeString(STRING["dialogs.statues.plaque"]);
+		writeString("A plaque reads...");
 
 		++_pageNum;
 		delaySeconds(PAGE1_SECONDS);
@@ -86,8 +86,7 @@ void Statue::draw() {
 		_bounds = getLineBounds(17, 24);
 		clearSurface();
 
-		writeString(0, 0, STRING[Common::String::format(
-			"dialogs.statues.messages.%d", _statueNum)]);
+		writeString(0, 0, STATUE_MESSAGES[_statueNum]);
 
 		Sound::sound(SOUND_2);
 		++_pageNum;
diff --git a/engines/mm/mm1/views/locations/tavern.cpp b/engines/mm/mm1/views/locations/tavern.cpp
index ccb37b7571a..388b9ef3d7c 100644
--- a/engines/mm/mm1/views/locations/tavern.cpp
+++ b/engines/mm/mm1/views/locations/tavern.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/locations/tavern.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/events.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
@@ -88,55 +89,50 @@ bool Tavern::msgAction(const ActionMessage &msg) {
 void Tavern::draw() {
 	Location::draw();
 
-	writeString(20, 1, STRING["dialogs.tavern.drink"]);
-	writeString(20, 2, STRING["dialogs.tavern.tip"]);
-	writeString(20, 3, STRING["dialogs.tavern.listen"]);
+	writeString(20, 1, "a) Have a drink\n");
+	writeString(20, 2, "b) Tip bartender\n");
+	writeString(20, 3, "c) Listen for rumors");
 }
 
 void Tavern::haveADrink() {
 	if (g_globals->_currCharacter->_condition) {
 		Sound::sound(SOUND_2);
-		displayMessage(STRING["dialogs.tavern.terrible"]);
+		displayMessage("You look terrible, go see the clerics.");
 	} else if (subtractGold(1)) {
 		if (++g_globals->_currCharacter->_numDrinks < 3 ||
 				g_engine->getRandomNumber(10) <
 				g_globals->_currCharacter->_endurance) {
-			displayMessage(STRING["dialogs.tavern.great_stuff"]);
+			displayMessage("Great stuff!");
 		} else {
 			if (!(g_globals->_currCharacter->_condition & BAD_CONDITION))
 				g_globals->_currCharacter->_condition |= POISONED;
 
 			Sound::sound(SOUND_2);
-			displayMessage(13, STRING["dialogs.tavern.you_feel_sick"]);
+			displayMessage(13, "You feel sick!!");
 		}
 	}
 }
 
 void Tavern::tipBartender() {
 	if (g_globals->_currCharacter->_condition) {
-		displayMessage(STRING["dialogs.tavern.go_see_clerics"]);
+		displayMessage("You look terrible, go see the clerics.");
 	} else if (subtractGold(1)) {
 		if (g_globals->_currCharacter->_numDrinks == 0) {
-			displayMessage(STRING["dialogs.tavern.have_a_drink"]);
+			displayMessage("Have a drink, then we'll talk.");
 		} else if (g_engine->getRandomNumber(3) != 3) {
-			displayMessage(STRING["dialogs.tavern.have_another_round"]);
+			displayMessage("Thanks a lot, have another round!");
 		} else {
 			int townNum = g_maps->_currentMap->dataByte(Maps::MAP_ID);
-			displayMessage(STRING[Common::String::format(
-				"dialogs.tavern.tips.%d_%d",
-				townNum, g_globals->_currCharacter->_numDrinks
-			)]);
+			displayMessage(TAVERN_TIPS[townNum - 1][g_globals->_currCharacter->_numDrinks - 1]);
 		}
 	}
 }
 
 void Tavern::listenForRumors() {
-	Common::String msg = STRING["dialogs.tavern.rumors.none"];
+	Common::String msg = "No rumors today.";
 	if (!g_globals->_heardRumor) {
 		g_globals->_heardRumor = true;
-		msg = STRING[Common::String::format(
-			"dialogs.tavern.rumors.%d",
-			g_engine->getRandomNumber(16))];
+		msg = TAVERN_RUMORS[g_engine->getRandomNumber(16)];
 	}
 
 	displayMessage(msg);
diff --git a/engines/mm/mm1/views/locations/temple.cpp b/engines/mm/mm1/views/locations/temple.cpp
index 256999008ba..722ad9f54e4 100644
--- a/engines/mm/mm1/views/locations/temple.cpp
+++ b/engines/mm/mm1/views/locations/temple.cpp
@@ -136,30 +136,30 @@ void Temple::changeCharacter(uint index) {
 void Temple::draw() {
 	Location::draw();
 
-	writeString(21, 0, STRING["dialogs.temple.service_cost"]);
+	writeString(21, 0, "service        cost");
 
-	writeString(18, 2, STRING["dialogs.temple.a"]);
+	writeString(18, 2, "A) Restore health");
 	_textPos.x = 36;
 	if (_healCost)
 		writeNumber(_healCost);
 	else
 		writeString("----");
 
-	writeString(18, 3, STRING["dialogs.temple.b"]);
+	writeString(18, 3, "B) Uncurse items");
 	_textPos.x = 36;
 	if (_uncurseCost)
 		writeNumber(_uncurseCost);
 	else
 		writeString("----");
 
-	writeString(18, 4, STRING["dialogs.temple.c"]);
+	writeString(18, 4, "C) Restore align");
 	_textPos.x = 36;
 	if (_alignmentCost)
 		writeNumber(_alignmentCost);
 	else
 		writeString("----");
 
-	writeString(18, 5, STRING["dialogs.temple.d"]);
+	writeString(18, 5, "D) Make donation");
 	_textPos.x = 36;
 	writeNumber(_donateCost);
 }
@@ -225,9 +225,9 @@ void Temple::donate() {
 				g_globals->_activeSpells._arr[i] = 75;
 
 			Sound::sound(SOUND_3);
-			displayMessage(STRING["dialogs.temple.protected"]);
+			displayMessage("*** Today you shall be protected!");
 		} else {
-			displayMessage(STRING["dialogs.temple.thankyou"]);
+			displayMessage("Thank you!");
 		}
 	}
 }
diff --git a/engines/mm/mm1/views/locations/training.cpp b/engines/mm/mm1/views/locations/training.cpp
index 18e605f9272..b4c3adfca0c 100644
--- a/engines/mm/mm1/views/locations/training.cpp
+++ b/engines/mm/mm1/views/locations/training.cpp
@@ -86,21 +86,21 @@ void Training::checkCharacter() {
 void Training::draw() {
 	Location::draw();
 
-	writeString(18, 1, STRING["dialogs.training.for_level"]);
+	writeString(18, 1, "Training for level ");
 	writeNumber(_currLevel + 1);
 
 	if (_currLevel >= MAX_LEVEL) {
-		writeString(24, 3, STRING["dialogs.training.no_way"]);
+		writeString(24, 3, "No way!");
 
 	} else if (_remainingExp > 0) {
 		writeString(21, 3, Common::String::format(
-			STRING["dialogs.training.need"].c_str(), _remainingExp));
-		writeString(20, 5, STRING["dialogs.training.xp"]);
+			"Need %d more", _remainingExp));
+		writeString(20, 5, "experience points");
 
 	} else {
 		writeString(21, 3, Common::String::format(
-			STRING["dialogs.training.cost"].c_str(), _cost));
-		writeString(18, 5, STRING["dialogs.training.cost"]);
+			"Cost %d gold", _cost));
+		writeString(18, 5, "Cost %d gold");
 	}
 }
 
@@ -151,7 +151,7 @@ void Training::train() {
 	if (c._condition) {
 		Sound::sound(SOUND_3);
 		clearSurface();
-		writeString(8, 5, STRING["dialogs.training.condition"]);
+		writeString(8, 5, "*** Check condition ***");
 		delaySeconds(3);
 
 	} else if (!_canAfford) {
@@ -164,14 +164,14 @@ void Training::train() {
 		Sound::sound(SOUND_2);
 
 		clearSurface();
-		writeString(0, 3, STRING["dialogs.training.congrats"]);
+		writeString(0, 3, "Congratulations! you are now level ");
 		writeNumber(c._level._base);
 
 		writeString(7, 5, Common::String::format(
-			STRING["dialogs.training.hp"].c_str(), lvl._numHP));
+			"You gained %d hit points", lvl._numHP));
 
 		if (lvl._numSpells != 0)
-			writeString(7, 6, STRING["dialogs.training.new_spells"]);
+			writeString(7, 6, "You gained new spells!");
 
 		Sound::sound(SOUND_2);
 		delaySeconds(10);
diff --git a/engines/mm/mm1/views/main_menu.cpp b/engines/mm/mm1/views/main_menu.cpp
index 474acd24d8b..de18e2a93af 100644
--- a/engines/mm/mm1/views/main_menu.cpp
+++ b/engines/mm/mm1/views/main_menu.cpp
@@ -29,16 +29,16 @@ namespace Views {
 
 void MainMenu::draw() {
 	drawTextBorder();
-	writeString(12, 4, STRING["dialogs.main_menu.title1"]);
-	writeString(6, 6, STRING["dialogs.main_menu.title2"]);
-	writeString(15, 9, STRING["dialogs.main_menu.title3"]);
-	writeString(15, 10, STRING["dialogs.main_menu.title4"]);
+	writeString(12, 4, "Might and Magic");
+	writeString(6, 6, "Secret of the Inner Sanctum");
+	writeString(15, 9, "Options");
+	writeString(15, 10, "-------");
 
-	writeString(5, 12, STRING["dialogs.main_menu.option1"]);
-	writeString(5, 14, STRING["dialogs.main_menu.option2"]);
-	writeString(5, 16, STRING["dialogs.main_menu.option3"]);
-	writeString(4, 22, STRING["dialogs.main_menu.copyright1"]);
-	writeString(10, 24, STRING["dialogs.main_menu.copyright2"]);
+	writeString(5, 12, "'C'.......Create New Characters");
+	writeString(5, 14, "'V'.......View All Characters");
+	writeString(5, 16, "'#'.......Go to Town");
+	writeString(4, 22, "Copr. 1986,1987-Jon Van Caneghem");
+	writeString(10, 24, "All Rights Reserved");
 }
 
 bool MainMenu::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/order.cpp b/engines/mm/mm1/views/order.cpp
index 5944ab32b1d..91e4164916b 100644
--- a/engines/mm/mm1/views/order.cpp
+++ b/engines/mm/mm1/views/order.cpp
@@ -89,9 +89,9 @@ bool Order::msgAction(const ActionMessage &msg) {
 
 void Order::draw() {
 	clearSurface();
-	writeString(0, 0, STRING["dialogs.order.title"]);
-	writeString(0, 2, STRING["dialogs.misc.go_back"]);
-	writeString(17, 1, STRING["dialogs.order.old"]);
+	writeString(0, 0, " re-order party  new  1  2  3  4  5  6");
+	writeString(0, 2, "'Esc' to go back");
+	writeString(17, 1, "old");
 
 	_textPos.x = 21;
 	for (uint i = 0; i < _indexes.size(); ++i)
diff --git a/engines/mm/mm1/views/protect.cpp b/engines/mm/mm1/views/protect.cpp
index 881d667be06..3b944750e15 100644
--- a/engines/mm/mm1/views/protect.cpp
+++ b/engines/mm/mm1/views/protect.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/protect.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 
 namespace MM {
@@ -28,7 +29,7 @@ namespace Views {
 
 void Protect::draw() {
 	clearSurface();
-	writeString(STRING["dialogs.protect.title"]);
+	writeString("Protect:  Spells currently active");
 	escToGoBack();
 
 	// Protection spells
@@ -37,15 +38,14 @@ void Protect::draw() {
 	for (int i = 0; i < 7; ++i) {
 		if (g_globals->_activeSpells._arr[i]) {
 			printProtectionFrom();
-			writeString(STRING[Common::String::format(
-				"dialogs.protect.%d", i + 1)]);
+			writeString(PROTECTION_NAMES[i]);
 			printProtectionLevel(i);
 		}
 	}
 
 	// Light spell
 	if (g_globals->_activeSpells._arr[7]) {
-		writeString(STRING["dialogs.protect.8"]);
+		writeString("Light (");
 		writeNumber(g_globals->_activeSpells._arr[7]);
 		writeChar(')');
 		newLine();
@@ -57,8 +57,7 @@ void Protect::draw() {
 			newLine();
 
 		if (g_globals->_activeSpells._arr[i]) {
-			writeString(STRING[Common::String::format(
-				"dialogs.protect.%d", i + 1)]);
+			writeString(PROTECTION_NAMES[i]);
 			newLine();
 		}
 	}
@@ -69,12 +68,12 @@ void Protect::draw() {
 	if (g_globals->_activeSpells._arr[17]) {
 		writeNumber(g_globals->_activeSpells._arr[17]);
 		_textPos.x++;
-		writeString(STRING["dialogs.protect.to_attacks"]);
+		writeString("to attacks");
 	}
 }
 
 void Protect::printProtectionFrom() {
-	writeString(STRING["dialogs.protect.protection"]);
+	writeString("Protection from");
 	_textPos.x = 17;
 }
 
diff --git a/engines/mm/mm1/views/quick_ref.cpp b/engines/mm/mm1/views/quick_ref.cpp
index 2cbcae87076..891f76ab980 100644
--- a/engines/mm/mm1/views/quick_ref.cpp
+++ b/engines/mm/mm1/views/quick_ref.cpp
@@ -38,7 +38,7 @@ bool QuickRef::msgUnfocus(const UnfocusMessage &msg) {
 
 void QuickRef::draw() {
 	clearSurface();
-	writeString(STRING["dialogs.quick_ref.title"]);
+	writeString("#     name        hit pts   spell pts ac");
 
 	// Print list of characters, hit pts, spell pts, and ac
 	size_t partySize = getPartySize();
@@ -79,7 +79,7 @@ void QuickRef::draw() {
 		writeNumber(0, 9 + idx, idx + 1);
 
 		_textPos.x++;
-		writeString(STRING["dialogs.quick_ref.food"]);
+		writeString("food= ");
 		writeNumber(c._food);
 
 		_textPos.x = 12;
@@ -88,7 +88,7 @@ void QuickRef::draw() {
 
 	writeString(12, 21, Common::String::format("F1-F%d ",
 		(int)g_globals->_party.size()));
-	writeString(STRING["dialogs.quick_ref.to_view"]);
+	writeString("to view");
 
 	escToGoBack();
 }
diff --git a/engines/mm/mm1/views/rest.cpp b/engines/mm/mm1/views/rest.cpp
index 77afd761240..0274e897a06 100644
--- a/engines/mm/mm1/views/rest.cpp
+++ b/engines/mm/mm1/views/rest.cpp
@@ -51,7 +51,7 @@ bool Rest::msgFocus(const FocusMessage &msg) {
 void Rest::draw() {
 	if (_mode == CONFIRM) {
 		clearSurface();
-		writeString(0, 0, STRING["dialogs.game.rest.rest_here"]);
+		writeString(0, 0, "Rest here (Y/N)?");
 	}
 }
 
@@ -84,7 +84,7 @@ bool Rest::msgAction(const ActionMessage &msg) {
 void Rest::tooDangerous() {
 	_mode = TOO_DANGEROUS;
 	clearSurface();
-	writeString(2, 0, STRING["dialogs.game.rest.too_dangerous"]);
+	writeString(2, 0, "*** Too dangerous to rest here!");
 	delaySeconds(3);
 }
 
diff --git a/engines/mm/mm1/views/search.cpp b/engines/mm/mm1/views/search.cpp
index 21ada2243e5..c6e3b511174 100644
--- a/engines/mm/mm1/views/search.cpp
+++ b/engines/mm/mm1/views/search.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views/search.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/views/trap.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -43,8 +44,7 @@ bool Search::msgGame(const GameMessage &msg) {
 
 	} else {
 		// Otherwise send an info message to say nothing was found
-		Common::String line = STRING["dialogs.search.search"] +
-			STRING["dialogs.search.nothing"];
+		Common::String line = "Search: nothing";
 		send(InfoMessage(0, 1, line));
 	}
 
@@ -76,17 +76,16 @@ void Search::draw() {
 	switch (_mode) {
 	case INITIAL:
 		Sound::sound(SOUND_2);
-		line = STRING["dialogs.search.search"] +
-			STRING["dialogs.search.you_found"];
+		line = "Search: You found...";
 		writeString(0, 1, line);
 		delaySeconds(2);
 		break;
 
 	case OPTIONS:
-		writeString(1, 1, STRING["dialogs.search.options"]);
-		writeString(20, 1, STRING["dialogs.search.options1"]);
-		writeString(20, 2, STRING["dialogs.search.options2"]);
-		writeString(20, 3, STRING["dialogs.search.options3"]);
+		writeString(1, 1, "Options:");
+		writeString(20, 1, "1) Open It");
+		writeString(20, 2, "2) Find/Remove Trap");
+		writeString(20, 3, "3) Detect Magic/Trap");
 		escToGoBack(0, 3);
 		break;
 
@@ -101,7 +100,7 @@ void Search::draw() {
 
 	case WHO_WILL_TRY: {
 		line = Common::String::format(
-			STRING["dialogs.misc.who_will_try"].c_str(),
+			"Who will try '1'-'%c'",
 			'0' + g_globals->_party.size()
 		);
 		writeString(10, 1, line);
@@ -151,7 +150,7 @@ bool Search::msgKeypress(const KeypressMessage &msg) {
 			if ((g_globals->_currCharacter->_condition &
 				(BAD_CONDITION | DEAD | STONE | ASLEEP)) != 0) {
 				clearSurface();
-				writeString(3, 2, STRING["dialogs.search.check_condition"]);
+				writeString(3, 2, "*** Check condition ***");
 				delaySeconds(4);
 			} else {
 				switch (_optionMode) {
@@ -215,8 +214,7 @@ void Search::timeout() {
 
 		// Show the name of the container type in the game view
 		send("View", HeaderMessage(
-			STRING[Common::String::format("dialogs.search.containers.%d",
-				g_globals->_treasure._container)]
+			SEARCH_CONTAINERS[g_globals->_treasure._container]
 		));
 
 		// Display a graphic for the container type
@@ -307,13 +305,13 @@ void Search::detectMagicTrap2() {
 	if (c._class == PALADIN || c._class == CLERIC) {
 		Sound::sound(SOUND_2);
 		clearSurface();
-		writeString(6, 2, STRING["dialogs.search.bad_class"]);
+		writeString(6, 2, "*** Bad class ***");
 		delaySeconds(4);
 
 	} else if (c._sp == 0) {
 		Sound::sound(SOUND_2);
 		clearSurface();
-		writeString(6, 2, STRING["dialogs.search.no_sp"]);
+		writeString(6, 2, "*** No spell points ***");
 		delaySeconds(4);
 
 	} else {
@@ -324,7 +322,7 @@ void Search::detectMagicTrap2() {
 
 		clearSurface();
 		writeString(5, 1, Common::String::format(
-			STRING["dialogs.search.magic_trap"].c_str(),
+			"Magic (%c)  Trap (%c)",
 			magic, trapped));
 
 		delaySeconds(8);
@@ -355,7 +353,7 @@ void Search::getTreasure() {
 }
 
 void Search::drawTreasure() {
-	writeString(15, 0, STRING["dialogs.search.it_opens"]);
+	writeString(15, 0, "It opens!");
 
 	// Split up the gold across the party
 	uint32 goldPerPerson = g_globals->_treasure.getGold() /
@@ -372,7 +370,7 @@ void Search::drawTreasure() {
 	}
 	
 	writeString(0, 2, Common::String::format(
-		STRING["dialogs.search.each_share"].c_str(),
+		"each share is worth %d gold",
 		goldPerPerson));
 	g_globals->_treasure.setGold(0);
 
@@ -387,7 +385,7 @@ void Search::drawTreasure() {
 		Character &c = g_globals->_party[charNum];
 
 		writeString(0, _lineNum++, Common::String::format(
-			STRING["dialogs.search.found_gems"].c_str(),
+			"%s found %d gems",
 			c._name,
 			gems));
 		c._gems = MIN((int)c._gems + gems, 0xffff);
@@ -417,7 +415,7 @@ void Search::drawItem() {
 
 			// Add line for found item
 			writeString(0, _lineNum++, Common::String::format(
-				STRING["dialogs.search.found_item"].c_str(),
+				"%s found %s",
 				c._name,
 				item->_name.c_str()
 			));
diff --git a/engines/mm/mm1/views/spells/cast_spell.cpp b/engines/mm/mm1/views/spells/cast_spell.cpp
index f019d6e2a73..cdadcac5af1 100644
--- a/engines/mm/mm1/views/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views/spells/cast_spell.cpp
@@ -100,11 +100,11 @@ void CastSpell::draw() {
 		return;
 
 	escToGoBack(0);
-	writeString(7, 0, STRING["dialogs.character.cast_spell"]);
+	writeString(7, 0, "cast spell:  level=");
 	if (_state >= SELECT_NUMBER) {
 		writeChar(' ');
 		writeNumber(_spellLevel);
-		writeString(19, 1, STRING["dialogs.character.number"]);
+		writeString(19, 1, "number=");
 	}
 
 	if (_state > SELECT_NUMBER) {
@@ -130,7 +130,7 @@ void CastSpell::draw() {
 		break;
 
 	case PRESS_ENTER:
-		writeString(24, 4, STRING["spells.enter_to_cast"]);
+		writeString(24, 4, "'Enter' to cast");
 		break;
 
 	case ENDING:
@@ -210,7 +210,7 @@ void CastSpell::performSpell(Character *chr) {
 	c._gems = MAX((int)c._gems - _requiredGems, 0);
 
 	if (!isMagicAllowed()) {
-		spellDone(STRING["spells.magic_doesnt_work"], 5);
+		spellDone("Magic doesn't work here", 5);
 	} else {
 		// Cast the spell
 		switch (Game::SpellsParty::cast(_spellIndex, chr)) {
diff --git a/engines/mm/mm1/views/spells/detect_magic.cpp b/engines/mm/mm1/views/spells/detect_magic.cpp
index 8439869c1ae..fd5332fa256 100644
--- a/engines/mm/mm1/views/spells/detect_magic.cpp
+++ b/engines/mm/mm1/views/spells/detect_magic.cpp
@@ -35,7 +35,7 @@ void DetectMagic::draw() {
 	clearSurface();
 	escToGoBack(0);
 
-	writeString(0, 0, STRING["dialogs.spells.detect_charges"]);
+	writeString(0, 0, "Magic (charges)");
 	_textPos.x = 20;
 	_textPos.y = 0;
 
diff --git a/engines/mm/mm1/views/spells/duplication.cpp b/engines/mm/mm1/views/spells/duplication.cpp
index 4ab1f74e330..92dbe4e485f 100644
--- a/engines/mm/mm1/views/spells/duplication.cpp
+++ b/engines/mm/mm1/views/spells/duplication.cpp
@@ -36,7 +36,7 @@ void Duplication::draw() {
 	clearSurface();
 	escToGoBack(0);
 
-	writeString(10, 0, STRING["dialogs.character.which"]);
+	writeString(10, 0, "which item: 'a'-'f'?");
 }
 
 bool Duplication::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/spells/fly.cpp b/engines/mm/mm1/views/spells/fly.cpp
index 6ee1a20ad6e..01c0d942993 100644
--- a/engines/mm/mm1/views/spells/fly.cpp
+++ b/engines/mm/mm1/views/spells/fly.cpp
@@ -43,16 +43,16 @@ void Fly::draw() {
 	clearSurface();
 	escToGoBack(0);
 
-	writeString(9, 0, STRING["dialogs.spells.fly_to_x"]);
+	writeString(9, 0, "Fly to (A-E): ");
 	writeChar((_mode == SELECT_X) ? '_' : 'A' + _xIndex);
 
 	if (_mode == SELECT_Y || _mode == CAST) {
-		writeString(16, 1, STRING["dialogs.spells.fly_to_y"]);
+		writeString(16, 1, "(1-4): ");
 		writeChar((_mode == SELECT_Y) ? '_' : '1' + _yIndex);
 	}
 
 	if (_mode == CAST) {
-		writeString(24, 3, STRING["spells.enter_to_cast"]);
+		writeString(24, 3, "'Enter' to cast");
 	}
 }
 
diff --git a/engines/mm/mm1/views/spells/location.cpp b/engines/mm/mm1/views/spells/location.cpp
index 571c45c797c..0b472d4c852 100644
--- a/engines/mm/mm1/views/spells/location.cpp
+++ b/engines/mm/mm1/views/spells/location.cpp
@@ -39,29 +39,29 @@ void Location::draw() {
 	clearSurface();
 	escToGoBack(0);
 
-	writeString(0, 0, STRING["dialogs.spells.location_loc"]);
+	writeString(0, 0, "Location: ");
 
 	v = map[Maps::MAP_TYPE];
 	if (v == 0xff) {
-		writeString(STRING["dialogs.spells.location_unknown"]);
+		writeString("Unknown");
 	} else {
 		if (v == 0) {
-			writeString(STRING["dialogs.spells.location_outdoors"]);
+			writeString("Outdoors");
 		} else if (!(v & 0x80)) {
 			writeChar('0' + map[37]);
-			writeString(STRING["dialogs.spells.location_under"]);
+			writeString("0' under");
 		} else if (v == 0xfe) {
-			writeString(STRING["dialogs.spells.location_town"]);
+			writeString("In town");
 		} else {
-			writeString(STRING["dialogs.spells.location_castle"]);
+			writeString("in castle");
 		}
 
-		writeString(21, 0, STRING["dialogs.spells.location_sector"]);
+		writeString(21, 0, "Map sector: ");
 		writeChar(map[Maps::MAP_SECTOR1] & 0x7f);
 		writeChar('-');
 		writeChar(map[Maps::MAP_SECTOR2] & 0x7f);
 
-		writeString(21, 1, STRING["dialogs.spells.location_surface_x"]);
+		writeString(21, 1, "Surface: ");
 		writeString("X=");
 
 		if (map[Maps::MAP_TYPE]) {
@@ -69,7 +69,7 @@ void Location::draw() {
 			writeString(35, 1, "Y=");
 			writeNumber(map[Maps::MAP_SURFACE_Y]);
 
-			writeString(22, 2, STRING["dialogs.spells.location_inside_x"]);
+			writeString(22, 2, "Inside: ");
 			writeString("X=");
 		}
 
@@ -77,7 +77,7 @@ void Location::draw() {
 		writeString(35, 2, "Y=");
 		writeNumber(maps._mapPos.y);
 
-		writeString(22, 3, STRING["dialogs.spells.location_facing"]);
+		writeString(22, 3, "Facing: ");
 		switch (maps._forwardMask) {
 		case Maps::DIRMASK_N:
 			writeChar('N');
diff --git a/engines/mm/mm1/views/spells/recharge_item.cpp b/engines/mm/mm1/views/spells/recharge_item.cpp
index 9fbf13c5bfd..205debbc30f 100644
--- a/engines/mm/mm1/views/spells/recharge_item.cpp
+++ b/engines/mm/mm1/views/spells/recharge_item.cpp
@@ -36,7 +36,7 @@ void RechargeItem::draw() {
 	clearSurface();
 	escToGoBack(0);
 
-	writeString(10, 0, STRING["dialogs.character.which"]);
+	writeString(10, 0, "which item: 'a'-'f'?");
 }
 
 bool RechargeItem::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/spells/spell_view.cpp b/engines/mm/mm1/views/spells/spell_view.cpp
index efdc79a27a8..46e7b319bc3 100644
--- a/engines/mm/mm1/views/spells/spell_view.cpp
+++ b/engines/mm/mm1/views/spells/spell_view.cpp
@@ -32,7 +32,7 @@ namespace Spells {
 void SpellView::spellFailed() {
 	// Spell failed
 	clearSurface();
-	writeString(10, 2, Game::SpellCasting::spellResultMessage(STRING["spells.failed"]));
+	writeString(10, 2, Game::SpellCasting::spellResultMessage("Spell failed"));
 
 	Sound::sound(SOUND_2);
 	delaySeconds(3);
@@ -40,7 +40,7 @@ void SpellView::spellFailed() {
 
 void SpellView::spellDone() {
 	clearSurface();
-	writeString(14, 2, Game::SpellCasting::spellResultMessage(STRING["spells.done"]));
+	writeString(14, 2, Game::SpellCasting::spellResultMessage("Done"));
 	g_globals->_party.updateAC();
 
 	Sound::sound(SOUND_2);
diff --git a/engines/mm/mm1/views/spells/teleport.cpp b/engines/mm/mm1/views/spells/teleport.cpp
index cefac2d2b15..b6b84ceafc2 100644
--- a/engines/mm/mm1/views/spells/teleport.cpp
+++ b/engines/mm/mm1/views/spells/teleport.cpp
@@ -42,16 +42,16 @@ void Teleport::draw() {
 	clearSurface();
 	escToGoBack(0);
 
-	writeString(10, 1, STRING["dialogs.spells.teleport_dir"]);
+	writeString(10, 1, "Direction (N,E,S,W): ");
 	writeChar((_mode == SELECT_DIRECTION) ? '_' : _direction);
 
 	if (_mode != SELECT_DIRECTION) {
-		writeString(11, 2, STRING["dialogs.spells.teleport_squares"]);
+		writeString(11, 2, "# of squares (0-9): ");
 		writeChar((_mode == SELECT_SQUARES) ? '_' : '0' + _squares);
 	}
 
 	if (_mode == CAST)
-		writeString(23, 4, STRING["spells.enter_to_cast"]);
+		writeString(23, 4, "'Enter' to cast");
 }
 
 bool Teleport::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views/text_view.cpp b/engines/mm/mm1/views/text_view.cpp
index cfdaea97992..a1b85b792ea 100644
--- a/engines/mm/mm1/views/text_view.cpp
+++ b/engines/mm/mm1/views/text_view.cpp
@@ -143,7 +143,7 @@ void TextView::escToGoBack(int x, int y) {
 	if (y == -1)
 		y = (_bounds.height() / FONT_SIZE) - 1;
 
-	writeString(x, y, STRING["dialogs.misc.go_back"]);
+	writeString(x, y, "'Esc' to go back");
 }
 
 void TextView::timeout() {
diff --git a/engines/mm/mm1/views/trap.cpp b/engines/mm/mm1/views/trap.cpp
index d7427614757..c2e7c8011e1 100644
--- a/engines/mm/mm1/views/trap.cpp
+++ b/engines/mm/mm1/views/trap.cpp
@@ -27,6 +27,29 @@ namespace MM {
 namespace MM1 {
 namespace Views {
 
+static const char *const TRAP_TEXT[11] = {
+	"Darts!! a swarm of poisonous darts\n"
+		"        fill the air!",
+	"Spikes!! Deadly spikes spring forth!",
+	"Arrows!! A sudden onslaught of poisonous         arrows permeate the party!",
+	"Blades!! Razor sharp blades slice\n"
+		"         through the party!",
+	"Boiling oil!! Streams of boiling oil\n"
+		"              cover the party!",
+	"Gas!! A faint hiss can be heard as\n"
+		"      noxious fumes fill the air!",
+	"Fireball!! A fiery explosion engulfs\n"
+		"           the area!",
+	"Lightning bolt!! Electric currents\n"
+		"                 singe the party!",
+	"Acid!! A fine mist of volatile acid\n"
+		"       sprays the party!",
+	"Ice storm!! Particles of splintered ice\n"
+		"            hail through the air!",
+	"Death ray!! A blinding light sears\n"
+		"            through the party!"
+};
+
 Trap::Trap() : TextView("Trap") {
 	_bounds = getLineBounds(20, 24);
 }
@@ -50,10 +73,9 @@ void Trap::draw() {
 	clearSurface();
 
 	if (_mode == MODE_TRIGGER) {
-		writeString(9, 1, STRING["dialogs.trap.oops"]);
+		writeString(9, 1, "Oops a trap!");
 	} else {
-		writeString(0, 1,
-			STRING[Common::String::format("dialogs.trap.%d", _trapType)]);
+		writeString(0, 1, TRAP_TEXT[_trapType]);
 	}
 }
 
diff --git a/engines/mm/mm1/views/unlock.cpp b/engines/mm/mm1/views/unlock.cpp
index d40d10ffd1d..c9f28d1289e 100644
--- a/engines/mm/mm1/views/unlock.cpp
+++ b/engines/mm/mm1/views/unlock.cpp
@@ -74,7 +74,7 @@ void Unlock::draw() {
 	clearSurface();
 
 	writeString(4, 1, Common::String::format(
-		STRING["dialogs.misc.who_will_try"].c_str(),
+		"Who will try '1'-'%c'",
 		'0' + g_globals->_party.size()));
 	escToGoBack(0, 3);
 }
@@ -115,11 +115,11 @@ void Unlock::charSelected(uint charIndex) {
 
 		if (val < c._trapCtr) {
 			g_maps->_currentMap->unlockDoor();
-			send(InfoMessage(11, 1, STRING["dialogs.unlock.success"]));
+			send(InfoMessage(11, 1, "Success!"));
 
 		} else if (getRandomNumber(100) <
 				g_maps->_currentMap->dataByte(Maps::MAP_TRAP_THRESHOLD)) {
-			send(InfoMessage(8, 1, STRING["dialogs.unlock.failed"]));
+			send(InfoMessage(8, 1, "Unlock failed!"));
 
 		} else {
 			g_maps->_currentMap->unlockDoor();


Commit: d36a16fa974060538948ccbe465bba2b9c137477
    https://github.com/scummvm/scummvm/commit/d36a16fa974060538948ccbe465bba2b9c137477
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:10+11:00

Commit Message:
MM: MM1: Replace STRING lookups in enhanced views with literal text

Changed paths:
    engines/mm/mm1/views_enh/active_quest.cpp
    engines/mm/mm1/views_enh/bestiary.cpp
    engines/mm/mm1/views_enh/character_base.cpp
    engines/mm/mm1/views_enh/character_info.cpp
    engines/mm/mm1/views_enh/character_inventory.cpp
    engines/mm/mm1/views_enh/character_manage.cpp
    engines/mm/mm1/views_enh/character_select.cpp
    engines/mm/mm1/views_enh/character_view.cpp
    engines/mm/mm1/views_enh/characters.cpp
    engines/mm/mm1/views_enh/color_questions.cpp
    engines/mm/mm1/views_enh/combat.cpp
    engines/mm/mm1/views_enh/create_characters.cpp
    engines/mm/mm1/views_enh/dead.cpp
    engines/mm/mm1/views_enh/encounter.cpp
    engines/mm/mm1/views_enh/exchange.cpp
    engines/mm/mm1/views_enh/game.cpp
    engines/mm/mm1/views_enh/interactions/access_code.cpp
    engines/mm/mm1/views_enh/interactions/alamar.cpp
    engines/mm/mm1/views_enh/interactions/alien.cpp
    engines/mm/mm1/views_enh/interactions/arenko.cpp
    engines/mm/mm1/views_enh/interactions/arrested.cpp
    engines/mm/mm1/views_enh/interactions/chess.cpp
    engines/mm/mm1/views_enh/interactions/dog_statue.cpp
    engines/mm/mm1/views_enh/interactions/ghost.cpp
    engines/mm/mm1/views_enh/interactions/giant.cpp
    engines/mm/mm1/views_enh/interactions/gypsy.cpp
    engines/mm/mm1/views_enh/interactions/hacker.cpp
    engines/mm/mm1/views_enh/interactions/ice_princess.cpp
    engines/mm/mm1/views_enh/interactions/inspectron.cpp
    engines/mm/mm1/views_enh/interactions/keeper.cpp
    engines/mm/mm1/views_enh/interactions/leprechaun.cpp
    engines/mm/mm1/views_enh/interactions/lion.cpp
    engines/mm/mm1/views_enh/interactions/lord_archer.cpp
    engines/mm/mm1/views_enh/interactions/lord_ironfist.cpp
    engines/mm/mm1/views_enh/interactions/orango.cpp
    engines/mm/mm1/views_enh/interactions/prisoners.cpp
    engines/mm/mm1/views_enh/interactions/resistances.cpp
    engines/mm/mm1/views_enh/interactions/ruby.cpp
    engines/mm/mm1/views_enh/interactions/scummvm.cpp
    engines/mm/mm1/views_enh/interactions/statue.cpp
    engines/mm/mm1/views_enh/interactions/trivia.cpp
    engines/mm/mm1/views_enh/interactions/volcano_god.cpp
    engines/mm/mm1/views_enh/items_view.cpp
    engines/mm/mm1/views_enh/locations/blacksmith.cpp
    engines/mm/mm1/views_enh/locations/blacksmith_items.cpp
    engines/mm/mm1/views_enh/locations/inn.cpp
    engines/mm/mm1/views_enh/locations/location.cpp
    engines/mm/mm1/views_enh/locations/market.cpp
    engines/mm/mm1/views_enh/locations/tavern.cpp
    engines/mm/mm1/views_enh/locations/temple.cpp
    engines/mm/mm1/views_enh/locations/training.cpp
    engines/mm/mm1/views_enh/main_menu.cpp
    engines/mm/mm1/views_enh/map_popup.cpp
    engines/mm/mm1/views_enh/protect.cpp
    engines/mm/mm1/views_enh/quick_ref.cpp
    engines/mm/mm1/views_enh/rest.cpp
    engines/mm/mm1/views_enh/search.cpp
    engines/mm/mm1/views_enh/spells/cast_spell.cpp
    engines/mm/mm1/views_enh/spells/detect_magic.cpp
    engines/mm/mm1/views_enh/spells/duplication.cpp
    engines/mm/mm1/views_enh/spells/fly.cpp
    engines/mm/mm1/views_enh/spells/location.cpp
    engines/mm/mm1/views_enh/spells/recharge_item.cpp
    engines/mm/mm1/views_enh/spells/spellbook.cpp
    engines/mm/mm1/views_enh/spells/teleport.cpp
    engines/mm/mm1/views_enh/town_select.cpp
    engines/mm/mm1/views_enh/trade.cpp
    engines/mm/mm1/views_enh/trap.cpp
    engines/mm/mm1/views_enh/unlock.cpp
    engines/mm/mm1/views_enh/which_character.cpp
    engines/mm/mm1/views_enh/who_will_try.cpp
    engines/mm/mm1/views_enh/won_game.cpp


diff --git a/engines/mm/mm1/views_enh/active_quest.cpp b/engines/mm/mm1/views_enh/active_quest.cpp
index 5096ae53159..6e46727f480 100644
--- a/engines/mm/mm1/views_enh/active_quest.cpp
+++ b/engines/mm/mm1/views_enh/active_quest.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/active_quest.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/maps/map06.h"
 #include "mm/mm1/maps/map17.h"
@@ -28,6 +29,7 @@
 #include "mm/mm1/maps/map29.h"
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/metaengine.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -101,18 +103,14 @@ void setFormalQuestText(Common::String &title, Common::String &details,
 		byte questNum) {
 	if (questNum >= 1 && questNum <= 7) {
 		title = "Lord Ironfist";
-		details = STRING[Common::String::format(
-			"maps.map43.quests.%d", questNum)];
+		details = IRONFIST_QUESTS[questNum - 1];
 	} else if (questNum >= 8 && questNum <= 14) {
 		title = "Lord Inspectron";
-		details = STRING[Common::String::format(
-			"maps.map35.quests.%d", questNum - 8)];
+		details = mapText(INSPECTRON_QUESTS[questNum - 8]);
 	} else if (questNum >= 15 && questNum <= 21) {
 		title = "Lord Hacker";
-		details = Common::String::format("%s %s",
-			STRING["maps.map36.hacker3"].c_str(),
-			STRING[Common::String::format("maps.map36.ingredients.%d",
-				questNum - 15)].c_str());
+		details = Common::String::format("Bring me %s",
+			INGREDIENTS[questNum - 15]);
 	}
 }
 
@@ -524,7 +522,7 @@ void ActiveQuest::draw() {
 	ScrollView::draw();
 	setReduced(false);
 	writeLine(0, "Quest Log", ALIGN_MIDDLE);
-	writeString(152, 172, STRING["enhdialogs.misc.go_back"]);
+	writeString(152, 172, "Go back");
 
 	Common::Array<QuestLogEntry> entries;
 	buildQuestList(entries);
diff --git a/engines/mm/mm1/views_enh/bestiary.cpp b/engines/mm/mm1/views_enh/bestiary.cpp
index 03eeac876bf..b370144795b 100644
--- a/engines/mm/mm1/views_enh/bestiary.cpp
+++ b/engines/mm/mm1/views_enh/bestiary.cpp
@@ -98,7 +98,7 @@ void Bestiary::draw() {
 	writeString(262, 172, "Next");
 
 	setTextColor(0);
-	writeString(152, 172, STRING["enhdialogs.misc.go_back"]);
+	writeString(152, 172, "Go back");
 }
 
 void Bestiary::select(int index) {
diff --git a/engines/mm/mm1/views_enh/character_base.cpp b/engines/mm/mm1/views_enh/character_base.cpp
index eed6178533a..80742bf8ba0 100644
--- a/engines/mm/mm1/views_enh/character_base.cpp
+++ b/engines/mm/mm1/views_enh/character_base.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/character_base.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/utils/strings.h"
 #include "mm/mm1/globals.h"
 
@@ -40,45 +41,45 @@ void CharacterBase::printStats() {
 	const Character &c = *g_globals->_currCharacter;
 	printSummary();
 
-	writeLine(4, STRING["stats.attributes.int"], ALIGN_RIGHT, COL1_X);
+	writeLine(4, "Int=", ALIGN_RIGHT, COL1_X);
 	writeLine(4, Common::String::format("%u", c._intelligence._base), ALIGN_LEFT, COL1_X);
-	writeLine(5, STRING["stats.attributes.mgt"], ALIGN_RIGHT, COL1_X);
+	writeLine(5, "Mgt=", ALIGN_RIGHT, COL1_X);
 	writeLine(5, Common::String::format("%u", c._might._base), ALIGN_LEFT, COL1_X);
-	writeLine(6, STRING["stats.attributes.per"], ALIGN_RIGHT, COL1_X);
+	writeLine(6, "Per=", ALIGN_RIGHT, COL1_X);
 	writeLine(6, Common::String::format("%u", c._personality._base), ALIGN_LEFT, COL1_X);
-	writeLine(7, STRING["stats.attributes.end"], ALIGN_RIGHT, COL1_X);
+	writeLine(7, "End=", ALIGN_RIGHT, COL1_X);
 	writeLine(7, Common::String::format("%u", c._endurance._base), ALIGN_LEFT, COL1_X);
-	writeLine(8, STRING["stats.attributes.spd"], ALIGN_RIGHT, COL1_X);
+	writeLine(8, "Spd=", ALIGN_RIGHT, COL1_X);
 	writeLine(8, Common::String::format("%u", c._speed._base), ALIGN_LEFT, COL1_X);
-	writeLine(9, STRING["stats.attributes.acy"], ALIGN_RIGHT, COL1_X);
+	writeLine(9, "Acy=", ALIGN_RIGHT, COL1_X);
 	writeLine(9, Common::String::format("%u", c._accuracy._base), ALIGN_LEFT, COL1_X);
-	writeLine(10, STRING["stats.attributes.luc"], ALIGN_RIGHT, COL1_X);
+	writeLine(10, "Luc=", ALIGN_RIGHT, COL1_X);
 	writeLine(10, Common::String::format("%u", c._luck._base), ALIGN_LEFT, COL1_X);
 
-	writeLine(4, STRING["stats.attributes.level"], ALIGN_RIGHT, 90);
+	writeLine(4, "Level=", ALIGN_RIGHT, 90);
 	writeNumber(c._level);
-	writeLine(4, STRING["stats.attributes.age"], ALIGN_LEFT, 120);
+	writeLine(4, "Age=", ALIGN_LEFT, 120);
 	writeNumber(c._age);
 
-	writeLine(6, STRING["stats.attributes.sp"], ALIGN_RIGHT, 90);
+	writeLine(6, "SP=", ALIGN_RIGHT, 90);
 	writeLine(6, Common::String::format("%u", c._sp._current), ALIGN_LEFT, 90);
 	writeLine(6, Common::String::format("/%u", c._sp._base), ALIGN_LEFT, 120);
 	writeLine(6, Common::String::format("(%u)", c._spellLevel._current), ALIGN_LEFT, 160);
 
-	writeLine(8, STRING["stats.attributes.hp"], ALIGN_RIGHT, 90);
+	writeLine(8, "HP=", ALIGN_RIGHT, 90);
 	writeLine(8, Common::String::format("%u", c._hpCurrent), ALIGN_LEFT, 90);
 	writeLine(8, Common::String::format("/%u", c._hpMax), ALIGN_LEFT, 120);
 
-	writeLine(10, STRING["stats.attributes.ac"], ALIGN_RIGHT, 90);
+	writeLine(10, "AC=", ALIGN_RIGHT, 90);
 	writeLine(10, Common::String::format("%u", c._ac._current), ALIGN_LEFT, 90);
 
-	writeLine(4, STRING["stats.attributes.exp"], ALIGN_RIGHT, COL3_X);
+	writeLine(4, "Exp=", ALIGN_RIGHT, COL3_X);
 	writeLine(4, Common::String::format("%u", c._exp), ALIGN_LEFT, COL3_X);
-	writeLine(6, STRING["stats.attributes.gems"], ALIGN_RIGHT, COL3_X);
+	writeLine(6, "Gems=", ALIGN_RIGHT, COL3_X);
 	writeLine(6, Common::String::format("%u", c._gems), ALIGN_LEFT, COL3_X);
-	writeLine(8, STRING["stats.attributes.gold"], ALIGN_RIGHT, COL3_X);
+	writeLine(8, "Gold=", ALIGN_RIGHT, COL3_X);
 	writeLine(8, Common::String::format("%u", c._gold), ALIGN_LEFT, COL3_X);
-	writeLine(10, STRING["stats.attributes.food"], ALIGN_RIGHT, COL3_X);
+	writeLine(10, "Food=", ALIGN_RIGHT, COL3_X);
 	writeLine(10, Common::String::format("%u", c._food), ALIGN_LEFT, COL3_X);
 
 	printCondition();
@@ -88,22 +89,22 @@ void CharacterBase::printStats() {
 void CharacterBase::printSummary() {
 	const Character &c = *g_globals->_currCharacter;
 	Common::String suffix = Common::String::format(": %s %s ",
-		STRING[Common::String::format("stats.sex.%d", c._sex)].c_str(),
+		SEX_NAMES[c._sex - 1],
 		((c._alignment >= GOOD && c._alignment <= EVIL) ?
-		STRING[Common::String::format("stats.alignments.%d", c._alignment)] :
-		STRING["stats.none"]).c_str()
+		ALIGNMENT_NAMES[c._alignment - 1] :
+		"None")
 	);
 
 	if (c._race >= HUMAN && c._race <= HALF_ORC)
-		suffix += STRING[Common::String::format("stats.races.%d", c._race)];
+		suffix += RACE_NAMES[c._race - 1];
 	else
-		suffix += STRING["stats.none"];
+		suffix += "None";
 	suffix += ' ';
 
 	if (c._class >= KNIGHT && c._class <= ROBBER)
-		suffix += STRING[Common::String::format("stats.classes.%d", c._class)];
+		suffix += CLASS_NAMES[c._class - 1];
 	else
-		suffix += STRING["stats.none"];
+		suffix += "None";
 
 	writeString(35, LINE1_Y, truncateString(c._name,
 		_innerBounds.width() - 35 - (int)getStringWidth(suffix)));
@@ -113,13 +114,13 @@ void CharacterBase::printSummary() {
 void CharacterBase::printCondition() {
 	const Character &c = *g_globals->_currCharacter;
 
-	writeLine(2, STRING["stats.attributes.cond"], ALIGN_RIGHT, 90);
+	writeLine(2, "Cond=", ALIGN_RIGHT, 90);
 	writeLine(2, c.getConditionString(), ALIGN_LEFT, 90);
 }
 
 void CharacterBase::printInventory() {
 	const Character &c = *g_globals->_currCharacter;
-	writeString(0, BACKPACK_Y, STRING["stats.inventory"]);
+	writeString(0, BACKPACK_Y, "-----<Equipped>----------<Backpack>----");
 	for (int i = 0; i < 5; ++i)
 		writeChar('-');
 
diff --git a/engines/mm/mm1/views_enh/character_info.cpp b/engines/mm/mm1/views_enh/character_info.cpp
index 69e6679fcb7..70e0156e340 100644
--- a/engines/mm/mm1/views_enh/character_info.cpp
+++ b/engines/mm/mm1/views_enh/character_info.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/character_info.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/views_enh/scroll_popup.h"
 #include "mm/shared/utils/strings.h"
 #include "mm/mm1/globals.h"
@@ -71,17 +72,16 @@ CharacterInfo::CharacterInfo() : PartyView("CharacterInfo") {
 	_bounds = Common::Rect(0, 0, 320, 146);
 	_statInfo.setReduced(true);
 
-	static const char *FIELDS[CHAR_ICONS_COUNT] = {
-		"might", "intelligence", "personality",
-		"endurance", "speed",
-		"accuracy", "luck", "age", "level", "ac",
-		"hp", "sp", "spells",
-		"experience", "gold", "gems", "food", "condition"
+	static const char *const FIELDS[CHAR_ICONS_COUNT] = {
+		"Mgt", "Int", "Per",
+		"End", "Spd",
+		"Acy", "Luc", "Age", "Lvl", "AC",
+		"H.P.", "S.P.", "Spells",
+		"Experience", "Gold", "Gems", "Food", "Condition"
 	};
 
 	for (int i = 0; i < ICONS_COUNT; ++i) {
-		ICONS_TEXT[i] = STRING[Common::String::format(
-			"enhdialogs.character.stats.%s", FIELDS[i])];
+		ICONS_TEXT[i] = FIELDS[i];
 	}
 }
 
@@ -230,9 +230,9 @@ void CharacterInfo::drawTitle() {
 	Common::String suffix = Common::String::format(
 		" : %s %s %s %s",
 		sex,
-		STRING[Common::String::format("stats.alignments.%d", (int)c._alignment)].c_str(),
-		STRING[Common::String::format("stats.races.%d", (int)c._race)].c_str(),
-		STRING[Common::String::format("stats.classes.%d", (int)c._class)].c_str()
+		ALIGNMENT_NAMES[(int)c._alignment - 1],
+		RACE_NAMES[(int)c._race - 1],
+		CLASS_NAMES[(int)c._class - 1]
 	);
 	Common::String name = truncateString(camelCase(c._name),
 		269 - (int)getStringWidth(suffix));
@@ -254,10 +254,10 @@ void CharacterInfo::drawIcons() {
 	}
 
 	// Text for buttons
-	writeString(277, 25, STRING["enhdialogs.character.item"]);
-	writeString(273, 57, STRING["enhdialogs.character.quick"]);
-	writeString(276, 90, STRING["enhdialogs.character.exchange"]);
-	writeString(278, 122, STRING["enhdialogs.misc.exit"]);
+	writeString(277, 25, "\x01""37Item");
+	writeString(273, 57, "\x01""37Quick");
+	writeString(276, 90, "\x01""37Exch");
+	writeString(278, 122, "Exit");
 }
 
 void CharacterInfo::drawStats() {
@@ -298,8 +298,7 @@ void CharacterInfo::drawStats() {
 			// Food
 			Common::String str = Common::String::format("%d %s",
 				c._food,
-				STRING[c._food == 1 ? "enhdialogs.character.stats.day" :
-					"enhdialogs.character.stats.days"].c_str());
+				c._food == 1 ? "Day" : "Days");
 			setTextColor(15);
 			writeString(pt.x, pt.y, str);
 		} else {
@@ -447,23 +446,22 @@ void CharacterInfo::showAttribute(int attrNum) {
 		c._luck._base, c._age, c._level._base,
 		c._ac._current, c._hp, c._sp._base
 	};
-	const char *TITLES[12] = {
-		"might", "intelligence", "personality",
-		"endurance", "speed", "accuracy", "luck",
-		"age", "level", "ac", "hp", "sp"
+	const char *const TITLES[12] = {
+		"Might", "Intelligence", "Personality", "Endurance",
+		"Speed", "Accuracy", "Luck", "Age",
+		"Level", "Armor Class", "Hit Points", "Spell Points"
 	};
 
 	if (attrib < 12) {
 		_statInfo.addLine(
-			STRING[Common::String::format("enhdialogs.character.long.%s",
-				TITLES[attrib]).c_str()],
+			TITLES[attrib],
 			ALIGN_MIDDLE);
 
 		int xc = (bounds.width() - FRAME_BORDER_SIZE * 2) / 2;
-		_statInfo.addText(STRING["enhdialogs.character.long.current"],
+		_statInfo.addText("Current",
 			1, 0, ALIGN_RIGHT, xc - 8);
 		_statInfo.addText("/", 1, 0, ALIGN_MIDDLE);
-		_statInfo.addText(STRING["enhdialogs.character.long.base"],
+		_statInfo.addText("Base",
 			1, 0, ALIGN_LEFT, xc + 8);
 
 		_statInfo.addText(Common::String::format("%u", CURR[attrib]),
@@ -474,7 +472,7 @@ void CharacterInfo::showAttribute(int attrNum) {
 
 	} else if (attrib == 15) {
 		// Experience
-		_statInfo.addLine(STRING["enhdialogs.character.stats.experience"], ALIGN_MIDDLE);
+		_statInfo.addLine("Experience", ALIGN_MIDDLE);
 		_statInfo.addLine(Common::String::format("%u", c._exp), ALIGN_MIDDLE);
 
 	} else if (attrib >= 16 && attrib <= 18) {
@@ -482,24 +480,24 @@ void CharacterInfo::showAttribute(int attrNum) {
 		_statInfo.setBounds(bounds);
 
 		uint value = c._gold;
-		Common::String title = STRING["enhdialogs.character.stats.gold"];
+		Common::String title = "Gold";
 		if (attrib == 17) {
 			value = c._gems;
-			title = STRING["enhdialogs.character.stats.gems"];
+			title = "Gems";
 		} else if (attrib == 18) {
 			value = c._food;
-			title = STRING["enhdialogs.character.stats.food"];
+			title = "Food";
 		}
 
 		_statInfo.addLine(title, ALIGN_MIDDLE);
-		_statInfo.addLine(Common::String::format("%u %s",
-			value, STRING["enhdialogs.character.long.on_hand"].c_str()),
+		_statInfo.addLine(Common::String::format("%u On Hand",
+			value),
 			ALIGN_MIDDLE);
 	} else {
 		// Condition
 		Common::String conditionStr = c.getConditionString();
 
-		_statInfo.addLine(STRING["enhdialogs.character.stats.condition"], ALIGN_MIDDLE);
+		_statInfo.addLine("Condition", ALIGN_MIDDLE);
 		_statInfo.addLine(conditionStr, ALIGN_MIDDLE);
 	}
 
diff --git a/engines/mm/mm1/views_enh/character_inventory.cpp b/engines/mm/mm1/views_enh/character_inventory.cpp
index 5774c3ecd3c..135c6927dd6 100644
--- a/engines/mm/mm1/views_enh/character_inventory.cpp
+++ b/engines/mm/mm1/views_enh/character_inventory.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/character_inventory.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/views_enh/combat.h"
 #include "mm/mm1/views_enh/game_messages.h"
 #include "mm/mm1/views_enh/trade.h"
@@ -51,14 +52,14 @@ CharacterInventory::CharacterInventory(const Common::String &name) : ItemsView(n
 
 void CharacterInventory::setup() {
 	_btnSprites.load("items.icn");
-	addButton(2, STRING["enhdialogs.items.buttons.arms"], Common::KEYCODE_a);
-	addButton(6, STRING["enhdialogs.items.buttons.backpack"], Common::KEYCODE_b);
-	addButton(8, STRING["enhdialogs.items.buttons.equip"], Common::KEYCODE_e);
-	addButton(10, STRING["enhdialogs.items.buttons.remove"], Common::KEYCODE_r);
-	addButton(12, STRING["enhdialogs.items.buttons.discard"], Common::KEYCODE_d);
-	addButton(6, STRING["enhdialogs.items.buttons.trade"], Common::KEYCODE_t);
-	addButton(14, STRING["enhdialogs.items.buttons.use"], Common::KEYCODE_u);
-	addButton(16, STRING["enhdialogs.misc.exit"], Common::KEYCODE_ESCAPE);
+	addButton(2, "\x01""37Arms", Common::KEYCODE_a);
+	addButton(6, "\x01""37BkPack", Common::KEYCODE_b);
+	addButton(8, "\x01""37Equip", Common::KEYCODE_e);
+	addButton(10, "\x01""37Rem", Common::KEYCODE_r);
+	addButton(12, "\x01""37Disc", Common::KEYCODE_d);
+	addButton(6, "\x01""37Trade", Common::KEYCODE_t);
+	addButton(14, "\x01""37Use", Common::KEYCODE_u);
+	addButton(16, "Exit", Common::KEYCODE_ESCAPE);
 }
 
 bool CharacterInventory::msgFocus(const FocusMessage &msg) {
@@ -164,13 +165,11 @@ void CharacterInventory::gatherShare(GatherShareButton action) {
 
 void CharacterInventory::drawTitle() {
 	const Character &c = *g_globals->_currCharacter;
-	const Common::String fmt = STRING[(_mode == ARMS_MODE) ?
-		"enhdialogs.items.arms_for" :
-		"enhdialogs.items.backpack_for"];
+	const Common::String fmt = ((_mode == ARMS_MODE) ? "Equipment for %s the %s" : "Backpack for %s the %s");
 
 	const Common::String title = Common::String::format(fmt.c_str(),
 		c._name,
-		STRING[Common::String::format("stats.classes.%d", c._class)].c_str()
+		CLASS_NAMES[c._class - 1]
 	);
 
 	setReduced(false);
@@ -324,19 +323,18 @@ void CharacterInventory::selectButton(SelectedButton btnMode) {
 		performAction();
 
 	} else {
-		Common::String btn = STRING["enhdialogs.items.equip"];
+		Common::String btn = "Equip";
 		if (btnMode == BTN_REMOVE)
-			btn = STRING["enhdialogs.items.remove"];
+			btn = "Remove";
 		else if (btnMode == BTN_DISCARD)
-			btn = STRING["enhdialogs.items.discard"];
+			btn = "Discard";
 		else if (btnMode == BTN_USE)
-			btn = STRING["enhdialogs.items.use"];
+			btn = "Use";
 		else if (btnMode == BTN_CHARGE)
-			btn = STRING["enhdialogs.items.charge"];
+			btn = "Charge";
 
 		send("WhichItem", GameMessage("DISPLAY",
-			Common::String::format("%s %s", btn.c_str(),
-				STRING["enhdialogs.items.which_item"].c_str())
+			Common::String::format("%s which item?", btn.c_str())
 		));
 	}
 }
diff --git a/engines/mm/mm1/views_enh/character_manage.cpp b/engines/mm/mm1/views_enh/character_manage.cpp
index b5849dec6e5..2dc8d93d462 100644
--- a/engines/mm/mm1/views_enh/character_manage.cpp
+++ b/engines/mm/mm1/views_enh/character_manage.cpp
@@ -75,20 +75,20 @@ void CharacterManage::draw() {
 	switch (_state) {
 	case DISPLAY:
 		setReduced(true);
-		writeString(35, 174, STRING["enhdialogs.character.portrait"]);
-		writeString(105, 174, STRING["enhdialogs.character.rename"]);
-		writeString(175, 174, STRING["enhdialogs.character.delete"]);
-		writeString(245, 174, STRING["enhdialogs.misc.go_back"]);
+		writeString(35, 174, "Portrait");
+		writeString(105, 174, "Rename");
+		writeString(175, 174, "Delete");
+		writeString(245, 174, "Go back");
 		break;
 
 	case RENAME:
 		_state = DISPLAY;
-		writeString(80, 172, STRING["dialogs.view_character.name"]);
+		writeString(80, 172, "Name: ");
 		_textEntry.display(130, 180, 15, false, abortFunc, enterFunc);
 		break;
 
 	case DELETE:
-		writeString(110, 171, STRING["enhdialogs.character.are_you_sure"]);
+		writeString(110, 171, "Are you sure? (Y/N)");
 		break;
 	}
 }
diff --git a/engines/mm/mm1/views_enh/character_select.cpp b/engines/mm/mm1/views_enh/character_select.cpp
index a3597acfb7a..dfbe856f93a 100644
--- a/engines/mm/mm1/views_enh/character_select.cpp
+++ b/engines/mm/mm1/views_enh/character_select.cpp
@@ -35,7 +35,7 @@ void CharacterSelect::draw() {
 	ScrollView::draw();
 
 	_fontReduced = true;
-	writeString(0, 0, STRING["enhdialogs.character_select.title"]);
+	writeString(0, 0, "Select target");
 }
 
 bool CharacterSelect::msgAction(const ActionMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/character_view.cpp b/engines/mm/mm1/views_enh/character_view.cpp
index 396aa1399b4..a952eeb6aae 100644
--- a/engines/mm/mm1/views_enh/character_view.cpp
+++ b/engines/mm/mm1/views_enh/character_view.cpp
@@ -33,7 +33,7 @@ CharacterView::CharacterView() : CharacterBase("CharacterView") {
 
 void CharacterView::draw() {
 	CharacterBase::draw();
-	writeString(120, 174, STRING["dialogs.misc.go_back"]);
+	writeString(120, 174, "Go back");
 }
 
 bool CharacterView::msgMouseUp(const MouseUpMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/characters.cpp b/engines/mm/mm1/views_enh/characters.cpp
index 6b16dc4e085..3feba21862f 100644
--- a/engines/mm/mm1/views_enh/characters.cpp
+++ b/engines/mm/mm1/views_enh/characters.cpp
@@ -41,10 +41,10 @@ void Characters::draw() {
 
 	// Write title
 	setReduced(false);
-	writeString(0, 0, STRING["dialogs.view_characters.title"], ALIGN_MIDDLE);
+	writeString(0, 0, "View all characters", ALIGN_MIDDLE);
 
 	if (g_globals->_roster.empty()) {
-		writeString(0, 40, STRING["dialogs.misc.no_characters"], ALIGN_MIDDLE);
+		writeString(0, 40, "No available characters", ALIGN_MIDDLE);
 
 	} else {
 		// Write out the roster
@@ -73,10 +73,10 @@ void Characters::draw() {
 		}
 
 		setReduced(false);
-		writeString(0, 152, STRING["enhdialogs.characters.left_click"], ALIGN_MIDDLE);
+		writeString(0, 152, "Left click portraits to view", ALIGN_MIDDLE);
 	}
 
-	writeString(135, 174, STRING["enhdialogs.misc.go_back"]);
+	writeString(135, 174, "Go back");
 }
 
 bool Characters::msgMouseDown(const MouseDownMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/color_questions.cpp b/engines/mm/mm1/views_enh/color_questions.cpp
index 3605cf6074d..424a73ba188 100644
--- a/engines/mm/mm1/views_enh/color_questions.cpp
+++ b/engines/mm/mm1/views_enh/color_questions.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/color_questions.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map17.h"
 #include "mm/mm1/globals.h"
 
@@ -66,18 +67,17 @@ void ColorQuestions::draw() {
 	setReduced(false);
 
 	if (!_showingResponse) {
-		writeString(0, 0, STRING["maps.map17.color"]);
+		writeString(0, 0, "What is your color (1-8)?");
 
 		setReduced(true);
 		for (int option = 0; option < 8; ++option) {
 			Common::String prefix = Common::String::format("%c) ", '1' + option);
 			writeString(20 + 105 * (option % 2), 10 + (option / 2) * 8, prefix, ALIGN_RIGHT);
-			writeString(STRING[Common::String::format("colors.%d", option)]);
+			writeString(COLOR_NAMES[option]);
 		}
 	} else {
 		const Character &c = g_globals->_party[_charIndex];
-		Common::String result = STRING[c.hasBadCondition() ?
-			"maps.map17.wrong" : "maps.map17.correct"];
+		Common::String result = (c.hasBadCondition() ? "Wrong!" : "Correct!");
 		writeLine(1, result, ALIGN_MIDDLE);
 	}
 }
diff --git a/engines/mm/mm1/views_enh/combat.cpp b/engines/mm/mm1/views_enh/combat.cpp
index 160276d6688..fccd2e48dda 100644
--- a/engines/mm/mm1/views_enh/combat.cpp
+++ b/engines/mm/mm1/views_enh/combat.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/combat.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/views/character_view_combat.h"
 #include "mm/mm1/game/encounter.h"
 #include "mm/mm1/globals.h"
@@ -184,13 +185,12 @@ void Combat::draw() {
 		return;
 	case MONSTER_ADVANCES:
 		writeBottomText(0, 0, _monsterName);
-		writeString(STRING["dialogs.combat.advances"]);
+		writeString(" advances!");
 		writeRound();
 		writeMonsters();
 		if (beginCombatLogEvent())
-			addCombatLog(Common::String::format("%s %s",
-				_monsterName.c_str(),
-				STRING["dialogs.combat.advances"].c_str()));
+			addCombatLog(Common::String::format("%s  advances!",
+				_monsterName.c_str()));
 		delayFrames(combatMessageDelayFrames());
 		return;
 	case MONSTERS_AFFECTED:
@@ -612,7 +612,7 @@ bool Combat::msgMouseUp(const MouseUpMessage &msg) {
 
 void Combat::writeOptions() {
 	if (_option != OPTION_NONE)
-		writeString(40, 170, STRING["enhdialogs.misc.go_back"]);
+		writeString(40, 170, "Go back");
 
 	switch (_option) {
 	case OPTION_NONE:
@@ -640,7 +640,7 @@ void Combat::writeOptions() {
 
 void Combat::writeAllOptions() {
 	const Character &c = *g_globals->_currCharacter;
-	writeBottomText(0, 0, STRING["dialogs.combat.options_for"]);
+	writeBottomText(0, 0, "Options for: ");
 	writeBottomText(0, 2, c._name);
 
 	// Highlight the currently active character
@@ -668,11 +668,11 @@ void Combat::writeAllOptions() {
 		_allowCast = true;
 	}
 
-	writeOption(0, 2, 'R', STRING["enhdialogs.combat.retreat"]);
+	writeOption(0, 2, 'R', "\x01""37Retreat");
 
-	writeOption(1, 0, 'E', STRING["enhdialogs.combat.exchange"]);
-	writeOption(1, 1, 'U', STRING["enhdialogs.combat.use"]);
-	writeOption(1, 2, 'B', STRING["enhdialogs.combat.block"]);
+	writeOption(1, 0, 'E', "\x01""37Exchange");
+	writeOption(1, 1, 'U', "\x01""37Use");
+	writeOption(1, 2, 'B', "\x01""37Block");
 
 	writeOption(2, 2, 'L', "\x01""37Log");
 }
@@ -789,9 +789,9 @@ bool Combat::beginCombatLogEvent() {
 
 
 void Combat::writeDelaySelect() {
-	writeBottomText(0, 0, STRING["dialogs.combat.set_delay"]);
+	writeBottomText(0, 0, "Set delay (0-9):");
 	writeBottomText(40, 3, Common::String::format(
-		STRING["dialogs.combat.delay_currently"].c_str(),
+		"(currently= %d)",
 		g_globals->_delay));
 }
 
@@ -806,7 +806,7 @@ void Combat::writeFightSelect() {
 	_attackableCount = MIN(_attackersCount, (int)_remainingMonsters.size());
 
 	writeBottomText(0, 1, Common::String::format(
-		STRING["dialogs.combat.fight_which"].c_str(), 'A' + _attackableCount - 1),
+		"Fight which 'A'-'%c'?", 'A' + _attackableCount - 1),
 		ALIGN_MIDDLE);
 }
 
@@ -814,7 +814,7 @@ void Combat::writeShootSelect() {
 	_attackableCount = MIN(_attackersCount, (int)_remainingMonsters.size());
 
 	writeBottomText(0, 1, Common::String::format(
-		STRING["dialogs.combat.shoot_which"].c_str(), 'A' + _attackableCount - 1),
+		"Shoot which 'A'-'%c'?", 'A' + _attackableCount - 1),
 		ALIGN_MIDDLE);
 }
 
@@ -828,16 +828,16 @@ void Combat::writeSpellTargetSelect() {
 
 
 void Combat::writeAttackOptions() {
-	writeOption(0, 0, 'A', STRING["dialogs.combat.attack"]);
-	writeOption(0, 1, 'F', STRING["dialogs.combat.fight"]);
+	writeOption(0, 0, 'A', "\x01""37Attack(A)");
+	writeOption(0, 1, 'F', "\x01""37Fight(+)");
 }
 
 void Combat::writeCastOption() {
-	writeOption(2, 1, 'C', STRING["dialogs.combat.cast"]);
+	writeOption(2, 1, 'C', "\x01""37Cast");
 }
 
 void Combat::writeShootOption() {
-	writeOption(2, 0, 'S', STRING["dialogs.combat.shoot"]);
+	writeOption(2, 0, 'S', "\x01""37Shoot");
 }
 
 void Combat::clearSurface() {
@@ -913,15 +913,15 @@ void Combat::writeOption(uint col, uint row, char c, const Common::String &msg)
 
 void Combat::writeStaticContent() {
 	setReduced(false);
-	writeString(0, 0, STRING["dialogs.combat.combat"]);
-	writeString(0, 7 * LINE_H, STRING["dialogs.combat.delay"]);
-	writeString(0, 8 * LINE_H, STRING["dialogs.combat.protect"]);
-	writeString(0, 9 * LINE_H, STRING["dialogs.combat.quickref"]);
-	writeString(0, 10 * LINE_H, STRING["dialogs.combat.view_char"]);
+	writeString(0, 0, "Combat");
+	writeString(0, 7 * LINE_H, "D Delay");
+	writeString(0, 8 * LINE_H, "P Protect");
+	writeString(0, 9 * LINE_H, "Q QuickRef");
+	writeString(0, 10 * LINE_H, "# View Ch");
 }
 
 void Combat::writeHandicap() {
-	writeString(0, 12 * LINE_H, STRING["dialogs.combat.handicap"]);
+	writeString(0, 12 * LINE_H, "Handicap");
 
 	clearArea(Common::Rect(0, 13 * LINE_H, 100, 14 * LINE_H));
 
@@ -929,22 +929,21 @@ void Combat::writeHandicap() {
 
 	switch (_handicap) {
 	case HANDICAP_EVEN:
-		writeString(STRING["dialogs.combat.even"]);
+		writeString("Even");
 		break;
 	case HANDICAP_PARTY:
-		writeString(STRING["dialogs.combat.party_plus"]);
+		writeString("Party +");
 		writeNumber(_handicapDelta);
 		break;
 	case HANDICAP_MONSTER:
-		writeString(STRING["dialogs.combat.monster_plus"]);
+		writeString("Monster +");
 		writeNumber(_handicapDelta);
 		break;
 	}
 }
 
 void Combat::writeRound() {
-	writeString(0, LINE_H, Common::String::format("%s%d",
-		STRING["dialogs.combat.round"].c_str(), _roundNum));
+	writeString(0, LINE_H, Common::String::format("Round #%d", _roundNum));
 }
 
 void Combat::writePartyNumbers() {
@@ -993,11 +992,10 @@ void Combat::writeMonsterStatus(int monsterNum) {
 			}
 		}
 
-		writeString(STRING[Common::String::format("dialogs.combat.status.%d",
-			status)]);
+		writeString(COMBAT_STATUS[status]);
 	} else if (_monsterP->_hp != _monsterP->_defaultHP) {
 		writeDots();
-		writeString(STRING["dialogs.combat.status.wounded"]);
+		writeString("(wounded) ");
 	}
 
 	_monsterP = currMonster;
@@ -1032,12 +1030,12 @@ void Combat::clearPartyArea() {
 
 void Combat::writeDefeat() {
 	const Common::Rect oldBounds = _bounds;
-	Common::String line1 = STRING["dialogs.combat.defeating1"];
-	Common::String line2 = STRING["dialogs.combat.defeating2"];
+	Common::String line1 = "! For defeating the monsters !";
+	Common::String line2 = "!   each survivor receives   !";
 	line1 = Common::String(line1.c_str() + 1, line1.c_str() + line1.size() - 2);
 	line2 = Common::String(line2.c_str() + 1, line2.c_str() + line2.size() - 2);
-	Common::String line3 = Common::String::format("%d %s",
-		_totalExperience, STRING["dialogs.combat.xp"].c_str());
+	Common::String line3 = Common::String::format("%d experience points.",
+		_totalExperience);
 
 	setBounds(Common::Rect(50, 40, 270, 100));
 	ScrollView::draw();
@@ -1055,8 +1053,7 @@ void Combat::writeDefeat() {
 }
 
 void Combat::highlightNextRound() {
-	Common::String s = Common::String::format("%s%d",
-		STRING["dialogs.combat.round"].c_str(),
+	Common::String s = Common::String::format("Round #%d",
 		_roundNum);
 
 	for (uint i = 0; i < s.size(); ++i)
@@ -1072,11 +1069,11 @@ void Combat::writeMonsterEffects() {
 	int line = 0;
 	if (_monstersRegenerate)
 		writeBottomText(0, line++,
-			STRING["dialogs.combat.regenerate"]);
+			"some monsters regenerate!");
 
 	if (_monstersResistSpells)
 		writeBottomText(0, line,
-			STRING["dialogs.combat.overcome"]);
+			"some monsters overcome spells!");
 
 	writeMonsters();
 
@@ -1093,10 +1090,7 @@ void Combat::writeMonsterAction(bool flees) {
 
 	const Common::String message = Common::String::format(
 			"%s %s", _monsterName.c_str(),
-			STRING[flees ?
-				"dialogs.combat.monster_flees" :
-				"dialogs.combat.monster_wanders"
-			].c_str());
+			flees ? "runs away..." : "wanders aimlessly");
 
 	writeBottomText(0, 0, message);
 
@@ -1123,8 +1117,8 @@ void Combat::writeMonsterAttack() {
 	resetBottom();
 
 	Common::String monsterName = _monsterP->_name;
-	Common::String attackStyle = STRING[Common::String::format(
-		"dialogs.combat.attack_types.%d", _monsterAttackStyle)];
+	Common::String attackStyle = _monsterAttackStyle == 99 ?
+		"shoots at" : ATTACK_TYPES[_monsterAttackStyle - 1];
 	Character &c = *g_globals->_currCharacter;
 
 	Common::String line = Common::String::format("%s %s %s",
@@ -1169,9 +1163,8 @@ void Combat::writeMonsterAttack() {
 }
 
 void Combat::writeInfiltration() {
-	Common::String line = Common::String::format("%s %s",
-		_monsterP->getDisplayName().c_str(),
-		STRING["dialogs.combat.infiltration"].c_str());
+	Common::String line = Common::String::format("%s infiltrates the ranks!",
+		_monsterP->getDisplayName().c_str());
 
 	resetBottom();
 	writeBottomText(0, 0, line);
@@ -1183,10 +1176,8 @@ void Combat::writeInfiltration() {
 }
 
 void Combat::writeWaitsForOpening() {
-	Common::String line = Common::String::format("%s %s",
-		_monsterP->getDisplayName().c_str(),
-		STRING["dialogs.combat.infiltration"].c_str()
-	);
+	Common::String line = Common::String::format("%s infiltrates the ranks!",
+		_monsterP->getDisplayName().c_str());
 
 	resetBottom();
 	writeBottomText(0, 0, line);
@@ -1256,8 +1247,7 @@ void Combat::writeCharAttackDamage() {
 
 	writeBottomText(0, 0, Common::String::format("%s %s %s",
 		g_globals->_currCharacter->_name,
-		STRING[_isShooting ? "dialogs.combat.shoots" :
-		"dialogs.combat.attacks"].c_str(),
+		_isShooting ? "shoots" : "attacks",
 		_monsterP->_name.c_str()
 	));
 	_isShooting = false;
@@ -1265,22 +1255,19 @@ void Combat::writeCharAttackDamage() {
 	writeBottomText(0, 1, getAttackString());
 
 	if (_monsterP->_status == MONFLAG_DEAD) {
-		writeBottomText(0, 2, Common::String::format("%s %s",
-			_monsterP->_name.c_str(),
-			STRING["dialogs.combat.goes_down"].c_str()));
+		writeBottomText(0, 2, Common::String::format("%s goes down!!!",
+			_monsterP->_name.c_str()));
 	}
 
 	if (beginCombatLogEvent()) {
 		addCombatLog(Common::String::format("%s %s %s",
 			g_globals->_currCharacter->_name,
-			STRING[wasShooting ? "dialogs.combat.shoots" :
-				"dialogs.combat.attacks"].c_str(),
+			wasShooting ? "shoots" : "attacks",
 			_monsterP->_name.c_str()));
 		addCombatLog(getAttackString());
 		if (_monsterP->_status == MONFLAG_DEAD)
-			addCombatLog(Common::String::format("%s %s",
-				_monsterP->_name.c_str(),
-				STRING["dialogs.combat.goes_down"].c_str()));
+			addCombatLog(Common::String::format("%s goes down!!!",
+				_monsterP->_name.c_str()));
 	}
 }
 
@@ -1290,58 +1277,53 @@ void Combat::writeCharAttackNoEffect() {
 
 	writeBottomText(0, 0, Common::String::format("%s %s %s",
 		g_globals->_currCharacter->_name,
-		STRING[_isShooting ? "dialogs.combat.shoots" :
-		"dialogs.combat.attacks"].c_str(),
+		_isShooting ? "shoots" : "attacks",
 		_monsterP->_name.c_str()
 	));
 	_isShooting = false;
 
-	writeBottomText(0, 1, STRING["dialogs.combat.weapon_no_effect"]);
+	writeBottomText(0, 1, "Weapon has no effect!");
 
 	if (beginCombatLogEvent()) {
 		addCombatLog(Common::String::format("%s %s %s",
 			g_globals->_currCharacter->_name,
-			STRING[wasShooting ? "dialogs.combat.shoots" :
-				"dialogs.combat.attacks"].c_str(),
+			wasShooting ? "shoots" : "attacks",
 			_monsterP->_name.c_str()));
-		addCombatLog(STRING["dialogs.combat.weapon_no_effect"]);
+		addCombatLog("Weapon has no effect!");
 	}
 }
 
 Common::String Combat::getAttackString() {
 	Common::String line1;
 	if (_numberOfTimes == 1) {
-		line1 = STRING["dialogs.combat.once"];
+		line1 = "once";
 	} else {
-		line1 = Common::String::format("%d %s", _numberOfTimes,
-			STRING["dialogs.combat.times"].c_str());
+		line1 = Common::String::format("%d times", _numberOfTimes);
 	}
 
-	line1 += Common::String::format(" %s ", STRING["dialogs.combat.and"].c_str());
+	line1 += " and ";
 
 	if (_displayedDamage == 0) {
-		line1 += STRING["dialogs.combat.misses"];
+		line1 += "misses";
 	} else {
-		line1 += STRING["dialogs.combat.hit"];
+		line1 += "hit";
 
 		if (_numberOfTimes > 1) {
 			line1 += ' ';
 
 			if (_timesHit == 1) {
-				line1 += STRING["dialogs.combat.once"];
+				line1 += "once";
 			} else {
-				line1 += Common::String::format("%d %s", _timesHit,
-					STRING["dialogs.combat.times"].c_str());
+				line1 += Common::String::format("%d times", _timesHit);
 			}
 		}
 
-		line1 += Common::String::format(" %s %d %s",
-			STRING["dialogs.combat.for"].c_str(), _displayedDamage,
-			STRING[_damage == 1 ? "dialogs.combat.point" : "dialogs.combat.points"].c_str());
+		line1 += Common::String::format(" for %d %s", _displayedDamage,
+			_damage == 1 ? "point" : "points");
 
 		if (line1.size() < 30) {
 			line1 += ' ';
-			line1 += STRING["dialogs.combat.of_damage"];
+			line1 += "of damage!";
 		} else {
 			line1 += '!';
 		}
diff --git a/engines/mm/mm1/views_enh/create_characters.cpp b/engines/mm/mm1/views_enh/create_characters.cpp
index 0620b73a30a..2169e3053d0 100644
--- a/engines/mm/mm1/views_enh/create_characters.cpp
+++ b/engines/mm/mm1/views_enh/create_characters.cpp
@@ -22,6 +22,7 @@
 #include "common/file.h"
 #include "common/textconsole.h"
 #include "mm/mm1/views_enh/create_characters.h"
+#include "mm/mm1/data/text.h"
 #include "mm/shared/utils/strings.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
@@ -97,7 +98,7 @@ static Common::String getAlignmentString(Alignment alignment) {
 	if (alignment == NEUTRAL)
 		return "Neutral";
 
-	return STRING[Common::String::format("stats.alignments.%d", alignment)];
+	return ALIGNMENT_NAMES[alignment - 1];
 }
 
 void CreateCharacters::NewCharacter::clear() {
@@ -351,14 +352,14 @@ void CreateCharacters::draw() {
 		_newChar._portraits.draw(&s, 0, Common::Point(10, 10));
 	}
 
-	writeString(135, 174, STRING["enhdialogs.misc.go_back"]);
-	writeString(70, 125, STRING["enhdialogs.create_characters.roll"]);
+	writeString(135, 174, "Go back");
+	writeString(70, 125, "\x01""37Roll");
 
 	switch (_state) {
 	case SELECT_CLASS:
 		printClasses();
 		if (g_globals->_roster.full())
-			writeLine(9, STRING["dialogs.create_characters.full"], ALIGN_MIDDLE, 190);
+			writeLine(9, "*** Roster is Full ***", ALIGN_MIDDLE, 190);
 		break;
 
 	case SELECT_RACE:
@@ -391,15 +392,15 @@ void CreateCharacters::draw() {
 }
 
 void CreateCharacters::printAttributes() {
-	writeLine(0, STRING["dialogs.create_characters.title"], ALIGN_MIDDLE);
+	writeLine(0, "Create New Characters", ALIGN_MIDDLE);
 
-	writeLine(5, STRING["enhdialogs.create_characters.intellect"], ALIGN_RIGHT, 90);
-	writeLine(6, STRING["enhdialogs.create_characters.might"], ALIGN_RIGHT, 90);
-	writeLine(7, STRING["enhdialogs.create_characters.personality"], ALIGN_RIGHT, 90);
-	writeLine(8, STRING["enhdialogs.create_characters.endurance"], ALIGN_RIGHT, 90);
-	writeLine(9, STRING["enhdialogs.create_characters.speed"], ALIGN_RIGHT, 90);
-	writeLine(10, STRING["enhdialogs.create_characters.accuracy"], ALIGN_RIGHT, 90);
-	writeLine(11, STRING["enhdialogs.create_characters.luck"], ALIGN_RIGHT, 90);
+	writeLine(5, "Intellect =", ALIGN_RIGHT, 90);
+	writeLine(6, "Might =", ALIGN_RIGHT, 90);
+	writeLine(7, "Personality =", ALIGN_RIGHT, 90);
+	writeLine(8, "Endurance =", ALIGN_RIGHT, 90);
+	writeLine(9, "Speed =", ALIGN_RIGHT, 90);
+	writeLine(10, "Accuracy =", ALIGN_RIGHT, 90);
+	writeLine(11, "Luck =", ALIGN_RIGHT, 90);
 
 	for (int i = 0; i < 7; ++i, _textPos.y += 2) {
 		writeLine(5 + i,
@@ -421,7 +422,7 @@ void CreateCharacters::printClasses() {
 	for (int classNum = KNIGHT; classNum <= SORCERER; ++classNum) {
 		Common::String text = Common::String::format("%d) %s",
 			classNum,
-			STRING[Common::String::format("stats.classes.%d", classNum)].c_str()
+			CLASS_NAMES[classNum - 1]
 		);
 		bool enabled = _newChar._classesAllowed[classNum] && !rosterFull;
 		int buttonNum = enabled ? addSelection(4, classNum, text) : -1;
@@ -430,38 +431,38 @@ void CreateCharacters::printClasses() {
 
 	}
 
-	Common::String text = Common::String::format("6) %s", STRING["stats.classes.6"].c_str());
+	Common::String text = "6) Robber";
 	int buttonNum = rosterFull ? -1 : addSelection(4, ROBBER, text);
 	setTextColor(rosterFull ? 1 : (_selectedButton == buttonNum ? 15 : 0));
 	writeLine(10, text, ALIGN_LEFT, 170);
 
 	setTextColor(0);
-	writeLine(13, STRING["dialogs.create_characters.select_class"], ALIGN_MIDDLE, RIGHT_X);
+	writeLine(13, "Select class", ALIGN_MIDDLE, RIGHT_X);
 	writeLine(14, "(1-6)", ALIGN_MIDDLE, RIGHT_X);
 }
 
 void CreateCharacters::printRaces() {
-	writeLine(5, STRING["enhdialogs.create_characters.class"], ALIGN_RIGHT, RIGHT_X);
-	writeString(STRING[Common::String::format("stats.classes.%d", _newChar._class)]);
+	writeLine(5, "Class = ", ALIGN_RIGHT, RIGHT_X);
+	writeString(CLASS_NAMES[_newChar._class - 1]);
 
 	for (int i = 1; i <= 5; ++i) {
 		Common::String text = Common::String::format("%d) %s", i,
-			STRING[Common::String::format("stats.races.%d", i)].c_str());
+			RACE_NAMES[i - 1]);
 		int buttonNum = addSelection(6, i, text);
 		byte oldColor = setTextColor(_selectedButton == buttonNum ? 15 : 0);
 		writeLine(6 + i, text, ALIGN_LEFT, 170);
 		setTextColor(oldColor);
 	}
 
-	writeLine(13, STRING["dialogs.create_characters.select_race"], ALIGN_MIDDLE, RIGHT_X);
+	writeLine(13, "Select a race", ALIGN_MIDDLE, RIGHT_X);
 	writeLine(14, "(1-5)", ALIGN_MIDDLE, RIGHT_X);
 }
 
 void CreateCharacters::printAlignments() {
-	writeLine(5, STRING["enhdialogs.create_characters.class"], ALIGN_RIGHT, RIGHT_X);
-	writeString(STRING[Common::String::format("stats.classes.%d", _newChar._class)]);
-	writeLine(6, STRING["enhdialogs.create_characters.race"], ALIGN_RIGHT, RIGHT_X);
-	writeString(STRING[Common::String::format("stats.races.%d", _newChar._race)]);
+	writeLine(5, "Class = ", ALIGN_RIGHT, RIGHT_X);
+	writeString(CLASS_NAMES[_newChar._class - 1]);
+	writeLine(6, "Race = ", ALIGN_RIGHT, RIGHT_X);
+	writeString(RACE_NAMES[_newChar._race - 1]);
 
 	for (int i = 1; i <= 3; ++i) {
 		Common::String text = Common::String::format("%d) %s", i,
@@ -472,20 +473,20 @@ void CreateCharacters::printAlignments() {
 		setTextColor(oldColor);
 	}
 
-	writeLine(13, STRING["dialogs.create_characters.select_alignment"], ALIGN_MIDDLE, RIGHT_X);
+	writeLine(13, "Select alignment", ALIGN_MIDDLE, RIGHT_X);
 	writeLine(14, "(1-3)", ALIGN_MIDDLE, RIGHT_X);
 }
 
 void CreateCharacters::printSexes() {
-	writeLine(5, STRING["enhdialogs.create_characters.class"], ALIGN_RIGHT, RIGHT_X);
-	writeString(STRING[Common::String::format("stats.classes.%d", _newChar._class)]);
-	writeLine(6, STRING["enhdialogs.create_characters.race"], ALIGN_RIGHT, RIGHT_X);
-	writeString(STRING[Common::String::format("stats.races.%d", _newChar._race)]);
-	writeLine(7, STRING["enhdialogs.create_characters.alignment"], ALIGN_RIGHT, RIGHT_X);
+	writeLine(5, "Class = ", ALIGN_RIGHT, RIGHT_X);
+	writeString(CLASS_NAMES[_newChar._class - 1]);
+	writeLine(6, "Race = ", ALIGN_RIGHT, RIGHT_X);
+	writeString(RACE_NAMES[_newChar._race - 1]);
+	writeLine(7, "Align = ", ALIGN_RIGHT, RIGHT_X);
 	writeString(getAlignmentString(_newChar._alignment));
 
-	Common::String male = Common::String::format("1) %s", STRING["stats.sex.1"].c_str());
-	Common::String female = Common::String::format("2) %s", STRING["stats.sex.2"].c_str());
+	Common::String male = "1) Male";
+	Common::String female = "2) Female";
 	int maleButton = addSelection(8, 1, male);
 	byte oldColor = setTextColor(_selectedButton == maleButton ? 15 : 0);
 	writeLine(9, male, ALIGN_LEFT, 170);
@@ -495,42 +496,42 @@ void CreateCharacters::printSexes() {
 	writeLine(10, female, ALIGN_LEFT, 170);
 	setTextColor(oldColor);
 
-	writeLine(14, STRING["dialogs.create_characters.select_sex"], ALIGN_MIDDLE, RIGHT_X);
+	writeLine(14, "Select a sex", ALIGN_MIDDLE, RIGHT_X);
 	writeLine(15, "(1-2)", ALIGN_MIDDLE, RIGHT_X);
 }
 
 void CreateCharacters::printSelections() {
-	writeLine(5, STRING["enhdialogs.create_characters.class"], ALIGN_RIGHT, RIGHT_X);
-	writeString(STRING[Common::String::format("stats.classes.%d", _newChar._class)]);
-	writeLine(6, STRING["enhdialogs.create_characters.race"], ALIGN_RIGHT, RIGHT_X);
-	writeString(STRING[Common::String::format("stats.races.%d", _newChar._race)]);
-	writeLine(7, STRING["enhdialogs.create_characters.alignment"], ALIGN_RIGHT, RIGHT_X);
+	writeLine(5, "Class = ", ALIGN_RIGHT, RIGHT_X);
+	writeString(CLASS_NAMES[_newChar._class - 1]);
+	writeLine(6, "Race = ", ALIGN_RIGHT, RIGHT_X);
+	writeString(RACE_NAMES[_newChar._race - 1]);
+	writeLine(7, "Align = ", ALIGN_RIGHT, RIGHT_X);
 	writeString(getAlignmentString(_newChar._alignment));
-	writeLine(8, STRING["enhdialogs.create_characters.sex"], ALIGN_RIGHT, RIGHT_X);
-	writeString(STRING[Common::String::format("stats.sex.%d", _newChar._sex)]);
+	writeLine(8, "Sex = ", ALIGN_RIGHT, RIGHT_X);
+	writeString(SEX_NAMES[_newChar._sex - 1]);
 }
 
 void CreateCharacters::printPortraits() {
 	printSelections();
-	writeLine(10, STRING["enhdialogs.create_characters.select_portrait"], ALIGN_MIDDLE, RIGHT_X);
+	writeLine(10, "Select a portrait", ALIGN_MIDDLE, RIGHT_X);
 
 	Graphics::ManagedSurface s = getSurface();
 	_newChar._portraits.draw(&s, 0, Common::Point(160, 120));
 
-	writeString(250, 126, STRING["enhdialogs.create_characters.select"]);
+	writeString(250, 126, "\x01""37Select");
 }
 
 void CreateCharacters::printSelectName() {
 	printSelections();
-	writeLine(10, STRING["enhdialogs.create_characters.enter_name"], ALIGN_MIDDLE, RIGHT_X);
+	writeLine(10, "Enter name:", ALIGN_MIDDLE, RIGHT_X);
 }
 
 void CreateCharacters::printSummary() {
 	printSelections();
-	writeLine(9, STRING["enhdialogs.create_characters.name"], ALIGN_RIGHT, RIGHT_X);
+	writeLine(9, "Name = ", ALIGN_RIGHT, RIGHT_X);
 	writeString(_newChar._name);
 
-	writeLine(12, STRING["dialogs.create_characters.save_character"], ALIGN_MIDDLE, RIGHT_X);
+	writeLine(12, "Save character", ALIGN_MIDDLE, RIGHT_X);
 }
 
 bool CreateCharacters::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/dead.cpp b/engines/mm/mm1/views_enh/dead.cpp
index 68bbd2a96b3..885500b87c7 100644
--- a/engines/mm/mm1/views_enh/dead.cpp
+++ b/engines/mm/mm1/views_enh/dead.cpp
@@ -46,12 +46,12 @@ void Dead::draw() {
 	ScrollView::draw();
 	setReduced(false);
 
-	writeLine(4, STRING["dialogs.dead.1"], ALIGN_MIDDLE);
-	writeLine(6, STRING["dialogs.dead.2"], ALIGN_MIDDLE);
-	writeLine(8, STRING["dialogs.dead.3"], ALIGN_MIDDLE);
-	writeLine(10, STRING["dialogs.dead.4"], ALIGN_MIDDLE);
-	writeLine(12, STRING["dialogs.dead.5"], ALIGN_MIDDLE);
-	writeLine(14, STRING["dialogs.dead.6"], ALIGN_MIDDLE);
+	writeLine(4, "The shadow of death", ALIGN_MIDDLE);
+	writeLine(6, "has fallen upon your party!", ALIGN_MIDDLE);
+	writeLine(8, "Fortunately, you may renew", ALIGN_MIDDLE);
+	writeLine(10, "your quest from the last", ALIGN_MIDDLE);
+	writeLine(12, "inn in which you stayed", ALIGN_MIDDLE);
+	writeLine(14, "by pressing 'Enter'", ALIGN_MIDDLE);
 }
 
 bool Dead::msgAction(const ActionMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/encounter.cpp b/engines/mm/mm1/views_enh/encounter.cpp
index 96b918a34ce..faabee7610b 100644
--- a/engines/mm/mm1/views_enh/encounter.cpp
+++ b/engines/mm/mm1/views_enh/encounter.cpp
@@ -67,51 +67,51 @@ void Encounter::draw() {
 		view.setBounds(Common::Rect(pt.x - 50, pt.y - 9, pt.x + 50, pt.y + 18));
 		view.draw();
 
-		writeLine(7, STRING["dialogs.encounter.title"], ALIGN_MIDDLE);
+		writeLine(7, " Encounter! ", ALIGN_MIDDLE);
 		delaySeconds(2);
 		break;
 	}
 
 	case SURPRISED_BY_MONSTERS:
-		writeLine(0, STRING["dialogs.encounter.surprised"], ALIGN_MIDDLE);
+		writeLine(0, "The monsters surprised you!", ALIGN_MIDDLE);
 		enc._encounterType = Game::FORCE_SURPRISED;
 		delaySeconds(2);
 		break;
 
 	case SURPRISED_MONSTERS:
-		writeLine(0, STRING["dialogs.encounter.surprise"], ALIGN_MIDDLE);
-		writeLine(2, STRING["dialogs.encounter.approach"], ALIGN_MIDDLE);
+		writeLine(0, "You surprised a group of monsters,", ALIGN_MIDDLE);
+		writeLine(2, "Approach (Y/N)?", ALIGN_MIDDLE);
 		break;
 
 	case ENCOUNTER_OPTIONS: {
 		// Write the encounter options
 		setReduced(false);
-		writeString(0, 5, STRING["enhdialogs.encounter.options"]);
+		writeString(0, 5, "Options:");
 		setReduced(true);
-		writeString(88, 5, STRING["enhdialogs.encounter.attack"]);
-		writeString(164, 5, STRING["enhdialogs.encounter.retreat"]);
-		writeString(88, 25, STRING["enhdialogs.encounter.bribe"]);
-		writeString(164, 25, STRING["enhdialogs.encounter.surrender"]);
+		writeString(88, 5, "\x01""37Attack");
+		writeString(164, 5, "\x01""37Retreat");
+		writeString(88, 25, "\x01""37Bribe");
+		writeString(164, 25, "\x01""37Surrender");
 		break;
 	}
 
 	case NOWHERE_TO_RUN:
-		writeLine(0, STRING["dialogs.encounter.nowhere_to_run"], ALIGN_MIDDLE);
+		writeLine(0, "Nowhere to run", ALIGN_MIDDLE);
 		delaySeconds(2);
 		break;
 
 	case SURROUNDED:
-		writeLine(0, STRING["dialogs.encounter.surround"], ALIGN_MIDDLE);
+		writeLine(0, "The monsters surround you!", ALIGN_MIDDLE);
 		delaySeconds(2);
 		break;
 
 	case SURRENDER_FAILED:
-		writeLine(0, STRING["dialogs.encounter.surrender_failed"], ALIGN_MIDDLE);
+		writeLine(0, "The monsters don't take prisoners!", ALIGN_MIDDLE);
 		delaySeconds(2);
 		break;
 
 	case NO_RESPONSE:
-		writeLine(0, STRING["dialogs.encounter.no_response"], ALIGN_MIDDLE);
+		writeLine(0, "No response", ALIGN_MIDDLE);
 		delaySeconds(2);
 		break;
 
@@ -119,17 +119,17 @@ void Encounter::draw() {
 		enc._bribeFleeCtr++;
 		enc._bribeAlignmentCtr++;
 		writeLine(0, Common::String::format(
-			STRING["dialogs.encounter.give_up"].c_str(),
+			"Give up all your %s (Y/N)?",
 			_bribeTypeStr.c_str()), ALIGN_MIDDLE);
 		break;
 
 	case NOT_ENOUGH:
-		writeLine(0, STRING["dialogs.encounter.not_enough"], ALIGN_MIDDLE);
+		writeLine(0, "Not enough", ALIGN_MIDDLE);
 		delaySeconds(2);
 		break;
 
 	case COMBAT:
-		writeLine(0, STRING["dialogs.encounter.combat"], ALIGN_MIDDLE);
+		writeLine(0, "Combat!", ALIGN_MIDDLE);
 		delaySeconds(2);
 		break;
 
@@ -163,7 +163,7 @@ void Encounter::draw() {
 			_mode == SURPRISED_BY_MONSTERS) {
 		if (enc._alignmentsChanged) {
 			setDisplayArea(false);
-			writeLine(3, STRING["dialogs.encounter.alignment_slips"], ALIGN_MIDDLE);
+			writeLine(3, "*** Alignment slips ***", ALIGN_MIDDLE);
 			Sound::sound(SOUND_2);
 		}
 
@@ -313,13 +313,13 @@ void Encounter::bribe() {
 		int val = getRandomNumber(100);
 		if (val < 6) {
 			_bribeType = BRIBE_GEMS;
-			_bribeTypeStr = STRING["dialogs.encounter.gems"];
+			_bribeTypeStr = "gems";
 		} else if (val < 16) {
 			_bribeType = BRIBE_FOOD;
-			_bribeTypeStr = STRING["dialogs.encounter.food"];
+			_bribeTypeStr = "food";
 		} else {
 			_bribeType = BRIBE_GOLD;
-			_bribeTypeStr = STRING["dialogs.encounter.gold"];
+			_bribeTypeStr = "gold";
 		}
 
 		redraw();
diff --git a/engines/mm/mm1/views_enh/exchange.cpp b/engines/mm/mm1/views_enh/exchange.cpp
index e509d8b1a4d..0b9e6bd85f4 100644
--- a/engines/mm/mm1/views_enh/exchange.cpp
+++ b/engines/mm/mm1/views_enh/exchange.cpp
@@ -43,7 +43,7 @@ bool Exchange::msgFocus(const FocusMessage &msg) {
 void Exchange::draw() {
 	PartyView::draw();
 
-	writeString(10, 5, STRING["enhdialogs.exchange"]);
+	writeString(10, 5, "Exhange with whom?");
 }
 
 bool Exchange::msgAction(const ActionMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/game.cpp b/engines/mm/mm1/views_enh/game.cpp
index 85e641f94b4..7a16d8e375f 100644
--- a/engines/mm/mm1/views_enh/game.cpp
+++ b/engines/mm/mm1/views_enh/game.cpp
@@ -100,7 +100,7 @@ bool Game::msgAction(const ActionMessage &msg) {
 		if (g_maps->_currentMap->mappingAllowed())
 			addView("MapPopup");
 		else
-			send(InfoMessage(STRING["enhdialogs.map.disabled"]));
+			send(InfoMessage("Mapping is currently unavailable here"));
 		return true;
 	case KEYBIND_MENU:
 		g_engine->openMainMenuDialog();
diff --git a/engines/mm/mm1/views_enh/interactions/access_code.cpp b/engines/mm/mm1/views_enh/interactions/access_code.cpp
index c4cf229b8f9..585cf4ce4a7 100644
--- a/engines/mm/mm1/views_enh/interactions/access_code.cpp
+++ b/engines/mm/mm1/views_enh/interactions/access_code.cpp
@@ -29,8 +29,8 @@ namespace ViewsEnh {
 namespace Interactions {
 
 AccessCode::AccessCode() : InteractionQuery("AccessCode", 8) {
-	_title = STRING["maps.emap08.access_code"];
-	addText(STRING["maps.map08.enter_code"]);
+	_title = "Access Code";
+	addText("Covered with flashing lights the wall speaks: \"Please enter access code:\" ");
 }
 
 void AccessCode::answerEntered() {
diff --git a/engines/mm/mm1/views_enh/interactions/alamar.cpp b/engines/mm/mm1/views_enh/interactions/alamar.cpp
index 0c7365ab49e..e92be86bd64 100644
--- a/engines/mm/mm1/views_enh/interactions/alamar.cpp
+++ b/engines/mm/mm1/views_enh/interactions/alamar.cpp
@@ -50,29 +50,23 @@ bool Alamar::msgFocus(const FocusMessage &msg) {
 			g_globals->_party[i]._quest = 255;
 	}
 
-	_title = STRING["maps.emap49.king_alamar"];
+	_title = "King Alamar";
 
 	if (_succeeded) {
-		addText(Common::String::format("%s%s",
-			STRING["maps.map49.alamar1"].c_str(),
-			STRING["maps.map49.alamar3"].c_str()
+		addText(Common::String::format("The omnipotent King Alamar speaks,  %s",
+			"\"My saviors, you're always welcome here! You should find the Inner Sanctum... Live long "
+				"and prosper.\""
 		));
 
 	} else if (map[HAS_EYE]) {
-		_title = STRING["maps.emap49.sheltem"];
-		addText(Common::String::format("%s%s",
-			STRING["maps.map49.alamar1"].c_str(),
-			STRING["maps.map49.alamar4"].c_str()
-		));
+		_title = "Imposter King Alamar";
+		addText("The omnipotent King Alamar speaks,  \"You've discovered my true identity! Into the soul maze you go...\"");
 
 		for (int i = 0; i < 6; ++i)
 			Sound::sound(SOUND_2);
 
 	} else {
-		addText(Common::String::format("%s%s",
-			STRING["maps.map49.alamar1"].c_str(),
-			STRING["maps.map49.alamar2"].c_str()
-		));
+		addText("The omnipotent King Alamar speaks,  \"Varnlings, I quest thee to find the Crypt of Carmenca. Away with you...\"");
 	}
 
 	return true;
diff --git a/engines/mm/mm1/views_enh/interactions/alien.cpp b/engines/mm/mm1/views_enh/interactions/alien.cpp
index 66919cc99fa..f2491967242 100644
--- a/engines/mm/mm1/views_enh/interactions/alien.cpp
+++ b/engines/mm/mm1/views_enh/interactions/alien.cpp
@@ -30,11 +30,11 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Alien::Alien() : Interaction("Alien", 37) {
-	_title = STRING["maps.emap31.alien_title"];
-	addText(STRING["maps.emap31.alien"]);
-	addButton(STRING["maps.emap31.option_a"], 'A');
-	addButton(STRING["maps.emap31.option_b"], 'B');
-	addButton(STRING["maps.emap31.option_c"], 'C');
+	_title = "Alien";
+	addText("Scattered remains of a metallic craft! An alien grunts, \"Eep op oop!\" Reaction?");
+	addButton("Hostile", 'A');
+	addButton("Neutral", 'B');
+	addButton("Friendly", 'C');
 }
 
 bool Alien::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/interactions/arenko.cpp b/engines/mm/mm1/views_enh/interactions/arenko.cpp
index f374d11fea8..40574fb2f37 100644
--- a/engines/mm/mm1/views_enh/interactions/arenko.cpp
+++ b/engines/mm/mm1/views_enh/interactions/arenko.cpp
@@ -32,7 +32,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Arenko::Arenko() : Interaction("Arenko", 9) {
-	_title = STRING["maps.emap28.arenko_title"];
+	_title = "Arenko";
 }
 
 bool Arenko::msgFocus(const FocusMessage &msg) {
@@ -40,17 +40,18 @@ bool Arenko::msgFocus(const FocusMessage &msg) {
 	clearButtons();
 
 	if (!map.arenkoQuestActive()) {
-		addText(STRING["maps.map28.arenko"]);
+		addText("I am Arenko Guire and this is my grove. Climb all the trees, without leaving the area, "
+			"and return.");
 		map.startArenkoQuest();
 
 	} else if (map[VAL1] < 19) {
-		addText(STRING["maps.map28.keep_climbing"]);
+		addText("Keep climbing.");
 
 	} else {
-		addText(STRING["maps.map28.well_done"]);
-		addButton(STRING["maps.emap28.gold"], 'A');
-		addButton(STRING["maps.emap28.gems"], 'B');
-		addButton(STRING["maps.emap28.item"], 'C');
+		addText("Well done! What is your pleasure?");
+		addButton("Gold", 'A');
+		addButton("Gems", 'B');
+		addButton("Item", 'C');
 	}
 
 	return true;
diff --git a/engines/mm/mm1/views_enh/interactions/arrested.cpp b/engines/mm/mm1/views_enh/interactions/arrested.cpp
index f3c9315f152..0a1d7776313 100644
--- a/engines/mm/mm1/views_enh/interactions/arrested.cpp
+++ b/engines/mm/mm1/views_enh/interactions/arrested.cpp
@@ -29,16 +29,16 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Arrested::Arrested() : Interaction("Arrested", 26), Game::Arrested() {
-	_title = STRING["maps.emap04.town_guards"];
+	_title = "Town Guards";
 }
 
 bool Arrested::msgFocus(const FocusMessage &msg) {
-	addText(STRING["maps.emap04.guards"]);
+	addText("Town guards: you're under arrest!");
 	clearButtons();
-	addButton(STRING["maps.emap04.attack"], 'A');
-	addButton(STRING["maps.emap04.bribe"], 'B');
-	addButton(STRING["maps.emap04.run"], 'R');
-	addButton(STRING["maps.emap04.surrender"], 'S');
+	addButton("\x01""37Attack", 'A');
+	addButton("\x01""37Bribe", 'B');
+	addButton("\x01""37Run", 'R');
+	addButton("\x01""37Surrender", 'S');
 
 	return true;
 }
@@ -69,7 +69,7 @@ void Arrested::surrender(int numYears) {
 
 	// Display sentence
 	Common::String str = Common::String::format(
-		STRING["maps.emap04.sentence"].c_str(), numYears);
+		"High court sentence: %d years", numYears);
 
 	SoundMessage msg(str);
 	msg._delaySeconds = 3;
diff --git a/engines/mm/mm1/views_enh/interactions/chess.cpp b/engines/mm/mm1/views_enh/interactions/chess.cpp
index 666affd47f0..d17d7d298b9 100644
--- a/engines/mm/mm1/views_enh/interactions/chess.cpp
+++ b/engines/mm/mm1/views_enh/interactions/chess.cpp
@@ -29,8 +29,10 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Chess::Chess() : InteractionQuery("Chess", 23, 13) {
-	_title = STRING["maps.emap29.og_title"];
-	addText(STRING["maps.map29.og"]);
+	_title = "Og";
+	addText("Painted on these grounds is a black &\n"
+		"white checkered motif covered with idols similar to the ones you carry. A large being "
+		"approaches... Og speaks, \"Queen to queens level 3\"");
 }
 
 void Chess::answerEntered() {
diff --git a/engines/mm/mm1/views_enh/interactions/dog_statue.cpp b/engines/mm/mm1/views_enh/interactions/dog_statue.cpp
index 60020a0a95c..61ca8213136 100644
--- a/engines/mm/mm1/views_enh/interactions/dog_statue.cpp
+++ b/engines/mm/mm1/views_enh/interactions/dog_statue.cpp
@@ -30,7 +30,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 DogStatue::DogStatue() : Interaction("DogStatue") {
-	_title = STRING["maps.emap42.title"];
+	_title = "Dog Statue";
 }
 
 bool DogStatue::msgFocus(const FocusMessage &msg) {
@@ -62,17 +62,18 @@ bool DogStatue::msgFocus(const FocusMessage &msg) {
 
 	if (_completedQuests) {
 		addText(Common::String::format("%s%s",
-			STRING["maps.map42.statue1"].c_str(),
-			STRING["maps.map42.statue2"].c_str()
+			"Standing atop a pedestal in the center of the room is a statue of a small sable and white "
+				"dog. ",
+			"It comes to life and speaks,\"Congratulations, you've completed a major quest (+10000 "
+				"exp) Remember B.J.!\""
 		));
 	} else {
-		addText(Common::String::format("%s%s",
-			STRING["maps.map42.statue1"].c_str(),
-			STRING["maps.map42.statue3"].c_str()
-		));
+		addText(Common::String::format("%sDesecrate it (Y/N)?",
+			"Standing atop a pedestal in the center of the room is a statue of a small sable and white "
+				"dog. "));
 
-		addButton(STRING["maps.yes"], 'Y');
-		addButton(STRING["maps.no"], 'N');
+		addButton("\x01""37Yes", 'Y');
+		addButton("\x01""37No", 'N');
 	}
 
 	return TextView::msgFocus(msg);
diff --git a/engines/mm/mm1/views_enh/interactions/ghost.cpp b/engines/mm/mm1/views_enh/interactions/ghost.cpp
index 7c51f6a8ede..4831bd7bfd1 100644
--- a/engines/mm/mm1/views_enh/interactions/ghost.cpp
+++ b/engines/mm/mm1/views_enh/interactions/ghost.cpp
@@ -30,16 +30,17 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Ghost::Ghost() : Interaction("Ghost", 33) {
-	_title = STRING["maps.emap37.okrim"];
+	_title = "Okrim";
 }
 
 bool Ghost::msgFocus(const FocusMessage &msg) {
 	Interaction::msgFocus(msg);
-	addText(STRING["maps.map37.okrim1"]);
+	addText("A ghostlike figure rises from the body of Okrim and speaks, \"In exchange for my ring, "
+		"forfeit a life. Accept (Y/N)?\"");
 
 	clearButtons();
-	addButton(STRING["maps.accept"], 'Y');
-	addButton(STRING["maps.decline"], 'N');
+	addButton("Accept", 'Y');
+	addButton("Decline", 'N');
 
 	return true;
 }
@@ -69,7 +70,7 @@ bool Ghost::msgKeypress(const KeypressMessage &msg) {
 		// Preserve Enhanced's acknowledgement text, but stage the same
 		// Ring reward that Classic places in treasure immediately.
 		g_globals->_treasure._items[2] = RING_OF_OKRIM_ID;
-		addText(STRING["maps.map37.okrim2"]);
+		addText("\"May the shadow of death greet you with open arms!\"");
 		clearButtons();
 		return true;
 	}
diff --git a/engines/mm/mm1/views_enh/interactions/giant.cpp b/engines/mm/mm1/views_enh/interactions/giant.cpp
index 3cbfcb923be..9cd98699c2f 100644
--- a/engines/mm/mm1/views_enh/interactions/giant.cpp
+++ b/engines/mm/mm1/views_enh/interactions/giant.cpp
@@ -31,7 +31,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Giant::Giant() : Interaction("Giant", 8) {
-	_title = STRING["maps.emap30.giant_title"];
+	_title = "Giant";
 	_animated = false;
 }
 
diff --git a/engines/mm/mm1/views_enh/interactions/gypsy.cpp b/engines/mm/mm1/views_enh/interactions/gypsy.cpp
index f011a9051ee..5a32c496813 100644
--- a/engines/mm/mm1/views_enh/interactions/gypsy.cpp
+++ b/engines/mm/mm1/views_enh/interactions/gypsy.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/interactions/gypsy.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 
 
@@ -29,7 +30,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Gypsy::Gypsy() : Interaction("Gypsy", 9) {
-	_title = STRING["maps.emap23.gypsy_title"];
+	_title = "Gypsy";
 }
 
 bool Gypsy::msgFocus(const FocusMessage &msg) {
@@ -56,9 +57,8 @@ void Gypsy::charSwitched(Character *priorChar) {
 	}
 
 	Common::String line = Common::String::format(
-		STRING["maps.map23.your_sign_is"].c_str(),
-		STRING[Common::String::format("colors.%d",
-			c._flags[4] & CHARFLAG4_SIGN)].c_str()
+		"\"Your sign is the %s\"",
+		COLOR_NAMES[c._flags[4] & CHARFLAG4_SIGN]
 	);
 	addText(line);
 
diff --git a/engines/mm/mm1/views_enh/interactions/hacker.cpp b/engines/mm/mm1/views_enh/interactions/hacker.cpp
index c45d3f64b8c..5154604ee11 100644
--- a/engines/mm/mm1/views_enh/interactions/hacker.cpp
+++ b/engines/mm/mm1/views_enh/interactions/hacker.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/interactions/hacker.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map36.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -30,7 +31,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Hacker::Hacker() : Interaction("Hacker", 35) {
-	_title = STRING["maps.emap36.hacker_title"];
+	_title = "Lord Hacker";
 }
 
 bool Hacker::msgFocus(const FocusMessage &msg) {
@@ -51,9 +52,9 @@ bool Hacker::msgGame(const GameMessage &msg) {
 	clearButtons();
 
 	if (_mode == CAN_ACCEPT) {
-		addText(STRING["maps.map36.hacker2"]);
-		addButton(STRING["maps.accept"], 'Y');
-		addButton(STRING["maps.decline"], 'N');
+		addText("\"Your services are needed!\" Accept (Y/N)?");
+		addButton("Accept", 'Y');
+		addButton("Decline", 'N');
 
 	} else {
 		// There's an active quest, so check for completion
@@ -64,7 +65,7 @@ bool Hacker::msgGame(const GameMessage &msg) {
 		if (questNum >= 15 && questNum <= 21)
 			line = map.checkQuestComplete();
 		else
-			line = STRING["maps.map36.hacker4"];
+			line = "\"Sorry, you're already quested.\"";
 
 		g_maps->_mapPos.x--;
 		map.redrawGame();
@@ -93,11 +94,8 @@ bool Hacker::msgKeypress(const KeypressMessage &msg) {
 
 			_mode = ACCEPTED_QUEST;
 			clearButtons();
-			Common::String line = Common::String::format("%s %s",
-				STRING["maps.map36.hacker3"].c_str(),
-				STRING[Common::String::format(
-					"maps.map36.ingredients.%d",
-					g_globals->_party[0]._quest - 15)].c_str()
+			Common::String line = Common::String::format("Bring me %s",
+				INGREDIENTS[g_globals->_party[0]._quest - 15]
 			);
 			addText(line);
 
diff --git a/engines/mm/mm1/views_enh/interactions/ice_princess.cpp b/engines/mm/mm1/views_enh/interactions/ice_princess.cpp
index a7404108c71..76fef939b40 100644
--- a/engines/mm/mm1/views_enh/interactions/ice_princess.cpp
+++ b/engines/mm/mm1/views_enh/interactions/ice_princess.cpp
@@ -29,8 +29,9 @@ namespace ViewsEnh {
 namespace Interactions {
 
 IcePrincess::IcePrincess() : InteractionQuery("IcePrincess", 10, 19) {
-	_title = STRING["maps.emap19.title"];
-	addText(STRING["maps.emap19.ice_princess"]);
+	_title = "Ice Princess";
+	addText("On a throne adorned with precious gems the ice princess speaks, \"Conqueror of worlds, "
+		"maker of dreams The greatest force of all, yet elusive it seems.\"");
 }
 
 void IcePrincess::answerEntered() {
diff --git a/engines/mm/mm1/views_enh/interactions/inspectron.cpp b/engines/mm/mm1/views_enh/interactions/inspectron.cpp
index 6e8eb74848d..eeb3e270f90 100644
--- a/engines/mm/mm1/views_enh/interactions/inspectron.cpp
+++ b/engines/mm/mm1/views_enh/interactions/inspectron.cpp
@@ -20,9 +20,11 @@
  */
 
 #include "mm/mm1/views_enh/interactions/inspectron.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map35.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
+#include "mm/mm1/utils/strings.h"
 
 namespace MM {
 namespace MM1 {
@@ -30,7 +32,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Inspectron::Inspectron() : Interaction("Inspectron", 18) {
-	_title = STRING["maps.emap35.inspectron_title"];
+	_title = "Lord Inspectron";
 }
 
 bool Inspectron::msgFocus(const FocusMessage &msg) {
@@ -51,9 +53,9 @@ bool Inspectron::msgGame(const GameMessage &msg) {
 	clearButtons();
 
 	if (_mode == CAN_ACCEPT) {
-		addText(STRING["maps.map35.inspectron2"]);
-		addButton(STRING["maps.accept"], 'Y');
-		addButton(STRING["maps.decline"], 'N');
+		addText("\"Your services are needed!\" Accept (Y/N)?");
+		addButton("Accept", 'Y');
+		addButton("Decline", 'N');
 
 	} else {
 		// There's an active quest, so check for completion
@@ -64,7 +66,7 @@ bool Inspectron::msgGame(const GameMessage &msg) {
 		if (questNum >= 8 && questNum <= 14)
 			line = map.checkQuestComplete();
 		else
-			line = STRING["maps.map35.inspectron4"];
+			line = "\"Sorry, but since you are currently quested, i can't engage your services.\"";
 
 		g_maps->_mapPos.y++;
 		map.redrawGame();
@@ -84,10 +86,7 @@ bool Inspectron::msgKeypress(const KeypressMessage &msg) {
 			_mode = ACCEPTED_QUEST;
 
 			clearButtons();
-			addText(STRING[Common::String::format(
-				"maps.map35.quests.%d",
-				g_globals->_party[0]._quest - 8
-			)]);
+			addText(mapText(INSPECTRON_QUESTS[g_globals->_party[0]._quest - 8]));
 
 			redraw();
 			return true;
diff --git a/engines/mm/mm1/views_enh/interactions/keeper.cpp b/engines/mm/mm1/views_enh/interactions/keeper.cpp
index 3b9db4d5aee..6145c846424 100644
--- a/engines/mm/mm1/views_enh/interactions/keeper.cpp
+++ b/engines/mm/mm1/views_enh/interactions/keeper.cpp
@@ -30,7 +30,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Keeper::Keeper() : Interaction("Keeper", 10) {
-	_title = STRING["maps.emap54.keeper"];
+	_title = "Keeper";
 	_animated = false;
 }
 
@@ -38,7 +38,8 @@ bool Keeper::msgFocus(const FocusMessage &msg) {
 	Interaction::msgFocus(msg);
 
 	_pageNum = 0;
-	addText(STRING["maps.map54.keeper1"]);
+	addText("A metallic panel slides open, revealing a mysterious man in a white coat... (Press Space "
+		"to communicate)");
 	return true;
 }
 
@@ -47,7 +48,9 @@ void Keeper::viewAction() {
 
 	switch (++_pageNum) {
 	case 1:
-		addText(STRING["maps.emap54.keeper2"]);
+		addText("In a serene voice, the data keeper says, \"Welcome to your inner sanctum Varnlings! I am "
+			"very pleased that you've made it this far. You are to be commended. I've been monitoring "
+			"your progress.\"");
 		redraw();
 		break;
 
@@ -56,14 +59,17 @@ void Keeper::viewAction() {
 		_isWorthy = map.isWorthy(perfTotal);
 
 		addText(Common::String::format(
-			STRING["maps.emap54.keeper3"].c_str(), perfTotal));
+			"Turning to the strange mechanical device, he inserts a flat object into a slot. Your "
+				"party's current performance total=%u.", perfTotal));
 		redraw();
 		break;
 	}
 
 	case 3:
-		addText(STRING[_isWorthy ? "maps.emap54.keeper5" :
-			"maps.emap54.keeper4"].c_str());
+		addText(_isWorthy ?
+			"Excellent rating! this is a rare occasion, for only a privileged few are given the "
+				"opportunity for transfer to another V.A.R.N. (Vehicular Astropod Research Nacelle)." :
+			"\"You are not yet worthy for transfer. Return after defeating the imposter.\"");
 		redraw();
 		break;
 
@@ -72,7 +78,9 @@ void Keeper::viewAction() {
 			leave();
 			map.sorpigalInn();
 		} else {
-			addText(STRING["maps.map54.keeper6"]);
+			addText("Return now to the\n"
+				"Inn of Sorpigal for rest and celebration then continue on to your new assignment at the "
+				"Gates To Another World...\" (+50000 exp for your accomplishment!)");
 			redraw();
 		}
 		break;
diff --git a/engines/mm/mm1/views_enh/interactions/leprechaun.cpp b/engines/mm/mm1/views_enh/interactions/leprechaun.cpp
index 3e8063e1709..294b5e65332 100644
--- a/engines/mm/mm1/views_enh/interactions/leprechaun.cpp
+++ b/engines/mm/mm1/views_enh/interactions/leprechaun.cpp
@@ -28,13 +28,14 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Leprechaun::Leprechaun() : Interaction("Leprechaun", 15) {
-	_title = STRING["maps.emap00.leprechaun_title"];
-	addText(STRING["maps.emap00.leprechaun"]);
-	addButton(STRING["stats.towns.1"], '1');
-	addButton(STRING["stats.towns.2"], '2');
-	addButton(STRING["stats.towns.3"], '3');
-	addButton(STRING["stats.towns.4"], '4');
-	addButton(STRING["stats.towns.5"], '5');
+	_title = "Leprechaun";
+	addText("A tenacious leprechaun appears saying, \"Traveling the roads is quite dangerous. Only 1 "
+		"gem and i'll teleport you.\"");
+	addButton("Sorpigal", '1');
+	addButton("Portsmith", '2');
+	addButton("Algary", '3');
+	addButton("Dusk", '4');
+	addButton("Erliquin", '5');
 }
 
 bool Leprechaun::msgFocus(const FocusMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/interactions/lion.cpp b/engines/mm/mm1/views_enh/interactions/lion.cpp
index d77b942301c..e66a87280dc 100644
--- a/engines/mm/mm1/views_enh/interactions/lion.cpp
+++ b/engines/mm/mm1/views_enh/interactions/lion.cpp
@@ -29,8 +29,8 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Lion::Lion() : InteractionQuery("Lion", 10) {
-	_title = STRING["maps.emap32.statue_title"];
-	addText(STRING["maps.emap32.statue"]);
+	_title = "Lion Statue";
+	addText("Statue of a lion growls, \"Password?\" ");
 }
 
 void Lion::answerEntered() {
diff --git a/engines/mm/mm1/views_enh/interactions/lord_archer.cpp b/engines/mm/mm1/views_enh/interactions/lord_archer.cpp
index f22863d88a0..f9057ccc0aa 100644
--- a/engines/mm/mm1/views_enh/interactions/lord_archer.cpp
+++ b/engines/mm/mm1/views_enh/interactions/lord_archer.cpp
@@ -30,10 +30,11 @@ namespace ViewsEnh {
 namespace Interactions {
 
 LordArcher::LordArcher() : Interaction("LordArcher", 33) {
-	_title = STRING["maps.emap40.title"];
-	addText(STRING["maps.emap40.archer"]);
-	addButton(STRING["maps.yes"], 'Y');
-	addButton(STRING["maps.no"], 'N');
+	_title = "Lord Archer";
+	addText("\"I am Lord Archer, the Raven. I take from those who have and give to those who have not! "
+		"Submit (Y/N)?\"");
+	addButton("\x01""37Yes", 'Y');
+	addButton("\x01""37No", 'N');
 }
 
 bool LordArcher::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/interactions/lord_ironfist.cpp b/engines/mm/mm1/views_enh/interactions/lord_ironfist.cpp
index 0e27e45f732..30df43f08d5 100644
--- a/engines/mm/mm1/views_enh/interactions/lord_ironfist.cpp
+++ b/engines/mm/mm1/views_enh/interactions/lord_ironfist.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/interactions/lord_ironfist.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map43.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -30,7 +31,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 LordIronfist::LordIronfist() : Interaction("LordIronfist", 18) {
-	_title = STRING["maps.emap43.title"];
+	_title = "Lord Ironfist";
 }
 
 bool LordIronfist::msgFocus(const FocusMessage &msg) {
@@ -40,10 +41,7 @@ bool LordIronfist::msgFocus(const FocusMessage &msg) {
 	if (_mode == ACCEPTING) {
 		_mode = ACCEPTED_QUEST;
 
-		addText(STRING[Common::String::format(
-			"maps.map43.quests.%d",
-			g_globals->_party[0]._quest
-		)]);
+		addText(IRONFIST_QUESTS[g_globals->_party[0]._quest - 1]);
 
 	} else {
 		const Character &c = g_globals->_party[0];
@@ -53,13 +51,10 @@ bool LordIronfist::msgFocus(const FocusMessage &msg) {
 		if (_mode == CAN_ACCEPT) {
 			Sound::sound(SOUND_2);
 
-			addText(Common::String::format("%s%s",
-				STRING["maps.map43.ironfist1"].c_str(),
-				STRING["maps.map43.ironfist2"].c_str()
-			));
+			addText("Lord Ironfist speaks: \"Your services are needed!\" Accept (Y/N)?");
 
-			addButton(STRING["maps.accept"], 'Y');
-			addButton(STRING["maps.decline"], 'N');
+			addButton("Accept", 'Y');
+			addButton("Decline", 'N');
 
 		} else {
 			// There's an active quest, so check for completion
@@ -70,12 +65,11 @@ bool LordIronfist::msgFocus(const FocusMessage &msg) {
 			if (questNum < 8)
 				line = map.checkQuestComplete();
 			else
-				line = STRING["maps.map43.ironfist4"];
+				line = "\"Sorry, but since you are currently quested, i can't engage your services.\"";
 
 			g_maps->_mapPos.x++;
 
-			addText(Common::String::format("%s%s",
-				STRING["maps.map43.ironfist1"].c_str(),
+			addText(Common::String::format("Lord Ironfist speaks: %s",
 				line.c_str()
 			));
 		}
diff --git a/engines/mm/mm1/views_enh/interactions/orango.cpp b/engines/mm/mm1/views_enh/interactions/orango.cpp
index b489835a292..af7ec5c39a5 100644
--- a/engines/mm/mm1/views_enh/interactions/orango.cpp
+++ b/engines/mm/mm1/views_enh/interactions/orango.cpp
@@ -30,8 +30,9 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Orango::Orango() : InteractionQuery("Orango", 15, 13) {
-	_title = STRING["maps.emap48.title"];
-	addText(STRING["maps.emap48.orango1"]);
+	_title = "Strange Alien";
+	addText("A strange alien being in a silver jumpsuit proclaims, \"This is a soul maze and you are "
+		"its prisoner! To escape, you must find your captor's name hidden within these walls.\"");
 }
 
 void Orango::answerEntered() {
diff --git a/engines/mm/mm1/views_enh/interactions/prisoners.cpp b/engines/mm/mm1/views_enh/interactions/prisoners.cpp
index 15d0d16d930..651b843d19b 100644
--- a/engines/mm/mm1/views_enh/interactions/prisoners.cpp
+++ b/engines/mm/mm1/views_enh/interactions/prisoners.cpp
@@ -33,7 +33,7 @@ Prisoner::Prisoner(const Common::String &name, int portrait, const Common::Strin
 		byte flag, Alignment freeAlignment, Alignment leaveAlignment) :
 		Interaction(name, portrait), _line(line1), _flag(flag),
 		_freeAlignment(freeAlignment), _leaveAlignment(leaveAlignment) {
-	_title = STRING["maps.eprisoners.title"];
+	_title = "Prisoner";
 }
 
 bool Prisoner::msgFocus(const FocusMessage &msg) {
@@ -41,9 +41,9 @@ bool Prisoner::msgFocus(const FocusMessage &msg) {
 
 	addText(_line);
 	clearButtons();
-	addButton(STRING["maps.eprisoners.options1"], '1');
-	addButton(STRING["maps.eprisoners.options2"], '2');
-	addButton(STRING["maps.eprisoners.options3"], '3');
+	addButton("Set the prisoner free. ", '1');
+	addButton("Torment the prisoner. ", '2');
+	addButton("Leave without disturbing.", '3');
 
 	// Since the prisoner options are 1 to 3, disable party bindings
 	MetaEngine::setKeybindingMode(KeybindingMode::KBMODE_MENUS);
@@ -61,14 +61,14 @@ bool Prisoner::msgKeypress(const KeypressMessage &msg) {
 	int align;
 	switch (msg.keycode) {
 	case Common::KEYCODE_1:
-		line = STRING["maps.prisoners.flees"];
+		line = "The prisoner flees!";
 		align = _freeAlignment;
 		g_maps->clearSpecial();
 		flee();
 		break;
 
 	case Common::KEYCODE_2:
-		line = STRING["maps.prisoners.cowers"];
+		line = "The prisoner cowers!";
 		align = _leaveAlignment;
 		break;
 
@@ -109,33 +109,33 @@ void Prisoner::timeout() {
 /*------------------------------------------------------------------------*/
 
 ChildPrisoner::ChildPrisoner() :
-		Prisoner("ChildPrisoner", 34, STRING["maps.prisoners.child"],
+		Prisoner("ChildPrisoner", 34, "A small child shivers fearfully as you approach.  ",
 		CHARFLAG1_4, GOOD, EVIL) {
 }
 
 ManPrisoner::ManPrisoner() :
-	Prisoner("ManPrisoner", 23, STRING["maps.prisoners.man"],
+	Prisoner("ManPrisoner", 23, "Man in shackles moans in agony!  ",
 	CHARFLAG1_20, EVIL, GOOD) {
 }
 
 CloakedPrisoner::CloakedPrisoner() :
-	Prisoner("CloakedPrisoner", 16, STRING["maps.prisoners.cloaked"],
+	Prisoner("CloakedPrisoner", 16, "A mysterious cloaked figure, bound and gagged, lies motionless in the corner.  ",
 		CHARFLAG1_40, EVIL, GOOD) {
 	_animated = false;
 }
 
 DemonPrisoner::DemonPrisoner() :
-	Prisoner("DemonPrisoner", 41, STRING["maps.prisoners.demon"],
+	Prisoner("DemonPrisoner", 41, "A vicious demon lunges at the party, restrained only by a collar and chain!  ",
 		CHARFLAG1_10, EVIL, GOOD) {
 }
 
 MutatedPrisoner::MutatedPrisoner() :
-	Prisoner("MutatedPrisoner", 1, STRING["maps.prisoners.mutated"],
+	Prisoner("MutatedPrisoner", 1, "Mutated creature chained to the floor grunts as you approach.  ",
 		CHARFLAG1_2, GOOD, EVIL) {
 }
 
 MaidenPrisoner::MaidenPrisoner() :
-	Prisoner("MaidenPrisoner", 2, STRING["maps.prisoners.maiden"],
+	Prisoner("MaidenPrisoner", 2, "Inside a steel cage,a fair maiden weeps.  ",
 		CHARFLAG1_8, GOOD, EVIL) {
 }
 
@@ -147,17 +147,17 @@ void MaidenPrisoner::flee() {
 /*------------------------------------------------------------------------*/
 
 VirginPrisoner::VirginPrisoner() : Interaction("VirginPrisoner", 2) {
-	addText(STRING["maps.emap11.virgin"]);
-	addButton(STRING["maps.emap11.virgin_a"], 'A');
-	addButton(STRING["maps.emap11.virgin_b"], 'B');
-	addButton(STRING["maps.emap11.virgin_c"], 'C');
+	addText("A luscious virgin shackled to the wall!");
+	addButton("Kiss her", 'A');
+	addButton("Release her", 'B');
+	addButton("Leave", 'C');
 }
 
 bool VirginPrisoner::msgKeypress(const KeypressMessage &msg) {
 	switch (msg.keycode) {
 	case Common::KEYCODE_a:
 		g_events->close();
-		g_events->send(SoundMessage(STRING["maps.map11.tip1"]));
+		g_events->send(SoundMessage("Try setting 1B,2J"));
 		break;
 	case Common::KEYCODE_b:
 		g_events->close();
diff --git a/engines/mm/mm1/views_enh/interactions/resistances.cpp b/engines/mm/mm1/views_enh/interactions/resistances.cpp
index b2e15f75875..5d37b38a4b9 100644
--- a/engines/mm/mm1/views_enh/interactions/resistances.cpp
+++ b/engines/mm/mm1/views_enh/interactions/resistances.cpp
@@ -29,7 +29,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Resistances::Resistances() : Interaction("Resistances", 4) {
-	_title = STRING["maps.emap02.resistances"];
+	_title = "Resistances";
 }
 
 bool Resistances::msgFocus(const FocusMessage &msg) {
@@ -46,22 +46,22 @@ void Resistances::draw() {
 		const Character &c = *g_globals->_currCharacter;
 
 		setReduced(true);
-		writeLine(0, STRING["maps.emap02.magic"], ALIGN_LEFT, 0);
+		writeLine(0, "Magic", ALIGN_LEFT, 0);
 		writeLine(0, c._resistances._s._magic, ALIGN_RIGHT, 45);
-		writeLine(0, STRING["maps.emap02.fire"], ALIGN_LEFT, 50);
+		writeLine(0, "Fire", ALIGN_LEFT, 50);
 		writeLine(0, c._resistances._s._fire, ALIGN_RIGHT, 90);
-		writeLine(0, STRING["maps.emap02.cold"], ALIGN_LEFT, 95);
+		writeLine(0, "Cold", ALIGN_LEFT, 95);
 		writeLine(0, c._resistances._s._cold, ALIGN_RIGHT, 145);
-		writeLine(0, STRING["maps.emap02.electricity"], ALIGN_LEFT, 150);
+		writeLine(0, "Elc", ALIGN_LEFT, 150);
 		writeLine(0, c._resistances._s._electricity, ALIGN_RIGHT, 195);
 
-		writeLine(1, STRING["maps.emap02.acid"], ALIGN_LEFT, 0);
+		writeLine(1, "Acid", ALIGN_LEFT, 0);
 		writeLine(1, c._resistances._s._acid, ALIGN_RIGHT, 45);
-		writeLine(1, STRING["maps.emap02.fear"], ALIGN_LEFT, 50);
+		writeLine(1, "Fear", ALIGN_LEFT, 50);
 		writeLine(1, c._resistances._s._fear, ALIGN_RIGHT, 90);
-		writeLine(1, STRING["maps.emap02.poison"], ALIGN_LEFT, 95);
+		writeLine(1, "Poison", ALIGN_LEFT, 95);
 		writeLine(1, c._resistances._s._poison, ALIGN_RIGHT, 145);
-		writeLine(1, STRING["maps.emap02.sleep"], ALIGN_LEFT, 150);
+		writeLine(1, "Sleep", ALIGN_LEFT, 150);
 		writeLine(1, c._resistances._s._psychic, ALIGN_RIGHT, 195);
 	}
 }
diff --git a/engines/mm/mm1/views_enh/interactions/ruby.cpp b/engines/mm/mm1/views_enh/interactions/ruby.cpp
index 7e68e626718..a1c472df706 100644
--- a/engines/mm/mm1/views_enh/interactions/ruby.cpp
+++ b/engines/mm/mm1/views_enh/interactions/ruby.cpp
@@ -29,8 +29,9 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Ruby::Ruby() : InteractionQuery("Ruby", 12) {
-	_title = STRING["maps.emap39.title"];
-	addText(STRING["maps.emap39.ruby1"]);
+	_title = "Ruby";
+	addText("In the center of the room, a huge hexagonal ruby stands glowing atop a pedestal. You "
+		"hear: Glass that glitters, rubies that glow when i twinkle, I cast a rainbow. What am i?");
 }
 
 void Ruby::answerEntered() {
diff --git a/engines/mm/mm1/views_enh/interactions/scummvm.cpp b/engines/mm/mm1/views_enh/interactions/scummvm.cpp
index f9d99c9c395..0b0bc65fdca 100644
--- a/engines/mm/mm1/views_enh/interactions/scummvm.cpp
+++ b/engines/mm/mm1/views_enh/interactions/scummvm.cpp
@@ -29,8 +29,9 @@ namespace ViewsEnh {
 namespace Interactions {
 
 ScummVM::ScummVM() : Interaction("ScummVM", 38) {
-	_title = STRING["maps.map55.title"];
-	addText(STRING["maps.map55.message"]);
+	_title = "ScummVM";
+	addText("Well done! I didn't think anyone would ever find this secret map. You deserve a hefty "
+		"reward. +10000 Gold, +1000 Gems each");
 }
 
 void ScummVM::viewAction() {
diff --git a/engines/mm/mm1/views_enh/interactions/statue.cpp b/engines/mm/mm1/views_enh/interactions/statue.cpp
index cedb5e2f0d7..571d06c69d8 100644
--- a/engines/mm/mm1/views_enh/interactions/statue.cpp
+++ b/engines/mm/mm1/views_enh/interactions/statue.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/interactions/statue.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 
 
@@ -29,7 +30,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Statue::Statue() : Interaction("Statue", 33) {
-	_title = STRING["dialogs.statues.statue"];
+	_title = "Statue";
 	_animated = false;
 }
 
@@ -45,13 +46,9 @@ bool Statue::msgGame(const GameMessage &msg) {
 }
 
 bool Statue::msgFocus(const FocusMessage &msg) {
-	Common::String statueType = STRING[Common::String::format(
-		"dialogs.statues.names.%d", _statueNum)];
-	Common::String str = Common::String::format("%s%s. %s",
-		STRING["dialogs.statues.stone"].c_str(),
-		statueType.c_str(),
-		STRING["dialogs.statues.plaque"].c_str()
-	);
+	Common::String statueType = STATUE_NAMES[_statueNum];
+	Common::String str = Common::String::format("On this stone statue of %s. A plaque reads...",
+		statueType.c_str());
 
 	addText(str);
 	return true;
@@ -60,8 +57,7 @@ bool Statue::msgFocus(const FocusMessage &msg) {
 void Statue::viewAction() {
 	switch (++_pageNum) {
 	case 1:
-		addText(STRING[Common::String::format(
-			"dialogs.statues.messages.%d", _statueNum)]);
+		addText(STATUE_MESSAGES[_statueNum]);
 		redraw();
 		break;
 	default:
diff --git a/engines/mm/mm1/views_enh/interactions/trivia.cpp b/engines/mm/mm1/views_enh/interactions/trivia.cpp
index c9c5a07f520..5573482e75d 100644
--- a/engines/mm/mm1/views_enh/interactions/trivia.cpp
+++ b/engines/mm/mm1/views_enh/interactions/trivia.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/interactions/trivia.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/maps/map21.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/sound.h"
@@ -30,15 +31,13 @@ namespace ViewsEnh {
 namespace Interactions {
 
 Trivia::Trivia() : InteractionQuery("Trivia", 14) {
-	_title = STRING["maps.emap21.title"];
+	_title = "Trivia Island";
 }
 
 bool Trivia::msgGame(const GameMessage &msg) {
 	if (msg._name == "TRIVIA") {
-		_question = STRING[Common::String::format(
-			"maps.map21.questions.%d", msg._value)];
-		_correctAnswer = STRING[Common::String::format(
-			"maps.map21.answers.%d", msg._value)];
+		_question = TRIVIA_QUESTIONS[msg._value];
+		_correctAnswer = TRIVIA_ANSWERS[msg._value];
 
 		addText(_question);
 		open();
@@ -51,7 +50,7 @@ bool Trivia::msgGame(const GameMessage &msg) {
 void Trivia::answerEntered() {
 	if (_answer.equalsIgnoreCase(_correctAnswer)) {
 		g_maps->clearSpecialAndRememberReward();
-		send(InfoMessage(STRING["maps.map21.correct"]));
+		send(InfoMessage("Correct! +50 gems"));
 		g_globals->_party[0]._gems = MIN(
 			(int)g_globals->_party[0]._gems + 50, 0xffff);
 		Sound::sound(SOUND_3);
@@ -59,7 +58,7 @@ void Trivia::answerEntered() {
 	} else {
 		g_maps->_mapPos.x = 15;
 		g_maps->_currentMap->updateGame();
-		send(InfoMessage(STRING["maps.map21.incorrect"]));
+		send(InfoMessage("Wrong!"));
 	}
 }
 
diff --git a/engines/mm/mm1/views_enh/interactions/volcano_god.cpp b/engines/mm/mm1/views_enh/interactions/volcano_god.cpp
index 6bf33addcf6..f1bba5d5a86 100644
--- a/engines/mm/mm1/views_enh/interactions/volcano_god.cpp
+++ b/engines/mm/mm1/views_enh/interactions/volcano_god.cpp
@@ -30,7 +30,7 @@ namespace ViewsEnh {
 namespace Interactions {
 
 VolcanoGod::VolcanoGod() : InteractionQuery("VolcanoGod", 8, 10) {
-	_title = STRING["maps.emap11.volcano_god"];
+	_title = "Volcano God";
 }
 
 bool VolcanoGod::msgFocus(const FocusMessage &msg) {
@@ -73,13 +73,13 @@ void VolcanoGod::setMode(Mode newMode) {
 
 	switch (_mode) {
 	case CHOOSE_OPTION:
-		addText(STRING["maps.emap11.god_text"]);
-		addButton(STRING["maps.emap11.god_a"], 'A');
-		addButton(STRING["maps.emap11.god_b"], 'B');
-		addButton(STRING["maps.emap11.god_c"], 'C');
+		addText("The Volcano God bellows, \"What do you seek here?\"");
+		addButton("Challenge", 'A');
+		addButton("A Riddle", 'B');
+		addButton("A Clue", 'C');
 		break;
 	case ENTER_RESPONSE:
-		addText(STRING["maps.emap11.question"]);
+		addText("Who was brave yet failed?\"");
 		_showEntry = true;
 		break;
 	}
diff --git a/engines/mm/mm1/views_enh/items_view.cpp b/engines/mm/mm1/views_enh/items_view.cpp
index b1742910e0f..338c92f34da 100644
--- a/engines/mm/mm1/views_enh/items_view.cpp
+++ b/engines/mm/mm1/views_enh/items_view.cpp
@@ -98,7 +98,7 @@ void ItemsView::draw() {
 		}
 	}
 	if (_items.size() == 0)
-		writeLine(2, STRING["enhdialogs.misc.no_items"], ALIGN_LEFT, 10);
+		writeLine(2, "Inventory empty", ALIGN_LEFT, 10);
 	setTextColor(0);
 }
 
@@ -155,11 +155,11 @@ void ItemsView::timeout() {
 }
 
 void ItemsView::backpackFull() {
-	displayMessage(STRING["dialogs.misc.backpack_full"]);
+	displayMessage("Backpack is full");
 }
 
 void ItemsView::notEnoughGold() {
-	displayMessage(STRING["dialogs.misc.not_enough_gold"]);
+	displayMessage("Not enough gold");
 }
 
 void ItemsView::displayMessage(const Common::String &msg) {
diff --git a/engines/mm/mm1/views_enh/locations/blacksmith.cpp b/engines/mm/mm1/views_enh/locations/blacksmith.cpp
index c638016c079..e338e8ebc8d 100644
--- a/engines/mm/mm1/views_enh/locations/blacksmith.cpp
+++ b/engines/mm/mm1/views_enh/locations/blacksmith.cpp
@@ -41,18 +41,18 @@ void Blacksmith::draw() {
 	const Character &c = *g_globals->_currCharacter;
 
 	setReduced(false);
-	writeLine(0, STRING["enhdialogs.location.store"], ALIGN_MIDDLE);
-	writeLine(1, STRING["enhdialogs.location.options_for"], ALIGN_MIDDLE);
+	writeLine(0, "Store", ALIGN_MIDDLE);
+	writeLine(1, "options for", ALIGN_MIDDLE);
 	writeLine(3, c._name, ALIGN_MIDDLE);
 
-	writeLine(6, STRING["enhdialogs.blacksmith.browse"], ALIGN_LEFT, 23);
+	writeLine(6, "\x01""37Browse", ALIGN_LEFT, 23);
 
-	writeLine(10, STRING["enhdialogs.location.gold"]);
+	writeLine(10, "Gold");
 	writeLine(10, Common::String::format("%d",
 		g_globals->_currCharacter->_gold), ALIGN_RIGHT);
 
 	setReduced(true);
-	writeString(27, 122, STRING["enhdialogs.location.esc"]);
+	writeString(27, 122, "Esc");
 }
 
 bool Blacksmith::msgKeypress(const KeypressMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/locations/blacksmith_items.cpp b/engines/mm/mm1/views_enh/locations/blacksmith_items.cpp
index 7326f4adc85..f513db9d508 100644
--- a/engines/mm/mm1/views_enh/locations/blacksmith_items.cpp
+++ b/engines/mm/mm1/views_enh/locations/blacksmith_items.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/locations/blacksmith_items.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/views_enh/locations/blacksmith.h"
 #include "mm/mm1/views_enh/confirm.h"
 #include "mm/mm1/globals.h"
@@ -31,11 +32,11 @@ namespace Locations {
 
 BlacksmithItems::BlacksmithItems() : ItemsView("BlacksmithItems") {
 	_btnSprites.load("buy.icn");
-	addButton(0, STRING["enhdialogs.blacksmith.buttons.weapons"], Common::KEYCODE_w);
-	addButton(2, STRING["enhdialogs.blacksmith.buttons.armor"], Common::KEYCODE_a);
-	addButton(6, STRING["enhdialogs.blacksmith.buttons.misc"], Common::KEYCODE_m);
-	addButton(10, STRING["enhdialogs.blacksmith.buttons.sell"], Common::KEYCODE_s);
-	addButton(12, STRING["enhdialogs.misc.exit"], Common::KEYCODE_ESCAPE);
+	addButton(0, "\x01""37Weap", Common::KEYCODE_w);
+	addButton(2, "\x01""37Armor", Common::KEYCODE_a);
+	addButton(6, "\x01""37Misc", Common::KEYCODE_m);
+	addButton(10, "\x01""37Sell", Common::KEYCODE_s);
+	addButton(12, "Exit", Common::KEYCODE_ESCAPE);
 }
 
 bool BlacksmithItems::msgFocus(const FocusMessage &msg) {
@@ -58,18 +59,18 @@ void BlacksmithItems::draw() {
 void BlacksmithItems::drawTitle() {
 	const Character &c = *g_globals->_currCharacter;
 
-	Common::String areaName = STRING["enhdialogs.blacksmith.areas.weapons"];
+	Common::String areaName = "Weapons";
 	if (_mode == ARMOR_MODE)
-		areaName = STRING["enhdialogs.blacksmith.areas.armor"];
+		areaName = "Armor";
 	if (_mode == MISC_MODE)
-		areaName = STRING["enhdialogs.blacksmith.areas.misc"];
+		areaName = "Misc";
 
 	if (_mode == SELL_MODE) {
 		// Show sell mode title
 		Common::String title = Common::String::format(
-			STRING["enhdialogs.items.backpack_for"].c_str(),
+			"Backpack for %s the %s",
 			c._name,
-			STRING[Common::String::format("stats.classes.%d", c._class)].c_str()
+			CLASS_NAMES[c._class - 1]
 		);
 
 		setReduced(false);
@@ -78,17 +79,15 @@ void BlacksmithItems::drawTitle() {
 	} else {
 		// Show title with "Available <Area>", "Gold", and "Cost"
 		setReduced(false);
-		Common::String title = Common::String::format("%s %s",
-			STRING["enhdialogs.blacksmith.available"].c_str(),
+		Common::String title = Common::String::format("Available %s",
 			areaName.c_str());
 		writeString(0, 0, title);
 
-		Common::String gold = Common::String::format("%s - %d",
-			STRING["enhdialogs.blacksmith.gold"].c_str(),
+		Common::String gold = Common::String::format("Gold - %d",
 			c._gold);
 		writeString(160, 0, gold);
 
-		writeString(0, 0, STRING["enhdialogs.blacksmith.cost"], ALIGN_RIGHT);
+		writeString(0, 0, "Cost", ALIGN_RIGHT);
 	}
 }
 
@@ -167,14 +166,14 @@ void BlacksmithItems::itemSelected() {
 	_buySellItem = _selectedItem;
 
 	if (_mode != SELL_MODE) {
-		buySell = STRING["enhdialogs.blacksmith.buy"];
+		buySell = "Buy";
 		gold = Common::String::format(
-			STRING["enhdialogs.blacksmith.for_gold"].c_str(),
+			"for %d gold",
 			item._cost);
 	} else {
-		buySell = STRING["enhdialogs.blacksmith.sell"];
+		buySell = "Sell";
 		gold = Common::String::format(
-			STRING["enhdialogs.blacksmith.for_gold"].c_str(),
+			"for %d gold",
 			item.getSellCost());
 	}
 
@@ -215,7 +214,7 @@ void BlacksmithItems::itemConfirmed() {
 			notEnoughGold();
 			break;
 		default:
-			displayMessage(STRING["dialogs.blacksmith.thankyou"]);
+			displayMessage("Thank you!");
 			break;
 		}
 	}
diff --git a/engines/mm/mm1/views_enh/locations/inn.cpp b/engines/mm/mm1/views_enh/locations/inn.cpp
index 7a77f5cc9b4..e868ce00a37 100644
--- a/engines/mm/mm1/views_enh/locations/inn.cpp
+++ b/engines/mm/mm1/views_enh/locations/inn.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/locations/inn.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
@@ -83,15 +84,14 @@ void Inn::draw() {
 
 	// Write title
 	Common::String title = Common::String::format(
-		STRING["dialogs.inn.title"].c_str(),
+		"(%c) Inn of ",
 		'0' + g_globals->_startingTown);
-	title += STRING[Common::String::format(
-		"stats.towns.%d", g_globals->_startingTown)];
+	title += TOWN_NAMES[g_globals->_startingTown - 1];
 	setReduced(false);
 	writeString(0, 0, title, ALIGN_MIDDLE);
 
 	if (_charNums.empty()) {
-		writeString(0, 40, STRING["dialogs.misc.no_characters"], ALIGN_MIDDLE);
+		writeString(0, 40, "No available characters", ALIGN_MIDDLE);
 
 	} else {
 		// Write out the roster
@@ -122,17 +122,17 @@ void Inn::draw() {
 
 		setReduced(false);
 		if (_partyChars.size() == 6)
-			writeString(0, 130, STRING["dialogs.inn.full"], ALIGN_MIDDLE);
+			writeString(0, 130, "*** Party is full ***", ALIGN_MIDDLE);
 
-		writeString(0, 142, STRING["enhdialogs.inn.left_click"], ALIGN_MIDDLE);
-		writeString(0, 152, STRING["enhdialogs.inn.right_click"], ALIGN_MIDDLE);
+		writeString(0, 142, "Left click potraits to add/remove", ALIGN_MIDDLE);
+		writeString(0, 152, "Right click to view", ALIGN_MIDDLE);
 	}
 
 	if (!_partyChars.empty()) {
-		writeString(100, 168, STRING["enhdialogs.inn.exit"]);
-		writeString(170, 168, STRING["enhdialogs.misc.go_back"]);
+		writeString(100, 168, "Exit Inn");
+		writeString(170, 168, "Go back");
 	} else {
-		writeString(134, 168, STRING["enhdialogs.misc.go_back"]);
+		writeString(134, 168, "Go back");
 	}
 }
 
diff --git a/engines/mm/mm1/views_enh/locations/location.cpp b/engines/mm/mm1/views_enh/locations/location.cpp
index b7fd31f1df3..9976cfe507f 100644
--- a/engines/mm/mm1/views_enh/locations/location.cpp
+++ b/engines/mm/mm1/views_enh/locations/location.cpp
@@ -79,12 +79,12 @@ bool Location::subtractGold(uint amount) {
 
 void Location::notEnoughGold() {
 	Sound::sound(SOUND_2);
-	displayMessage(STRING["dialogs.misc.not_enough_gold"]);
+	displayMessage("Not enough gold");
 }
 
 void Location::backpackFull() {
 	Sound::sound(SOUND_2);
-	displayMessage(STRING["dialogs.misc.backpack_full"]);
+	displayMessage("Backpack is full");
 }
 
 bool Location::msgUnfocus(const UnfocusMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/locations/market.cpp b/engines/mm/mm1/views_enh/locations/market.cpp
index d26419c7686..786ee032f47 100644
--- a/engines/mm/mm1/views_enh/locations/market.cpp
+++ b/engines/mm/mm1/views_enh/locations/market.cpp
@@ -49,14 +49,14 @@ bool Market::msgFocus(const FocusMessage &msg) {
 void Market::draw() {
 	Location::draw();
 
-	writeLine(0, STRING["enhdialogs.market.title"],ALIGN_MIDDLE);
-	writeLine(1, STRING["enhdialogs.location.options"], ALIGN_MIDDLE);
+	writeLine(0, "Market",ALIGN_MIDDLE);
+	writeLine(1, "options", ALIGN_MIDDLE);
 
-	writeLine(6, STRING["enhdialogs.market.buy_food"], ALIGN_MIDDLE);
-	writeLine(7, Common::String::format("%d %s",
-		_foodCost, STRING["dialogs.market.gp"].c_str()),
+	writeLine(6, "Buy food", ALIGN_MIDDLE);
+	writeLine(7, Common::String::format("%d GP/ea",
+		_foodCost),
 		ALIGN_MIDDLE);
-	writeLine(10, STRING["enhdialogs.location.gold"], ALIGN_LEFT);
+	writeLine(10, "Gold", ALIGN_LEFT);
 
 	uint gold = g_globals->_party.getPartyGold();
 	writeLine(10, Common::String::format("%d", gold), ALIGN_RIGHT);
@@ -105,8 +105,8 @@ void Market::buyFood() {
 	}
 
 	displayMessage(numPurchases ?
-		STRING["enhdialogs.market.thankyou"] :
-		STRING["enhdialogs.market.no_gold"]
+		"Thank you, come again!" :
+		"No gold, no food!"
 	);
 }
 
diff --git a/engines/mm/mm1/views_enh/locations/tavern.cpp b/engines/mm/mm1/views_enh/locations/tavern.cpp
index b4690de12c8..89a6cd63922 100644
--- a/engines/mm/mm1/views_enh/locations/tavern.cpp
+++ b/engines/mm/mm1/views_enh/locations/tavern.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/locations/tavern.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 #include "mm/mm1/mm1.h"
 #include "mm/mm1/sound.h"
@@ -43,21 +44,21 @@ void Tavern::draw() {
 	const Character &c = *g_globals->_currCharacter;
 
 	setReduced(false);
-	writeLine(0, STRING["enhdialogs.tavern.title"], ALIGN_MIDDLE);
-	writeLine(1, STRING["enhdialogs.location.options_for"], ALIGN_MIDDLE);
+	writeLine(0, "Tavern", ALIGN_MIDDLE);
+	writeLine(1, "options for", ALIGN_MIDDLE);
 	writeString(0, 23, c._name, ALIGN_MIDDLE);
 
-	writeString(18, 46, STRING["enhdialogs.tavern.drink"]);
-	writeString(18, 57, STRING["enhdialogs.tavern.gather"]);
-	writeString(18, 68, STRING["enhdialogs.tavern.tip"]);
-	writeString(18, 79, STRING["enhdialogs.tavern.rumor"]);
+	writeString(18, 46, "\x01""37Drink");
+	writeString(18, 57, "\x01""37Gather");
+	writeString(18, 68, "\x01""37Tip");
+	writeString(18, 79, "\x01""37Rumors");
 
-	writeLine(10, STRING["enhdialogs.location.gold"]);
+	writeLine(10, "Gold");
 	writeLine(10, Common::String::format("%d",
 		g_globals->_currCharacter->_gold), ALIGN_RIGHT);
 
 	setReduced(true);
-	writeString(27, 122, STRING["enhdialogs.location.esc"]);
+	writeString(27, 122, "Esc");
 }
 
 bool Tavern::msgGame(const GameMessage &msg) {
@@ -116,52 +117,47 @@ void Tavern::messageShown() {
 void Tavern::haveADrink() {
 	if (g_globals->_currCharacter->_condition) {
 		Sound::sound(SOUND_2);
-		displayMessage(STRING["dialogs.tavern.terrible"]);
+		displayMessage("You look terrible, go see the clerics.");
 
 	} else if (subtractGold(1)) {
 		if (++g_globals->_currCharacter->_numDrinks < 3 ||
 			g_engine->getRandomNumber(10) <
 			g_globals->_currCharacter->_endurance) {
-			displayMessage(STRING["dialogs.tavern.great_stuff"]);
+			displayMessage("Great stuff!");
 
 		} else {
 			if (!(g_globals->_currCharacter->_condition & BAD_CONDITION))
 				g_globals->_currCharacter->_condition |= POISONED;
 
 			Sound::sound(SOUND_2);
-			displayMessage(STRING["dialogs.tavern.you_feel_sick"]);
+			displayMessage("You feel sick!!");
 		}
 	}
 }
 
 void Tavern::tipBartender() {
 	if (g_globals->_currCharacter->_condition) {
-		displayMessage(STRING["dialogs.tavern.go_see_clerics"]);
+		displayMessage("You look terrible, go see the clerics.");
 
 	} else if (subtractGold(1)) {
 		if (g_globals->_currCharacter->_numDrinks == 0) {
-			displayMessage(STRING["dialogs.tavern.have_a_drink"]);
+			displayMessage("Have a drink, then we'll talk.");
 
 		} else if (g_engine->getRandomNumber(3) != 3) {
-			displayMessage(STRING["dialogs.tavern.have_another_round"]);
+			displayMessage("Thanks a lot, have another round!");
 
 		} else {
 			int townNum = g_maps->_currentMap->dataByte(Maps::MAP_ID);
-			displayMessage(STRING[Common::String::format(
-				"dialogs.tavern.tips.%d_%d",
-				townNum, g_globals->_currCharacter->_numDrinks
-			)]);
+			displayMessage(TAVERN_TIPS[townNum - 1][g_globals->_currCharacter->_numDrinks - 1]);
 		}
 	}
 }
 
 void Tavern::listenForRumors() {
-	Common::String msg = STRING["dialogs.tavern.rumors.none"];
+	Common::String msg = "No rumors today.";
 	if (!g_globals->_heardRumor) {
 		g_globals->_heardRumor = true;
-		msg = STRING[Common::String::format(
-			"dialogs.tavern.rumors.%d",
-			g_engine->getRandomNumber(16))];
+		msg = TAVERN_RUMORS[g_engine->getRandomNumber(16)];
 	}
 
 	displayMessage(msg);
diff --git a/engines/mm/mm1/views_enh/locations/temple.cpp b/engines/mm/mm1/views_enh/locations/temple.cpp
index db0694d232c..a7716e6b9bd 100644
--- a/engines/mm/mm1/views_enh/locations/temple.cpp
+++ b/engines/mm/mm1/views_enh/locations/temple.cpp
@@ -72,27 +72,27 @@ void Temple::draw() {
 	Location::draw();
 
 	setReduced(false);
-	writeLine(0, STRING["enhdialogs.temple.title"], ALIGN_MIDDLE);
-	writeLine(1, STRING["enhdialogs.location.options_for"], ALIGN_MIDDLE);
+	writeLine(0, "Temple", ALIGN_MIDDLE);
+	writeLine(1, "options for", ALIGN_MIDDLE);
 	writeString(0, 23, camelCase(g_globals->_currCharacter->_name), ALIGN_MIDDLE);
 
 	setReduced(true);
-	writeOption(0, 'H', STRING["enhdialogs.temple.heal"],
+	writeOption(0, 'H', "\x01""37Heal",
 		_healCost ? Common::String::format("%d", _healCost) : "----");
-	writeOption(1, 'U', STRING["enhdialogs.temple.uncurse"],
+	writeOption(1, 'U', "\x01""37Uncurse",
 		_uncurseCost ? Common::String::format("%d", _uncurseCost) : "----");
-	writeOption(2, 'R', STRING["enhdialogs.temple.realign"],
+	writeOption(2, 'R', "\x01""37Re-align",
 		_alignmentCost ? Common::String::format("%d", _alignmentCost) : "----");
-	writeOption(3, 'D', STRING["enhdialogs.temple.donate"],
+	writeOption(3, 'D', "\x01""37Donate",
 		Common::String::format("%d", _donateCost));
 
 	setReduced(false);
-	writeLine(10, STRING["enhdialogs.location.gold"]);
+	writeLine(10, "Gold");
 	writeLine(10, Common::String::format("%d",
 		g_globals->_currCharacter->_gold), ALIGN_RIGHT);
 
 	setReduced(true);
-	writeString(27, 122, STRING["enhdialogs.location.esc"]);
+	writeString(27, 122, "Esc");
 }
 
 void Temple::writeOption(int row, char c, const Common::String &text,
@@ -269,9 +269,9 @@ void Temple::donate() {
 				g_globals->_activeSpells._arr[i] = 75;
 
 			Sound::sound(SOUND_3);
-			displayMessage(STRING["dialogs.temple.protected"]);
+			displayMessage("*** Today you shall be protected!");
 		} else {
-			displayMessage(STRING["dialogs.temple.thankyou"]);
+			displayMessage("Thank you!");
 		}
 	}
 }
diff --git a/engines/mm/mm1/views_enh/locations/training.cpp b/engines/mm/mm1/views_enh/locations/training.cpp
index d4c8775239e..967a3703018 100644
--- a/engines/mm/mm1/views_enh/locations/training.cpp
+++ b/engines/mm/mm1/views_enh/locations/training.cpp
@@ -50,30 +50,30 @@ void Training::draw() {
 	const Character &c = *g_globals->_currCharacter;
 
 	setReduced(false);
-	writeLine(0, STRING["enhdialogs.training.title"], ALIGN_MIDDLE);
-	writeLine(1, STRING["enhdialogs.location.options"], ALIGN_MIDDLE);
+	writeLine(0, "Training", ALIGN_MIDDLE);
+	writeLine(1, "options", ALIGN_MIDDLE);
 
 	if (_currLevel >= MAX_LEVEL) {
 		writeLine(3, c._name, ALIGN_MIDDLE);
-		writeLine(5, STRING["dialogs.training.no_way"], ALIGN_MIDDLE);
+		writeLine(5, "No way!", ALIGN_MIDDLE);
 
 	} else if (_remainingExp > 0) {
 		writeLine(3, Common::String::format(
-			STRING["enhdialogs.training.needs"].c_str(),
+			"%s\nneeds %d experience for level %d.",
 			c._name, _remainingExp, _currLevel + 1), ALIGN_MIDDLE);
 
 	} else {
 		writeLine(3, Common::String::format(
-			STRING["enhdialogs.training.eligible"].c_str(),
+			"%s\nis eligible for level %d.",
 			c._name, _currLevel + 1), ALIGN_MIDDLE);
 	}
 
-	writeLine(10, STRING["enhdialogs.location.gold"]);
+	writeLine(10, "Gold");
 	writeLine(10, Common::String::format("%d", c._gold), ALIGN_RIGHT);
 
 	setReduced(true);
-	writeString(5, 122, STRING["enhdialogs.training.train"]);
-	writeString(43, 122, STRING["enhdialogs.training.esc"]);
+	writeString(5, 122, "\x01""37Train");
+	writeString(43, 122, "ESC");
 }
 
 bool Training::msgGame(const GameMessage &msg) {
@@ -163,7 +163,7 @@ void Training::train() {
 	if (c._condition) {
 		// Having a condition prevents characters from training
 		Sound::sound(SOUND_3);
-		displayMessage(STRING["dialogs.training.condition"]);
+		displayMessage("*** Check condition ***");
 
 	} else if (!_canAfford) {
 		// Can't afford training
@@ -175,16 +175,15 @@ void Training::train() {
 		Character::LevelIncrease lvl = c.increaseLevel();
 		Sound::sound(SOUND_2);
 
-		Common::String msg = Common::String::format("%s%d",
-			STRING["dialogs.training.congrats"].c_str(),
+		Common::String msg = Common::String::format("Congratulations! you are now level %d",
 			c._level._base);
 
-		msg = Common::String::format(STRING["dialogs.training.hp"].c_str(),
+		msg = Common::String::format("You gained %d hit points",
 			lvl._numHP);
 
 		if (lvl._numSpells != 0) {
 			msg += ". ";
-			msg += STRING["dialogs.training.new_spells"];
+			msg += "You gained new spells!";
 		}
 
 		displayMessage(msg);
diff --git a/engines/mm/mm1/views_enh/main_menu.cpp b/engines/mm/mm1/views_enh/main_menu.cpp
index c8b6f5d9fc6..d764d01ae01 100644
--- a/engines/mm/mm1/views_enh/main_menu.cpp
+++ b/engines/mm/mm1/views_enh/main_menu.cpp
@@ -111,13 +111,13 @@ void MainMenu::draw() {
 
 	drawCircles();
 
-	writeString(0, 100, STRING["dialogs.main_menu.title3"], ALIGN_MIDDLE);
-	writeString(0, 110, STRING["dialogs.main_menu.title4"], ALIGN_MIDDLE);
+	writeString(0, 100, "Options", ALIGN_MIDDLE);
+	writeString(0, 110, "-------", ALIGN_MIDDLE);
 
-	Common::String line1 = STRING["dialogs.main_menu.option1"];
-	Common::String line2 = STRING["dialogs.main_menu.option2"];
-	Common::String line31 = STRING["dialogs.main_menu.option3e1"];
-	Common::String line32 = STRING["dialogs.main_menu.option3e2"];
+	Common::String line1 = "'C'.......Create New Characters";
+	Common::String line2 = "'V'.......View All Characters";
+	Common::String line31 = "'1' to '5'";
+	Common::String line32 = "Go to Town";
 
 	writeString(80, 120, Common::String(line1.c_str(), line1.c_str() + 3));
 	writeString(80, 130, Common::String(line2.c_str(), line2.c_str() + 3));
@@ -133,15 +133,15 @@ void MainMenu::draw() {
 	writeString(110, 140, line32);
 	setTextColor(oldColor);
 
-	writeString(0, 165, STRING["dialogs.main_menu.copyright1"], ALIGN_MIDDLE);
-	writeString(0, 175, STRING["dialogs.main_menu.scummvm"], ALIGN_MIDDLE);
+	writeString(0, 165, "Copr. 1986,1987-Jon Van Caneghem", ALIGN_MIDDLE);
+	writeString(0, 175, "Enhanced version provided by ScummVM", ALIGN_MIDDLE);
 }
 
 MainMenu::Choice MainMenu::getChoiceAt(const Common::Point &pos) {
 	const int height = g_globals->_fontNormal.getFontHeight();
-	const Common::String line1 = STRING["dialogs.main_menu.option1"];
-	const Common::String line2 = STRING["dialogs.main_menu.option2"];
-	const Common::String line32 = STRING["dialogs.main_menu.option3e2"];
+	const Common::String line1 = "'C'.......Create New Characters";
+	const Common::String line2 = "'V'.......View All Characters";
+	const Common::String line32 = "Go to Town";
 	const Common::String create = Common::String(line1.c_str() + line1.findLastOf('.') + 1);
 	const Common::String view = Common::String(line2.c_str() + line2.findLastOf('.') + 1);
 
diff --git a/engines/mm/mm1/views_enh/map_popup.cpp b/engines/mm/mm1/views_enh/map_popup.cpp
index c870ac15fc9..38184042132 100644
--- a/engines/mm/mm1/views_enh/map_popup.cpp
+++ b/engines/mm/mm1/views_enh/map_popup.cpp
@@ -61,16 +61,16 @@ void MapPopup::draw() {
 	Common::String dir;
 	switch (g_maps->_forwardMask) {
 	case Maps::DIRMASK_N:
-		dir = STRING["enhdialogs.map.north"];
+		dir = "North";
 		break;
 	case Maps::DIRMASK_S:
-		dir = STRING["enhdialogs.map.south"];
+		dir = "South";
 		break;
 	case Maps::DIRMASK_E:
-		dir = STRING["enhdialogs.map.east"];
+		dir = "East";
 		break;
 	case Maps::DIRMASK_W:
-		dir = STRING["enhdialogs.map.west"];
+		dir = "West";
 		break;
 	default:
 		break;
diff --git a/engines/mm/mm1/views_enh/protect.cpp b/engines/mm/mm1/views_enh/protect.cpp
index 76438f4f1bf..cc68965bfbb 100644
--- a/engines/mm/mm1/views_enh/protect.cpp
+++ b/engines/mm/mm1/views_enh/protect.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/protect.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/game/combat.h"
 #include "mm/mm1/globals.h"
 
@@ -36,8 +37,8 @@ void Protect::draw() {
 	ScrollView::draw();
 
 	setReduced(false);
-	writeLine(0, STRING["dialogs.protect.title"], ALIGN_MIDDLE);
-	writeString(152, 172, STRING["enhdialogs.misc.go_back"]);
+	writeLine(0, "Protect:  Spells currently active", ALIGN_MIDDLE);
+	writeString(152, 172, "Go back");
 
 	setReduced(true);
 	int yp = 4;
@@ -45,9 +46,8 @@ void Protect::draw() {
 	// Protection spells
 	for (int i = 0; i < 7; ++i) {
 		if (g_globals->_activeSpells._arr[i]) {
-			Common::String line = Common::String::format("%s %s + %d%%",
-				STRING["dialogs.protect.protection"].c_str(),
-				STRING[Common::String::format("dialogs.protect.%d", i + 1)].c_str(),
+			Common::String line = Common::String::format("Protection from %s + %d%%",
+				PROTECTION_NAMES[i],
 				g_globals->_activeSpells._arr[i]);
 			writeString(0, yp += 8, line, ALIGN_MIDDLE);
 		}
@@ -55,8 +55,7 @@ void Protect::draw() {
 
 	// Light spell
 	if (g_globals->_activeSpells._arr[7]) {
-		writeString(0, yp += 8, Common::String::format("%s%d)",
-			STRING["dialogs.protect.8"].c_str(),
+		writeString(0, yp += 8, Common::String::format("Light (%d)",
 			g_globals->_activeSpells._arr[7]), ALIGN_MIDDLE);
 	}
 
@@ -78,8 +77,7 @@ void Protect::draw() {
 		}
 
 		if (g_globals->_activeSpells._arr[i]) {
-			Common::String line = STRING[Common::String::format(
-				"dialogs.protect.%d", i + 1)];
+			Common::String line = PROTECTION_NAMES[i];
 			if (i == 8) {
 				line += Common::String::format(" +%d AC",
 					g_globals->_activeSpells._arr[i]);
@@ -100,10 +98,8 @@ void Protect::draw() {
 			} else if (i == 16) {
 				line += " -50% damage";
 			} else if (i == 17) {
-				line += Common::String::format("%d %s",
-					g_globals->_activeSpells._arr[17],
-					STRING["dialogs.protect.to_attacks"].c_str()
-				);
+				line += Common::String::format("%d to attacks",
+					g_globals->_activeSpells._arr[17]);
 			}
 
 			writeString(0, yp += 8, line, ALIGN_MIDDLE);
diff --git a/engines/mm/mm1/views_enh/quick_ref.cpp b/engines/mm/mm1/views_enh/quick_ref.cpp
index a3e3a67579b..d02e987676c 100644
--- a/engines/mm/mm1/views_enh/quick_ref.cpp
+++ b/engines/mm/mm1/views_enh/quick_ref.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/quick_ref.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/views_enh/character_info.h"
 #include "mm/mm1/maps/maps.h"
 #include "mm/mm1/globals.h"
@@ -55,17 +56,17 @@ void QuickRef::draw() {
 	ScrollPopup::draw();
 
 	// Title
-	writeString(0, 0, STRING["enhdialogs.quickref.title"], ALIGN_MIDDLE);
+	writeString(0, 0, "Quick Reference Chart", ALIGN_MIDDLE);
 
 	// Header line
 	writeString(COLUMN_NUM, 20, "#");
-	writeString(COLUMN_NAME, 20, STRING["enhdialogs.quickref.headers.name"]);
-	writeString(COLUMN_CLASS, 20, STRING["enhdialogs.quickref.headers.class"]);
-	writeString(COLUMN_LEVEL, 20, STRING["enhdialogs.quickref.headers.level"], ALIGN_RIGHT);
-	writeString(COLUMN_HP, 20, STRING["enhdialogs.quickref.headers.hp"]);
-	writeString(COLUMN_SP, 20, STRING["enhdialogs.quickref.headers.sp"]);
-	writeString(COLUMN_AC, 20, STRING["enhdialogs.quickref.headers.ac"]);
-	writeString(COLUMN_CONDITION, 20, STRING["enhdialogs.quickref.headers.cond"]);
+	writeString(COLUMN_NAME, 20, "Name");
+	writeString(COLUMN_CLASS, 20, "Cls");
+	writeString(COLUMN_LEVEL, 20, "Lvl", ALIGN_RIGHT);
+	writeString(COLUMN_HP, 20, "H.P.");
+	writeString(COLUMN_SP, 20, "S.P.");
+	writeString(COLUMN_AC, 20, "A.C.");
+	writeString(COLUMN_CONDITION, 20, "Cond");
 
 	// Write out individual character lines
 	for (uint i = 0; i < g_globals->_party.size(); ++i)
@@ -83,8 +84,7 @@ void QuickRef::writeCharacterLine(int charNum) {
 
 	writeString(COLUMN_NAME, yp, truncateString(c._name, COLUMN_CLASS - COLUMN_NAME - 3));
 
-	Common::String classStr = STRING[Common::String::format(
-		"stats.classes.%d", c._class)];
+	Common::String classStr = CLASS_NAMES[c._class - 1];
 	writeString(COLUMN_CLASS, yp, Common::String(
 		classStr.c_str(), classStr.c_str() + 3));
 
diff --git a/engines/mm/mm1/views_enh/rest.cpp b/engines/mm/mm1/views_enh/rest.cpp
index f2b32749257..a10af0d064d 100644
--- a/engines/mm/mm1/views_enh/rest.cpp
+++ b/engines/mm/mm1/views_enh/rest.cpp
@@ -44,9 +44,9 @@ void Rest::draw() {
 	YesNo::draw();
 
 	if (_mode == CONFIRM) {
-		writeString(0, 0, STRING["dialogs.game.rest.rest_here"], ALIGN_MIDDLE);
+		writeString(0, 0, "Rest here (Y/N)?", ALIGN_MIDDLE);
 	} else {
-		writeString(0, 0, STRING["enhdialogs.rest.too_dangerous"], ALIGN_MIDDLE);
+		writeString(0, 0, "Too dangerous to rest here!", ALIGN_MIDDLE);
 	}
 }
 
diff --git a/engines/mm/mm1/views_enh/search.cpp b/engines/mm/mm1/views_enh/search.cpp
index d75e75e8a0e..e5f735a24b1 100644
--- a/engines/mm/mm1/views_enh/search.cpp
+++ b/engines/mm/mm1/views_enh/search.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/search.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/views_enh/select_number.h"
 #include "mm/mm1/views_enh/trap.h"
 #include "mm/mm1/views_enh/who_will_try.h"
@@ -47,8 +48,7 @@ bool Search::msgGame(const GameMessage &msg) {
 
 	} else {
 		// Otherwise send an info message to say nothing was found
-		Common::String line = STRING["dialogs.search.search"] +
-			STRING["dialogs.search.nothing"];
+		Common::String line = "Search: nothing";
 		send(InfoMessage(0, 1, line));
 	}
 
@@ -81,18 +81,17 @@ void Search::draw() {
 	switch (_mode) {
 	case INITIAL:
 		Sound::sound(SOUND_2);
-		line = STRING["dialogs.search.search"] +
-			STRING["dialogs.search.you_found"];
+		line = "Search: You found...";
 		writeString(0, 0, line);
 		delaySeconds(2);
 		break;
 
 	case OPTIONS:
-		writeString(0, 0, STRING["dialogs.search.options"]);
-		writeString(80, 0, STRING["dialogs.search.options1"]);
-		writeString(80, 9, STRING["dialogs.search.options2"]);
-		writeString(80, 18, STRING["dialogs.search.options3"]);
-		writeString(96, 32, STRING["enhdialogs.misc.go_back"]);
+		writeString(0, 0, "Options:");
+		writeString(80, 0, "1) Open It");
+		writeString(80, 9, "2) Find/Remove Trap");
+		writeString(80, 18, "3) Detect Magic/Trap");
+		writeString(96, 32, "Go back");
 		break;
 
 	case RESPONSE:
@@ -187,8 +186,7 @@ void Search::timeout() {
 
 		// Show the name of the container type in the game view
 		send("View", HeaderMessage(
-			STRING[Common::String::format("dialogs.search.containers.%d",
-				g_globals->_treasure._container)]
+			SEARCH_CONTAINERS[g_globals->_treasure._container]
 		));
 
 		// Display a graphic for the container type
@@ -285,13 +283,13 @@ void Search::detectMagicTrap2() {
 	if (c._class == PALADIN || c._class == CLERIC) {
 		Sound::sound(SOUND_2);
 		_responseLine = 2;
-		_responseText = STRING["dialogs.search.bad_class"];
+		_responseText = "*** Bad class ***";
 		delaySeconds(4);
 
 	} else if (c._sp == 0) {
 		Sound::sound(SOUND_2);
 		_responseLine = 2;
-		_responseText = STRING["dialogs.search.no_sp"];
+		_responseText = "*** No spell points ***";
 		delaySeconds(4);
 
 	} else {
@@ -303,7 +301,7 @@ void Search::detectMagicTrap2() {
 
 		_responseLine = 1;
 		_responseText = Common::String::format(
-			STRING["dialogs.search.magic_trap"].c_str(),
+			"Magic (%c)  Trap (%c)",
 			magic, trapped);
 		delaySeconds(8);
 	}
@@ -362,7 +360,7 @@ void Search::getTreasure() {
 }
 
 void Search::drawTreasure() {
-	writeLine(0, STRING["dialogs.search.it_opens"], ALIGN_MIDDLE);
+	writeLine(0, "It opens!", ALIGN_MIDDLE);
 
 	// Split up the gold across the party
 	uint32 goldPerPerson = g_globals->_treasure.getGold() /
@@ -379,7 +377,7 @@ void Search::drawTreasure() {
 	}
 	
 	writeLine(2, Common::String::format(
-		STRING["dialogs.search.each_share"].c_str(),
+		"each share is worth %d gold",
 		goldPerPerson));
 	g_globals->_treasure.setGold(0);
 
@@ -394,7 +392,7 @@ void Search::drawTreasure() {
 		Character &c = g_globals->_party[charNum];
 
 		writeLine(_lineNum++, Common::String::format(
-			STRING["dialogs.search.found_gems"].c_str(),
+			"%s found %d gems",
 			c._name,
 			gems));
 		c._gems = MIN((int)c._gems + gems, 0xffff);
@@ -439,7 +437,7 @@ void Search::drawItem() {
 			recipient->_backpack.add(itemId, item->_maxCharges);
 
 			writeLine(_lineNum++, Common::String::format(
-				STRING["dialogs.search.found_item"].c_str(),
+				"%s found %s",
 				recipient->_name,
 				item->_name.c_str()
 			));
diff --git a/engines/mm/mm1/views_enh/spells/cast_spell.cpp b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
index 7a5c4a8e7d0..3dafcefa963 100644
--- a/engines/mm/mm1/views_enh/spells/cast_spell.cpp
+++ b/engines/mm/mm1/views_enh/spells/cast_spell.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/spells/cast_spell.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/game/spells_party.h"
 #include "mm/mm1/globals.h"
 
@@ -63,36 +64,36 @@ void CastSpell::draw() {
 	_fontReduced = false;
 
 	const Character &c = *g_globals->_currCharacter;
-	writeString(0, 0, STRING["enhdialogs.cast_spell.title"], ALIGN_MIDDLE);
+	writeString(0, 0, "Cast Spell", ALIGN_MIDDLE);
 	writeString(0, 16, c._name, ALIGN_MIDDLE);
-	writeString(0, 40, STRING["enhdialogs.cast_spell.spell_ready"]);
+	writeString(0, 40, "Spell Ready:");
 
 	setTextColor(37);
 
 	int spellNum = c.spellNumber();
-	Common::String spellName = STRING["enhdialogs.cast_spell.none"];
+	Common::String spellName = "None Ready";
 	if (spellNum >= 0 && spellNum < 47) {
-		spellName = STRING[Common::String::format("spells.cleric.%d", spellNum)];
+		spellName = CLERIC_SPELL_NAMES[spellNum];
 
 		// MM1 Cleric 2-5 is Protection From Fire.
 		if (spellNum == 12)
 			spellName = "Protection From Fire";
 	} else if (spellNum >= 47) {
-		spellName = STRING[Common::String::format("spells.wizard.%d", spellNum - 47)];
+		spellName = WIZARD_SPELL_NAMES[spellNum - 47];
 	}
 	writeString(0, 60, spellName, ALIGN_MIDDLE);
 
 	_fontReduced = true;
 	setTextColor(0);
-	writeString(0, 80, STRING["enhdialogs.cast_spell.cost"]);
-	writeString(0, 90, STRING["enhdialogs.cast_spell.cur_sp"]);
+	writeString(0, 80, "Cost");
+	writeString(0, 90, "Cur SP");
 	writeString(0, 80, Common::String::format("%d/%d",
 		_requiredSp, _requiredGems), ALIGN_RIGHT);
 	writeString(0, 90, Common::String::format("%d", c._sp._current), ALIGN_RIGHT);
 
-	writeString(0, 122, STRING["enhdialogs.cast_spell.cast"]);
-	writeString(30, 122, STRING["enhdialogs.cast_spell.new"]);
-	writeString(60, 122, STRING["enhdialogs.cast_spell.esc"]);
+	writeString(0, 122, "\x01""37Cast");
+	writeString(30, 122, "\x01""37New");
+	writeString(60, 122, "ESC");
 
 	_fontReduced = false;
 }
@@ -233,8 +234,8 @@ void CastSpell::castSpell(Character *target) {
 	Character &c = *g_globals->_currCharacter;
 	if (isInCombat()) {
 		Common::String spellName = _spellIndex < 47 ?
-			STRING[Common::String::format("spells.cleric.%d", _spellIndex)] :
-			STRING[Common::String::format("spells.wizard.%d", _spellIndex - 47)];
+			CLERIC_SPELL_NAMES[_spellIndex] :
+			WIZARD_SPELL_NAMES[_spellIndex - 47];
 		if (_spellIndex == 12)
 			spellName = "Protection From Fire";
 		const Common::String logLine = target ?
@@ -249,7 +250,7 @@ void CastSpell::castSpell(Character *target) {
 	c._gems = MAX((int)c._gems - _requiredGems, 0);
 
 	if (!isMagicAllowed()) {
-		showResult(STRING["spells.magic_doesnt_work"]);
+		showResult("Magic doesn't work here");
 
 	} else {
 		// Cast the spell
@@ -257,11 +258,11 @@ void CastSpell::castSpell(Character *target) {
 
 		switch (result) {
 		case Game::SR_FAILED:
-			showResult(STRING["spells.failed"]);
+			showResult("Spell failed");
 			break;
 
 		case Game::SR_SUCCESS_DONE:
-			showResult(STRING["spells.done"]);
+			showResult("Done");
 			break;
 
 		default:
diff --git a/engines/mm/mm1/views_enh/spells/detect_magic.cpp b/engines/mm/mm1/views_enh/spells/detect_magic.cpp
index 19bdd6a8175..b0270f4652a 100644
--- a/engines/mm/mm1/views_enh/spells/detect_magic.cpp
+++ b/engines/mm/mm1/views_enh/spells/detect_magic.cpp
@@ -39,7 +39,7 @@ void DetectMagic::draw() {
 	ScrollView::draw();
 
 	setReduced(true);
-	writeString(0, 0, STRING["dialogs.spells.detect_charges"], ALIGN_RIGHT);
+	writeString(0, 0, "Magic (charges)", ALIGN_RIGHT);
 
 	getMagicStrings();
 
@@ -55,9 +55,9 @@ void DetectMagic::draw() {
 	}
 
 	if (inv.empty())
-		writeLine(1, STRING["enhdialogs.misc.no_items"]);
+		writeLine(1, "Inventory empty");
 
-	writeString(15, 66, STRING["enhdialogs.misc.go_back"]);
+	writeString(15, 66, "Go back");
 	setReduced(false);
 }
 
diff --git a/engines/mm/mm1/views_enh/spells/duplication.cpp b/engines/mm/mm1/views_enh/spells/duplication.cpp
index 0ffcff6c2ea..42fe78013fb 100644
--- a/engines/mm/mm1/views_enh/spells/duplication.cpp
+++ b/engines/mm/mm1/views_enh/spells/duplication.cpp
@@ -30,10 +30,10 @@ namespace Spells {
 Duplication::Duplication() : CharacterInventory("Duplication") {
 	clearButtons();
 
-	addButton(2, STRING["enhdialogs.items.buttons.arms"], Common::KEYCODE_a);
-	addButton(6, STRING["enhdialogs.items.buttons.backpack"], Common::KEYCODE_b);
-	addButton(14, STRING["enhdialogs.items.buttons.copy"], Common::KEYCODE_c);
-	addButton(16, STRING["enhdialogs.misc.exit"], Common::KEYCODE_ESCAPE);
+	addButton(2, "\x01""37Arms", Common::KEYCODE_a);
+	addButton(6, "\x01""37BkPack", Common::KEYCODE_b);
+	addButton(14, "\x01""37Copy", Common::KEYCODE_c);
+	addButton(16, "Exit", Common::KEYCODE_ESCAPE);
 }
 
 bool Duplication::msgKeypress(const KeypressMessage &msg) {
@@ -57,7 +57,7 @@ void Duplication::performAction() {
 	bool result = duplicate(*g_globals->_currCharacter, inv, _selectedItem);
 	close();
 
-	g_events->send(InfoMessage(STRING[result ? "spells.done" : "spells.failed"]));
+	g_events->send(InfoMessage(result ? "Done" : "Spell failed"));
 }
 
 } // namespace Spells
diff --git a/engines/mm/mm1/views_enh/spells/fly.cpp b/engines/mm/mm1/views_enh/spells/fly.cpp
index d0880f56728..28dbe82d52c 100644
--- a/engines/mm/mm1/views_enh/spells/fly.cpp
+++ b/engines/mm/mm1/views_enh/spells/fly.cpp
@@ -49,13 +49,13 @@ void Fly::draw() {
 	ScrollView::draw();
 
 	setReduced(true);
-	writeString(20, 30, STRING["enhdialogs.misc.go_back"]);
+	writeString(20, 30, "Go back");
 
-	writeLine(0, STRING["dialogs.spells.fly_to_x"], ALIGN_RIGHT, TEXT_X);
+	writeLine(0, "Fly to (A-E): ", ALIGN_RIGHT, TEXT_X);
 	writeChar((_mode == SELECT_X) ? '_' : 'A' + _xIndex);
 
 	if (_mode == SELECT_Y || _mode == CAST) {
-		writeLine(1, STRING["dialogs.spells.fly_to_y"], ALIGN_RIGHT, TEXT_X);
+		writeLine(1, "(1-4): ", ALIGN_RIGHT, TEXT_X);
 		writeChar((_mode == SELECT_Y) ? '_' : '1' + _yIndex);
 	}
 
diff --git a/engines/mm/mm1/views_enh/spells/location.cpp b/engines/mm/mm1/views_enh/spells/location.cpp
index c7a40cd0527..ee27e249ca2 100644
--- a/engines/mm/mm1/views_enh/spells/location.cpp
+++ b/engines/mm/mm1/views_enh/spells/location.cpp
@@ -43,31 +43,31 @@ void Location::draw() {
 	ScrollView::draw();
 
 	setReduced(true);
-	writeString(20, 30, STRING["enhdialogs.misc.go_back"]);
+	writeString(20, 30, "Go back");
 
-	writeLine(0, STRING["dialogs.spells.location_loc"], ALIGN_LEFT, 0);
+	writeLine(0, "Location: ", ALIGN_LEFT, 0);
 
 	v = map[Maps::MAP_TYPE];
 	if (v == 0xff) {
-		writeString(STRING["dialogs.spells.location_unknown"]);
+		writeString("Unknown");
 	} else {
 		if (v == 0) {
-			writeString(STRING["dialogs.spells.location_outdoors"]);
+			writeString("Outdoors");
 		} else if (!(v & 0x80)) {
 			writeChar('0' + map[37]);
-			writeString(STRING["dialogs.spells.location_under"]);
+			writeString("0' under");
 		} else if (v == 0xfe) {
-			writeString(STRING["dialogs.spells.location_town"]);
+			writeString("In town");
 		} else {
-			writeString(STRING["dialogs.spells.location_castle"]);
+			writeString("in castle");
 		}
 
-		writeLine(0, STRING["dialogs.spells.location_sector"], ALIGN_LEFT, 111);
+		writeLine(0, "Map sector: ", ALIGN_LEFT, 111);
 		writeChar(map[Maps::MAP_SECTOR1] & 0x7f);
 		writeChar('-');
 		writeChar(map[Maps::MAP_SECTOR2] & 0x7f);
 
-		writeLine(1, STRING["dialogs.spells.location_surface_x"], ALIGN_RIGHT, TEXT_X1);
+		writeLine(1, "Surface: ", ALIGN_RIGHT, TEXT_X1);
 		writeString("X=");
 
 		if (map[Maps::MAP_TYPE]) {
@@ -75,7 +75,7 @@ void Location::draw() {
 			writeLine(1, "Y=", ALIGN_LEFT, TEXT_X2);
 			writeNumber(map[Maps::MAP_SURFACE_Y]);
 
-			writeLine(2, STRING["dialogs.spells.location_inside_x"], ALIGN_RIGHT, TEXT_X1);
+			writeLine(2, "Inside: ", ALIGN_RIGHT, TEXT_X1);
 			writeString("X=");
 		}
 
@@ -84,7 +84,7 @@ void Location::draw() {
 		writeString("Y=");
 		writeNumber(maps._mapPos.y);
 
-		writeLine(3, STRING["dialogs.spells.location_facing"], ALIGN_RIGHT, TEXT_X1);
+		writeLine(3, "Facing: ", ALIGN_RIGHT, TEXT_X1);
 		switch (maps._forwardMask) {
 		case Maps::DIRMASK_N:
 			writeChar('N');
diff --git a/engines/mm/mm1/views_enh/spells/recharge_item.cpp b/engines/mm/mm1/views_enh/spells/recharge_item.cpp
index c4c4ce8ea3a..35aa7c8d685 100644
--- a/engines/mm/mm1/views_enh/spells/recharge_item.cpp
+++ b/engines/mm/mm1/views_enh/spells/recharge_item.cpp
@@ -30,10 +30,10 @@ namespace Spells {
 RechargeItem::RechargeItem() : CharacterInventory("RechargeItem") {
 	clearButtons();
 
-	addButton(2, STRING["enhdialogs.items.buttons.arms"], Common::KEYCODE_a);
-	addButton(6, STRING["enhdialogs.items.buttons.backpack"], Common::KEYCODE_b);
-	addButton(14, STRING["enhdialogs.items.buttons.charge"], Common::KEYCODE_c);
-	addButton(16, STRING["enhdialogs.misc.exit"], Common::KEYCODE_ESCAPE);
+	addButton(2, "\x01""37Arms", Common::KEYCODE_a);
+	addButton(6, "\x01""37BkPack", Common::KEYCODE_b);
+	addButton(14, "\x01""37Charg", Common::KEYCODE_c);
+	addButton(16, "Exit", Common::KEYCODE_ESCAPE);
 }
 
 bool RechargeItem::msgKeypress(const KeypressMessage &msg) {
@@ -57,7 +57,7 @@ void RechargeItem::performAction() {
 	bool result = charge(inv, _selectedItem);
 	close();
 
-	g_events->send(InfoMessage(STRING[result ? "spells.done" : "spells.failed"]));
+	g_events->send(InfoMessage(result ? "Done" : "Spell failed"));
 }
 
 } // namespace Spells
diff --git a/engines/mm/mm1/views_enh/spells/spellbook.cpp b/engines/mm/mm1/views_enh/spells/spellbook.cpp
index 11b875eb172..5991e706842 100644
--- a/engines/mm/mm1/views_enh/spells/spellbook.cpp
+++ b/engines/mm/mm1/views_enh/spells/spellbook.cpp
@@ -21,6 +21,7 @@
 
 #include "mm/mm1/views_enh/spells/spellbook.h"
 #include "common/config-manager.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/game/spells_party.h"
 #include "mm/mm1/globals.h"
 
@@ -94,16 +95,14 @@ void Spellbook::draw() {
 
 	// Title line
 	_fontReduced = true;
-	Common::String title = Common::String::format("%s %s",
-		STRING["enhdialogs.spellbook.title"].c_str(),
+	Common::String title = Common::String::format("Spells for %s",
 		c._name
 	);
 	title = truncateString(title, _innerBounds.width());
 	writeString(0, 0, title, ALIGN_MIDDLE);
 
 	// Write current spell points
-	Common::String sp = Common::String::format("%s - %d",
-		STRING["enhdialogs.spellbook.spell_points"].c_str(), c._sp._current);
+	Common::String sp = Common::String::format("Spell Pts - %d", c._sp._current);
 	writeString(7, 111, sp);
 
 	// Iterate over the lines
@@ -118,7 +117,7 @@ void Spellbook::draw() {
 		if (_count == 0) {
 			if (i == 0) {
 				setTextColor(37);
-				writeString(12, yp, STRING["enhdialogs.spellbook.non_caster"]);
+				writeString(12, yp, "Not a spell caster...");
 			}
 		} else if (displayIndex < _count) {
 			const int spellIndex = _spellOrder[displayIndex];
@@ -133,11 +132,8 @@ void Spellbook::draw() {
 			setTextColor(textColor);
 
 			// Spell name and requirements
-			Common::String spellName = STRING[Common::String::format(
-				"spells.%s.%d",
-				_isWizard ? "wizard" : "cleric",
-				spellIndex
-			)];
+			Common::String spellName = _isWizard ?
+				WIZARD_SPELL_NAMES[spellIndex] : CLERIC_SPELL_NAMES[spellIndex];
 
 			// MM1 Cleric 2-5 is Protection From Fire.
 			if (!_isWizard && spellIndex == 12)
diff --git a/engines/mm/mm1/views_enh/spells/teleport.cpp b/engines/mm/mm1/views_enh/spells/teleport.cpp
index 83c1602e761..00a96eeb06a 100644
--- a/engines/mm/mm1/views_enh/spells/teleport.cpp
+++ b/engines/mm/mm1/views_enh/spells/teleport.cpp
@@ -45,18 +45,18 @@ void Teleport::draw() {
 	ScrollView::draw();
 
 	setReduced(true);
-	writeString(20, 30, STRING["enhdialogs.misc.go_back"]);
+	writeString(20, 30, "Go back");
 
-	writeLine(0, STRING["dialogs.spells.teleport_dir"], ALIGN_RIGHT, TEXT_X);
+	writeLine(0, "Direction (N,E,S,W): ", ALIGN_RIGHT, TEXT_X);
 	writeChar((_mode == SELECT_DIRECTION) ? '_' : _direction);
 
 	if (_mode == SELECT_SQUARES || _mode == CAST) {
-		writeLine(1, STRING["dialogs.spells.teleport_squares"], ALIGN_RIGHT, TEXT_X);
+		writeLine(1, "# of squares (0-9): ", ALIGN_RIGHT, TEXT_X);
 		writeChar((_mode == SELECT_SQUARES) ? '_' : '0' + _squares);
 	}
 
 	if (_mode == CAST)
-		writeString(0, 30, STRING["spells.enter_to_cast"], ALIGN_RIGHT);
+		writeString(0, 30, "'Enter' to cast", ALIGN_RIGHT);
 
 	setReduced(false);
 }
@@ -106,7 +106,7 @@ void Teleport::teleport() {
 
 	g_events->replaceView("Game", true);
 	if (map[Maps::MAP_FLAGS] & 2) {
-		g_events->send(SoundMessage(STRING["spells.failed"]));
+		g_events->send(SoundMessage("Spell failed"));
 
 	} else {
 		switch (_direction) {
diff --git a/engines/mm/mm1/views_enh/town_select.cpp b/engines/mm/mm1/views_enh/town_select.cpp
index 828e709cd0d..e9e195c6f59 100644
--- a/engines/mm/mm1/views_enh/town_select.cpp
+++ b/engines/mm/mm1/views_enh/town_select.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "mm/mm1/views_enh/town_select.h"
+#include "mm/mm1/data/text.h"
 #include "mm/mm1/globals.h"
 
 namespace MM {
@@ -37,7 +38,7 @@ TownSelect::TownSelect() : ScrollView("TownSelect") {
 	for (int i = 1; i <= 5; ++i) {
 		int row = i - 1;
 		int y = 22 + row * 12;
-		Common::String text = STRING[Common::String::format("stats.towns.%d", i)];
+		Common::String text = TOWN_NAMES[i - 1];
 		addButton(Common::Rect(BTN_X, y, TEXT_X + (int)getStringWidth(text),
 				y + BTN_SIZE),
 			(Common::KeyCode)(Common::KEYCODE_0 + i));
@@ -48,7 +49,7 @@ void TownSelect::draw() {
 	ScrollView::draw();
 	Graphics::ManagedSurface s = getSurface();
 	setReduced(false);
-	writeString(0, 0, STRING["spells.which_town"], ALIGN_MIDDLE);
+	writeString(0, 0, "Which town (1-5)?", ALIGN_MIDDLE);
 
 	Graphics::ManagedSurface btnSmall(BTN_SIZE, BTN_SIZE);
 	btnSmall.blitFrom(g_globals->_blankButton, Common::Rect(0, 0, 20, 20),
@@ -63,7 +64,7 @@ void TownSelect::draw() {
 			Common::String::format("%d", i), ALIGN_MIDDLE);
 
 		byte oldColor = setTextColor(i == _selectedButton + 1 ? 15 : 0);
-		writeString(TEXT_X, y, STRING[Common::String::format("stats.towns.%d", i)]);
+		writeString(TEXT_X, y, TOWN_NAMES[i - 1]);
 		setTextColor(oldColor);
 	}
 }
diff --git a/engines/mm/mm1/views_enh/trade.cpp b/engines/mm/mm1/views_enh/trade.cpp
index d8b6e8846c7..c8215b9cdda 100644
--- a/engines/mm/mm1/views_enh/trade.cpp
+++ b/engines/mm/mm1/views_enh/trade.cpp
@@ -66,7 +66,7 @@ void Trade::draw() {
 		drawOptions();
 		break;
 	default:
-		writeString(0, 5, STRING["enhdialogs.trade.how_much"]);
+		writeString(0, 5, "How much:");
 		break;
 	}
 }
@@ -102,7 +102,8 @@ bool Trade::msgKeypress(const KeypressMessage &msg) {
 		case Common::KEYCODE_i:
 			if (!_shareMode) {
 				close();
-				send(InfoMessage(STRING["enhdialogs.trade.items_help"]));
+				send(InfoMessage("To trade items, select an item from the inventory, and then select the portrait of the "
+					"character to trade to"));
 			}
 			break;
 		default:
@@ -160,14 +161,14 @@ void Trade::setMode(TradeMode mode) {
 }
 
 void Trade::drawOptions() {
-	writeString(0, 5, _shareMode ? STRING["dialogs.character.share_all"] :
-		STRING["enhdialogs.trade.which"]);
-	writeString(28, 25, STRING["enhdialogs.misc.exit"]);
-	writeString(108, 5, STRING["enhdialogs.trade.gold"]);
-	writeString(186, 5, STRING["enhdialogs.trade.gems"]);
-	writeString(108, 25, STRING["enhdialogs.trade.food"]);
+	writeString(0, 5, _shareMode ? "share all:" :
+		"Trade which:");
+	writeString(28, 25, "Exit");
+	writeString(108, 5, "\x01""37Gold");
+	writeString(186, 5, "Ge\x01""37ms");
+	writeString(108, 25, "\x01""37Food");
 	if (!_shareMode)
-		writeString(186, 25, STRING["enhdialogs.trade.items"]);
+		writeString(186, 25, "\x01""37Items");
 }
 
 void Trade::amountEntered(uint amount) {
diff --git a/engines/mm/mm1/views_enh/trap.cpp b/engines/mm/mm1/views_enh/trap.cpp
index e271b8c6675..9eb3e3b1d7f 100644
--- a/engines/mm/mm1/views_enh/trap.cpp
+++ b/engines/mm/mm1/views_enh/trap.cpp
@@ -27,6 +27,20 @@ namespace MM {
 namespace MM1 {
 namespace ViewsEnh {
 
+static const char *const TRAP_TEXT[11] = {
+	"Darts!! a swarm of poisonous darts fill the air!",
+	"Spikes!! Deadly spikes spring forth!",
+	"Arrows!! A sudden onslaught of poisonous arrows permeate the party!",
+	"Blades!! Razor sharp blades slice through the party!",
+	"Boiling oil!! Streams of boiling oil cover the party!",
+	"Gas!! A faint hiss can be heard as noxious fumes fill the air!",
+	"Fireball!! A fiery explosion engulfs the area!",
+	"Lightning bolt!! Electric currents singe the party!",
+	"Acid!! A fine mist of volatile acid sprays the party!",
+	"Ice storm!! Particles of splintered ice hail through the air!",
+	"Death ray!! A blinding light sears through the party!"
+};
+
 Trap::Trap() : ScrollView("Trap") {
 	setBounds(Common::Rect(0, 144, 234, 200));
 }
@@ -50,9 +64,9 @@ void Trap::draw() {
 	ScrollView::draw();
 
 	if (_mode == MODE_TRIGGER) {
-		writeString(0, 0, STRING["dialogs.trap.oops"], ALIGN_MIDDLE);
+		writeString(0, 0, "Oops a trap!", ALIGN_MIDDLE);
 	} else {
-		writeString(0, 0, STRING[Common::String::format("dialogs.trap.%d", _trapType)]);
+		writeString(0, 0, TRAP_TEXT[_trapType]);
 	}
 }
 
diff --git a/engines/mm/mm1/views_enh/unlock.cpp b/engines/mm/mm1/views_enh/unlock.cpp
index 30195f3cbb1..2f0be500d9d 100644
--- a/engines/mm/mm1/views_enh/unlock.cpp
+++ b/engines/mm/mm1/views_enh/unlock.cpp
@@ -58,7 +58,7 @@ bool Unlock::msgGame(const GameMessage &msg) {
 	if (canUnlock) {
 		WhoWillTry::display(charSelected);
 	} else {
-		g_events->send(InfoMessage(STRING["dialogs.unlock.none"]));
+		g_events->send(InfoMessage("Unlock: No door present"));
 	}
 
 	return true;
@@ -80,11 +80,11 @@ void Unlock::charSelected(int charIndex) {
 
 		if (val < c._trapCtr) {
 			g_maps->_currentMap->unlockDoor();
-			g_engine->send(InfoMessage(11, 1, STRING["dialogs.unlock.success"]));
+			g_engine->send(InfoMessage(11, 1, "Success!"));
 
 		} else if (g_engine->getRandomNumber(100) <
 				g_maps->_currentMap->dataByte(Maps::MAP_TRAP_THRESHOLD)) {
-			g_engine->send(InfoMessage(8, 1, STRING["dialogs.unlock.failed"]));
+			g_engine->send(InfoMessage(8, 1, "Unlock failed!"));
 
 		} else {
 			g_maps->_currentMap->unlockDoor();
diff --git a/engines/mm/mm1/views_enh/which_character.cpp b/engines/mm/mm1/views_enh/which_character.cpp
index b6d2ce49759..8ce61e444d3 100644
--- a/engines/mm/mm1/views_enh/which_character.cpp
+++ b/engines/mm/mm1/views_enh/which_character.cpp
@@ -45,8 +45,7 @@ void WhichCharacter::draw() {
 	}
 
 	PartyView::draw();
-	writeString(10, 5, STRING[g_events->isInCombat() ?
-		"enhdialogs.misc.exchange" : "enhdialogs.trade.dest"]);
+	writeString(10, 5, g_events->isInCombat() ? "Exchange with whom?" : "Trade to whom?");
 }
 
 bool WhichCharacter::msgAction(const ActionMessage &msg) {
diff --git a/engines/mm/mm1/views_enh/who_will_try.cpp b/engines/mm/mm1/views_enh/who_will_try.cpp
index 7d33c5fa161..60bbb5c89aa 100644
--- a/engines/mm/mm1/views_enh/who_will_try.cpp
+++ b/engines/mm/mm1/views_enh/who_will_try.cpp
@@ -89,7 +89,7 @@ void WhoWillTry::selectChar(uint charNum) {
 		const Character &c = g_globals->_party[charNum];
 
 		if ((c._condition & (BAD_CONDITION | DEAD | STONE | ASLEEP)) != 0) {
-			InfoMessage msg(STRING["dialogs.misc.check_condition"], ALIGN_MIDDLE);
+			InfoMessage msg("*** Check condition ***", ALIGN_MIDDLE);
 			msg._delaySeconds = 3;
 			msg._callback = []() {
 				WhoWillTry *view = static_cast<WhoWillTry *>(g_events->findView("WhoWillTry"));
diff --git a/engines/mm/mm1/views_enh/won_game.cpp b/engines/mm/mm1/views_enh/won_game.cpp
index d339a559375..8e763ae3fc3 100644
--- a/engines/mm/mm1/views_enh/won_game.cpp
+++ b/engines/mm/mm1/views_enh/won_game.cpp
@@ -33,8 +33,12 @@ WonGame::WonGame() : ScrollView("WonGame") {
 
 void WonGame::draw() {
 	ScrollView::draw();
-	writeLine(0, STRING["maps.map18.gates"], ALIGN_MIDDLE);
-	writeLine(2, STRING["maps.map18.congratulations"], ALIGN_MIDDLE);
+	writeLine(0, "The Gates to Another World! ", ALIGN_MIDDLE);
+	writeLine(2, "Congratulations distinguished \n"
+		"travelers! The gates are now\n"
+		"open to you. In order to pass\n"
+		"through you must first find\n"
+		"Might and Magic Book Two !!!", ALIGN_MIDDLE);
 	Sound::sound(SOUND_3);
 }
 


Commit: bf187c5f0fc232e84f4a3dfadaaf49c6bb9f16b2
    https://github.com/scummvm/scummvm/commit/bf187c5f0fc232e84f4a3dfadaaf49c6bb9f16b2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:10+11:00

Commit Message:
MM: MM1: Remove STRING lookup and StringsData

Changed paths:
  R engines/mm/shared/utils/strings_data.cpp
  R engines/mm/shared/utils/strings_data.h
    engines/mm/mm1/globals.cpp
    engines/mm/mm1/globals.h
    engines/mm/module.mk


diff --git a/engines/mm/mm1/globals.cpp b/engines/mm/mm1/globals.cpp
index dfbaca941fd..81a6ce0c14f 100644
--- a/engines/mm/mm1/globals.cpp
+++ b/engines/mm/mm1/globals.cpp
@@ -26,7 +26,6 @@
 #include "mm/mm1/globals.h"
 #include "mm/mm1/views_enh/game_frame.h"
 #include "mm/mm1/mm1.h"
-#include "mm/shared/utils/strings.h"
 #include "graphics/fontman.h"
 
 namespace MM {
@@ -64,9 +63,6 @@ bool Globals::load(bool isEnhanced) {
 		return false;
 	}
 
-	if (!_strings.load("strings_en.yml"))
-		return false;
-
 	if (!_font.load("font.bmp"))
 		return false;
 
@@ -131,34 +127,6 @@ bool Globals::load(bool isEnhanced) {
 	return true;
 }
 
-Common::String Globals::operator[](const Common::String &name) const {
-	bool isMapStr = name.hasPrefix("maps.map");
-
-	if (g_engine->isEnhanced()) {
-		if (isMapStr) {
-			// Map strings support having alternate versions in Enhanced version
-			Common::String altName = Common::String::format("maps.emap%s",
-				name.c_str() + 8);
-			if (_strings.contains(altName))
-				return _strings[altName];
-		}
-
-		if (name.hasPrefix("dialogs.")) {
-			Common::String altName = Common::String::format("enh%s", name.c_str());
-			if (_strings.contains(altName))
-				return _strings[altName];
-		}
-	}
-
-	assert(_strings.contains(name));
-	Common::String result = _strings[name];
-
-	if (g_engine->isEnhanced() && name.hasPrefix("maps."))
-		result = searchAndReplace(result, "\n", " ");
-
-	return result;
-}
-
 void Globals::synchronize(Common::Serializer &s) {
 	s.syncAsByte(_startingTown);
 	s.syncAsByte(_minimapOn);
diff --git a/engines/mm/mm1/globals.h b/engines/mm/mm1/globals.h
index 63c7d55cb0a..75ce1736058 100644
--- a/engines/mm/mm1/globals.h
+++ b/engines/mm/mm1/globals.h
@@ -24,7 +24,6 @@
 
 #include "common/serializer.h"
 #include "graphics/font.h"
-#include "mm/shared/utils/strings_data.h"
 #include "mm/shared/utils/xeen_font.h"
 #include "mm/xeen/sprites.h"
 #include "mm/mm1/data/bestiary.h"
@@ -44,7 +43,6 @@ namespace MM1 {
 
 class Globals : public GameState {
 public:
-	StringsData _strings;
 	Roster _roster;
 	Common::Array<Character *> _combatParty;
 	Character *_currCharacter = nullptr;
@@ -96,11 +94,6 @@ public:
 	 */
 	bool load(bool isEnhanced);
 
-	/**
-	 * Returns a string
-	 */
-	Common::String operator[](const Common::String &name) const;
-
 	/**
 	 * Saves global data to/from savegames
 	 */
@@ -109,8 +102,6 @@ public:
 
 extern Globals *g_globals;
 
-#define STRING (*g_globals)
-
 } // namespace MM1
 } // namespace MM
 
diff --git a/engines/mm/module.mk b/engines/mm/module.mk
index 25246518e35..df0863ef9b1 100644
--- a/engines/mm/module.mk
+++ b/engines/mm/module.mk
@@ -6,7 +6,6 @@ MODULE_OBJS := \
 	shared/utils/bitmap_font.o \
 	shared/classic/pc_speaker.o \
 	shared/utils/strings.o \
-	shared/utils/strings_data.o \
 	shared/utils/xeen_font.o \
 	shared/xeen/cc_archive.o \
 	shared/xeen/file.o \
diff --git a/engines/mm/shared/utils/strings_data.cpp b/engines/mm/shared/utils/strings_data.cpp
deleted file mode 100644
index 678edf0495a..00000000000
--- a/engines/mm/shared/utils/strings_data.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "common/array.h"
-#include "common/file.h"
-#include "mm/shared/utils/strings_data.h"
-#include "mm/shared/utils/strings.h"
-
-namespace MM {
-
-bool StringsData::load(const Common::Path &filename) {
-	Common::File f;
-	Common::Array<Common::String> prefixKeys;
-	Common::String key, value, fullKey;
-	size_t p;
-
-	if (!f.open(filename))
-		return false;
-
-	while (!f.eos()) {
-		// Get the next line
-		Common::String line = f.readLine();
-
-		// Check for blank or comment lines
-		Common::String lineTrimmed = line;
-		lineTrimmed.trim();
-		if (lineTrimmed.empty() || lineTrimmed.hasPrefix("#"))
-			continue;
-
-		// Count number of tabs for identation amount
-		size_t numTabs = 0;
-		while (line.hasPrefix("\t")) {
-			line.deleteChar(0);
-			++numTabs;
-		}
-
-		// Split key and value if present
-		p = line.findFirstOf(":");
-		if (p == Common::String::npos)
-			error("Line encountered without colon");
-
-		key = Common::String(line.c_str(), line.c_str() + p);
-		value = Common::String(line.c_str() + p + 1);
-		key.trim();
-		value.trim();
-
-		// Strip quotes from start and end of string
-		if (value.hasPrefix("\"") && value.hasSuffix("\"")) {
-			value.deleteChar(0);
-			value.deleteLastChar();
-			value = searchAndReplace(value, "\"\"", "");
-			value = searchAndReplace(value, "\\\"", "\"");
-		}
-
-		// Replace any sequences
-		for (uint i = 0; i < value.size(); ++i) {
-			if (value[i] == '\\' && (value[i + 1] == 'n' ||
-				value[i + 1] == 'r')) {
-				value.deleteChar(i);
-				value.setChar('\n', i);
-			} else if (!strncmp(value.c_str() + i, "\\x", 2)) {
-				Common::String hex(value.c_str() + i + 2,
-					value.c_str() + i + 4);
-
-				value.deleteChar(i);
-				value.deleteChar(i);
-				value.deleteChar(i);
-				value.setChar((char)hexToInt(hex), i);
-			} else if (value[i] == '"' && i < (value.size() - 1) &&
-					value[i + 1] == '"') {
-				value.deleteChar(i);
-			}
-		}
-
-		// Handle the entries
-		if (numTabs == prefixKeys.size()) {
-			// Do nothing
-		} else if (numTabs < prefixKeys.size()) {
-			// Drop off prefixes to the desired indentation
-			prefixKeys.resize(numTabs);	
-		} else {
-			error("Incorrect indentation");
-		}
-
-		if (value.empty()) {
-			prefixKeys.push_back(key);
-		} else {
-			// Form a key from the prefix and current key
-			fullKey = "";
-			for (size_t i = 0; i < prefixKeys.size(); ++i) {
-				fullKey += prefixKeys[i];
-				fullKey += ".";
-			}
-
-			fullKey = fullKey + key;
-			(*this)[fullKey] = value;
-		}
-	}
-
-	return true;
-}
-
-} // namespace MM
diff --git a/engines/mm/shared/utils/strings_data.h b/engines/mm/shared/utils/strings_data.h
deleted file mode 100644
index d2d1434a9fa..00000000000
--- a/engines/mm/shared/utils/strings_data.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef MM_UTILS_STRINGS_DATA_H
-#define MM_UTILS_STRINGS_DATA_H
-
-#include "common/hash-str.h"
-#include "common/path.h"
-
-namespace MM {
-
-class StringsData : public Common::StringMap {
-public:
-	StringsData() : Common::StringMap() {}
-
-	/**
-	 * Loads the data
-	 */
-	bool load(const Common::Path &filename);
-};
-
-} // namespace MM
-
-#endif


Commit: 8f14098c87ea6480db209dba129d7f7a3244c847
    https://github.com/scummvm/scummvm/commit/8f14098c87ea6480db209dba129d7f7a3244c847
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:10+11:00

Commit Message:
MM: MM1: Fix tavern rumors list

The original accidentally split the 'king is in seclusion' line.

Changed paths:
    engines/mm/mm1/data/text.cpp
    engines/mm/mm1/data/text.h
    engines/mm/mm1/views/locations/tavern.cpp
    engines/mm/mm1/views_enh/locations/tavern.cpp


diff --git a/engines/mm/mm1/data/text.cpp b/engines/mm/mm1/data/text.cpp
index 089df36da63..141a7250acd 100644
--- a/engines/mm/mm1/data/text.cpp
+++ b/engines/mm/mm1/data/text.cpp
@@ -321,7 +321,7 @@ const char *const STATUE_MESSAGES[8] = {
 	"This beast once roamed the\nEnchanted Forest and now rules\na great fortress there"
 };
 
-const char *const TAVERN_RUMORS[17] = {
+const char *const TAVERN_RUMORS[16] = {
 	"All portals are connected",
 	"Sorpigal has 8 statues",
 	"The brothers live by docks",
@@ -335,7 +335,6 @@ const char *const TAVERN_RUMORS[17] = {
 	"Kilburn is near the wyvern peaks",
 	"A dragons breath is only as strong\x8b""as it's condition",
 	"The king is in seclusion",
-	"",
 	"The inner sanctum is a myth",
 	"The canine has the key",
 	"ScummVM is totally awesome!"
diff --git a/engines/mm/mm1/data/text.h b/engines/mm/mm1/data/text.h
index ef5c8cc9b93..67fe95808df 100644
--- a/engines/mm/mm1/data/text.h
+++ b/engines/mm/mm1/data/text.h
@@ -44,7 +44,7 @@ extern const char *const PROTECTION_NAMES[18];
 extern const char *const SEARCH_CONTAINERS[11];
 extern const char *const STATUE_NAMES[8];
 extern const char *const STATUE_MESSAGES[8];
-extern const char *const TAVERN_RUMORS[17];
+extern const char *const TAVERN_RUMORS[16];
 extern const char *const INGREDIENTS[7];
 extern const char *const INSPECTRON_QUESTS[7];
 extern const char *const IRONFIST_QUESTS[7];
diff --git a/engines/mm/mm1/views/locations/tavern.cpp b/engines/mm/mm1/views/locations/tavern.cpp
index 388b9ef3d7c..6e3442ce8d5 100644
--- a/engines/mm/mm1/views/locations/tavern.cpp
+++ b/engines/mm/mm1/views/locations/tavern.cpp
@@ -132,7 +132,7 @@ void Tavern::listenForRumors() {
 	Common::String msg = "No rumors today.";
 	if (!g_globals->_heardRumor) {
 		g_globals->_heardRumor = true;
-		msg = TAVERN_RUMORS[g_engine->getRandomNumber(16)];
+		msg = TAVERN_RUMORS[g_engine->getRandomNumber(16) - 1];
 	}
 
 	displayMessage(msg);
diff --git a/engines/mm/mm1/views_enh/locations/tavern.cpp b/engines/mm/mm1/views_enh/locations/tavern.cpp
index 89a6cd63922..7de20e098a0 100644
--- a/engines/mm/mm1/views_enh/locations/tavern.cpp
+++ b/engines/mm/mm1/views_enh/locations/tavern.cpp
@@ -157,7 +157,7 @@ void Tavern::listenForRumors() {
 	Common::String msg = "No rumors today.";
 	if (!g_globals->_heardRumor) {
 		g_globals->_heardRumor = true;
-		msg = TAVERN_RUMORS[g_engine->getRandomNumber(16)];
+		msg = TAVERN_RUMORS[g_engine->getRandomNumber(16) - 1];
 	}
 
 	displayMessage(msg);


Commit: 43c163c15c6934147ce1a2a4fba9e26a78697e70
    https://github.com/scummvm/scummvm/commit/43c163c15c6934147ce1a2a4fba9e26a78697e70
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:11+11:00

Commit Message:
MM: MM1: Fix incorrect hex escape in tavern text

Changed paths:
    engines/mm/mm1/data/text.cpp


diff --git a/engines/mm/mm1/data/text.cpp b/engines/mm/mm1/data/text.cpp
index 141a7250acd..1f982f12f04 100644
--- a/engines/mm/mm1/data/text.cpp
+++ b/engines/mm/mm1/data/text.cpp
@@ -391,7 +391,7 @@ const char *const TAVERN_TIPS[5][4] = {
 		"See man in cave below (1,2)",
 		"Check walls near (12,3)",
 		"Statue at (2,4) is your first job",
-		"Similar pieces of a puzzle may not\\8dbelong to the same puzzle"
+		"Similar pieces of a puzzle may not\x8d""belong to the same puzzle"
 	},
 	{
 		"Seek quests behind moons",


Commit: c2e2176295ba5a45270ef3f0121109b2ef2459f0
    https://github.com/scummvm/scummvm/commit/c2e2176295ba5a45270ef3f0121109b2ef2459f0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:11+11:00

Commit Message:
MM: MM1: Remove extra space in breathes string constants

Changed paths:
    engines/mm/mm1/game/spells_monsters.cpp


diff --git a/engines/mm/mm1/game/spells_monsters.cpp b/engines/mm/mm1/game/spells_monsters.cpp
index 44632615e15..fc53472f55b 100644
--- a/engines/mm/mm1/game/spells_monsters.cpp
+++ b/engines/mm/mm1/game/spells_monsters.cpp
@@ -99,7 +99,7 @@ void SpellsMonsters::spell02_energyBlast() {
 }
 
 void SpellsMonsters::spell03_fire() {
-	add("breathes  fire");
+	add("breathes fire");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
 
@@ -225,7 +225,7 @@ void SpellsMonsters::spell13_fireball() {
 }
 
 void SpellsMonsters::spell14_fireBreath() {
-	add("breathes  fire");
+	add("breathes fire");
 	++g_globals->_spellsState._resistanceIndex;
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
 
@@ -342,7 +342,7 @@ void SpellsMonsters::spell24_commandsEnergy() {
 }
 
 void SpellsMonsters::spell25_poison() {
-	add("breathes  poison");
+	add("breathes poison");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -352,7 +352,7 @@ void SpellsMonsters::spell25_poison() {
 }
 
 void SpellsMonsters::spell26_lightning() {
-	add("breathes  lightning");
+	add("breathes lightning");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -362,7 +362,7 @@ void SpellsMonsters::spell26_lightning() {
 }
 
 void SpellsMonsters::spell27_frost() {
-	add("breathes  frost");
+	add("breathes frost");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_COLD;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -372,7 +372,7 @@ void SpellsMonsters::spell27_frost() {
 }
 
 void SpellsMonsters::spell28_spikes() {
-	add("breathes  spikes");
+	add("breathes spikes");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -382,7 +382,7 @@ void SpellsMonsters::spell28_spikes() {
 }
 
 void SpellsMonsters::spell29_acid() {
-	add("breathes  acid");
+	add("breathes acid");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ACID;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;
@@ -392,7 +392,7 @@ void SpellsMonsters::spell29_acid() {
 }
 
 void SpellsMonsters::spell30_fire() {
-	add("breathes  fire");
+	add("breathes fire");
 	g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
 	g_globals->_spellsState._damage = g_globals->_encounters._monsterList[getMonsterIndex()]._hp;
 	++g_globals->_spellsState._resistanceIndex;


Commit: a54101d98caace63b0de76530177c6c8c7a0e1c6
    https://github.com/scummvm/scummvm/commit/a54101d98caace63b0de76530177c6c8c7a0e1c6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:11+11:00

Commit Message:
MM: MM1: Fix indexing for tavern tips

Changed paths:
    engines/mm/mm1/views/locations/tavern.cpp


diff --git a/engines/mm/mm1/views/locations/tavern.cpp b/engines/mm/mm1/views/locations/tavern.cpp
index 6e3442ce8d5..a242e0cd1d1 100644
--- a/engines/mm/mm1/views/locations/tavern.cpp
+++ b/engines/mm/mm1/views/locations/tavern.cpp
@@ -123,7 +123,7 @@ void Tavern::tipBartender() {
 			displayMessage("Thanks a lot, have another round!");
 		} else {
 			int townNum = g_maps->_currentMap->dataByte(Maps::MAP_ID);
-			displayMessage(TAVERN_TIPS[townNum - 1][g_globals->_currCharacter->_numDrinks - 1]);
+			displayMessage(TAVERN_TIPS[townNum - 1][MIN(g_globals->_currCharacter->_numDrinks / 4, 3)]);
 		}
 	}
 }


Commit: 2f734f9dcd76e2d289d80483ddb36697be7e5d09
    https://github.com/scummvm/scummvm/commit/2f734f9dcd76e2d289d80483ddb36697be7e5d09
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-10-24T17:13:12+11:00

Commit Message:
NEWS: Added dragoo84's Might & Magic 1 bugfix/improvements patch

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index 01462adac4e..15fa6e24ef2 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -16,6 +16,10 @@ For a more comprehensive changelog of the latest experimental code, see:
  MADS:
    - MT32 crash fixes for Dragonsphere
 
+ MM:
+   - Integrated Dragoo84's M&M1 Bugfix and Enhanced Mode improvements pack
+   - Fixed several M&M1 tavern bugs
+
 #### 2026.4.0 "Never-slop" (2026-10-10)
 
  New games:




More information about the Scummvm-git-logs mailing list