scummvm/engines/dgds/menu.cpp
Matthew Duggan 6a5916cf6c DGDS: Map menu ids in DE version of Heart of China
Most games use the same menu IDs for the same menus, but this version uses some
different IDs to the others.  To avoid having to redo too much for a single
game variant, just map the menu IDs.

This fixes #15818.
2025-03-31 20:12:07 +11:00

934 lines
26 KiB
C++

/* 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/system.h"
#include "audio/mixer.h"
#include "graphics/cursorman.h"
#include "graphics/font.h"
#include "graphics/fontman.h"
#include "graphics/managed_surface.h"
#include "graphics/surface.h"
#include "dgds/includes.h"
#include "dgds/font.h"
#include "dgds/globals.h"
#include "dgds/menu.h"
#include "dgds/request.h"
#include "dgds/scene.h"
#include "dgds/sound.h"
#include "dgds/minigames/china_train.h"
namespace Dgds {
// TODO: These are the IDs for Dragon, this code needs updates for China/Beamish/etc
enum MenuButtonIds {
kMainMenuWillySave = 110,
kMainMenuWillyLoad = 111,
kMainMenuWillyRestart = 112,
kMainMenuWillyQuit = 113,
kMainMenuWillyVCRHelp = 114,
kMainMenuWillyHelp = 120,
kMainMenuWillySoundsOnOff = 115,
kMainMenuWillyMusicOnOff = 116,
kMenuWillyCreditsDone = 176,
kMenuWillyHelpDone = 174,
kMenuMainPlay = 120,
kMenuMainControls = 20,
kMenuMainOptions = 121,
kMenuMainCalibrate = 118, // or Credits in Willy Beamish
kMenuMainFiles = 119, // or Play in Willy Beamish
kMenuMainQuit = 122,
kMenuControlsVCR = 127,
kMenuControlsPlay = 128,
kMenuSliderControlsDifficulty = 123,
kMenuSliderControlsTextSpeed = 125,
kMenuSliderControlsDetailLevel = 131,
kMenuOptionsJoystickOnOff = 139,
kMenuOptionsJoystickOnOffHoC = 174,
kMenuOptionsMouseOnOff = 138,
kMenuOptionsMouseOnOffHoC = 173,
kMenuOptionsSoundsOnOff = 137,
kMenuOptionsMusicOnOff = 140,
kMenuOptionsSoundsOnOffHoC = 175,
kMenuOptionsSoundsOnOffDE = 172, // German version
kMenuOptionsMusicOnOffHoC = 171,
kMenuOptionsVCR = 135,
kMenuOptionsPlay = 136,
//kMenuCalibrateJoystickBtn = 145,
//kMenuCalibrateMouseBtn = 146,
//kMenuCalibrateVCR = 144,
//kMenuCalibratePlay = 147,
//kMenuCalibrateVCRHoC = 159,
//kMenuCalibratePlayHoC = 158,
kMenuFilesSave = 107,
kMenuFilesRestore = 106,
kMenuFilesRestart = 105,
kMenuFilesVCR = 103,
kMenuFilesPlay = 130,
//kMenuSavePrevious = 58,
//kMenuSaveNext = 59,
//kMenuSaveSave = 53,
//kMenuSaveCancel = 54,
//kMenuSaveChangeDirectory = 55,
//kMenuChangeDirectoryOK = 95,
//kMenuChangeDirectoryCancel = 96,
//kMenuMouseCalibrationCalibrate = 157,
//kMenuMouseCalibrationPlay = 155,
//kMenuJoystickCalibrationOK = 132,
kMenuQuitYes = 134,
kMenuQuitNo = 133,
kMenuMaybeBetterSaveYes = 137,
kMenuMaybeBetterSaveNo = 138,
// Intro menu in Rise of the Dragon
kMenuIntroSkip = 143,
kMenuIntroPlay = 144,
// Intro menu in Heart of China / Willy Beamish
kMenuIntroJumpToIntroduction = 156,
kMenuIntroJumpToGame = 157,
kMenuIntroRestore = 150,
kMenuRestartYes = 163,
kMenuRestartNo = 164,
// For Dragon/HOC arcade
kMenuReplayArcadeYes = 139,
kMenuReplayArcadeNo = 140,
kMenuFrustratedArcadeWin = 147,
kMenuFrustratedArcadeKeepTrying = 148,
kMenuGameOverQuit = 169,
kMenuGameOverRestart = 168,
kMenuGameOverRestore = 170,
// Tank/train menu in Heart of China
kMenuTankTrainSkipArcade = 153,
kMenuTankTrainPlayArcade = 154,
kMenuButtonNone = 0,
};
Menu::Menu() : _curMenu(kMenuNone), _dragGadget(nullptr), _selectedItem(0), _numSelectable(0), _creditsOffset(0),
_vcrHelpMode(false) {
_screenBuffer.create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
}
Menu::~Menu() {
_screenBuffer.free();
}
uint16 Menu::mapMenuNum(uint16 num) const {
// The DE version of Heart of China used different menu IDs for some things.
// Instead of putting mappings all through the game, just translate them on
// load to match the other game versions.
DgdsEngine *engine = DgdsEngine::getInstance();
if (engine->getGameId() == GID_HOC && engine->getGameLang() == Common::DE_DEU) {
switch (num) {
case 50: return kMenuMain;
case 80: return kMenuSkipPlayIntro;
case 82: return kMenuSkipArcade;
default: return num;
}
} else {
return num;
}
}
void Menu::setRequestData(const REQFileData &data) {
for (auto &req : data._requests) {
_menuRequests[mapMenuNum(req._fileNum)] = req;
}
}
void Menu::loadVCRHelp(const char *fname) {
DgdsEngine *engine = DgdsEngine::getInstance();
Common::SeekableReadStream *s = engine->getResourceManager()->getResource(fname);
if (!s)
error("Couldn't open RES file for menu help %s", fname);
static const MenuButtonIds helpIds[] = {
kMenuButtonNone, // Help not available right now
kMainMenuWillySave,
kMainMenuWillyLoad,
kMainMenuWillyRestart,
kMainMenuWillyQuit,
kMainMenuWillyVCRHelp,
kMainMenuWillySoundsOnOff,
kMainMenuWillyMusicOnOff,
kMainMenuWillyHelp,
kMenuMainCalibrate, // Same ID as Credits in Willy Beamish
kMenuMainFiles, // Same ID as Resume in Willy Beamish
};
int menuId = 0;
_helpStrings.clear();
Common::Array<Common::String> message;
while (!s->eos() && !s->err()) {
Common::String str = s->readLine();
if (str == "!" || s->eos()) {
_helpStrings[helpIds[menuId]] = message;
message.clear();
menuId++;
} else {
message.push_back(str);
}
}
delete s;
}
void Menu::loadCredits() {
DgdsEngine *engine = DgdsEngine::getInstance();
Common::SeekableReadStream *s = engine->getResourceManager()->getResource("CREDITS.RES");
if (!s)
error("Couldn't open CREDITS.RES");
_credits.clear();
while (!s->eos() && !s->err()) {
_credits.push_back(s->readLine());
}
_creditsOffset = 0;
}
void Menu::setScreenBuffer() {
Graphics::Surface *dst = g_system->lockScreen();
_screenBuffer.copyFrom(*dst);
g_system->unlockScreen();
}
bool Menu::updateOptionsGadget(Gadget *gadget) {
Audio::Mixer *mixer = DgdsEngine::getInstance()->_mixer;
const char *mouseStr, *soundStr, *musicStr, *onStr, *offStr;
if (DgdsEngine::getInstance()->getGameLang() == Common::EN_ANY) {
mouseStr = "MOUSE";
soundStr = "SOUND";
musicStr = "MUSIC";
onStr = "ON";
offStr = "OFF";
} else if (DgdsEngine::getInstance()->getGameLang() == Common::DE_DEU) {
mouseStr = "MAUS";
soundStr = "TON";
musicStr = "MUSIK";
onStr = "AN";
offStr = "AUS";
} else {
error("Unsupported language %d", DgdsEngine::getInstance()->getGameLang());
}
switch (gadget->_gadgetNo) {
case kMenuOptionsJoystickOnOff:
case kMenuOptionsJoystickOnOffHoC:
gadget->_buttonName = Common::String::format("JOYSTICK %s", onStr);
return true;
case kMenuOptionsMouseOnOff:
case kMenuOptionsMouseOnOffHoC:
gadget->_buttonName = Common::String::format("%s %s", mouseStr, onStr);
return true;
case kMenuOptionsSoundsOnOff: // same id as kMenuMaybeBetterSaveYes
case kMenuOptionsSoundsOnOffDE:
case kMenuOptionsSoundsOnOffHoC: {
bool isMuted = mixer->isSoundTypeMuted(Audio::Mixer::kSFXSoundType);
gadget->_buttonName = Common::String::format("%s %s", soundStr, isMuted ? offStr : onStr);
return true;
}
case kMenuOptionsMusicOnOff:
case kMenuOptionsMusicOnOffHoC: {
bool isMuted = mixer->isSoundTypeMuted(Audio::Mixer::kMusicSoundType);
gadget->_buttonName = Common::String::format("%s %s", musicStr, isMuted ? offStr : onStr);
return true;
}
default:
return false;
}
}
void Menu::configureGadget(MenuId menu, Gadget *gadget) {
DgdsEngine *engine = DgdsEngine::getInstance();
// a bit of a hack - set up the gadget with the correct value before we draw it.
if (menu == kMenuControls) {
SliderGadget *slider = dynamic_cast<SliderGadget *>(gadget);
if (!slider)
return;
switch (gadget->_gadgetNo) {
case kMenuSliderControlsDifficulty:
slider->setSteps(3, false);
slider->setValue(engine->getDifficulty()); // TODO: set a difficulty value
break;
case kMenuSliderControlsTextSpeed:
slider->setSteps(10, false);
slider->setValue(engine->getTextSpeed());
break;
case kMenuSliderControlsDetailLevel:
slider->setSteps(2, true);
slider->setValue(engine->getDetailLevel());
break;
default:
break;
// do nothing.
}
} else if (menu == kMenuOptions) {
updateOptionsGadget(gadget);
} else if (menu == kMenuMain && engine->getGameId() == GID_WILLY) {
// HACK: Enable this button which for some reason is disabled
// by default in data?
if (gadget->_gadgetNo == kMainMenuWillyHelp)
toggleGadget(kMainMenuWillyHelp, true);
}
}
void Menu::onTick() {
if (_curMenu == kMenuWillyCredits)
drawMenu(kMenuWillyCredits);
}
void Menu::drawMenu(MenuId menu, bool clearScreen /* = true*/) {
bool firstDraw = (_curMenu != menu);
_curMenu = menu;
Common::Array<Common::SharedPtr<Gadget> > gadgets = _menuRequests[_curMenu]._gadgets;
// Restore background when drawing submenus
if (clearScreen)
g_system->copyRectToScreen(_screenBuffer.getPixels(), _screenBuffer.pitch, 0, 0, _screenBuffer.w, _screenBuffer.h);
// This is not very efficient, but it only happens once when the menu is opened.
Graphics::Surface *screen = g_system->lockScreen();
Graphics::ManagedSurface managed(screen->w, screen->h, screen->format);
managed.blitFrom(*screen);
_menuRequests[_curMenu].drawBg(&managed);
_numSelectable = 0;
for (Common::SharedPtr<Gadget> &gptr : gadgets) {
Gadget *gadget = gptr.get();
if (gadget->_gadgetType == kGadgetButton || gadget->_gadgetType == kGadgetSlider) {
if (firstDraw)
configureGadget(menu, gadget);
gadget->draw(&managed);
_numSelectable++;
}
}
if (firstDraw) {
_selectedItem = _numSelectable - 1;
putMouseOnSelectedItem();
}
drawMenuText(managed);
if (_curMenu == kMenuWillyCredits)
drawCreditsText(managed);
// Can't use transparent blit here as the font is often color 0.
screen->copyRectToSurface(*managed.surfacePtr(), 0, 0, Common::Rect(screen->w, screen->h));
g_system->unlockScreen();
g_system->updateScreen();
}
void Menu::hideMenu() {
_curMenu = kMenuNone;
if (_vcrHelpMode) {
_vcrHelpMode = false;
DgdsEngine::getInstance()->setMouseCursor(-1);
}
}
Gadget *Menu::getSelectedItem() {
int item = 0;
Common::Array<Common::SharedPtr<Gadget> > gadgets = _menuRequests[_curMenu]._gadgets;
for (Common::SharedPtr<Gadget> &gptr : gadgets) {
Gadget *gadget = gptr.get();
if (gadget->_gadgetType == kGadgetButton || gadget->_gadgetType == kGadgetSlider) {
if (item == _selectedItem)
return gadget;
item++;
}
}
return nullptr;
}
void Menu::putMouseOnSelectedItem() {
Gadget *selected = getSelectedItem();
if (!selected)
return;
const Common::Point midPt = selected->midPoint();
// put the mouse on the first button/slider
g_system->warpMouse(midPt.x, midPt.y);
return;
}
void Menu::drawMenuText(Graphics::ManagedSurface &dst) {
Common::Array<Common::SharedPtr<Gadget> > gadgets = _menuRequests[_curMenu]._gadgets;
Common::Array<TextItem> textItems = _menuRequests[_curMenu]._textItemList;
if (gadgets.empty())
return;
// TODO: Get the parent coordinates properly
uint16 parentX = gadgets[0].get()->_parentX;
uint16 parentY = gadgets[0].get()->_parentY;
uint16 pos = 0;
for (TextItem &textItem : textItems) {
// HACK: Skip the first entry, which corresponds to the header
if (pos == 0) {
pos++;
continue;
}
const DgdsFont *font = RequestData::getMenuFont();
int w = font->getStringWidth(textItem._txt);
font->drawString(dst.surfacePtr(), textItem._txt, parentX + textItem._x, parentY + textItem._y, w, 0);
pos++;
}
}
static byte _creditsColor(int y) {
if (y < 31 || y > 86)
return 17; // Cyan
else if (y < 32 || y > 85)
return 18; // Slightly darker cyan
else if (y < 33 || y > 84)
return 19; // Darker cyan
else if (y < 34 || y > 83)
return 30; // Grey
return 0; // Black.
}
void Menu::drawCreditsText(Graphics::ManagedSurface &dst) {
const DgdsFont *font = RequestData::getMenuFont();
const int lineHeight = font->getFontHeight();
DgdsRect dlgRect = _menuRequests[_curMenu]._rect;
const int dlgWidth = dlgRect.width;
const int yMin = 30;
const int yMax = 87;
for (uint i = 0; i < _credits.size(); i++) {
int dstY = i * lineHeight + yMax - _creditsOffset / 4;
if (dstY > yMax)
break;
if (dstY > yMin) {
int lineW = font->getStringWidth(_credits[i]);
int xoff = dlgRect.x + (dlgWidth - lineW) / 2;
font->drawString(dst.surfacePtr(), _credits[i], xoff, dlgRect.y + dstY, dlgRect.width, _creditsColor(dstY));
}
}
_creditsOffset++;
if ((uint)_creditsOffset / 4 > lineHeight * (_credits.size() + 1) + (yMax - yMin))
_creditsOffset = 0;
}
Gadget *Menu::getClickedMenuItem(const Common::Point &mouseClick) {
if (_curMenu == kMenuNone)
return nullptr;
Common::Array<Common::SharedPtr<Gadget> > gadgets = _menuRequests[_curMenu]._gadgets;
for (Common::SharedPtr<Gadget> &gptr : gadgets) {
Gadget *gadget = gptr.get();
if (gadget->_gadgetType == kGadgetButton || gadget->_gadgetType == kGadgetSlider) {
if (gadget->containsPoint(mouseClick)) {
return gadget;
}
}
}
return nullptr;
}
void Menu::onMouseLDown(const Common::Point &mouse) {
SliderGadget *slider = dynamic_cast<SliderGadget *>(getClickedMenuItem(mouse));
if (slider) {
_dragGadget = slider;
_dragStartPt = mouse;
}
}
void Menu::onMouseMove(const Common::Point &mouse) {
if (!_dragGadget)
return;
_dragGadget->onDrag(mouse);
drawMenu(_curMenu);
}
void Menu::onMouseLUp(const Common::Point &mouse) {
DgdsEngine *engine = DgdsEngine::getInstance();
if (_dragGadget && mouse != _dragStartPt) {
int16 setting = _dragGadget->onDragFinish(mouse);
switch (_dragGadget->_gadgetNo) {
case kMenuSliderControlsDifficulty:
engine->setDifficulty(setting);
break;
case kMenuSliderControlsTextSpeed:
engine->setTextSpeed(setting);
break;
case kMenuSliderControlsDetailLevel:
engine->setDetailLevel(static_cast<DgdsDetailLevel>(setting));
break;
default:
break;
}
drawMenu(_curMenu);
_dragGadget = nullptr;
_dragStartPt = Common::Point();
return;
}
_dragGadget = nullptr;
Gadget *gadget = getClickedMenuItem(mouse);
bool isToggle = false;
if (!gadget)
return;
// Click animation
if (dynamic_cast<ButtonGadget *>(gadget) && !_vcrHelpMode) {
gadget->toggle(false);
if (_curMenu == kMenuOptions)
/*isToggle = */updateOptionsGadget(gadget);
drawMenu(_curMenu, false);
g_system->delayMillis(300);
gadget->toggle(true);
isToggle = true;
}
if (_curMenu == kMenuOptions)
handleClickOptionsMenu(mouse);
else if (_curMenu == kMenuSkipPlayIntro)
handleClickSkipPlayIntroMenu(mouse);
else
isToggle &= handleClick(mouse);
if (isToggle)
drawMenu(_curMenu);
}
static void _toggleSoundType(Audio::Mixer::SoundType soundType) {
DgdsEngine *engine = DgdsEngine::getInstance();
Audio::Mixer *mixer = engine->_mixer;
//const char *typeStr = (soundType == Audio::Mixer::kMusicSoundType) ? "music" : "sfx";
if (!mixer->isSoundTypeMuted(soundType)) {
mixer->muteSoundType(soundType, true);
engine->_soundPlayer->muteSoundType(soundType);
} else {
mixer->muteSoundType(soundType, false);
engine->_soundPlayer->unmuteSoundType(soundType);
}
}
bool Menu::handleClick(const Common::Point &mouse) {
DgdsEngine *engine = DgdsEngine::getInstance();
int currentScene = engine->getScene()->getNum();
Gadget *gadget = getClickedMenuItem(mouse);
int16 clickedMenuItem = gadget->_gadgetNo;
if (_vcrHelpMode) {
doVcrHelp(clickedMenuItem);
return false;
}
switch (clickedMenuItem) {
case kMenuMainPlay:
case kMenuControlsPlay:
case kMenuOptionsPlay:
case kMenuFilesPlay:
case kMenuMaybeBetterSaveNo:
//case kMenuCalibratePlay:
//case kMenuCalibratePlayHoC:
//case kMenuMouseCalibrationPlay:
hideMenu();
if (engine->getGameId() == GID_WILLY && clickedMenuItem == kMainMenuWillyHelp) {
// TODO: Based on some variable this should instead:
//drawMenu(kMenuWillyHelp); // with the first message from WVCR.RES
// The OnLine help system
// is not available now.
engine->changeScene(80);
} else {
CursorMan.showMouse(false);
}
break;
case kMenuMainControls:
drawMenu(kMenuControls);
break;
case kMenuMainOptions:
drawMenu(kMenuOptions);
break;
case kMenuMainCalibrate: // same as credits button in Willy Beamish
if (engine->getGameId() == GID_WILLY) {
hideMenu();
loadCredits();
drawMenu(kMenuWillyCredits);
} else {
debug("Ignoring calibration request");
}
break;
//case kMenuJoystickCalibrationOK:
//case kMenuMouseCalibrationCalibrate: // NOTE: same ID as kMenuIntroJumpToGame (for HOC)
case kMenuIntroJumpToGame:
if (_curMenu == kMenuSkipPlayIntro) {
hideMenu();
engine->setShowClock(true);
engine->changeScene(24);
} else {
// Do nothing - the calibrate menu doesn't offer
// any functionality in ScummVM
//drawMenu(kMenuCalibrate);
drawMenu(_curMenu);
}
break;
case kMenuMainFiles: // Same ID as Play in Willy Beamish
//case kMenuSaveCancel:
if (engine->getGameId() == GID_WILLY)
hideMenu();
else
drawMenu(kMenuFiles);
break;
case kMenuMainQuit:
drawMenu(kMenuReallyQuit);
break;
case kMainMenuWillyVCRHelp:
startVcrHelp();
break;
//case kMenuCalibrateVCR: // NOTE: same ID as kMenuIntroPlay
case kMenuIntroPlay:
case kMenuControlsVCR:
case kMenuOptionsVCR:
//case kMenuCalibrateVCRHoC:
case kMenuFilesVCR:
case kMenuQuitNo:
case kMenuRestartNo:
drawMenu(kMenuMain);
break;
//case kMenuCalibrateJoystickBtn:
// drawMenu(kMenuCalibrateJoystick);
// break;
//case kMenuCalibrateMouseBtn:
// drawMenu(kMenuCalibrateMouse);
// break;
//case kMenuChangeDirectoryCancel:
// drawMenu(kMenuSaveDlg);
// break;
case kMenuFilesRestore:
case kMenuGameOverRestore:
case kMenuIntroRestore:
case kMainMenuWillyLoad:
if (g_engine->loadGameDialog()) {
hideMenu();
return false;
} else {
drawMenu(_curMenu);
}
break;
case kMenuFilesRestart:
case kMainMenuWillyRestart:
drawMenu(kMenuRestart);
break;
case kMenuFilesSave: // TODO: Add an option to support original save/load dialogs?
//case kMenuSavePrevious:
//case kMenuSaveNext:
//case kMenuSaveSave:
case kMenuMaybeBetterSaveYes:
case kMainMenuWillySave:
if (g_engine->saveGameDialog())
hideMenu();
else
drawMenu(_curMenu);
break;
//case kMenuSaveChangeDirectory:
// drawMenu(kMenuChangeDir);
// break;
//case kMenuChangeDirectoryOK:
// // Do nothing - the change directory menu doesn't offer
// // any functionality in ScummVM
// drawMenu(_curMenu);
// break;
case kMenuQuitYes:
g_engine->quitGame();
return false;
case kMenuRestartYes:
hideMenu();
engine->restartGame();
// don't draw the button coming back up.
return false;
case kMenuGameOverQuit:
case kMainMenuWillyQuit:
drawMenu(kMenuReallyQuit);
break;
case kMenuGameOverRestart:
drawMenu(kMenuRestart);
break;
case kMenuSliderControlsDifficulty: {
SliderGadget *slider = dynamic_cast<SliderGadget *>(gadget);
if (!slider)
break;
int16 setting = slider->onClick(mouse);
engine->setDifficulty(setting);
// redraw for update.
drawMenu(_curMenu);
break;
}
case kMenuSliderControlsTextSpeed: {
SliderGadget *slider = dynamic_cast<SliderGadget *>(gadget);
if (!slider)
break;
int16 setting = slider->onClick(mouse);
engine->setTextSpeed(setting);
drawMenu(_curMenu);
break;
}
case kMenuSliderControlsDetailLevel: {
SliderGadget *slider = dynamic_cast<SliderGadget *>(gadget);
if (!slider)
break;
int16 setting = slider->onClick(mouse);
engine->setDetailLevel(static_cast<DgdsDetailLevel>(setting));
drawMenu(_curMenu);
break;
}
case kMenuReplayArcadeYes:
case kMenuReplayArcadeNo:
if (_curMenu != kMenuReplayArcade)
break;
if (engine->getGameId() == GID_DRAGON) {
DragonGlobals *dragonGlobals = static_cast<DragonGlobals *>(engine->getGameGlobals());
dragonGlobals->setArcadeState(clickedMenuItem == kMenuReplayArcadeYes ? 20 : 10);
} else if (engine->getGameId() == GID_HOC) {
engine->getChinaTrain()->setMenuResult(clickedMenuItem == kMenuReplayArcadeYes);
}
hideMenu();
break;
case kMenuFrustratedArcadeWin:
case kMenuFrustratedArcadeKeepTrying:
if (_curMenu != kMenuArcadeFrustrated)
break;
if (engine->getGameId() == GID_DRAGON) {
DragonGlobals *dragonGlobals = static_cast<DragonGlobals *>(engine->getGameGlobals());
dragonGlobals->setArcadeState(clickedMenuItem == kMenuFrustratedArcadeWin ? 6 : 20);
} else if (engine->getGameId() == GID_HOC) {
engine->getChinaTrain()->setMenuResult(clickedMenuItem == kMenuFrustratedArcadeKeepTrying);
}
hideMenu();
break;
case kMenuTankTrainSkipArcade:
hideMenu();
if (currentScene == 73)
engine->changeScene(12); // skip tank mini-game
else if (currentScene == 84)
engine->changeScene(57); // skip train mini-game, return to travel map
break;
case kMenuTankTrainPlayArcade:
// Open the "save before arcade" menu before playing the tank
// or train mini-games.
drawMenu(kMenuSaveBeforeArcade);
break;
case kMainMenuWillySoundsOnOff:
_toggleSoundType(Audio::Mixer::kSFXSoundType);
updateOptionsGadget(gadget);
break;
case kMainMenuWillyMusicOnOff:
_toggleSoundType(Audio::Mixer::kMusicSoundType);
updateOptionsGadget(gadget);
break;
case kMenuWillyCreditsDone:
hideMenu();
break;
case kMenuWillyHelpDone:
drawMenu(kMenuMain, false);
break;
default:
debug(1, "Clicked ID %d", clickedMenuItem);
break;
}
return true;
}
void Menu::startVcrHelp() {
_vcrHelpMode = true;
Common::SharedPtr<Graphics::ManagedSurface> iconSurf = RequestData::getCorners()->getSurface(37);
int hotspotX = iconSurf->w / 2;
int hotspotY = iconSurf->h / 2;
CursorMan.replaceCursor(*(iconSurf->surfacePtr()), hotspotX, hotspotY, 0, 0);
}
void Menu::doVcrHelp(int16 button) {
if (_helpStrings.contains(button)) {
RequestData &helpRequest = _menuRequests[kMenuWillyVCRHelp];
const Common::Array<Common::String> msg = _helpStrings[button];
const DgdsFont *font = RequestData::getMenuFont();
const Gadget *doneButton = helpRequest.findGadgetByNumWithFlags3Not0x40(174);
int16 msgHeight = font->getFontHeight() * msg.size();
int16 msgAreaTop = helpRequest._textItemList.front()._y + font->getFontHeight();
int16 msgAreaHeight = doneButton->_y - msgAreaTop;
int16 msgAreaLeft = 0;
int16 msgAreaWidth = helpRequest._rect.width;
// Vertically centre the message
int16 msgTop = msgAreaTop + (msgAreaHeight - msgHeight) / 2;
while (helpRequest._textItemList.size() > 1)
helpRequest._textItemList.pop_back();
for (const Common::String &line : msg) {
TextItem helpText;
int msgLen = font->getStringWidth(line);
// Horizontally centre the line
helpText._x = msgAreaLeft + (msgAreaWidth - msgLen) / 2;
helpText._y = msgTop;
helpText._txt = line;
msgTop += font->getFontHeight();
helpRequest._textItemList.push_back(helpText);
}
// HACK: Set a different cursor to force an update.
DgdsEngine::getInstance()->setMouseCursor(1);
DgdsEngine::getInstance()->setMouseCursor(-1);
drawMenu(kMenuWillyVCRHelp, false);
_vcrHelpMode = false;
}
}
void Menu::handleClickOptionsMenu(const Common::Point &mouse) {
Gadget *gadget = getClickedMenuItem(mouse);
int16 clickedMenuItem = gadget->_gadgetNo;
switch (clickedMenuItem) {
case kMenuOptionsJoystickOnOff:
case kMenuOptionsJoystickOnOffHoC:
case kMenuOptionsMouseOnOff: // same id as kMenuMaybeBetterSaveNo
case kMenuOptionsMouseOnOffHoC:
// Do nothing - we don't toggle joystick or mouse functionality
break;
case kMenuOptionsSoundsOnOff: // same id as kMenuMaybeBetterSaveYes
case kMenuOptionsSoundsOnOffDE:
case kMenuOptionsSoundsOnOffHoC:
case kMainMenuWillySoundsOnOff:
_toggleSoundType(Audio::Mixer::kSFXSoundType);
updateOptionsGadget(gadget);
break;
case kMenuOptionsMusicOnOff:
case kMenuOptionsMusicOnOffHoC:
case kMainMenuWillyMusicOnOff:
_toggleSoundType(Audio::Mixer::kMusicSoundType);
updateOptionsGadget(gadget);
break;
default:
handleClick(mouse);
break;
}
}
void Menu::handleClickSkipPlayIntroMenu(const Common::Point &mouse) {
DgdsEngine *engine = DgdsEngine::getInstance();
Gadget *gadget = getClickedMenuItem(mouse);
int16 clickedMenuItem = gadget->_gadgetNo;
switch (clickedMenuItem) {
case kMenuIntroPlay:
hideMenu();
break;
case kMenuIntroSkip:
hideMenu();
engine->setShowClock(true);
engine->changeScene(5);
break;
case kMenuIntroJumpToIntroduction:
hideMenu();
if (engine->getGameId() == GID_HOC)
engine->changeScene(100);
else if (engine->getGameId() == GID_WILLY)
engine->changeScene(24);
break;
case kMenuIntroJumpToGame:
hideMenu();
if (engine->getGameId() == GID_HOC)
engine->changeScene(24);
else if (engine->getGameId() == GID_WILLY)
engine->changeScene(4);
break;
default:
handleClick(mouse);
break;
}
}
void Menu::toggleGadget(int16 gadgetId, bool enable) {
Common::Array<Common::SharedPtr<Gadget> > gadgets = _menuRequests[_curMenu]._gadgets;
for (Common::SharedPtr<Gadget> &gptr : gadgets) {
Gadget *gadget = gptr.get();
if (gadget->_gadgetNo == gadgetId) {
gadget->toggle(enable);
return;
}
}
}
/**
* Choose the next menu item via keyboard - items are numbered backwards
* so this *decreases* the counter.
*/
void Menu::nextChoice() {
_selectedItem--;
if (_selectedItem < 0)
_selectedItem = _numSelectable - 1;
putMouseOnSelectedItem();
}
void Menu::prevChoice() {
_selectedItem++;
if (_selectedItem >= _numSelectable)
_selectedItem = 0;
putMouseOnSelectedItem();
}
void Menu::activateChoice() {
Gadget *selected = getSelectedItem();
if (!selected)
return;
handleClick(selected->midPoint());
}
} // End of namespace Dgds