[Scummvm-git-logs] scummvm master -> d2c9a9df4888e112dcc1f4822d962747f7c90783

dreammaster noreply at scummvm.org
Sat Feb 28 10:02:14 UTC 2026


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

Summary:
d2c9a9df48 MADS: PHANTOM: Beginnings of menu menu dialogs


Commit: d2c9a9df4888e112dcc1f4822d962747f7c90783
    https://github.com/scummvm/scummvm/commit/d2c9a9df4888e112dcc1f4822d962747f7c90783
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-02-28T21:02:00+11:00

Commit Message:
MADS: PHANTOM: Beginnings of menu menu dialogs

Changed paths:
  A engines/mads/phantom/dialogs_phantom.cpp
  A engines/mads/phantom/dialogs_phantom.h
  A engines/mads/phantom/menu_phantom.cpp
  A engines/mads/phantom/menu_phantom.h
    engines/mads/dialogs.cpp
    engines/mads/module.mk
    engines/mads/phantom/game_phantom.cpp
    engines/mads/phantom/game_phantom.h


diff --git a/engines/mads/dialogs.cpp b/engines/mads/dialogs.cpp
index 15bf276d521..d83e8c2945f 100644
--- a/engines/mads/dialogs.cpp
+++ b/engines/mads/dialogs.cpp
@@ -21,12 +21,14 @@
 
 #include "common/scummsys.h"
 #include "common/config-manager.h"
+#include "common/text-to-speech.h"
 #include "mads/mads.h"
 #include "mads/screen.h"
 #include "mads/msurface.h"
 #include "mads/nebular/dialogs_nebular.h"
-#include "common/config-manager.h"
-#include "common/text-to-speech.h"
+#ifdef MADSV2
+#include "mads/phantom/dialogs_phantom.h"
+#endif
 
 namespace MADS {
 
@@ -455,12 +457,14 @@ Dialogs *Dialogs::init(MADSEngine *vm) {
 	switch (vm->getGameID()) {
 	case GType_RexNebular:
 		return new Nebular::DialogsNebular(vm);
+#ifdef MADSV2
 	case GType_Phantom:
-		// return new Phantom::DialogsPhantom(vm);
+		return new Phantom::DialogsPhantom(vm);
 	case GType_Dragonsphere:
 		// return new DragonSphere::DialogsDragonSphere(vm);
 	case GType_Forest:
 		// return new Forest::DialogsForest(vm);
+#endif
 	default:
 		// Throw a warning for now, since the associated Dialogs class isn't implemented yet
 		warning("Dialogs: Unknown game");
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index 56561d92e1a..d60d33291ac 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -59,8 +59,10 @@ MODULE_OBJS += \
 	forest/game_forest.o \
 	forest/forest_scenes.o \
 	forest/globals_forest.o \
+	phantom/dialogs_phantom.o \
 	phantom/game_phantom.o \
 	phantom/globals_phantom.o \
+	phantom/menu_phantom.o \
 	phantom/phantom_scenes.o \
 	phantom/phantom_scenes1.o \
 	phantom/phantom_scenes2.o \
diff --git a/engines/mads/phantom/dialogs_phantom.cpp b/engines/mads/phantom/dialogs_phantom.cpp
new file mode 100644
index 00000000000..157b448b96f
--- /dev/null
+++ b/engines/mads/phantom/dialogs_phantom.cpp
@@ -0,0 +1,1211 @@
+/* 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/scummsys.h"
+#include "common/config-manager.h"
+#include "common/util.h"
+#include "common/translation.h"
+#include "backends/keymapper/keymapper.h"
+#include "gui/saveload.h"
+#include "mads/mads.h"
+#include "mads/screen.h"
+#include "mads/msurface.h"
+#include "mads/staticres.h"
+#include "mads/phantom/dialogs_phantom.h"
+#include "mads/phantom/game_phantom.h"
+#include "mads/phantom/menu_phantom.h"
+
+namespace MADS {
+namespace Phantom {
+
+bool DialogsPhantom::show(int messageId, int objectId) {
+	MADSAction &action = _vm->_game->_scene._action;
+	Common::StringArray msg = _vm->_game->getMessage(messageId);
+	Common::String title;
+	Common::String commandText;
+	Common::String valStr;
+	Common::String dialogText;
+	bool result = true;
+	bool centerFlag = false;
+	bool underlineFlag = false;
+	bool commandFlag = false;
+	bool crFlag = false;
+	TextDialog *dialog = nullptr;
+	_dialogWidth = 17;
+	_capitalizationMode = kUppercase;
+
+	// Loop through the lines of the returned text
+	for (uint idx = 0; idx < msg.size(); ++idx) {
+		Common::String srcLine = msg[idx];
+		const char *srcP = srcLine.c_str();
+
+		// Loop through the text of the line
+		while (srcP < srcLine.c_str() + srcLine.size()) {
+			if (*srcP == '[') {
+				// Starting a command
+				commandText = "";
+				commandFlag = true;
+			} else if (*srcP == ']') {
+				// Ending a command
+				if (commandFlag) {
+					if (commandCheck("CENTER", valStr, commandText)) {
+						centerFlag = true;
+					} else if (commandCheck("TITLE", valStr, commandText)) {
+						centerFlag = true;
+						underlineFlag = true;
+						crFlag = true;
+						int v = atoi(valStr.c_str());
+						if (v != 0)
+							_dialogWidth = v;
+					} else if (commandCheck("CR", valStr, commandText)) {
+						if (centerFlag) {
+							crFlag = true;
+						} else {
+							if (dialog)
+								delete dialog;
+
+							if (objectId == -1)
+								dialog = new TextDialog(_vm, FONT_INTERFACE, _defaultPosition, _dialogWidth);
+							else
+								dialog = new PictureDialog(_vm, _defaultPosition, _dialogWidth, objectId);
+
+							dialog->wordWrap(dialogText);
+							dialog->incNumLines();
+						}
+					} else if (commandCheck("ASK", valStr, commandText)) {
+						if (!dialog)
+							error("DialogsPhantom::show - Uninitialized dialog");
+						dialog->addInput();
+					} else if (commandCheck("VERB", valStr, commandText)) {
+						dialogText += getVocab(action._activeAction._verbId);
+					} else if (commandCheck("INDEX", valStr, commandText)) {
+						int idxLocal = atoi(valStr.c_str());
+						if (_indexList[idxLocal])
+							dialogText += getVocab(_indexList[idxLocal]);
+					} else if (commandCheck("NUMBER", valStr, commandText)) {
+						int idxLocal = atoi(valStr.c_str());
+						dialogText += Common::String::format("%.4d", _indexList[idxLocal]);
+					} else if (commandCheck("NOUN1", valStr, commandText)) {
+						if (!textNoun(dialogText, 1, valStr))
+							dialogText += getVocab(action._activeAction._objectNameId);
+					} else if (commandCheck("NOUN2", valStr, commandText)) {
+						if (!textNoun(dialogText, 2, valStr))
+							dialogText += getVocab(action._activeAction._indirectObjectId);
+					} else if (commandCheck("PREP", valStr, commandText)) {
+						dialogText += kArticleList[action._savedFields._articleNumber];
+					} else if (commandCheck("SENTENCE", valStr, commandText)) {
+						dialogText += action._sentence;
+					} else if (commandCheck("WIDTH", valStr, commandText)) {
+						_dialogWidth = atoi(valStr.c_str());
+					} else if (commandCheck("BAR", valStr, commandText)) {
+						if (!dialog)
+							error("DialogsPhantom::show - Uninitialized dialog");
+						dialog->addBarLine();
+					} else if (commandCheck("UNDER", valStr, commandText)) {
+						underlineFlag = true;
+					} else if (commandCheck("DOWN", valStr, commandText)) {
+						if (!dialog)
+							error("DialogsPhantom::show - Uninitialized dialog");
+						dialog->downPixelLine();
+					} else if (commandCheck("TAB", valStr, commandText)) {
+						if (!dialog)
+							error("DialogsPhantom::show - Uninitialized dialog");
+						int xp = atoi(valStr.c_str());
+						dialog->setLineXp(xp);
+					}
+				}
+
+				commandFlag = false;
+			} else if (commandFlag) {
+				// Add the next character to the command
+				commandText += *srcP;
+			} else {
+				// Add to the text to be displayed in the dialog
+				dialogText += *srcP;
+			}
+
+			++srcP;
+		}
+
+		if (!dialog) {
+			if (objectId == -1)
+				dialog = new TextDialog(_vm, FONT_INTERFACE, _defaultPosition, _dialogWidth);
+			else
+				dialog = new PictureDialog(_vm, _defaultPosition, _dialogWidth, objectId);
+		}
+
+		if (centerFlag) {
+			dialog->addLine(dialogText, underlineFlag);
+			if (crFlag)
+				dialog->incNumLines();
+		} else {
+			dialog->wordWrap(dialogText);
+		}
+
+		// Reset line processing flags in preparation for next line
+		dialogText = "";
+		commandFlag = false;
+		underlineFlag = false;
+		centerFlag = false;
+		crFlag = false;
+	}
+
+	if (!dialog)
+		error("DialogsPhantom::show - Uninitialized dialog");
+
+	if (!centerFlag)
+		dialog->incNumLines();
+
+	// Show the dialog
+	_vm->_events->setCursor(CURSOR_ARROW);
+	dialog->show();
+
+	delete dialog;
+	return result;
+}
+
+void DialogsPhantom::showItem(int objectId, int messageId, int speech) {
+	// MADS engine doesn't currently support speech
+	assert(!speech);
+
+	show(messageId, objectId);
+}
+
+Common::String DialogsPhantom::getVocab(int vocabId) {
+	assert(vocabId > 0);
+
+	Common::String vocab = _vm->_game->_scene.getVocab(vocabId);
+
+	switch (_capitalizationMode) {
+	case kUppercase:
+		vocab.toUppercase();
+		break;
+	case kLowercase:
+		vocab.toLowercase();
+		break;
+	case kUpperAndLower:
+		vocab.toLowercase();
+		vocab.setChar(toupper(vocab[0]), 0);
+	default:
+		break;
+	}
+
+	return vocab;
+}
+
+bool DialogsPhantom::textNoun(Common::String &dest, int nounId, const Common::String &source) {
+	// Ensure the destination has parameter specifications
+	if (!source.hasPrefix(":"))
+		return false;
+
+	// Extract the first (singular) result value
+	Common::String param1 = Common::String(source.c_str() + 1);
+	Common::String param2;
+	const char *sepChar = strchr(source.c_str() + 1, ':');
+	if (sepChar) {
+		param1 = Common::String(source.c_str() + 1, sepChar);
+
+		// Get the second, plural form
+		param2 = Common::String(sepChar + 1);
+	}
+
+	// Get the vocab to use
+	MADSAction &action = _vm->_game->_scene._action;
+	Common::String vocab = _vm->_dialogs->getVocab(action._activeAction._verbId);
+	Common::String *str;
+
+	if (vocab.hasSuffix("s") || vocab.hasSuffix("S")) {
+		str = &param2;
+	} else {
+		str = &param1;
+
+		if (param1 == "a ") {
+			switch (toupper(vocab[0])) {
+			case 'A':
+			case 'E':
+			case 'I':
+			case 'O':
+			case 'U':
+				param1 = "an ";
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	dest += *str;
+	return true;
+}
+
+bool DialogsPhantom::commandCheck(const char *idStr, Common::String &valStr,
+		const Common::String &command) {
+	uint idLen = strlen(idStr);
+
+	valStr = (command.size() <= idLen) ? "" : Common::String(command.c_str() + idLen);
+
+	// Check whether the command starts with the given Id
+	int result = scumm_strnicmp(idStr, command.c_str(), idLen) == 0;
+	if (!result)
+		return false;
+
+	// It does, so set the command case mode
+	if (Common::isUpper(command[0]) && Common::isUpper(command[1])) {
+		_capitalizationMode = kUppercase;
+	} else if (Common::isUpper(command[0])) {
+		_capitalizationMode = kUpperAndLower;
+	} else {
+		_capitalizationMode = kLowercase;
+	}
+
+	return true;
+}
+
+void DialogsPhantom::showDialog() {
+	while (_pendingDialog != DIALOG_NONE && !_vm->shouldQuit()) {
+		DialogId dialogId = _pendingDialog;
+		_pendingDialog = DIALOG_NONE;
+
+		Common::Keymapper *keymapper = _vm->getEventManager()->getKeymapper();
+		if (dialogId == MADS::DIALOG_MAIN_MENU) {
+			keymapper->getKeymap("menu-shortcuts")->setEnabled(true);
+		} else {
+			keymapper->getKeymap("menu-shortcuts")->setEnabled(false);
+		}
+
+		switch (dialogId) {
+		case DIALOG_MAIN_MENU: {
+			MainMenu *menu = new MainMenu(_vm);
+			menu->show();
+			delete menu;
+			break;
+		}
+		case DIALOG_DIFFICULTY: {
+			DifficultyDialog *dlg = new DifficultyDialog(_vm);
+			dlg->show();
+			delete dlg;
+			break;
+		}
+		case DIALOG_GAME_MENU: {
+			GameMenuDialog *dlg = new GameMenuDialog(_vm);
+			dlg->show();
+			delete dlg;
+			break;
+		}
+		case DIALOG_SAVE: {
+			showScummVMSaveDialog();
+			break;
+		}
+		case DIALOG_RESTORE: {
+			showScummVMRestoreDialog();
+			break;
+		}
+		case DIALOG_OPTIONS: {
+			OptionsDialog *dlg = new OptionsDialog(_vm);
+			dlg->show();
+			delete dlg;
+			break;
+		}
+		case DIALOG_ADVERT: {
+			AdvertView *dlg = new AdvertView(_vm);
+			dlg->show();
+			delete dlg;
+			break;
+		}
+		case DIALOG_TEXTVIEW: {
+			TextView *dlg = new PhantomTextView(_vm);
+			dlg->show();
+			delete dlg;
+			return;
+		}
+		case DIALOG_ANIMVIEW: {
+			AnimationView *dlg = new PhantomAnimationView(_vm);
+			dlg->show();
+			delete dlg;
+			break;
+		}
+		default:
+			break;
+		}
+	}
+}
+
+void DialogsPhantom::showScummVMSaveDialog() {
+	auto &game = *(Phantom::GamePhantom *)_vm->_game;
+	Scene &scene = game._scene;
+	GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+
+	int slot = dialog->runModalWithCurrentTarget();
+	if (slot >= 0) {
+		Common::String desc = dialog->getResultString();
+
+		if (desc.empty()) {
+			// create our own description for the saved game, the user didn't enter it
+			desc = dialog->createDefaultSaveDescription(slot);
+		}
+
+		scene._spriteSlots.reset();
+		scene.loadScene(scene._currentSceneId, game._aaName, true);
+		scene._userInterface.noInventoryAnim();
+		game._scene.drawElements(kTransitionFadeIn, false);
+
+		game.saveGame(slot, desc);
+	}
+
+	// Flag for scene loading that we're returning from a dialog
+	scene._currentSceneId = RETURNING_FROM_DIALOG;
+
+	delete dialog;
+}
+
+void DialogsPhantom::showScummVMRestoreDialog() {
+	auto &game = *(Phantom::GamePhantom *)_vm->_game;
+	GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+	Scene &scene = game._scene;
+
+	int slot = dialog->runModalWithCurrentTarget();
+	if (slot >= 0) {
+		game._loadGameSlot = slot;
+		game._scene._currentSceneId = RETURNING_FROM_LOADING;
+		game._currentSectionNumber = -1;
+	} else {
+		// Flag for scene loading that we're returning from a dialog
+		scene._currentSceneId = RETURNING_FROM_DIALOG;
+	}
+
+	delete dialog;
+}
+
+/*------------------------------------------------------------------------*/
+
+CopyProtectionDialog::CopyProtectionDialog(MADSEngine *vm, bool priorAnswerWrong) :
+TextDialog(vm, FONT_INTERFACE, Common::Point(-1, -1), 32) {
+	getHogAnusEntry(_hogEntry);
+
+	if (priorAnswerWrong) {
+		addLine("ANSWER INCORRECT!", true);
+		wordWrap("\n");
+		addLine("(But we'll give you another chance!)");
+	} else {
+		addLine("REX NEBULAR version 8.43", true);
+		wordWrap("\n");
+		addLine("(Copy Protection, for your convenience)");
+	}
+	wordWrap("\n");
+
+	wordWrap("Now comes the part that everybody hates.  But if we don't");
+	wordWrap("do this, nasty rodent-like people will pirate this game");
+	wordWrap("and a whole generation of talented designers, programmers,");
+	wordWrap("artists, and playtesters will go hungry, and will wander");
+	wordWrap("aimlessly through the land at night searching for peace.");
+	wordWrap("So let's grit our teeth and get it over with.  Just get");
+
+	Common::String line = "out your copy of ";
+	line += _hogEntry._bookId == 103 ? "the GAME MANUAL" : "REX'S LOGBOOK";
+	line += ".  See!  That was easy.  ";
+	wordWrap(line);
+
+	line = Common::String::format("Next, just turn to page %d. On line %d, find word number %d, ",
+		_hogEntry._pageNum, _hogEntry._lineNum, _hogEntry._wordNum);
+	wordWrap(line);
+
+	wordWrap("and type it on the line below (we've even given you");
+	wordWrap("first letter as a hint).  As soon as you do that, we can get");
+	wordWrap("right into this really COOL adventure game!\n");
+	wordWrap("\n");
+	wordWrap("                    ");
+	addInput();
+	wordWrap("\n");
+}
+
+void CopyProtectionDialog::show() {
+	draw();
+
+	Common::KeyState curKey;
+	const Common::Rect inputArea(110, 165, 210, 175);
+	MSurface *origInput = new MSurface(inputArea.width(), inputArea.height());
+	_vm->_screen->frameRect(inputArea, TEXTDIALOG_BLACK);
+	origInput->blitFrom(*_vm->_screen, inputArea, Common::Point(0, 0));
+		_font->setColors(TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE);
+	_vm->_screen->update();
+
+	bool firstTime = true;
+
+	while (!_vm->shouldQuit()) {
+		if (!firstTime) {
+			while (!_vm->shouldQuit() && !_vm->_events->isKeyPressed()) {
+				_vm->_events->delay(1);
+			}
+
+			if (_vm->shouldQuit())
+				break;
+
+			curKey = _vm->_events->getKey();
+
+			if (curKey.keycode == Common::KEYCODE_RETURN || curKey.keycode == Common::KEYCODE_KP_ENTER)
+				break;
+			else if (curKey.keycode == Common::KEYCODE_BACKSPACE)
+				_textInput.deleteLastChar();
+			else if (_textInput.size() < 14)
+				_textInput += curKey.ascii;
+
+			_vm->_events->_pendingKeys.clear();
+		} else {
+			firstTime = false;
+			_textInput = _hogEntry._word[0];
+		}
+
+		_vm->_screen->blitFrom(*origInput, Common::Point(inputArea.left, inputArea.top));
+		_font->writeString(_vm->_screen, _textInput,
+			Common::Point(inputArea.left + 2, inputArea.top + 1), 1);
+		_vm->_screen->update();
+	}
+
+	origInput->free();
+	delete origInput;
+}
+
+bool CopyProtectionDialog::isCorrectAnswer() {
+	return _hogEntry._word == _textInput;
+}
+
+
+bool CopyProtectionDialog::getHogAnusEntry(HOGANUS &entry) {
+	File f;
+	f.open("*HOGANUS.DAT");
+
+	// Read in the total number of entries, and randomly pick an entry to use
+	int numEntries = f.readUint16LE();
+	int entryIndex = _vm->getRandomNumber(1, numEntries);
+
+	// Read in the encrypted entry
+	f.seek(28 * entryIndex + 2);
+	byte entryData[28];
+	f.read(entryData, 28);
+
+	// Decrypt it
+	for (int i = 0; i < 28; ++i)
+		entryData[i] = ~entryData[i];
+
+	// Fill out the fields
+	entry._bookId = entryData[0];
+	entry._pageNum = READ_LE_UINT16(&entryData[2]);
+	entry._lineNum = READ_LE_UINT16(&entryData[4]);
+	entry._wordNum = READ_LE_UINT16(&entryData[6]);
+	entry._word = Common::String((char *)&entryData[8]);
+
+	f.close();
+	return true;
+}
+
+/*------------------------------------------------------------------------*/
+
+PictureDialog::PictureDialog(MADSEngine *vm, const Common::Point &pos,
+		int maxChars, int objectId) :
+		TextDialog(vm, FONT_INTERFACE, pos, maxChars), _objectId(objectId) {
+	// Turn off cycling if active
+	Scene &scene = _vm->_game->_scene;
+	_cyclingActive = scene._cyclingActive;
+	scene._cyclingActive = false;
+}
+
+PictureDialog::~PictureDialog() {
+	// Restore cycling flag
+	Scene &scene = _vm->_game->_scene;
+	scene._cyclingActive = _cyclingActive;
+}
+
+void PictureDialog::save() {
+	Palette &palette = *_vm->_palette;
+	byte map[PALETTE_COUNT];
+
+	// Save the entire screen
+	_savedSurface = new MSurface(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT);
+	_savedSurface->blitFrom(*_vm->_screen);
+
+	// Save palette information
+	Common::copy(&palette._mainPalette[0], &palette._mainPalette[PALETTE_SIZE], &_palette[0]);
+	Common::copy(&palette._palFlags[0], &palette._palFlags[PALETTE_COUNT], &_palFlags[0]);
+	_rgbList.copy(palette._rgbList);
+
+	// Set up palette allocation
+	Common::fill(&palette._colorFlags[0], &palette._colorFlags[3], true);
+
+	uint32 *palFlagP = &palette._palFlags[0];
+	for (int idx = 0; idx < PALETTE_COUNT; ++idx, ++palFlagP) {
+		if (idx < PALETTE_RESERVED_LOW_COUNT ||
+			idx >= (PALETTE_COUNT - PALETTE_RESERVED_HIGH_COUNT - 10)) {
+			*palFlagP = 1;
+			map[idx] = idx;
+		} else {
+			*palFlagP = 0;
+		}
+	}
+
+	// Reset the flag list
+	palette._rgbList.reset();
+
+	// Fade the screen to grey
+	int numColors = PALETTE_COUNT - PALETTE_RESERVED_LOW_COUNT - PALETTE_RESERVED_HIGH_COUNT;
+	palette.fadeOut(palette._mainPalette, &map[PALETTE_RESERVED_LOW_COUNT],
+		PALETTE_RESERVED_LOW_COUNT, numColors, 248, 8, 1, 16);
+
+	// Remap the greyed out screen to use the small greyscale range
+	// at the top end of the palette
+	_vm->_screen->translate(map);
+
+	// Load the inventory picture
+	Common::Path setName(Common::String::format("*OB%.3d.SS", _objectId));
+	SpriteAsset *asset = new SpriteAsset(_vm, setName, 0x8000);
+	palette.setFullPalette(palette._mainPalette);
+
+	// Get the inventory frame, and adjust the dialog position to allow for it
+	MSprite *frame = asset->getFrame(0);
+	_position.y = frame->h + 12;
+	if ((_position.y + _height) > _vm->_screen->h)
+		_position.y -= (_position.y + _height) - _vm->_screen->h;
+
+	// Draw the inventory picture
+	_vm->_screen->transBlitFrom(*frame, Common::Point(160 - frame->w / 2, 6),
+		frame->getTransparencyIndex());
+
+	// Adjust the dialog colors to use
+	TEXTDIALOG_CONTENT1 -= 10;
+	TEXTDIALOG_CONTENT2 -= 10;
+	TEXTDIALOG_EDGE -= 10;
+	TEXTDIALOG_BACKGROUND -= 10;
+	TEXTDIALOG_FC -= 10;
+	TEXTDIALOG_FD -= 10;
+	TEXTDIALOG_FE -= 10;
+}
+
+void PictureDialog::restore() {
+	if (_savedSurface) {
+		_vm->_screen->blitFrom(*_savedSurface);
+		_savedSurface->free();
+		delete _savedSurface;
+		_savedSurface = nullptr;
+
+		// Restore palette information
+		Palette &palette = *_vm->_palette;
+		Common::copy(&_palette[0], &_palette[PALETTE_SIZE], &palette._mainPalette[0]);
+		_vm->_palette->setFullPalette(palette._mainPalette);
+		Common::copy(&_palFlags[0], &_palFlags[PALETTE_COUNT], &palette._palFlags[0]);
+		palette._rgbList.copy(_rgbList);
+
+		_vm->_dialogs->_defaultPosition.y = -1;
+	}
+}
+
+/*------------------------------------------------------------------------*/
+
+GameDialog::DialogLine::DialogLine() {
+	_active = true;
+	_state = DLGSTATE_UNSELECTED;
+	_textDisplayIndex = -1;
+	_font = nullptr;
+	_widthAdjust = 0;
+	_msg = "";
+}
+
+GameDialog::DialogLine::DialogLine(const Common::String &s) {
+	_active = true;
+	_state = DLGSTATE_UNSELECTED;
+	_textDisplayIndex = -1;
+	_font = nullptr;
+	_widthAdjust = -1;
+	_msg = s;
+}
+
+/*------------------------------------------------------------------------*/
+
+GameDialog::GameDialog(MADSEngine *vm) : FullScreenDialog(vm) {
+	Game &game = *_vm->_game;
+	Scene &scene = game._scene;
+
+	_tempLine = 0;
+	_movedFlag = false;
+	_redrawFlag = false;
+	_selectedLine = -1;
+	_dirFlag = false;
+	_textLineCount = 0;
+	_lineIndex = -1;
+	_screenId = 920;
+
+	chooseBackground();
+	game.loadQuoteSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+		17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+		34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 0);
+	game._kernelMode = KERNEL_ROOM_PRELOAD;
+	_vm->_events->waitCursor();
+	scene.clearVocab();
+	scene._dynamicHotspots.clear();
+	// Clear scene sprites and objects
+	scene._spriteSlots.reset();
+	_vm->_game->_screenObjects.clear();
+	_vm->_dialogs->_defaultPosition = Common::Point(-1, -1);
+	_menuSpritesIndex = 0;
+}
+
+void GameDialog::display() {
+	Palette &palette = *_vm->_palette;
+	palette.initPalette();
+	palette.resetGamePalette(18, 10);
+
+	FullScreenDialog::display();
+
+	palette.setEntry(10, 0, 63, 0);
+	palette.setEntry(11, 0, 45, 0);
+	palette.setEntry(12, 63, 63, 0);
+	palette.setEntry(13, 45, 45, 0);
+	palette.setEntry(14, 63, 63, 63);
+	palette.setEntry(15, 45, 45, 45);
+
+	Scene &scene = _vm->_game->_scene;
+	SpriteAsset *menuSprites = new SpriteAsset(_vm, "*MENU", 0);
+	_menuSpritesIndex = scene._sprites.add(menuSprites);
+
+	_lineIndex = -1;
+	setClickableLines();
+
+	_vm->_events->setCursor(CURSOR_ARROW);
+}
+
+GameDialog::~GameDialog() {
+	_vm->_game->_scene._currentSceneId = RETURNING_FROM_DIALOG;
+}
+
+void GameDialog::clearLines() {
+	Scene &scene = _vm->_game->_scene;
+	_movedFlag = false;
+	_lines.clear();
+	scene._spriteSlots.fullRefresh(true);
+}
+
+void GameDialog::setClickableLines() {
+	ScreenObjects &screenObjects = _vm->_game->_screenObjects;
+
+	for (uint idx = 0; idx < _lines.size(); ++idx) {
+		if (_lines[idx]._active) {
+			const Common::Point &pt = _lines[idx]._pos;
+			int strWidth = _lines[idx]._font->getWidth(_lines[idx]._msg);
+			int maxHeight = _lines[idx]._font->getHeight();
+
+			screenObjects.add(Common::Rect(pt.x, pt.y, pt.x + strWidth, pt.y + maxHeight - 1),
+				SCREENMODE_VGA, CAT_COMMAND, idx);
+		}
+	}
+
+	if (_vm->_dialogs->_pendingDialog == DIALOG_SAVE ||
+			_vm->_dialogs->_pendingDialog == DIALOG_RESTORE) {
+		screenObjects.add(Common::Rect(293, 26, 312, 75), SCREENMODE_VGA, CAT_INV_LIST, 50);
+		screenObjects.add(Common::Rect(293, 78, 312, 127), SCREENMODE_VGA, CAT_INV_LIST, 51);
+	}
+}
+
+void GameDialog::addQuote(int id1, int id2, DialogTextAlign align,
+		const Common::Point &pt, Font *font) {
+	Common::String msg = _vm->_game->getQuote(id1).c_str();	// c_str() because we need a copy
+
+	if (id2 > 0)
+		msg += _vm->_game->getQuote(id2);
+
+	addLine(msg, align, pt, font);
+}
+
+void GameDialog::addLine(const Common::String &msg, DialogTextAlign align,
+		const Common::Point &pt, Font *font) {
+	Scene &scene = _vm->_game->_scene;
+	DialogLine *line;
+
+	if (font == nullptr)
+		font = _vm->_font->getFont(FONT_CONVERSATION);
+
+	if (_lineIndex < (int)_lines.size()) {
+		if (_lines.size() >= 20) {
+			++_lineIndex;
+			return;
+		}
+
+		_lines.push_back(msg);
+		line = &_lines[_lines.size() - 1];
+	} else {
+		line = &_lines[_lineIndex];
+		if (msg.compareToIgnoreCase(msg)) {
+			++_lineIndex;
+			return;
+		}
+
+		if (line->_textDisplayIndex >= 0) {
+			TextDisplay &textDisplay = scene._textDisplay[line->_textDisplayIndex];
+			if (textDisplay._active) {
+				textDisplay._expire = -1;
+				if (_textLineCount < 20) {
+					textDisplay._msg = msg;
+					++_textLineCount;
+				}
+			}
+		}
+	}
+
+	line->_font = font;
+	line->_state = DLGSTATE_UNSELECTED;
+	line->_pos = pt;
+	line->_widthAdjust = -1;
+	line->_textDisplayIndex = -1;
+
+	int xOffset;
+	switch (align) {
+	case ALIGN_NONE:
+		// No adjustment
+		break;
+
+	case ALIGN_CENTER:
+		xOffset = (MADS_SCREEN_WIDTH / 2) - font->getWidth(msg, -1) / 2;
+		line->_pos.x += xOffset;
+		break;
+
+	case ALIGN_AT_CENTER: {
+		const char *msgP = msg.c_str();
+		const char *ch = strchr(msgP, '@');
+		if (ch) {
+			xOffset = (MADS_SCREEN_WIDTH / 2) - font->getWidth(
+				Common::String(msgP, ch), line->_widthAdjust);
+			line->_pos.x += xOffset;
+
+			Common::String newMsg = msg.c_str();
+			newMsg.deleteChar(ch - msgP);
+			line->_msg = newMsg;
+		}
+		break;
+	}
+
+	case ALIGN_RIGHT:
+		xOffset = font->getWidth(msg, -1);
+		line->_pos.x -= xOffset;
+		break;
+
+	default:
+		break;
+	}
+
+	++_lineIndex;
+}
+
+void GameDialog::initVars() {
+	_tempLine = -1;
+	_selectedLine = -1;
+	_lineIndex = 0;
+	_textLineCount = 0;
+}
+
+void GameDialog::chooseBackground() {
+	switch (_vm->_game->_currentSectionNumber) {
+	case 1:
+	case 2:
+		_screenId = 921;
+		break;
+	case 3:
+	case 4:
+		_screenId = 922;
+		break;
+	case 5:
+	case 6:
+	case 7:
+		_screenId = 923;
+		break;
+	case 8:
+		_screenId = 924;
+		break;
+	default:
+		_screenId = 920;
+		break;
+	}
+}
+
+void GameDialog::setFrame(int frameNumber, int depth) {
+	Scene &scene = _vm->_game->_scene;
+	SpriteAsset *menuSprites = scene._sprites[_menuSpritesIndex];
+	MSprite *frame = menuSprites->getFrame(frameNumber - 1);
+
+	SpriteSlot &spriteSlot = scene._spriteSlots[scene._spriteSlots.add()];
+	spriteSlot._flags = IMG_UPDATE;
+	spriteSlot._seqIndex = 1;
+	spriteSlot._spritesIndex = _menuSpritesIndex;
+	spriteSlot._frameNumber = frameNumber;
+	spriteSlot._position = frame->_offset;
+	spriteSlot._depth = depth;
+	spriteSlot._scale = 100;
+}
+
+void GameDialog::show() {
+	display();
+
+	Scene &scene = _vm->_game->_scene;
+
+	while (_selectedLine == -1 && !_vm->shouldQuit()) {
+		handleEvents();
+		if (_redrawFlag) {
+			if (!_tempLine)
+				_tempLine = -1;
+
+			refreshText();
+			scene.drawElements(_vm->_game->_fx, _vm->_game->_fx);
+			_redrawFlag = false;
+		}
+
+		_vm->_events->waitForNextFrame();
+		_vm->_game->_fx = kTransitionNone;
+	}
+}
+
+void GameDialog::handleEvents() {
+	ScreenObjects &screenObjects = _vm->_game->_screenObjects;
+	EventsManager &events = *_vm->_events;
+	auto &dialogs = *(Phantom::DialogsPhantom *)_vm->_dialogs;
+	int tempLine = _tempLine;
+
+	// Mark all the lines as initially unselected
+	for (uint i = 0; i < _lines.size(); ++i)
+		_lines[i]._state = DLGSTATE_UNSELECTED;
+
+	// Process pending events
+	events.pollEvents();
+
+	if (events.isActionTriggered()) {
+		switch (events.getAction()) {
+		case kActionEscape:
+			_selectedLine = 0;
+			break;
+		default:
+			break;
+		}
+	}
+
+	// Scan for objects in the dialog
+	Common::Point mousePos = events.currentPos() - Common::Point(0, DIALOG_TOP);
+	int objIndex = screenObjects.scan(mousePos, SCREENMODE_VGA);
+
+	if (_movedFlag) {
+		int yp = mousePos.y;
+		if (yp < screenObjects[1]._bounds.top) {
+			if (!events._mouseReleased)
+				_lines[1]._state = DLGSTATE_SELECTED;
+			objIndex = 19;
+		}
+
+		if (yp < screenObjects[7]._bounds.bottom) {
+			if (!events._mouseReleased)
+				_lines[1]._state = DLGSTATE_SELECTED;
+			objIndex = 20;
+		}
+	}
+
+	int line = -1;
+	if (objIndex > 0 && (events._mouseStatus || events._mouseReleased)) {
+		line = screenObjects[objIndex]._descId;
+		if (dialogs._pendingDialog == DIALOG_SAVE || dialogs._pendingDialog == DIALOG_RESTORE) {
+			if (line > 7 && line <= 14) {
+				_lines[line]._state = DLGSTATE_UNSELECTED;
+				line -= 7;
+			}
+
+			bool movedFlag = line > 0 && line < 8;
+			if (events._mouseMoved)
+				_movedFlag = movedFlag;
+		}
+
+		if (screenObjects[objIndex]._category == CAT_COMMAND) {
+			_lines[line]._state = DLGSTATE_SELECTED;
+		}
+	}
+	if (!line)
+		line = -1;
+
+	if (dialogs._pendingDialog == DIALOG_ERROR && line == 1)
+		line = -1;
+
+	if (events._mouseReleased) {
+		if (!_movedFlag || line <= 18)
+			_selectedLine = line;
+		_redrawFlag = true;
+	}
+
+	_tempLine = line;
+	if (tempLine != line || _selectedLine >= 0)
+		_redrawFlag = true;
+}
+
+void GameDialog::refreshText() {
+	Scene &scene = _vm->_game->_scene;
+
+	for (uint i = 0; i < _lines.size(); ++i) {
+		if (_lines[i]._active) {
+			int fontColor;
+			switch (_lines[i]._state) {
+			case DLGSTATE_UNSELECTED:
+				fontColor = 0xB0A;
+				break;
+			case DLGSTATE_SELECTED:
+				fontColor = 0xD0C;
+				break;
+			default:
+				fontColor = 0xF0E;
+				break;
+			}
+
+			bool skipFlag = false;
+			if (_lines[i]._textDisplayIndex >= 0) {
+				TextDisplay &textDisplay = scene._textDisplay[_lines[i]._textDisplayIndex];
+				int currCol = textDisplay._color1;
+				if (currCol != fontColor) {
+					scene._textDisplay.expire(_lines[i]._textDisplayIndex);
+					_lines[i]._textDisplayIndex = -1;
+				} else {
+					skipFlag = true;
+				}
+			}
+
+			if (!skipFlag) {
+				_lines[i]._textDisplayIndex = scene._textDisplay.add(_lines[i]._pos.x, _lines[i]._pos.y,
+					fontColor, _lines[i]._widthAdjust, _lines[i]._msg, _lines[i]._font);
+			}
+		}
+	}
+}
+
+/*------------------------------------------------------------------------*/
+
+DifficultyDialog::DifficultyDialog(MADSEngine *vm) : GameDialog(vm) {
+	setLines();
+	_vm->_palette->resetGamePalette(18, 10);
+}
+
+void DifficultyDialog::setLines() {
+	Font *font = _vm->_font->getFont(FONT_CONVERSATION);
+	int yp = 78 - ((font->getHeight() + 1) * 4 + 6) / 2;
+
+	addQuote(41, 0, ALIGN_CENTER, Common::Point(0, yp), font);
+	yp += 6;
+
+	for (int id = 42; id <= 44; ++id) {
+		yp += font->getHeight();
+		addQuote(id, 0, ALIGN_CENTER, Common::Point(0, yp));
+	}
+}
+
+void DifficultyDialog::display() {
+	GameDialog::display();
+	setFrame(8, 2);
+}
+
+void DifficultyDialog::show() {
+	GameDialog::show();
+	auto &game = *(Phantom::GamePhantom *)_vm->_game;
+
+	switch (_selectedLine) {
+	case 1:
+		game._difficulty = Phantom::DIFFICULTY_EASY;
+		break;
+	case 2:
+		game._difficulty = Phantom::DIFFICULTY_HARD;
+		break;
+	default:
+		_vm->quitGame();
+	}
+}
+
+/*------------------------------------------------------------------------*/
+
+GameMenuDialog::GameMenuDialog(MADSEngine *vm) : GameDialog(vm) {
+	setLines();
+}
+
+void GameMenuDialog::setLines() {
+	Font *font = _vm->_font->getFont(FONT_CONVERSATION);
+
+	int yp = 64 - ((font->getHeight() + 1) * 4 + 6) / 2;
+
+	addQuote(10, 0, ALIGN_CENTER, Common::Point(0, yp), font);
+	yp += 6;
+
+	for (int id = 11; id <= 15; ++id) {
+		yp += font->getHeight();
+		addQuote(id, 0, ALIGN_CENTER, Common::Point(0, yp));
+	}
+}
+
+void GameMenuDialog::display() {
+	GameDialog::display();
+	setFrame(1, 2);
+}
+
+void GameMenuDialog::show() {
+	GameDialog::show();
+
+	switch (_selectedLine) {
+	case 1:
+		_vm->_dialogs->_pendingDialog = DIALOG_SAVE;
+		_vm->_dialogs->showDialog();
+		break;
+	case 2:
+		_vm->_dialogs->_pendingDialog = DIALOG_RESTORE;
+		_vm->_dialogs->showDialog();
+		break;
+	case 3:
+		_vm->_dialogs->_pendingDialog = DIALOG_OPTIONS;
+		_vm->_dialogs->showDialog();
+		break;
+	case 5:
+		_vm->quitGame();
+		break;
+	case 4:
+	default:
+		// Resume game
+		break;
+	}
+}
+
+/*------------------------------------------------------------------------*/
+
+OptionsDialog::OptionsDialog(MADSEngine *vm) : GameDialog(vm) {
+	setLines();
+}
+
+int OptionsDialog::getOptionQuote(int option) {
+#ifdef TODO
+	auto &game = *(Phantom::GamePhantom *)_vm->_game;
+
+	switch (option) {
+	case 17:	// Music
+		return _vm->_musicFlag ? 24 : 25;	// 24: ON, 25: OFF
+	case 18:	// Sound
+		return _vm->_soundFlag ? 26 : 27;	// 26: ON, 27: OFF
+	case 19:	// Interface
+		return !_vm->_easyMouse ? 28 : 29;	// 28: Standard, 29: Easy
+	case 20:	// Inventory
+		return _vm->_invObjectsAnimated ? 30 : 31;	// 30: Spinning, 31: Still
+	case 21:	// Text window
+		return !_vm->_textWindowStill ? 32 : 33;	// 32: Animated, 33: Still
+	case 22:	// Screen fade
+		return 34 + _vm->_screenFade;	// 34: Smooth, 35: Medium, 36: Fast
+	case 23:	// Storyline
+		return (game._storyMode == STORYMODE_NAUGHTY) ? 37 : 38;	// 37: Naughty, 38: Nice
+	default:
+		error("getOptionQuote: Unknown option");
+	}
+#endif
+	return 0;
+}
+
+void OptionsDialog::setLines() {
+	Font *font = _vm->_font->getFont(FONT_CONVERSATION);
+
+	int yp = 40 - ((font->getHeight() + 1) * 4 + 6) / 2;
+
+	addQuote(16, 0, ALIGN_CENTER, Common::Point(0, yp), font);
+	yp += 6;
+
+	for (int id = 17; id <= 23; ++id) {
+		yp += font->getHeight();
+		addQuote(id, getOptionQuote(id), ALIGN_AT_CENTER, Common::Point(0, yp));
+	}
+
+	yp += 28;
+	addQuote(1, 0, ALIGN_NONE, Common::Point(90, yp));
+	addQuote(2, 0, ALIGN_NONE, Common::Point(190, yp));
+}
+
+void OptionsDialog::display() {
+	GameDialog::display();
+	setFrame(2, 2);
+}
+
+void OptionsDialog::show() {
+//	auto &game = *(Phantom::GamePhantom *)_vm->_game;
+
+	// Previous options, restored when cancel is selected
+	bool prevMusicFlag = _vm->_musicFlag;
+	bool prevEasyMouse = _vm->_easyMouse;
+	bool prevInvObjectsAnimated = _vm->_invObjectsAnimated;
+	bool prevTextWindowStill = _vm->_textWindowStill;
+	ScreenFade prevScreenFade = _vm->_screenFade;
+//	StoryMode prevStoryMode = game._storyMode;
+
+	do {
+		_selectedLine = -1;
+		GameDialog::show();
+
+		switch (_selectedLine) {
+		case 1:	// Music
+			_vm->_musicFlag = _vm->_soundFlag = !_vm->_musicFlag;
+			break;
+		case 2:	// Sound
+			_vm->_musicFlag = _vm->_soundFlag = !_vm->_musicFlag;
+			break;
+		case 3:	// Interface
+			_vm->_easyMouse = !_vm->_easyMouse;
+			break;
+		case 4:	// Inventory
+			_vm->_invObjectsAnimated = !_vm->_invObjectsAnimated;
+			break;
+		case 5:	// Text window
+			_vm->_textWindowStill = !_vm->_textWindowStill;
+			break;
+		case 6:	// Screen fade
+			if (_vm->_screenFade == SCREEN_FADE_FAST)
+				_vm->_screenFade = SCREEN_FADE_MEDIUM;
+			else if (_vm->_screenFade == SCREEN_FADE_MEDIUM)
+				_vm->_screenFade = SCREEN_FADE_SMOOTH;
+			else
+				_vm->_screenFade = SCREEN_FADE_FAST;
+			break;
+//		case 7:	// Storyline
+//			game._storyMode = (game._storyMode == STORYMODE_NAUGHTY) ? STORYMODE_NICE : STORYMODE_NAUGHTY;
+//			break;
+		default:
+			break;
+		}
+
+		// Reload menu
+		_lineIndex = -1;
+		clearLines();
+		_vm->_game->_screenObjects.clear();
+		_vm->_game->_scene._spriteSlots.reset();
+		setLines();
+	} while (!_vm->shouldQuit() && _selectedLine != 0 && _selectedLine <= 7);
+
+	if (_selectedLine == 8) {
+		// OK button, save settings
+		_vm->saveOptions();
+	} else if (_selectedLine == 9) {
+		// Cancel button, revert all options from the saved ones
+		_vm->_musicFlag = _vm->_soundFlag = prevMusicFlag;
+		_vm->_easyMouse = prevEasyMouse;
+		_vm->_invObjectsAnimated = prevInvObjectsAnimated;
+		_vm->_textWindowStill = prevTextWindowStill;
+		_vm->_screenFade = prevScreenFade;
+		//game._storyMode = prevStoryMode;
+	}
+}
+
+} // namespace Phantom
+} // namespace MADS
diff --git a/engines/mads/phantom/dialogs_phantom.h b/engines/mads/phantom/dialogs_phantom.h
new file mode 100644
index 00000000000..f669aa42754
--- /dev/null
+++ b/engines/mads/phantom/dialogs_phantom.h
@@ -0,0 +1,269 @@
+/* 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 MADS_DIALOGS_PHANTOM_H
+#define MADS_DIALOGS_PHANTOM_H
+
+#include "common/scummsys.h"
+#include "mads/game.h"
+#include "mads/dialogs.h"
+
+namespace MADS {
+namespace Phantom {
+
+enum CapitalizationMode { kUppercase = 0, kLowercase = 1, kUpperAndLower = 2 };
+
+class DialogsPhantom : public Dialogs {
+	friend class Dialogs;
+private:
+	int _dialogWidth;
+	CapitalizationMode _capitalizationMode;
+
+	DialogsPhantom(MADSEngine *vm): Dialogs(vm), _capitalizationMode(kUppercase), _dialogWidth(0) {}
+
+	Common::String getVocab(int vocabId) override;
+
+	bool textNoun(Common::String &dest, int nounId, const Common::String &source);
+
+	bool commandCheck(const char *idStr, Common::String &valStr, const Common::String &command);
+
+	void showScummVMSaveDialog();
+	void showScummVMRestoreDialog();
+
+public:
+	void showDialog() override;
+
+	void showItem(int objectId, int messageId, int speech = -1) override;
+
+	bool show(int messageId, int objectId = -1) override;
+};
+
+struct HOGANUS {
+	int _bookId;
+	int _pageNum;
+	int _lineNum;
+	int _wordNum;
+	Common::String _word;
+};
+
+class CopyProtectionDialog : public TextDialog {
+private:
+	HOGANUS _hogEntry;
+	Common::String _textInput;
+
+	/**
+	 * Get a random copy protection entry from the HOGANUS resource
+	 */
+	bool getHogAnusEntry(HOGANUS &entry);
+public:
+	/**
+	 * Constructor
+	 */
+	CopyProtectionDialog(MADSEngine *vm, bool priorAnswerWrong);
+
+	/**
+	 * Show the dialog
+	 */
+	void show() override;
+
+	bool isCorrectAnswer();
+};
+
+class PictureDialog : public TextDialog {
+private:
+	int _objectId;
+	bool _cyclingActive;
+	byte _palette[PALETTE_SIZE];
+	uint32 _palFlags[PALETTE_COUNT];
+	RGBList _rgbList;
+protected:
+	void save() override;
+
+	void restore() override;
+public:
+	PictureDialog(MADSEngine *vm, const Common::Point &pos, int maxChars, int objectId);
+
+	~PictureDialog() override;
+};
+
+enum DialogTextAlign { ALIGN_NONE = 0, ALIGN_CENTER = -1, ALIGN_AT_CENTER = -2, ALIGN_RIGHT = -3 };
+
+enum DialogState { DLGSTATE_UNSELECTED = 0, DLGSTATE_SELECTED = 1, DLGSTATE_FOCUSED = 2 };
+
+class GameDialog: public FullScreenDialog {
+	struct DialogLine {
+		bool _active;
+		DialogState _state;
+		Common::Point _pos;
+		int _textDisplayIndex;
+		Common::String _msg;
+		Font *_font;
+		int _widthAdjust;
+
+		DialogLine();
+		DialogLine(const Common::String &s);
+	};
+protected:
+	Common::Array<DialogLine> _lines;
+	int _tempLine;
+	bool _movedFlag;
+	bool _redrawFlag;
+	int _selectedLine;
+	bool _dirFlag;
+	int _menuSpritesIndex;
+	int _lineIndex;
+	int _textLineCount;
+
+	/**
+	 * Display the dialog
+	 */
+	void display() override;
+
+	/**
+	 * Reset the lines list for the dialog
+	 */
+	void clearLines();
+
+	/**
+	 * Setup lines to be clickable
+	 */
+	void setClickableLines();
+
+	/**
+	 * Add a quote to the lines list
+	 */
+	void addQuote(int id1, int id2, DialogTextAlign align, const Common::Point &pt, Font *font = nullptr);
+
+	/**
+	 * Adds a line to the lines list
+	 */
+	void addLine(const Common::String &msg, DialogTextAlign align, const Common::Point &pt, Font *font = nullptr);
+
+	/**
+	 * Initializes variables
+	 */
+	void initVars();
+
+	/**
+	 * Sets the display for the screen background behind the dialog
+	 */
+	void setFrame(int frameNumber, int depth);
+
+	/**
+	 * Choose the background to display for the dialog
+	 */
+	void chooseBackground();
+
+	/**
+	 * Handle events whilst the dialog is active
+	 */
+	void handleEvents();
+
+	/**
+	 * Refresh the display of the dialog's text
+	 */
+	void refreshText();
+public:
+	/**
+	 * Constructor
+	 */
+	GameDialog(MADSEngine *vm);
+
+	/**
+	 * Destructor
+	 */
+	~GameDialog() override;
+
+	/**
+	 * Show the dialog
+	 */
+	virtual void show();
+};
+
+class DifficultyDialog : public GameDialog {
+private:
+	/**
+	 * Set the lines for the dialog
+	 */
+	void setLines();
+public:
+	DifficultyDialog(MADSEngine *vm);
+
+	/**
+	 * Display the dialog
+	 */
+	void display() override;
+
+	/**
+	* Show the dialog
+	*/
+	void show() override;
+};
+
+class GameMenuDialog : public GameDialog {
+private:
+	/**
+	 * Set the lines for the dialog
+	 */
+	void setLines();
+public:
+	GameMenuDialog(MADSEngine *vm);
+
+	/**
+	* Display the dialog
+	*/
+	void display() override;
+
+	/**
+	* Show the dialog
+	*/
+	void show() override;
+};
+
+class OptionsDialog : public GameDialog {
+private:
+	/**
+	 * Set the lines for the dialog
+	 */
+	void setLines();
+
+	/**
+	 * Gets the quote to be shown for an option
+	 */
+	int getOptionQuote(int option);
+public:
+	OptionsDialog(MADSEngine *vm);
+
+	/**
+	* Display the dialog
+	*/
+	void display() override;
+
+	/**
+	* Show the dialog
+	*/
+	void show() override;
+};
+
+} // namespace Phantom
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/phantom/game_phantom.cpp b/engines/mads/phantom/game_phantom.cpp
index 571add473b9..daa31b51ba3 100644
--- a/engines/mads/phantom/game_phantom.cpp
+++ b/engines/mads/phantom/game_phantom.cpp
@@ -23,8 +23,8 @@
 #include "mads/game.h"
 #include "mads/screen.h"
 #include "mads/msurface.h"
+#include "mads/menu_views.h"
 #include "mads/phantom/game_phantom.h"
-//#include "mads/nebular/dialogs_nebular.h"
 #include "mads/phantom/globals_phantom.h"
 #include "mads/phantom/phantom_scenes.h"
 
@@ -139,6 +139,47 @@ GamePhantom::GamePhantom(MADSEngine *vm) : Game(vm) {
 }
 
 void GamePhantom::startGame() {
+	// First handle any ending credits from a just finished game session.
+	// Note that, with the exception of the decompression ending, which doesn't
+	// use animations, the remaining animations will automatically launch their
+	// own text view credits when the animation is completed
+	switch (_winStatus) {
+	case 1:
+		// No shields failure ending
+		AnimationView::execute(_vm, "rexend1");
+		break;
+	case 2:
+		// Shields, but no targeting failure ending
+		AnimationView::execute(_vm, "rexend2");
+		break;
+	case 3:
+		AnimationView::execute(_vm, "rexend3");
+		break;
+	case 4:
+		// Decompression ending
+		TextView::execute(_vm, "ending4");
+		break;
+	default:
+		break;
+	}
+
+	do {
+		checkShowDialog();
+		_winStatus = 0;
+
+		_sectionNumber = 1;
+		initSection(_sectionNumber);
+		_vm->_events->setCursor(CURSOR_ARROW);
+		_statusFlag = true;
+
+		// Show the main menu
+		_vm->_dialogs->_pendingDialog = DIALOG_MAIN_MENU;
+		_vm->_dialogs->showDialog();
+	} while (!_vm->shouldQuit() && _vm->_dialogs->_pendingDialog != DIALOG_NONE);
+
+	if (_vm->shouldQuit())
+		return;
+
 	_scene._priorSceneId = 0;
 	_scene._currentSceneId = -1;
 	_scene._nextSceneId = 101;
diff --git a/engines/mads/phantom/game_phantom.h b/engines/mads/phantom/game_phantom.h
index 67406831728..c4076474c40 100644
--- a/engines/mads/phantom/game_phantom.h
+++ b/engines/mads/phantom/game_phantom.h
@@ -144,7 +144,7 @@ typedef Section1Handler Section3Handler;
 typedef Section1Handler Section4Handler;
 typedef Section1Handler Section5Handler;
 
-} // End of namespace Nebular
+} // End of namespace Phantom
 } // End of namespace MADS
 
 #endif
diff --git a/engines/mads/phantom/menu_phantom.cpp b/engines/mads/phantom/menu_phantom.cpp
new file mode 100644
index 00000000000..521aa5fefb6
--- /dev/null
+++ b/engines/mads/phantom/menu_phantom.cpp
@@ -0,0 +1,435 @@
+/* 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/scummsys.h"
+#include "common/config-manager.h"
+#include "mads/game.h"
+#include "mads/mads.h"
+#include "mads/menu_views.h"
+#include "mads/resources.h"
+#include "mads/scene.h"
+#include "mads/screen.h"
+#include "mads/phantom/menu_phantom.h"
+
+namespace MADS {
+namespace Phantom {
+
+#define NEBULAR_MENUSCREEN 990
+#define MADS_MENU_Y ((MADS_SCREEN_HEIGHT - MADS_SCENE_HEIGHT) / 2)
+#define MADS_MENU_ANIM_DELAY 70
+
+MainMenu::MainMenu(MADSEngine *vm): MenuView(vm) {
+	Common::fill(&_menuItems[0], &_menuItems[7], (SpriteAsset *)nullptr);
+	Common::fill(&_menuItemIndexes[0], &_menuItemIndexes[7], -1);
+	_delayTimeout = 0;
+	_menuItemIndex = -1;
+	_frameIndex = 0;
+	_skipFlag = false;
+	_highlightedIndex = -1;
+	_selectedIndex = -1;
+	_buttonDown = false;
+	_showEvolve = _showSets = false;
+
+	for (int i = 0; i < 7; ++i)
+		_menuItems[i] = nullptr;
+}
+
+MainMenu::~MainMenu() {
+	Scene &scene = _vm->_game->_scene;
+	for (int i = 0; i < 7; ++i) {
+		if (_menuItemIndexes[i] != -1)
+			scene._sprites.remove(_menuItemIndexes[i]);
+	}
+
+	scene._spriteSlots.reset();
+}
+
+bool MainMenu::shouldShowQuotes() {
+	return ConfMan.hasKey("ShowQuotes") && ConfMan.getBool("ShowQuotes");
+}
+
+void MainMenu::display() {
+	MenuView::display();
+	Scene &scene = _vm->_game->_scene;
+	ScreenObjects &screenObjects = _vm->_game->_screenObjects;
+	screenObjects.clear();
+
+	// Load each of the menu item assets and add to the scene sprites list
+	for (int i = 0; i < 7; ++i) {
+		Common::Path spritesName = Resources::formatName(NEBULAR_MENUSCREEN,
+			'A', i + 1, EXT_SS, "");
+		_menuItems[i] = new SpriteAsset(_vm, spritesName, 0);
+		_menuItemIndexes[i] = scene._sprites.add(_menuItems[i]);
+
+		// Register the menu item area in the screen objects
+		MSprite *frame0 = _menuItems[i]->getFrame(0);
+		Common::Point pt(frame0->_offset.x - (frame0->w / 2),
+			frame0->_offset.y - frame0->h);
+		screenObjects.add(
+			Common::Rect(pt.x, pt.y + DIALOG_TOP, pt.x + frame0->w,
+			pt.y + frame0->h + DIALOG_TOP), SCREENMODE_VGA, CAT_COMMAND, i);
+	}
+
+	// Set the cursor for when it's shown
+	_vm->_events->setCursor(CURSOR_ARROW);
+}
+
+void MainMenu::doFrame() {
+	// Delay between animation frames on the menu
+	uint32 currTime = g_system->getMillis();
+	if (currTime < _delayTimeout)
+		return;
+	_delayTimeout = currTime + MADS_MENU_ANIM_DELAY;
+
+	// If an item has already been selected, handle rotating out the other menu items
+	if (_selectedIndex != -1) {
+		if (_frameIndex == _menuItems[0]->getCount()) {
+			handleAction((MADSGameAction)_selectedIndex);
+		} else {
+			for (_menuItemIndex = 0; _menuItemIndex < 6; ++_menuItemIndex) {
+				if (_menuItemIndex == 4 && !shouldShowQuotes())
+					continue;
+
+				if (_menuItemIndex != _selectedIndex) {
+					addSpriteSlot();
+				}
+			}
+
+			// Move the menu items to the next frame
+			++_frameIndex;
+		}
+		return;
+	}
+
+	// If we've alerady reached the end of the menuitem animation, exit immediately
+	if (_menuItemIndex == 6)
+		return;
+
+	// If the user has chosen to skip the animation, show the full menu immediately
+	if (_skipFlag && _menuItemIndex >= 0) {
+		// Quickly loop through all the menu items to display each's final frame
+		for (; _menuItemIndex < 6; ++_menuItemIndex) {
+			if (_menuItemIndex == 4 && !shouldShowQuotes())
+				continue;
+
+			// Draw the final frame of the menuitem
+			_frameIndex = 0;
+			addSpriteSlot();
+		}
+
+		_vm->_events->showCursor();
+		showBonusItems();
+	} else {
+		if ((_menuItemIndex == -1) || (_frameIndex == 0)) {
+			if (++_menuItemIndex == 6) {
+
+				// Reached end of display animation
+				_vm->_events->showCursor();
+				showBonusItems();
+				return;
+			} else if (_menuItemIndex == 4 && !shouldShowQuotes()) {
+				++_menuItemIndex;
+			}
+
+			_frameIndex = _menuItems[_menuItemIndex]->getCount() - 1;
+		} else {
+			--_frameIndex;
+		}
+
+		// Move to the next menuitem frame
+		addSpriteSlot();
+	}
+}
+
+void MainMenu::addSpriteSlot() {
+	Scene &scene = _vm->_game->_scene;
+	SpriteSlots &spriteSlots = scene._spriteSlots;
+
+	int seqIndex = (_menuItemIndex < 6) ? _menuItemIndex : _frameIndex;
+	spriteSlots.deleteTimer(seqIndex);
+
+	SpriteAsset *menuItem = _menuItems[_menuItemIndex];
+	MSprite *spr = menuItem->getFrame(_frameIndex);
+
+	SpriteSlot &slot = spriteSlots[spriteSlots.add()];
+	slot._flags = IMG_UPDATE;
+	slot._seqIndex = seqIndex;
+	slot._spritesIndex = _menuItemIndexes[_menuItemIndex];
+	slot._frameNumber = _frameIndex + 1;
+	slot._position = spr->_offset;
+	slot._depth = 1;
+	slot._scale = 100;
+
+	_redrawFlag = true;
+}
+
+void MainMenu::showBonusItems() {
+	Scene &scene = _vm->_game->_scene;
+	_showEvolve = Common::File::exists("SECTION0.HAG") && Common::File::exists("evolve.res");
+	_showSets = Common::File::exists("SECTION0.HAG") && Common::File::exists("sets.res");
+
+	if (_showSets)
+		scene._kernelMessages.add(Common::Point(290, 143), 0x4140, 0, 0, 0, "S");
+	if (_showEvolve)
+		scene._kernelMessages.add(Common::Point(305, 143), 0x4140, 0, 0, 0, "E");
+}
+
+bool MainMenu::onEvent(Common::Event &event) {
+	Scene &scene = _vm->_game->_scene;
+	if (_selectedIndex != -1)
+		return false;
+
+	// Handle keypresses - these can be done at any time, even when the menu items are being drawn
+	if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
+		switch (event.customType) {
+		case kActionEscape:
+			handleAction(EXIT);
+			break;
+
+		case kActionStartGame:
+			handleAction(START_GAME);
+			break;
+
+		case kActionResumeGame:
+			handleAction(RESUME_GAME);
+			break;
+
+		case kActionShowIntro:
+			handleAction(SHOW_INTRO);
+			break;
+
+		case kActionCredits:
+			handleAction(CREDITS);
+			break;
+
+		case kActionQuotes:
+			handleAction(QUOTES);
+			break;
+
+		case kActionRestartAnimation: {
+			// Goodness knows why, but Rex has a key to restart the menuitem animations
+			// Restart the animation
+			_menuItemIndex = -1;
+			for (int i = 0; i < 6; ++i)
+				scene._spriteSlots.deleteTimer(i);
+
+			_skipFlag = false;
+			_vm->_events->hideCursor();
+			break;
+		}
+
+		default:
+			_skipFlag = true;
+			return false;
+		}
+
+		return true;
+	} else if (event.type == Common::EVENT_KEYDOWN) {
+		// Any other key skips the menu animation
+		_skipFlag = true;
+		return false;
+	}
+
+	switch (event.type) {
+	case Common::EVENT_LBUTTONDOWN:
+		if (_vm->_events->isCursorVisible()) {
+			_buttonDown = true;
+			int menuIndex = getHighlightedItem(event.mouse);
+
+			if (menuIndex != _highlightedIndex) {
+				scene._spriteSlots.deleteTimer(menuIndex);
+
+				_highlightedIndex = menuIndex;
+				if (_highlightedIndex != -1) {
+					_frameIndex = _highlightedIndex;
+					addSpriteSlot();
+				}
+			}
+		} else {
+			// Skip the menu animation
+			_skipFlag = true;
+		}
+		return true;
+
+	case Common::EVENT_MOUSEMOVE:
+		if (_buttonDown) {
+			int menuIndex = getHighlightedItem(event.mouse);
+			if (menuIndex != _highlightedIndex) {
+				if (_highlightedIndex != -1) {
+					// Revert to the unselected menu item
+					unhighlightItem();
+				}
+
+				if (menuIndex != -1) {
+					// Highlight new item
+					_highlightedIndex = menuIndex;
+					_frameIndex = _highlightedIndex;
+					addSpriteSlot();
+				}
+			}
+		}
+		break;
+
+	case Common::EVENT_LBUTTONUP:
+		_buttonDown = false;
+		if (_highlightedIndex != -1) {
+			_selectedIndex = _highlightedIndex;
+			unhighlightItem();
+			_frameIndex = 0;
+		} else if (_showSets && Common::Rect(290, 165, 300, 185).contains(event.mouse)) {
+			handleAction(SETS);
+		} else if (_showEvolve && Common::Rect(305, 165, 315, 185).contains(event.mouse)) {
+			handleAction(EVOLVE);
+		}
+
+		return true;
+
+	default:
+		break;
+	}
+
+	return false;
+}
+
+int MainMenu::getHighlightedItem(const Common::Point &pt) {
+	return _vm->_game->_screenObjects.scan(pt, SCREENMODE_VGA) - 1;
+}
+
+void MainMenu::unhighlightItem() {
+	// Revert to the unselected menu item
+	_vm->_game->_scene._spriteSlots.deleteTimer(_highlightedIndex);
+	_menuItemIndex = _highlightedIndex;
+	_frameIndex = 0;
+	addSpriteSlot();
+
+	_menuItemIndex = 6;
+	_highlightedIndex = -1;
+}
+
+void MainMenu::handleAction(MADSGameAction action) {
+	_vm->_events->hideCursor();
+	_breakFlag = true;
+
+	switch (action) {
+	case START_GAME:
+		// Show the difficulty dialog
+		_vm->_dialogs->_pendingDialog = DIALOG_DIFFICULTY;
+		break;
+
+	case RESUME_GAME:
+		// The original resumed the most recently saved game. Instead,
+		// just show the load game scren
+		_vm->_dialogs->_pendingDialog = DIALOG_RESTORE;
+		return;
+
+	case SHOW_INTRO:
+		AnimationView::execute(_vm, "rexopen");
+		break;
+
+	case CREDITS:
+		TextView::execute(_vm, "credits");
+		return;
+
+	case QUOTES:
+		TextView::execute(_vm, "quotes");
+		return;
+
+	case SETS:
+		AnimationView::execute(_vm, "sets");
+		break;
+
+	case EVOLVE:
+		AnimationView::execute(_vm, "evolve");
+		break;
+
+	case EXIT:
+		_vm->_dialogs->_pendingDialog = DIALOG_ADVERT;
+		break;
+	default:
+		break;
+	}
+}
+
+/*------------------------------------------------------------------------*/
+
+AdvertView::AdvertView(MADSEngine *vm): EventTarget(), _vm(vm) {
+	_breakFlag = false;
+}
+
+void AdvertView::show() {
+	bool altAdvert = _vm->getRandomNumber(1000) >= 500;
+	int screenId = altAdvert ? 995 : 996;
+	uint32 expiryTime = g_system->getMillis() + 10 * 1000;
+
+	_vm->_palette->resetGamePalette(4, 8);
+
+	// Load the advert background onto the screen
+	SceneInfo *sceneInfo = SceneInfo::init(_vm);
+	sceneInfo->load(screenId, 0, Common::String(), 0, _vm->_game->_scene._depthSurface,
+		*_vm->_screen);
+	_vm->_screen->markAllDirty();
+	_vm->_palette->setFullPalette(_vm->_palette->_mainPalette);
+
+	delete sceneInfo;
+
+	EventsManager &events = *_vm->_events;
+	events.setEventTarget(this);
+	events.hideCursor();
+
+	while (!_breakFlag && !_vm->shouldQuit()) {
+		_vm->_events->waitForNextFrame();
+		_vm->_game->_fx = kTransitionNone;
+
+		_breakFlag |= g_system->getMillis() >= expiryTime;
+	}
+
+	events.setEventTarget(nullptr);
+	_vm->quitGame();
+	events.pollEvents();
+}
+
+bool AdvertView::onEvent(Common::Event &event) {
+	if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START || event.type == Common::EVENT_KEYDOWN
+			|| event.type == Common::EVENT_JOYBUTTON_DOWN || event.type == Common::EVENT_LBUTTONDOWN) {
+		_breakFlag = true;
+		return true;
+	}
+
+	return false;
+}
+
+/*------------------------------------------------------------------------*/
+
+void PhantomAnimationView::scriptDone() {
+	AnimationView::scriptDone();
+
+	Common::String s = getResourceName();
+	if (s == "rexend1") {
+		TextView::execute(_vm, "ending1");
+	} else if (s == "rexend2") {
+		TextView::execute(_vm, "ending2");
+	} else if (s == "rexend3") {
+		TextView::execute(_vm, "credits");
+	}
+}
+
+} // namespace Phantom
+} // namespace MADS
diff --git a/engines/mads/phantom/menu_phantom.h b/engines/mads/phantom/menu_phantom.h
new file mode 100644
index 00000000000..d68a960514c
--- /dev/null
+++ b/engines/mads/phantom/menu_phantom.h
@@ -0,0 +1,160 @@
+/* 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 MADS_MENU_PHANTOM_H
+#define MADS_MENU_PHANTOM_H
+
+#include "common/scummsys.h"
+#include "mads/game.h"
+#include "mads/menu_views.h"
+#include "mads/msurface.h"
+#include "mads/phantom/dialogs_phantom.h"
+
+namespace MADS {
+
+class MADSEngine;
+
+namespace Phantom {
+
+enum MADSGameAction {
+	START_GAME, RESUME_GAME, SHOW_INTRO, CREDITS, QUOTES, EXIT,
+	SETS, EVOLVE
+};
+
+class MainMenu: public MenuView {
+private:
+	SpriteAsset *_menuItems[7];
+	int _menuItemIndexes[7];
+	int _menuItemIndex;
+	int _frameIndex;
+	uint32 _delayTimeout;
+	bool _skipFlag;
+	bool _showEvolve, _showSets;
+
+	/**
+	 * Currently highlighted menu item
+	 */
+	int _highlightedIndex;
+
+	/**
+	 * Flag for mouse button being pressed
+	 */
+	bool _buttonDown;
+
+	/**
+	 * Stores menu item selection
+	 */
+	int _selectedIndex;
+
+	/**
+	 * Get the highlighted menu item under the cursor
+	 */
+	int getHighlightedItem(const Common::Point &pt);
+
+	/**
+	 * Un-highlight a currently highlighted item
+	 */
+	void unhighlightItem();
+
+	/**
+	 * Execute a given menuitem
+	 */
+	void handleAction(MADSGameAction action);
+
+	/**
+	 * Add a sprite slot for the current menuitem frame
+	 */
+	void addSpriteSlot();
+
+	/**
+	 * Returns true if the Quotes item should be shown.
+	 * i.e. if the player has completed the game
+	 */
+	bool shouldShowQuotes();
+
+	/**
+	 * Show the bonus item icons, if available
+	 */
+	void showBonusItems();
+protected:
+	/**
+	 * Display the menu
+	 */
+	void display() override;
+
+	/**
+	 * Handle the menu item animations
+	 */
+	void doFrame() override;
+
+	/**
+	 * Event handler
+	 */
+	bool onEvent(Common::Event &event) override;
+public:
+	MainMenu(MADSEngine *vm);
+
+	~MainMenu() override;
+};
+
+class AdvertView : public EventTarget {
+private:
+	/**
+	 * Engine reference
+	 */
+	MADSEngine *_vm;
+
+	/**
+	 * Signals when to close the dialog
+	 */
+	bool _breakFlag;
+protected:
+	/**
+	* Event handler
+	*/
+	bool onEvent(Common::Event &event) override;
+public:
+	AdvertView(MADSEngine *vm);
+
+	~AdvertView() override {}
+
+	/**
+	 * Show the dialog
+	 */
+	void show();
+};
+
+class PhantomAnimationView : public AnimationView {
+protected:
+	void scriptDone() override;
+public:
+	PhantomAnimationView(MADSEngine *vm) : AnimationView(vm) {}
+};
+
+class PhantomTextView : public TextView {
+public:
+	PhantomTextView(MADSEngine *vm) : TextView(vm) {}
+};
+
+} // namespace Phantom
+} // namespace MADS
+
+#endif




More information about the Scummvm-git-logs mailing list