NANCY: Initial main menu implementation

Implemented the original engine's main menu and added an option to enable it from the custom options dialog. Several buttons in the main menu don't work yet since they require implementing additional game states.
This commit is contained in:
fracturehill 2021-04-04 17:29:45 +03:00
parent 9f35d133ca
commit 68e54edd50
8 changed files with 280 additions and 1 deletions

View File

@ -32,18 +32,22 @@ NancyOptionsWidget::NancyOptionsWidget(GuiObject *boss, const Common::String &na
OptionsContainerWidget(boss, name, "NancyOptionsDialog", false, domain) {
_playerSpeechCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "NancyOptionsDialog.PlayerSpeech", _("Player Speech"), _("Enable player speech. Only works if speech is enabled in the Audio settings."));
_characterSpeechCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "NancyOptionsDialog.CharacterSpeech", _("Character Speech"), _("Enable NPC speech. Only works if speech is enabled in the Audio settings."));
_originalMenusCheckbox = new GUI::CheckboxWidget(widgetsBoss(), "NancyOptionsDialog.OriginalMenus", _("Use original menus"), _("Use the original engine's main, save/load, and setup menus. ScummVM's Global Main Menu can still be accessed through its keymap."));
new GUI::StaticTextWidget(widgetsBoss(), "NancyOptionsDialog.SpeechSettingsLabel", _("Speech Options"));
new GUI::StaticTextWidget(widgetsBoss(), "NancyOptionsDialog.EngineSettingsLabel", _("Engine Options"));
}
void NancyOptionsWidget::load() {
_playerSpeechCheckbox->setState(ConfMan.getBool("player_speech", _domain));
_characterSpeechCheckbox->setState(ConfMan.getBool("character_speech", _domain));
_originalMenusCheckbox->setState(ConfMan.getBool("original_menus", _domain));
}
bool NancyOptionsWidget::save() {
ConfMan.setBool("player_speech", _playerSpeechCheckbox->getState(), _domain);
ConfMan.setBool("character_speech", _characterSpeechCheckbox->getState(), _domain);
ConfMan.setBool("original_menus", _characterSpeechCheckbox->getState(), _domain);
return true;
}
@ -55,6 +59,9 @@ void NancyOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::Str
.addWidget("SpeechSettingsLabel", "OptionsLabel")
.addWidget("PlayerSpeech", "Checkbox")
.addWidget("CharacterSpeech", "Checkbox")
.addSpace(16)
.addWidget("EngineSettingsLabel", "OptionsLabel")
.addWidget("OriginalMenus", "Checkbox")
.closeLayout()
.closeDialog();
}

View File

@ -42,6 +42,7 @@ private:
GUI::CheckboxWidget *_playerSpeechCheckbox;
GUI::CheckboxWidget *_characterSpeechCheckbox;
GUI::CheckboxWidget *_originalMenusCheckbox;
};
} // End of namespace Nancy

View File

@ -24,6 +24,7 @@ MODULE_OBJS = \
state/credits.o \
state/logo.o \
state/help.o \
state/mainmenu.o \
state/map.o \
state/scene.o \
cheat.o \

View File

@ -44,6 +44,7 @@
#include "engines/nancy/state/help.h"
#include "engines/nancy/state/map.h"
#include "engines/nancy/state/credits.h"
#include "engines/nancy/state/mainmenu.h"
namespace Nancy {
@ -153,6 +154,11 @@ void NancyEngine::setState(NancyState::NancyState state, NancyState::NancyState
setState(NancyState::kLogo);
return;
case NancyState::kMainMenu: {
if (ConfMan.getBool("original_menus")) {
break;
}
// Do not use the original engine's menus, call the GMM instead
State::State *s = getStateObject(_gameFlow.curState);
if (s) {
s->onStateExit();
@ -286,6 +292,7 @@ void NancyEngine::bootGameEngine() {
// Register default settings
ConfMan.registerDefault("player_speech", true);
ConfMan.registerDefault("character_speech", true);
ConfMan.registerDefault("original_menus", false);
// Load archive
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember("data1.cab");
@ -349,6 +356,8 @@ State::State *NancyEngine::getStateObject(NancyState::NancyState state) const {
return &State::Help::instance();
case NancyState::kScene:
return &State::Scene::instance();
case NancyState::kMainMenu:
return &State::MainMenu::instance();
default:
return nullptr;
}

View File

@ -67,6 +67,7 @@ public:
Common::Rect convertToScreen(const Common::Rect &rect) const;
Common::Rect getBounds() const { return Common::Rect(_drawSurface.w, _drawSurface.h); }
Graphics::ManagedSurface &getDrawSurface() { return _drawSurface; }
protected:
// Z order and blit type are extracted directly from the corresponding

View File

@ -20,6 +20,8 @@
*
*/
#include "common/config-manager.h"
#include "engines/nancy/nancy.h"
#include "engines/nancy/sound.h"
#include "engines/nancy/input.h"
@ -80,7 +82,12 @@ void Logo::stop() {
// The original engine checks for N+D and N+C key combos here.
// For the N+C key combo it looks for some kind of cheat file
// to initialize the game state with.
g_nancy->setState(NancyState::kScene);
if (ConfMan.getBool("original_menus")) {
g_nancy->setState(NancyState::kMainMenu);
} else {
g_nancy->setState(NancyState::kScene);
}
}
} // End of namespace State

View File

@ -0,0 +1,178 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "engines/nancy/nancy.h"
#include "engines/nancy/cursor.h"
#include "engines/nancy/sound.h"
#include "engines/nancy/input.h"
#include "engines/nancy/util.h"
#include "engines/nancy/state/mainmenu.h"
#include "engines/nancy/state/scene.h"
namespace Common {
DECLARE_SINGLETON(Nancy::State::MainMenu);
}
namespace Nancy {
namespace State {
void MainMenu::process() {
switch (_state) {
case kInit:
init();
// fall through
case kRun:
run();
break;
case kStop:
stop();
break;
}
}
void MainMenu::onStateExit() {
destroy();
}
void MainMenu::init() {
Common::SeekableReadStream *chunk = g_nancy->getBootChunkStream("MENU");
chunk->seek(0);
Common::String imageName;
readFilename(*chunk, imageName);
_background.init(imageName);
_background.registerGraphics();
g_nancy->_cursorManager->setCursorType(CursorManager::kNormalArrow);
g_nancy->_cursorManager->showCursor(true);
if (!g_nancy->_sound->isSoundPlaying("MSND")) {
g_nancy->_sound->playSound("MSND");
}
chunk->seek(0x20);
// Unlike every other rect in the engine, these use int16 instead of int32
for (uint i = 0; i < 8; ++i) {
_destRects.push_back(Common::Rect());
Common::Rect &rect = _destRects.back();
rect.left = chunk->readSint16LE();
rect.top = chunk->readSint16LE();
rect.right = chunk->readSint16LE();
rect.bottom = chunk->readSint16LE();
}
for (uint i = 0; i < 8; ++i) {
_srcRects.push_back(Common::Rect());
Common::Rect &rect = _srcRects.back();
rect.left = chunk->readSint16LE();
rect.top = chunk->readSint16LE();
rect.right = chunk->readSint16LE();
rect.bottom = chunk->readSint16LE();
}
_buttonDown.registerGraphics();
_buttonDown._redrawFrom = &_background;
_state = kRun;
}
void MainMenu::run() {
NancyInput input = g_nancy->_input->getInput();
_buttonDown.setVisible(false);
if (input.input & NancyInput::kLeftMouseButtonUp) {
for (uint i = 0; i < 8; ++i) {
if (_destRects[i].contains(input.mousePos)) {
if (i == 3 && !Scene::hasInstance()) {
g_nancy->_sound->playSound("BUDE");
_playedOKSound = false;
} else {
g_nancy->_sound->playSound("BUOK");
_playedOKSound = true;
}
_selected = i;
_state = kStop;
_buttonDown._drawSurface.create(_background.getDrawSurface(), _srcRects[i]);
_buttonDown._screenPosition = _destRects[i];
_buttonDown.setVisible(true);
return;
}
}
}
}
void MainMenu::stop() {
if (!g_nancy->_sound->isSoundPlaying(_playedOKSound ? "BUOK" : "BUDE")) {
switch (_selected) {
case 0:
// Credits
g_nancy->setState(NancyState::kCredits);
break;
case 1:
// New Game
if (Scene::hasInstance()) {
NancySceneState.destroy(); // Simply destroy the existing Scene and create a new one
}
g_nancy->setState(NancyState::kScene);
break;
case 2:
// Load and Save Game, TODO
_state = kRun;
break;
case 3:
// Continue
if (Scene::hasInstance()) {
g_nancy->setState(NancyState::kScene);
} else {
_state = kRun;
}
break;
case 4:
// Second Chance, TODO
_state = kRun;
break;
case 5:
// Game Setup, TODO
_state = kRun;
break;
case 6:
// Exit Game
g_nancy->quitGame(); // Consider returning to launcher instead?
break;
case 7:
// Help
g_nancy->setState(NancyState::kHelp);
break;
}
}
}
} // End of namespace State
} // End of namespace Nancy

View File

@ -0,0 +1,75 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef NANCY_STATE_MAINMENU_H
#define NANCY_STATE_MAINMENU_H
#include "common/singleton.h"
#include "engines/nancy/state/state.h"
#include "engines/nancy/ui/fullscreenimage.h"
namespace Nancy {
namespace State {
class MainMenu : public State, public Common::Singleton<MainMenu> {
friend class MainMenuButton;
public:
MainMenu() : _state(kInit), _selected(-1), _playedOKSound(false) {}
// State API
virtual void process() override;
virtual void onStateExit() override;
private:
// This is not a UI::Button subclass since the cursor doesn't change on hover
class ButtonDownLabel : public RenderObject {
friend class MainMenu;
public:
ButtonDownLabel() {}
virtual ~ButtonDownLabel() override {}
protected:
virtual uint16 getZOrder() const override { return 5; }
};
void init();
void run();
void stop();
enum State { kInit, kRun, kStop };
UI::FullScreenImage _background;
ButtonDownLabel _buttonDown;
State _state;
int16 _selected;
bool _playedOKSound;
Common::Array<Common::Rect> _destRects;
Common::Array<Common::Rect> _srcRects;
};
} // End of namespace State
} // End of namespace Nancy
#endif // NANCY_STATE_MAINMENU_H