MOHAWK: MYST: Allow changing the game language while on the main menu

This commit is contained in:
Bastien Bouclet 2020-03-27 19:45:25 +01:00
parent bfe7aad96a
commit c9476543e0
9 changed files with 194 additions and 150 deletions

View file

@ -98,24 +98,6 @@ bool MohawkEngine::hasFeature(EngineFeature f) const {
(f == kSupportsRTL);
}
Common::String MohawkEngine::getDatafileLanguageName(const char *prefix) const {
const ADGameFileDescription *fileDesc;
for (fileDesc = _gameDescription->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
if (Common::String(fileDesc->fileName).hasPrefix(prefix)) {
break;
}
}
if (!fileDesc->fileName) {
warning("Malformed detection entry");
return "";
}
size_t prefixLength = strlen(prefix);
return Common::String(&fileDesc->fileName[prefixLength], strlen(fileDesc->fileName) - prefixLength - 4);
}
#ifdef ENABLE_MYST
bool MohawkEngine_Myst::hasFeature(EngineFeature f) const {
@ -230,6 +212,19 @@ DetectedGames MohawkMetaEngine::detectGames(const Common::FSList &fslist) const
// Here we amend the detected games to set the list of supported languages.
for (uint i = 0; i < detectedGames.size(); i++) {
DetectedGame &game = detectedGames[i];
#ifdef ENABLE_MYST
if (game.gameId == "myst"
&& Common::checkGameGUIOption(GAMEOPTION_25TH, game.getGUIOptions())
&& Common::checkGameGUIOption(GAMEOPTION_ME, game.getGUIOptions())) {
const Mohawk::MystLanguage *languages = Mohawk::MohawkEngine_Myst::listLanguages();
while (languages->language != Common::UNK_LANG) {
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(languages->language));
languages++;
}
}
#endif
#ifdef ENABLE_RIVEN
if (game.gameId == "riven"
&& Common::checkGameGUIOption(GAMEOPTION_25TH, game.getGUIOptions())) {

View file

@ -339,17 +339,21 @@ static const MohawkGameDescription gameDescriptions[] = {
},
// Myst Masterpiece Edition - 25th Anniversary
// English Windows - Created by the ScummVM team
// Repacked by the ScummVM team
{
{
"myst",
"Masterpiece Edition - 25th Anniversary",
{
{"myst.dat", 0, "c4cae9f143b5947262e6cb2397e1617e", -1},
{"myst_french.dat", 0, "7c8230be50ffcac588e7db8788ad7614", -1},
{"myst_german.dat", 0, "3952554439960b22a360e8e006dfed58", -1},
{"myst_polish.dat", 0, "9ca82ff26fcbfacf40e4164523a50854", -1},
{"myst_spanish.dat", 0, "822ed3c0de912c10b877dcd2cc078493", -1},
{"menu.dat", 0, "7dc23051084f79b1c2bccc84cdec0503", -1},
AD_LISTEND
},
Common::EN_ANY,
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUI_OPTIONS_MYST_ME_25TH
@ -359,90 +363,6 @@ static const MohawkGameDescription gameDescriptions[] = {
0,
},
// Myst Masterpiece Edition - 25th Anniversary
// French Windows - Repacked by the ScummVM team
{
{
"myst",
"Masterpiece Edition - 25th Anniversary",
{
{"myst_french.dat", 0, "7c8230be50ffcac588e7db8788ad7614", -1},
{"menu.dat", 0, "7dc23051084f79b1c2bccc84cdec0503", -1},
AD_LISTEND
},
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUI_OPTIONS_MYST_ME_25TH
},
GType_MYST,
GF_ME | GF_25TH | GF_LANGUAGE_FILES,
0,
},
// Myst Masterpiece Edition - 25th Anniversary
// German Windows - Repacked by the ScummVM team
{
{
"myst",
"Masterpiece Edition - 25th Anniversary",
{
{"myst_german.dat", 0, "3952554439960b22a360e8e006dfed58", -1},
{"menu.dat", 0, "7dc23051084f79b1c2bccc84cdec0503", -1},
AD_LISTEND
},
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUI_OPTIONS_MYST_ME_25TH
},
GType_MYST,
GF_ME | GF_25TH | GF_LANGUAGE_FILES,
0,
},
// Myst Masterpiece Edition - 25th Anniversary
// Polish Windows - Repacked by the ScummVM team
{
{
"myst",
"Masterpiece Edition - 25th Anniversary",
{
{"myst_polish.dat", 0, "9ca82ff26fcbfacf40e4164523a50854", -1},
{"menu.dat", 0, "7dc23051084f79b1c2bccc84cdec0503", -1},
AD_LISTEND
},
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUI_OPTIONS_MYST_ME_25TH
},
GType_MYST,
GF_ME | GF_25TH | GF_LANGUAGE_FILES,
0,
},
// Myst Masterpiece Edition - 25th Anniversary
// Spanish Windows - Repacked by the ScummVM team
{
{
"myst",
"Masterpiece Edition - 25th Anniversary",
{
{"myst_spanish.dat", 0, "822ed3c0de912c10b877dcd2cc078493", -1},
{"menu.dat", 0, "7dc23051084f79b1c2bccc84cdec0503", -1},
AD_LISTEND
},
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUI_OPTIONS_MYST_ME_25TH
},
GType_MYST,
GF_ME | GF_25TH | GF_LANGUAGE_FILES,
0,
},
// Riven: The Sequel to Myst
// Version 1.0 (5CD)
// From clone2727

View file

@ -24,6 +24,7 @@
#include "mohawk/dialogs.h"
#include "gui/gui-manager.h"
#include "gui/message.h"
#include "gui/saveload.h"
#include "gui/ThemeEval.h"
#include "gui/widget.h"
@ -96,18 +97,22 @@ MystOptionsWidget::MystOptionsWidget(GuiObject *boss, const Common::String &name
_zipModeCheckbox(nullptr),
_transitionsCheckbox(nullptr),
_mystFlyByCheckbox(nullptr),
_languagePopUp(nullptr),
_dropPageButton(nullptr),
_showMapButton(nullptr),
_returnToMenuButton(nullptr) {
Common::String guiOptions = ConfMan.get("guioptions", _domain);
bool isDemo = checkGameGUIOption(GAMEOPTION_DEMO, guiOptions);
bool isME = checkGameGUIOption(GAMEOPTION_ME, guiOptions);
if (!checkGameGUIOption(GAMEOPTION_DEMO, ConfMan.get("guioptions", _domain))) {
if (!isDemo) {
// I18N: Option for fast scene switching
_zipModeCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "MystOptionsDialog.ZipMode", _("~Z~ip Mode Activated"));
}
_transitionsCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "MystOptionsDialog.Transistions", _("~T~ransitions Enabled"));
if (checkGameGUIOption(GAMEOPTION_ME, ConfMan.get("guioptions", _domain))) {
if (isME) {
_mystFlyByCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "MystOptionsDialog.PlayMystFlyBy", _("Play the Myst fly by movie"),
_("The Myst fly by movie was not played by the original engine."));
}
@ -128,6 +133,19 @@ MystOptionsWidget::MystOptionsWidget(GuiObject *boss, const Common::String &name
if (vm->getFeatures() & GF_DEMO) {
_returnToMenuButton = new GUI::ButtonWidget(widgetsBoss(), "MystOptionsDialog.MainMenu", _("Main Men~u~"), nullptr, kMenuCmd);
}
if (vm->getFeatures() & GF_25TH) {
GUI::StaticTextWidget *languageCaption = new GUI::StaticTextWidget(widgetsBoss(), "MystOptionsDialog.LanguageDesc", _("Language:"));
languageCaption->setAlign(Graphics::kTextAlignRight);
_languagePopUp = new GUI::PopUpWidget(widgetsBoss(), "MystOptionsDialog.Language");
const MystLanguage *languages = MohawkEngine_Myst::listLanguages();
while (languages->language != Common::UNK_LANG) {
_languagePopUp->appendEntry(Common::getLanguageDescription(languages->language), languages->language);
languages++;
}
}
}
}
@ -141,6 +159,11 @@ void MystOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::Stri
.addWidget("ZipMode", "Checkbox")
.addWidget("Transistions", "Checkbox")
.addWidget("PlayMystFlyBy", "Checkbox")
.addLayout(GUI::ThemeLayout::kLayoutHorizontal)
.addPadding(0, 0, 0, 0)
.addWidget("LanguageDesc", "OptionsLabel")
.addWidget("Language", "PopUp")
.closeLayout()
.addLayout(GUI::ThemeLayout::kLayoutHorizontal)
.addPadding(0, 0, 16, 0)
.addSpace()
@ -168,6 +191,14 @@ void MystOptionsWidget::load() {
_mystFlyByCheckbox->setState(ConfMan.getBool("playmystflyby", _domain));
}
if (_languagePopUp) {
Common::Language language = Common::parseLanguage(ConfMan.get("language", _domain));
const MystLanguage *languageDesc = MohawkEngine_Myst::getLanguageDesc(language);
if (languageDesc) {
_languagePopUp->setSelectedTag(languageDesc->language);
}
}
if (isInGame()) {
MohawkEngine_Myst *vm = static_cast<MohawkEngine_Myst *>(g_engine);
assert(vm);
@ -196,6 +227,29 @@ bool MystOptionsWidget::save() {
ConfMan.setBool("playmystflyby", _mystFlyByCheckbox->getState(), _domain);
}
if (_languagePopUp) {
MohawkEngine_Myst *vm = static_cast<MohawkEngine_Myst *>(g_engine);
assert(vm);
int32 selectedLanguage = _languagePopUp->getSelectedTag();
const MystLanguage *languageDesc = nullptr;
if (selectedLanguage >= 0) {
languageDesc = MohawkEngine_Myst::getLanguageDesc(static_cast<Common::Language>(selectedLanguage));
}
Common::Language newLanguage = Common::UNK_LANG;
if (languageDesc != nullptr) {
newLanguage = languageDesc->language;
ConfMan.set("language", Common::getLanguageCode(languageDesc->language));
}
Common::Language currentLanguage = vm->getLanguage();
if (newLanguage != currentLanguage && vm->isGameStarted()) {
GUI::MessageDialog dialog(_("The new language will be applied after restarting the game."));
dialog.runModal();
}
}
return true;
}

View file

@ -99,6 +99,7 @@ private:
GUI::CheckboxWidget *_zipModeCheckbox;
GUI::CheckboxWidget *_transitionsCheckbox;
GUI::CheckboxWidget *_mystFlyByCheckbox;
GUI::PopUpWidget *_languagePopUp;
GUI::ButtonWidget *_dropPageButton;
GUI::ButtonWidget *_showMapButton;

View file

@ -66,8 +66,7 @@ enum MohawkGameFeatures {
GF_25TH = (1 << 1), // Myst and Riven 25th Anniversary
GF_DVD = (1 << 2),
GF_DEMO = (1 << 3),
GF_LB_10 = (1 << 4), // very early Living Books 1.0 games
GF_LANGUAGE_FILES = (1 << 5) // Myst and Riven versions using language override files
GF_LB_10 = (1 << 4) // very early Living Books 1.0 games
};
struct MohawkGameDescription;
@ -92,7 +91,6 @@ public:
Common::Platform getPlatform() const;
uint8 getGameType() const;
virtual Common::Language getLanguage() const;
Common::String getDatafileLanguageName(const char *prefix) const;
bool hasFeature(EngineFeature f) const override;

View file

@ -81,6 +81,8 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription
_mainCursor = kDefaultMystCursor;
_showResourceRects = false;
_scheduledAction = kMystActionNone;
_currentLanguage = Common::UNK_LANG;
_currentLanguage = getLanguage();
_sound = nullptr;
_video = nullptr;
@ -233,17 +235,19 @@ Common::String MohawkEngine_Myst::wrapMovieFilename(const Common::String &movieN
}
Common::String MohawkEngine_Myst::selectLocalizedMovieFilename(const Common::String &movieName) {
Common::String language;
if (getFeatures() & GF_LANGUAGE_FILES) {
language = getDatafileLanguageName("myst_");
}
Common::Language language = getLanguage();
const MystLanguage *languageDesc = getLanguageDesc(language);
Common::String localizedMovieName = Common::String::format("%s/%s", language.c_str(), movieName.c_str());
if (!language.empty() && SearchMan.hasFile(localizedMovieName)) {
return localizedMovieName;
} else {
if (!languageDesc) {
return movieName;
}
Common::String localizedMovieName = Common::String::format("%s/%s", languageDesc->archiveSuffix, movieName.c_str());
if (!SearchMan.hasFile(localizedMovieName)) {
return movieName;
}
return localizedMovieName;
}
VideoEntryPtr MohawkEngine_Myst::playMovie(const Common::String &name, MystStack stack) {
@ -440,26 +444,66 @@ Common::Error MohawkEngine_Myst::run() {
return Common::kNoError;
}
const MystLanguage *MohawkEngine_Myst::listLanguages() {
static const MystLanguage languages[] = {
{ Common::EN_ANY, "english" },
{ Common::FR_FRA, "french" },
{ Common::DE_DEU, "german" },
{ Common::PL_POL, "polish" },
{ Common::ES_ESP, "spanish" },
{ Common::UNK_LANG, nullptr }
};
return languages;
}
const MystLanguage *MohawkEngine_Myst::getLanguageDesc(Common::Language language) {
const MystLanguage *languages = listLanguages();
while (languages->language != Common::UNK_LANG) {
if (languages->language == language) {
return languages;
}
languages++;
}
return nullptr;
}
Common::Language MohawkEngine_Myst::getLanguage() const {
Common::Language language = MohawkEngine::getLanguage();
if (language == Common::UNK_LANG) {
language = _currentLanguage;
}
// The language can be changed at run time in the 25th anniversary edition
if (language == Common::UNK_LANG) {
language = Common::parseLanguage(ConfMan.get("language"));
}
if (language == Common::UNK_LANG) {
language = Common::EN_ANY;
}
return language;
}
void MohawkEngine_Myst::loadStackArchives(MystStack stackId) {
for (uint i = 0; i < _mhk.size(); i++) {
delete _mhk[i];
}
_mhk.clear();
closeAllArchives();
Common::String language;
if (getFeatures() & GF_LANGUAGE_FILES) {
language = getDatafileLanguageName("myst_");
}
Common::Language language = getLanguage();
const MystLanguage *languageDesc = getLanguageDesc(language);
if (!language.empty()) {
loadArchive(mystFiles[stackId], language.c_str(), false);
if (languageDesc) {
loadArchive(mystFiles[stackId], languageDesc->archiveSuffix, false);
}
loadArchive(mystFiles[stackId], nullptr, true);
if (getFeatures() & GF_ME) {
if (!language.empty()) {
loadArchive("help", language.c_str(), false);
if (languageDesc) {
loadArchive("help", languageDesc->archiveSuffix, false);
}
loadArchive("help", nullptr, true);
@ -497,6 +541,17 @@ void MohawkEngine_Myst::registerDefaultSettings() {
ConfMan.registerDefault("transition_mode", false);
}
void MohawkEngine_Myst::applyGameSettings() {
// Allow changing the language when in the main menu when the game has not yet been started.
// It's not possible to reliably change the language one the game is started as the current
// view cannot be reconstructed using the save / stack state.
if ((getFeatures() & GF_25TH) && !isGameStarted()) {
_currentLanguage = Common::parseLanguage(ConfMan.get("language"));
_gfx->loadMenuFont();
changeToStack(_stack->getStackId(), _card->getId(), 0, 0);
}
}
Common::KeymapArray MohawkEngine_Myst::initKeymaps(const char *target) {
using namespace Common;
@ -731,6 +786,7 @@ void MohawkEngine_Myst::runOptionsDialog() {
int result = runDialog(dlg);
if (result > 0) {
syncSoundSettings();
applyGameSettings();
}
if (result > kMystActionNone && result <= kMystActionLast) {
@ -1006,7 +1062,7 @@ bool MohawkEngine_Myst::hasGameSaveSupport() const {
return !(getFeatures() & GF_DEMO) && getGameType() != GType_MAKINGOF;
}
bool MohawkEngine_Myst::isInteractive() {
bool MohawkEngine_Myst::isInteractive() const {
return !_stack->isScriptRunning() && !_waitingOnBlockingOperation;
}

View file

@ -42,6 +42,7 @@ class MystGraphics;
class MystScriptParser;
class MystConsole;
class MystGameState;
struct MystLanguage;
class MystOptionsWidget;
class MystSound;
class MystArea;
@ -185,7 +186,8 @@ public:
* When the game is interactive, the user can interact with the game world
* and perform other operations such as loading saved games, ...
*/
bool isInteractive();
bool isInteractive() const;
bool isGameStarted() const;
bool canLoadGameStateCurrently() override;
bool canSaveGameStateCurrently() override;
Common::Error loadGameState(int slot) override;
@ -196,6 +198,7 @@ public:
bool hasFeature(EngineFeature f) const override;
static void registerDefaultSettings();
void applyGameSettings() override;
static Common::Array<Common::Keymap *> initKeymaps(const char *target);
void resumeFromMainMenu();
@ -209,6 +212,10 @@ public:
void doAction(MystEventAction action);
void scheduleAction(MystEventAction action);
static const MystLanguage *listLanguages();
static const MystLanguage *getLanguageDesc(Common::Language language);
Common::Language getLanguage() const override;
private:
ResourceCache _cache;
@ -221,7 +228,6 @@ private:
void pauseEngineIntern(bool pause) override;
void goToMainMenu();
bool isGameStarted() const;
void dropPage();
@ -239,9 +245,15 @@ private:
uint16 _currentCursor;
uint16 _mainCursor; // Also defines the current page being held (white, blue, red, or none)
Common::Language _currentLanguage;
MystEventAction _scheduledAction;
};
struct MystLanguage {
Common::Language language;
const char *archiveSuffix;
};
} // End of namespace Mohawk
#endif

View file

@ -67,6 +67,22 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) :
_mainMenuBackupBackBuffer.reset(new Graphics::Surface());
if (_vm->getFeatures() & GF_25TH) {
loadMenuFont();
}
}
MystGraphics::~MystGraphics() {
delete _bmpDecoder;
_backBuffer->free();
delete _backBuffer;
delete _menuFont;
}
void MystGraphics::loadMenuFont() {
delete _menuFont;
_menuFont = nullptr;
const char *menuFontName = "NotoSans-ExtraBold.ttf";
#ifdef USE_FREETYPE2
int fontSize;
@ -85,15 +101,6 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) :
{
warning("Unable to open the menu font file '%s'", menuFontName);
}
}
}
MystGraphics::~MystGraphics() {
delete _bmpDecoder;
_backBuffer->free();
delete _backBuffer;
delete _menuFont;
}
MohawkSurface *MystGraphics::decodeImage(uint16 id) {

View file

@ -64,6 +64,7 @@ public:
void restoreStateForMainMenu();
Graphics::Surface *getThumbnailForMainMenu() const;
void loadMenuFont();
Common::Rect getTextBoundingBox(const Common::U32String &text, const Common::Rect &dest, Graphics::TextAlign align);
void drawText(uint16 image, const Common::U32String &text, const Common::Rect &dest, uint8 r, uint8 g, uint8 b, Graphics::TextAlign align, int16 deltaY);