/* 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 . * */ #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 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(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 > 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 &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 > gadgets = _menuRequests[_curMenu]._gadgets; for (Common::SharedPtr &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 > gadgets = _menuRequests[_curMenu]._gadgets; Common::Array 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 > gadgets = _menuRequests[_curMenu]._gadgets; for (Common::SharedPtr &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(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(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(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(gadget); if (!slider) break; int16 setting = slider->onClick(mouse); engine->setDifficulty(setting); // redraw for update. drawMenu(_curMenu); break; } case kMenuSliderControlsTextSpeed: { SliderGadget *slider = dynamic_cast(gadget); if (!slider) break; int16 setting = slider->onClick(mouse); engine->setTextSpeed(setting); drawMenu(_curMenu); break; } case kMenuSliderControlsDetailLevel: { SliderGadget *slider = dynamic_cast(gadget); if (!slider) break; int16 setting = slider->onClick(mouse); engine->setDetailLevel(static_cast(setting)); drawMenu(_curMenu); break; } case kMenuReplayArcadeYes: case kMenuReplayArcadeNo: if (_curMenu != kMenuReplayArcade) break; if (engine->getGameId() == GID_DRAGON) { DragonGlobals *dragonGlobals = static_cast(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(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 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 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 > gadgets = _menuRequests[_curMenu]._gadgets; for (Common::SharedPtr &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