mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
1488 lines
50 KiB
C++
1488 lines
50 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 "twine/menu/menu.h"
|
|
#include "audio/mixer.h"
|
|
#include "backends/audiocd/audiocd.h"
|
|
#include "backends/keymapper/keymapper.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/events.h"
|
|
#include "common/keyboard.h"
|
|
#include "common/scummsys.h"
|
|
#include "common/str.h"
|
|
#include "common/system.h"
|
|
#include "common/util.h"
|
|
#include "graphics/cursorman.h"
|
|
#include "twine/audio/music.h"
|
|
#include "twine/audio/sound.h"
|
|
#include "twine/input.h"
|
|
#include "twine/menu/interface.h"
|
|
#include "twine/menu/menuoptions.h"
|
|
#include "twine/movies.h"
|
|
#include "twine/renderer/redraw.h"
|
|
#include "twine/renderer/renderer.h"
|
|
#include "twine/renderer/screens.h"
|
|
#include "twine/resources/hqr.h"
|
|
#include "twine/resources/resources.h"
|
|
#include "twine/scene/actor.h"
|
|
#include "twine/scene/animations.h"
|
|
#include "twine/scene/gamestate.h"
|
|
#include "twine/scene/grid.h"
|
|
#include "twine/scene/movements.h"
|
|
#include "twine/scene/scene.h"
|
|
#include "twine/shared.h"
|
|
#include "twine/text.h"
|
|
#include "twine/twine.h"
|
|
|
|
namespace TwinE {
|
|
|
|
static const uint32 kPlasmaEffectFilesize = 262176;
|
|
|
|
#define HEIGHT_STANDARD 50
|
|
#define MENU_SPACE 6
|
|
|
|
#define SIZE_INV_OBJ_X 75
|
|
#define SIZE_INV_OBJ_Y 65
|
|
|
|
namespace MenuButtonTypes {
|
|
enum MenuButtonTypesEnum {
|
|
kMusicVolume = 1,
|
|
kSoundVolume = 2,
|
|
kCDVolume = 3,
|
|
kSpeechVolume = 4,
|
|
kAggressiveMode = 5,
|
|
kPolygonDetails = 6,
|
|
kShadowSettings = 7,
|
|
kSceneryZoom = 8,
|
|
kHighResolution = 9,
|
|
kWallCollision = 10,
|
|
kVoice = 11
|
|
};
|
|
}
|
|
|
|
#define checkMenuQuit(callMenu) \
|
|
if ((callMenu) == kQuitEngine) { \
|
|
return kQuitEngine; \
|
|
}
|
|
|
|
namespace _priv {
|
|
|
|
static MenuSettings createMainMenu(bool lba1) {
|
|
MenuSettings settings;
|
|
if (lba1) {
|
|
settings.setButtonsBoxHeight(200);
|
|
settings.addButton(TextId::kNewGame);
|
|
settings.addButton(TextId::kContinueGame);
|
|
settings.addButton(TextId::kOptions);
|
|
settings.addButton(TextId::kQuit);
|
|
} else {
|
|
settings.setButtonsBoxHeight(335);
|
|
settings.addButton(TextId::toContinueGame);
|
|
settings.addButton(TextId::toNewGame);
|
|
settings.addButton(TextId::toOptions);
|
|
settings.addButton(TextId::toQuit);
|
|
}
|
|
return settings;
|
|
}
|
|
|
|
static MenuSettings createLba1ClassicNewGame() {
|
|
MenuSettings settings;
|
|
settings.addButton(TextId::kReturnMenu);
|
|
settings.addButton(TextId::kNewGame);
|
|
settings.addButton(TextId::kNewGamePlus);
|
|
return settings;
|
|
}
|
|
|
|
static MenuSettings createGiveUpMenu() {
|
|
MenuSettings settings;
|
|
settings.setButtonsBoxHeight(240);
|
|
settings.addButton(TextId::kContinue);
|
|
settings.addButton(TextId::kGiveUp);
|
|
return settings;
|
|
}
|
|
|
|
static MenuSettings createGiveUpSaveMenu() {
|
|
MenuSettings settings;
|
|
settings.setButtonsBoxHeight(240);
|
|
settings.addButton(TextId::kContinue);
|
|
settings.addButton(TextId::kCreateSaveGame);
|
|
settings.addButton(TextId::kGiveUp);
|
|
return settings;
|
|
}
|
|
|
|
static MenuSettings createLanguageMenu(bool lba1) {
|
|
MenuSettings settings;
|
|
settings.addButton(TextId::kReturnMenu);
|
|
settings.addButton(TextId::kCustomVoicesEnglish, MenuButtonTypes::kVoice);
|
|
return settings;
|
|
}
|
|
|
|
static MenuSettings createOptionsMenu(bool lba1) {
|
|
MenuSettings settings;
|
|
settings.addButton(TextId::kReturnMenu);
|
|
settings.addButton(TextId::kVolumeSettings);
|
|
settings.addButton(TextId::kSaveManage);
|
|
settings.addButton(TextId::kCustomLanguageOption);
|
|
settings.addButton(TextId::kAdvanced);
|
|
return settings;
|
|
}
|
|
|
|
static MenuSettings createAdvancedOptionsMenu() {
|
|
MenuSettings settings;
|
|
settings.addButton(TextId::kReturnMenu);
|
|
settings.addButton(TextId::kBehaviourAggressiveManual, MenuButtonTypes::kAggressiveMode);
|
|
settings.addButton(TextId::kDetailsPolygonsHigh, MenuButtonTypes::kPolygonDetails);
|
|
settings.addButton(TextId::kDetailsShadowHigh, MenuButtonTypes::kShadowSettings);
|
|
settings.addButton(TextId::kSceneryZoomOn, MenuButtonTypes::kSceneryZoom);
|
|
settings.addButton(TextId::kCustomHighResOptionOn, MenuButtonTypes::kHighResolution);
|
|
settings.addButton(TextId::kCustomWallCollisionOff, MenuButtonTypes::kWallCollision);
|
|
return settings;
|
|
}
|
|
|
|
static MenuSettings createSaveManageMenu() {
|
|
MenuSettings settings;
|
|
settings.addButton(TextId::kReturnMenu);
|
|
settings.addButton(TextId::kCreateSaveGame);
|
|
settings.addButton(TextId::kDeleteSaveGame);
|
|
return settings;
|
|
}
|
|
|
|
static MenuSettings createVolumeMenu() {
|
|
MenuSettings settings;
|
|
settings.addButton(TextId::kReturnMenu);
|
|
settings.addButton(TextId::kMusicVolume, MenuButtonTypes::kMusicVolume);
|
|
settings.addButton(TextId::kSoundVolume, MenuButtonTypes::kSoundVolume);
|
|
settings.addButton(TextId::kCDVolume, MenuButtonTypes::kCDVolume);
|
|
settings.addButton(TextId::kSpeechVolume, MenuButtonTypes::kSpeechVolume);
|
|
return settings;
|
|
}
|
|
|
|
} // namespace _priv
|
|
|
|
const char *MenuSettings::getButtonText(Text *text, int buttonIndex) {
|
|
if (_buttonTexts[buttonIndex].empty()) {
|
|
TextId textId = getButtonTextId(buttonIndex);
|
|
char dialText[256] = "";
|
|
if (textId == TextId::kNewGamePlus) {
|
|
text->getMenuText(TextId::kNewGame, dialText, sizeof(dialText));
|
|
Common::strlcat(dialText, "+", sizeof(dialText));
|
|
} else {
|
|
text->getMenuText(textId, dialText, sizeof(dialText));
|
|
}
|
|
_buttonTexts[buttonIndex] = dialText;
|
|
}
|
|
return _buttonTexts[buttonIndex].c_str();
|
|
}
|
|
|
|
Menu::Menu(TwinEEngine *engine) {
|
|
_engine = engine;
|
|
|
|
_optionsMenuState = _priv::createOptionsMenu(engine->isLBA1());
|
|
_languageMenuState = _priv::createLanguageMenu(engine->isLBA1());
|
|
_giveUpMenuWithSaveState = _priv::createGiveUpSaveMenu();
|
|
_volumeMenuState = _priv::createVolumeMenu();
|
|
_saveManageMenuState = _priv::createSaveManageMenu();
|
|
_giveUpMenuState = _priv::createGiveUpMenu();
|
|
_mainMenuState = _priv::createMainMenu(engine->isLBA1());
|
|
_newGameMenuState = _priv::createLba1ClassicNewGame();
|
|
_advOptionsMenuState = _priv::createAdvancedOptionsMenu();
|
|
|
|
Common::fill(&_behaviourAnimState[0], &_behaviourAnimState[4], 0);
|
|
Common::fill(&_itemAngle[0], &_itemAngle[NUM_INVENTORY_ITEMS], 0);
|
|
}
|
|
|
|
Menu::~Menu() {
|
|
free(_plasmaEffectPtr);
|
|
}
|
|
|
|
void Menu::plasmaEffectRenderFrame() {
|
|
for (int32 j = 1; j < PLASMA_HEIGHT - 1; j++) {
|
|
for (int32 i = 1; i < PLASMA_WIDTH - 1; i++) {
|
|
/* Here we calculate the average of all 8 neighbour pixel values */
|
|
|
|
int16 c;
|
|
c = _plasmaEffectPtr[(i - 1) + (j - 1) * PLASMA_WIDTH]; // top-left
|
|
c += _plasmaEffectPtr[(i + 0) + (j - 1) * PLASMA_WIDTH]; // top
|
|
c += _plasmaEffectPtr[(i + 1) + (j - 1) * PLASMA_WIDTH]; // top-right
|
|
|
|
c += _plasmaEffectPtr[(i - 1) + (j + 0) * PLASMA_WIDTH]; // left
|
|
c += _plasmaEffectPtr[(i + 1) + (j + 0) * PLASMA_WIDTH]; // right
|
|
|
|
c += _plasmaEffectPtr[(i - 1) + (j + 1) * PLASMA_WIDTH]; // bottom-left
|
|
c += _plasmaEffectPtr[(i + 0) + (j + 1) * PLASMA_WIDTH]; // bottom
|
|
c += _plasmaEffectPtr[(i + 1) + (j + 1) * PLASMA_WIDTH]; // bottom-right
|
|
|
|
/* And the 2 least significant bits are used as a
|
|
* randomizing parameter for statistically fading the flames */
|
|
c = (c >> 3) | ((c & 0x0003) << 13);
|
|
|
|
if (!(c & 0x6500) &&
|
|
(j >= (PLASMA_HEIGHT - 4) || c > 0)) {
|
|
c--; /*fade this pixel*/
|
|
}
|
|
|
|
/* plot the pixel using the calculated color */
|
|
_plasmaEffectPtr[i + (PLASMA_HEIGHT + j) * PLASMA_WIDTH] = (uint8)c;
|
|
}
|
|
}
|
|
|
|
// flip the double-buffer while scrolling the effect vertically:
|
|
const uint8 *src = _plasmaEffectPtr + (PLASMA_HEIGHT + 1) * PLASMA_WIDTH;
|
|
memcpy(_plasmaEffectPtr, src, PLASMA_HEIGHT * PLASMA_WIDTH);
|
|
}
|
|
|
|
void Menu::processPlasmaEffect(const Common::Rect &rect, int32 color) {
|
|
if (_engine->isLBA2()) {
|
|
// TODO: effects are handled differently here.
|
|
return;
|
|
}
|
|
const int32 max_value = color + 15;
|
|
|
|
plasmaEffectRenderFrame();
|
|
|
|
const uint8 *in = _plasmaEffectPtr + 5 * PLASMA_WIDTH;
|
|
uint8 *out = (uint8 *)_engine->_imageBuffer.getBasePtr(0, 0);
|
|
|
|
for (int32 y = 0; y < PLASMA_HEIGHT / 2; y++) {
|
|
int32 yOffset = y * _engine->_imageBuffer.w;
|
|
const uint8 *colPtr = &in[y * PLASMA_WIDTH];
|
|
for (int32 x = 0; x < PLASMA_WIDTH; x++) {
|
|
const uint8 c = MIN(*colPtr / 2 + color, max_value);
|
|
/* 2x2 squares sharing the same pixel color: */
|
|
const int32 target = 2 * yOffset;
|
|
out[target + 0] = c;
|
|
out[target + 1] = c;
|
|
out[target + _engine->_imageBuffer.w + 0] = c;
|
|
out[target + _engine->_imageBuffer.w + 1] = c;
|
|
++colPtr;
|
|
++yOffset;
|
|
}
|
|
}
|
|
const Common::Rect prect(0, 0, PLASMA_WIDTH, PLASMA_HEIGHT);
|
|
_engine->_frontVideoBuffer.blitFrom(_engine->_imageBuffer, prect, rect);
|
|
}
|
|
|
|
void Menu::drawRectBorders(const Common::Rect &rect, int32 colorLeftTop, int32 colorRightBottom) { // DrawCadre
|
|
_engine->_interface->drawLine(rect.left, rect.top, rect.right, rect.top, colorLeftTop); // top line
|
|
_engine->_interface->drawLine(rect.left, rect.top + 1, rect.left, rect.bottom, colorLeftTop); // left line
|
|
_engine->_interface->drawLine(rect.right, rect.top + 1, rect.right, rect.bottom, colorRightBottom); // right line
|
|
_engine->_interface->drawLine(rect.left + 1, rect.bottom, rect.right - 1, rect.bottom, colorRightBottom); // bottom line
|
|
}
|
|
|
|
void Menu::drawRectBorders(int32 left, int32 top, int32 right, int32 bottom, int32 colorLeftTop, int32 colorRightBottom) { // DrawCadre
|
|
drawRectBorders(Common::Rect(left, top, right, bottom), colorLeftTop, colorLeftTop);
|
|
}
|
|
|
|
void Menu::drawButtonGfx(const MenuSettings *menuSettings, const Common::Rect &rect, int32 buttonId, const char *dialText, bool hover) {
|
|
if (hover) {
|
|
if (menuSettings == &_volumeMenuState && buttonId <= MenuButtonTypes::kSpeechVolume && buttonId >= MenuButtonTypes::kMusicVolume) {
|
|
int32 newWidth = 0;
|
|
switch (buttonId) {
|
|
case MenuButtonTypes::kMusicVolume: {
|
|
const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
|
|
newWidth = ruleThree32(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
|
|
break;
|
|
}
|
|
case MenuButtonTypes::kSoundVolume: {
|
|
const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kSFXSoundType);
|
|
newWidth = ruleThree32(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
|
|
break;
|
|
}
|
|
case MenuButtonTypes::kCDVolume: {
|
|
const AudioCDManager::Status status = _engine->_system->getAudioCDManager()->getStatus();
|
|
newWidth = ruleThree32(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, status.volume);
|
|
break;
|
|
}
|
|
case MenuButtonTypes::kSpeechVolume: {
|
|
const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType);
|
|
newWidth = ruleThree32(rect.left, rect.right, Audio::Mixer::kMaxMixerVolume, volume);
|
|
break;
|
|
}
|
|
}
|
|
|
|
processPlasmaEffect(rect, COLOR_80);
|
|
if (!(_engine->getRandomNumber() % 5)) {
|
|
_plasmaEffectPtr[(_engine->getRandomNumber() % PLASMA_WIDTH * 10) + 20 * PLASMA_WIDTH] = 255;
|
|
}
|
|
_engine->_interface->box(Common::Rect(newWidth, rect.top, rect.right, rect.bottom), COLOR_SELECT_MENU);
|
|
} else {
|
|
processPlasmaEffect(rect, COLOR_64);
|
|
if (!(_engine->getRandomNumber() % 5)) {
|
|
_plasmaEffectPtr[_engine->getRandomNumber() % PLASMA_WIDTH * 10 + 6400] = 255;
|
|
}
|
|
}
|
|
} else {
|
|
_engine->blitWorkToFront(rect);
|
|
_engine->_interface->shadeBox(rect, 4);
|
|
}
|
|
|
|
drawRectBorders(rect);
|
|
|
|
_engine->_text->setFontColor(COLOR_WHITE);
|
|
_engine->_text->setFont(INTER_LEAVE, INTER_SPACE);
|
|
const int32 textSize = _engine->_text->sizeFont(dialText);
|
|
_engine->_text->drawText((_engine->width() / 2) - (textSize / 2), rect.top + 7, dialText);
|
|
}
|
|
|
|
int16 Menu::drawButtons(MenuSettings *menuSettings, bool hover) {
|
|
int16 buttonNumber = menuSettings->getActiveButton();
|
|
const int32 maxButton = menuSettings->getButtonCount();
|
|
int32 topHeight = menuSettings->getButtonBoxHeight();
|
|
|
|
if (topHeight == 0) {
|
|
topHeight = 35;
|
|
} else {
|
|
topHeight = topHeight - (((maxButton - 1) * 6) + (maxButton * HEIGHT_STANDARD)) / 2;
|
|
}
|
|
|
|
if (maxButton <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
int16 mouseActiveButton = -1;
|
|
|
|
for (int16 i = 0; i < maxButton; ++i) {
|
|
if (menuSettings == &_advOptionsMenuState) {
|
|
int16 id = menuSettings->getButtonState(i);
|
|
switch (id) {
|
|
case MenuButtonTypes::kAggressiveMode:
|
|
if (_engine->_actor->_combatAuto) {
|
|
menuSettings->setButtonTextId(i, TextId::kBehaviourAggressiveAuto);
|
|
} else {
|
|
menuSettings->setButtonTextId(i, TextId::kBehaviourAggressiveManual);
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kPolygonDetails:
|
|
if (_engine->_cfgfile.PolygonDetails == 0) {
|
|
menuSettings->setButtonTextId(i, TextId::kDetailsPolygonsLow);
|
|
} else if (_engine->_cfgfile.PolygonDetails == 1) {
|
|
menuSettings->setButtonTextId(i, TextId::kDetailsPolygonsMiddle);
|
|
} else {
|
|
menuSettings->setButtonTextId(i, TextId::kDetailsPolygonsHigh);
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kShadowSettings:
|
|
if (_engine->_cfgfile.ShadowMode == 0) {
|
|
menuSettings->setButtonTextId(i, TextId::kShadowsDisabled);
|
|
} else if (_engine->_cfgfile.ShadowMode == 1) {
|
|
menuSettings->setButtonTextId(i, TextId::kShadowsFigures);
|
|
} else {
|
|
menuSettings->setButtonTextId(i, TextId::kDetailsShadowHigh);
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kSceneryZoom:
|
|
if (_engine->_cfgfile.SceZoom) {
|
|
menuSettings->setButtonTextId(i, TextId::kSceneryZoomOn);
|
|
} else {
|
|
menuSettings->setButtonTextId(i, TextId::kNoSceneryZoom);
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kHighResolution: {
|
|
if (ConfMan.getBool("usehighres")) {
|
|
menuSettings->setButtonTextId(i, TextId::kCustomHighResOptionOn);
|
|
} else {
|
|
menuSettings->setButtonTextId(i, TextId::kCustomHighResOptionOff);
|
|
}
|
|
break;
|
|
}
|
|
case MenuButtonTypes::kWallCollision: {
|
|
if (ConfMan.getBool("wallcollision")) {
|
|
menuSettings->setButtonTextId(i, TextId::kCustomWallCollisionOn);
|
|
} else {
|
|
menuSettings->setButtonTextId(i, TextId::kCustomWallCollisionOff);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
} else if (menuSettings == &_languageMenuState) {
|
|
int16 id = menuSettings->getButtonState(i);
|
|
switch (id) {
|
|
case MenuButtonTypes::kVoice: {
|
|
const int voiceLanguage = ConfMan.getInt("audio_language");
|
|
menuSettings->setButtonTextId(i, (TextId)((int)TextId::kCustomVoicesNone - voiceLanguage));
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
const int32 menuItemId = menuSettings->getButtonState(i);
|
|
const char *text = menuSettings->getButtonText(_engine->_text, i);
|
|
const int32 border = 45;
|
|
const int32 mainMenuButtonHeightHalf = HEIGHT_STANDARD / 2;
|
|
const Common::Rect rect(border, topHeight - mainMenuButtonHeightHalf, _engine->width() - border, topHeight + mainMenuButtonHeightHalf);
|
|
if (hover) {
|
|
if (i == buttonNumber) {
|
|
drawButtonGfx(menuSettings, rect, menuItemId, text, hover);
|
|
}
|
|
} else {
|
|
if (i == buttonNumber) {
|
|
drawButtonGfx(menuSettings, rect, menuItemId, text, true);
|
|
} else {
|
|
drawButtonGfx(menuSettings, rect, menuItemId, text, false);
|
|
}
|
|
}
|
|
if (_engine->_input->isMouseHovering(rect)) {
|
|
mouseActiveButton = i;
|
|
}
|
|
|
|
topHeight += HEIGHT_STANDARD + MENU_SPACE; // increase button top height
|
|
}
|
|
return mouseActiveButton;
|
|
}
|
|
|
|
void Menu::menuDemo() {
|
|
// TODO: lba2 only show the credits only in the main menu and you could force it by pressing shift+c
|
|
// TODO: lba2 has a cd audio track (2) for the credits
|
|
_engine->_menuOptions->showCredits();
|
|
if (_engine->_movie->playMovie(FLA_DRAGON3)) {
|
|
if (!_engine->_screens->loadImageDelay(TwineImage(Resources::HQR_RESS_FILE, 15, 16), 3)) {
|
|
if (!_engine->_screens->loadImageDelay(TwineImage(Resources::HQR_RESS_FILE, 17, 18), 3)) {
|
|
if (!_engine->_screens->loadImageDelay(TwineImage(Resources::HQR_RESS_FILE, 19, 20), 3)) {
|
|
if (_engine->_movie->playMovie(FLA_BATEAU)) {
|
|
if (_engine->_cfgfile.Version == USA_VERSION) {
|
|
_engine->_screens->loadImageDelay(_engine->_resources->relentLogo(), 3);
|
|
} else {
|
|
_engine->_screens->loadImageDelay(_engine->_resources->lbaLogo(), 3);
|
|
}
|
|
_engine->_screens->adelineLogo();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_engine->_screens->loadMenuImage(false);
|
|
}
|
|
|
|
int32 Menu::doGameMenu(MenuSettings *menuSettings) {
|
|
int16 currentButton = menuSettings->getActiveButton();
|
|
bool buttonsNeedRedraw = true;
|
|
const int32 numEntry = menuSettings->getButtonCount();
|
|
int32 maxButton = numEntry - 1;
|
|
|
|
_engine->_input->enableKeyMap(uiKeyMapId);
|
|
|
|
// if we are running the game already, the buttons are just rendered on top of the scene
|
|
if (_engine->_scene->isGameRunning()) {
|
|
_engine->restoreFrontBuffer();
|
|
} else {
|
|
_engine->_screens->loadMenuImage(false);
|
|
}
|
|
uint32 startMillis = _engine->_system->getMillis();
|
|
do {
|
|
FrameMarker frame(_engine);
|
|
const uint32 loopMillis = _engine->_system->getMillis();
|
|
_engine->readKeys();
|
|
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UIDown)) {
|
|
currentButton++;
|
|
if (currentButton == numEntry) { // if current button is the last, than next button is the first
|
|
currentButton = 0;
|
|
}
|
|
buttonsNeedRedraw = true;
|
|
startMillis = loopMillis;
|
|
} else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIUp)) {
|
|
currentButton--;
|
|
if (currentButton < 0) { // if current button is the first, than previous button is the last
|
|
currentButton = maxButton;
|
|
}
|
|
buttonsNeedRedraw = true;
|
|
startMillis = loopMillis;
|
|
}
|
|
|
|
const int16 id = menuSettings->getActiveButtonState();
|
|
if (menuSettings == &_advOptionsMenuState) {
|
|
switch (id) {
|
|
case MenuButtonTypes::kAggressiveMode:
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft) || _engine->_input->toggleActionIfActive(TwinEActionType::UIRight) || _engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
|
|
_engine->_actor->_combatAuto = !_engine->_actor->_combatAuto;
|
|
startMillis = loopMillis;
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kPolygonDetails:
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft)) {
|
|
_engine->_cfgfile.PolygonDetails--;
|
|
_engine->_cfgfile.PolygonDetails %= 3;
|
|
startMillis = loopMillis;
|
|
} else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIRight) || _engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
|
|
_engine->_cfgfile.PolygonDetails++;
|
|
_engine->_cfgfile.PolygonDetails %= 3;
|
|
startMillis = loopMillis;
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kShadowSettings:
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft)) {
|
|
_engine->_cfgfile.ShadowMode--;
|
|
_engine->_cfgfile.ShadowMode %= 3;
|
|
startMillis = loopMillis;
|
|
} else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIRight) || _engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
|
|
_engine->_cfgfile.ShadowMode++;
|
|
_engine->_cfgfile.ShadowMode %= 3;
|
|
startMillis = loopMillis;
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kSceneryZoom:
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft) || _engine->_input->toggleActionIfActive(TwinEActionType::UIRight) || _engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
|
|
_engine->_cfgfile.SceZoom = !_engine->_cfgfile.SceZoom;
|
|
startMillis = loopMillis;
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kHighResolution:
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft) || _engine->_input->toggleActionIfActive(TwinEActionType::UIRight) || _engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
|
|
const bool highRes = ConfMan.getBool("usehighres");
|
|
ConfMan.setBool("usehighres", !highRes);
|
|
startMillis = loopMillis;
|
|
}
|
|
break;
|
|
case MenuButtonTypes::kWallCollision:
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft) || _engine->_input->toggleActionIfActive(TwinEActionType::UIRight) || _engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
|
|
const bool highRes = ConfMan.getBool("wallcollision");
|
|
ConfMan.setBool("wallcollision", !highRes);
|
|
startMillis = loopMillis;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (menuSettings == &_languageMenuState) {
|
|
switch (id) {
|
|
case MenuButtonTypes::kVoice:
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft)) {
|
|
int voiceLanguage = ConfMan.getInt("audio_language");
|
|
--voiceLanguage;
|
|
if (voiceLanguage < 0) {
|
|
voiceLanguage = 3;
|
|
}
|
|
ConfMan.setInt("audio_language", voiceLanguage);
|
|
} else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIRight) || _engine->_input->toggleActionIfActive(TwinEActionType::UIEnter)) {
|
|
int voiceLanguage = ConfMan.getInt("audio_language");
|
|
++voiceLanguage;
|
|
if (voiceLanguage > 3) {
|
|
voiceLanguage = 0;
|
|
}
|
|
ConfMan.setInt("audio_language", voiceLanguage);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (menuSettings == &_volumeMenuState) {
|
|
Audio::Mixer *mixer = _engine->_system->getMixer();
|
|
switch (id) {
|
|
case MenuButtonTypes::kMusicVolume: {
|
|
int volume = mixer->getVolumeForSoundType(Audio::Mixer::SoundType::kMusicSoundType);
|
|
if (_engine->_input->isActionActive(TwinEActionType::UILeft)) {
|
|
volume -= 4;
|
|
startMillis = loopMillis;
|
|
} else if (_engine->_input->isActionActive(TwinEActionType::UIRight)) {
|
|
volume += 4;
|
|
startMillis = loopMillis;
|
|
}
|
|
_engine->_music->musicVolume(volume);
|
|
ConfMan.setInt("music_volume", mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType));
|
|
break;
|
|
}
|
|
case MenuButtonTypes::kSoundVolume: {
|
|
int volume = mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType);
|
|
if (_engine->_input->isActionActive(TwinEActionType::UILeft)) {
|
|
volume -= 4;
|
|
startMillis = loopMillis;
|
|
} else if (_engine->_input->isActionActive(TwinEActionType::UIRight)) {
|
|
volume += 4;
|
|
startMillis = loopMillis;
|
|
}
|
|
|
|
mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume);
|
|
ConfMan.setInt("sfx_volume", mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType));
|
|
break;
|
|
}
|
|
case MenuButtonTypes::kCDVolume: {
|
|
AudioCDManager::Status status = _engine->_system->getAudioCDManager()->getStatus();
|
|
if (_engine->_input->isActionActive(TwinEActionType::UILeft)) {
|
|
status.volume -= 4;
|
|
startMillis = loopMillis;
|
|
} else if (_engine->_input->isActionActive(TwinEActionType::UIRight)) {
|
|
status.volume += 4;
|
|
startMillis = loopMillis;
|
|
}
|
|
status.volume = CLIP(status.volume, 0, 255);
|
|
_engine->_system->getAudioCDManager()->setVolume(status.volume);
|
|
break;
|
|
}
|
|
case MenuButtonTypes::kSpeechVolume: {
|
|
int volume = mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType);
|
|
if (_engine->_input->isActionActive(TwinEActionType::UILeft)) {
|
|
volume -= 4;
|
|
startMillis = loopMillis;
|
|
} else if (_engine->_input->isActionActive(TwinEActionType::UIRight)) {
|
|
volume += 4;
|
|
startMillis = loopMillis;
|
|
}
|
|
mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume);
|
|
ConfMan.setInt("speech_volume", mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType));
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (buttonsNeedRedraw) {
|
|
// draw all buttons
|
|
const int16 mouseButtonHovered = drawButtons(menuSettings, false);
|
|
if (mouseButtonHovered != -1) {
|
|
currentButton = mouseButtonHovered;
|
|
}
|
|
menuSettings->setActiveButton(currentButton);
|
|
}
|
|
|
|
// draw plasma effect for the current selected button
|
|
const int16 mouseButtonHovered = drawButtons(menuSettings, true);
|
|
if (mouseButtonHovered != -1) {
|
|
if (mouseButtonHovered != currentButton) {
|
|
buttonsNeedRedraw = true;
|
|
}
|
|
currentButton = mouseButtonHovered;
|
|
}
|
|
|
|
if (_engine->shouldQuit()) {
|
|
return kQuitEngine;
|
|
}
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UIAbort)) {
|
|
for (int i = 0; i < menuSettings->getButtonCount(); ++i) {
|
|
const TextId textId = menuSettings->getButtonTextId(i);
|
|
if (textId == TextId::kReturnMenu || textId == TextId::kReturnGame || textId == TextId::kContinue) {
|
|
return (int32)textId;
|
|
}
|
|
}
|
|
startMillis = loopMillis;
|
|
}
|
|
if (menuSettings == &_mainMenuState) {
|
|
uint32 idleTime = 60 * 3 + 53 * 1000;
|
|
if (_engine->isDemo()) {
|
|
idleTime = 60 * 1000;
|
|
}
|
|
if (loopMillis - startMillis > idleTime) {
|
|
return kDemoMenu;
|
|
}
|
|
}
|
|
} while (!_engine->_input->toggleActionIfActive(TwinEActionType::UIEnter));
|
|
|
|
return (int32)menuSettings->getActiveButtonTextId();
|
|
}
|
|
|
|
int32 Menu::advoptionsMenu() {
|
|
_engine->restoreFrontBuffer();
|
|
|
|
ScopedCursor scoped(_engine);
|
|
for (;;) {
|
|
switch (doGameMenu(&_advOptionsMenuState)) {
|
|
case (int32)TextId::kReturnMenu: {
|
|
return 0;
|
|
}
|
|
case kQuitEngine:
|
|
return kQuitEngine;
|
|
case (int32)TextId::kBehaviourAggressiveManual:
|
|
case (int32)TextId::kDetailsPolygonsHigh:
|
|
case (int32)TextId::kDetailsShadowHigh:
|
|
case (int32)TextId::kSceneryZoomOn:
|
|
default:
|
|
warning("Unknown menu button handled");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32 Menu::savemanageMenu() {
|
|
_engine->restoreFrontBuffer();
|
|
|
|
ScopedCursor scoped(_engine);
|
|
for (;;) {
|
|
switch (doGameMenu(&_saveManageMenuState)) {
|
|
case (int32)TextId::kReturnMenu:
|
|
return 0;
|
|
case (int32)TextId::kCreateSaveGame:
|
|
_engine->_menuOptions->saveGameMenu();
|
|
break;
|
|
case (int32)TextId::kDeleteSaveGame:
|
|
_engine->_menuOptions->deleteSaveMenu();
|
|
break;
|
|
case kQuitEngine:
|
|
return kQuitEngine;
|
|
default:
|
|
warning("Unknown menu button handled");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32 Menu::volumeOptions() {
|
|
_engine->restoreFrontBuffer();
|
|
|
|
if (_engine->isLBA1()) {
|
|
if (_engine->isCDROM()) {
|
|
_engine->_music->playAllMusic(9);
|
|
} else {
|
|
_engine->_music->playMidiFile(9);
|
|
}
|
|
}
|
|
|
|
ScopedCursor scoped(_engine);
|
|
for (;;) {
|
|
switch (doGameMenu(&_volumeMenuState)) {
|
|
case (int32)TextId::kReturnMenu:
|
|
return 0;
|
|
case kQuitEngine:
|
|
return kQuitEngine;
|
|
case (int32)TextId::kMusicVolume:
|
|
case (int32)TextId::kSoundVolume:
|
|
case (int32)TextId::kCDVolume:
|
|
case (int32)TextId::kSpeechVolume:
|
|
default:
|
|
warning("Unknown menu button handled");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32 Menu::languageMenu() {
|
|
_engine->restoreFrontBuffer();
|
|
|
|
ScopedCursor scoped(_engine);
|
|
for (;;) {
|
|
switch (doGameMenu(&_languageMenuState)) {
|
|
case (int32)TextId::kReturnMenu:
|
|
return 0;
|
|
case kQuitEngine:
|
|
return kQuitEngine;
|
|
case (int32)MenuButtonTypes::kVoice:
|
|
default:
|
|
warning("Unknown menu button handled");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Menu::inGameOptionsMenu() {
|
|
_engine->_text->initDial(TextBankId::Options_and_menus);
|
|
_optionsMenuState.setButtonTextId(0, TextId::kReturnGame);
|
|
_engine->saveFrontBuffer();
|
|
optionsMenu();
|
|
_engine->_text->initSceneTextBank();
|
|
_optionsMenuState.setButtonTextId(0, TextId::kReturnMenu);
|
|
}
|
|
|
|
int32 Menu::optionsMenu() {
|
|
_engine->restoreFrontBuffer();
|
|
|
|
_engine->_sound->stopSamples();
|
|
if (_engine->isLBA1()) {
|
|
// LBA's Theme
|
|
if (_engine->isCDROM()) {
|
|
_engine->_music->playCdTrack(9);
|
|
} else {
|
|
_engine->_music->playMidiFile(9);
|
|
}
|
|
}
|
|
|
|
ScopedCursor scoped(_engine);
|
|
for (;;) {
|
|
switch (doGameMenu(&_optionsMenuState)) {
|
|
case (int32)TextId::kReturnGame:
|
|
case (int32)TextId::kReturnMenu: {
|
|
return 0;
|
|
}
|
|
case (int32)TextId::kVolumeSettings: {
|
|
checkMenuQuit(volumeOptions()) break;
|
|
}
|
|
case (int32)TextId::kCustomLanguageOption: {
|
|
checkMenuQuit(languageMenu()) break;
|
|
}
|
|
case (int32)TextId::kSaveManage: {
|
|
checkMenuQuit(savemanageMenu()) break;
|
|
}
|
|
case (int32)TextId::kAdvanced: {
|
|
checkMenuQuit(advoptionsMenu()) break;
|
|
}
|
|
case kQuitEngine:
|
|
return kQuitEngine;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32 Menu::newGameClassicMenu() {
|
|
_engine->restoreFrontBuffer();
|
|
|
|
ScopedCursor scoped(_engine);
|
|
for (;;) {
|
|
switch (doGameMenu(&_newGameMenuState)) {
|
|
case (int32)TextId::kReturnGame:
|
|
case (int32)TextId::kReturnMenu: {
|
|
return 0;
|
|
}
|
|
case (int32)TextId::kNewGamePlus:
|
|
_engine->_gameState->_endGameItems = true;
|
|
if (_engine->_menuOptions->newGameMenu()) {
|
|
return 1;
|
|
}
|
|
break;
|
|
case (int32)TextId::kNewGame: {
|
|
_engine->_gameState->_endGameItems = false;
|
|
if (_engine->_menuOptions->newGameMenu()) {
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
case kQuitEngine:
|
|
return kQuitEngine;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const byte cursorArrow[] = {
|
|
1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
1, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
1, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3,
|
|
1, 0, 0, 0, 1, 3, 3, 3, 3, 3, 3,
|
|
1, 0, 0, 0, 0, 1, 3, 3, 3, 3, 3,
|
|
1, 0, 0, 0, 0, 0, 1, 3, 3, 3, 3,
|
|
1, 0, 0, 0, 0, 0, 0, 1, 3, 3, 3,
|
|
1, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3,
|
|
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3,
|
|
1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
|
|
1, 0, 0, 1, 0, 0, 1, 3, 3, 3, 3,
|
|
1, 0, 1, 3, 1, 0, 0, 1, 3, 3, 3,
|
|
1, 1, 3, 3, 1, 0, 0, 1, 3, 3, 3,
|
|
1, 3, 3, 3, 3, 1, 0, 0, 1, 3, 3,
|
|
3, 3, 3, 3, 3, 1, 0, 0, 1, 3, 3,
|
|
3, 3, 3, 3, 3, 3, 1, 1, 1, 3, 3};
|
|
|
|
static const byte cursorPalette[] = {
|
|
0, 0, 0,
|
|
0xff, 0xff, 0xff};
|
|
|
|
bool Menu::init() {
|
|
_engine->_input->enableKeyMap(uiKeyMapId);
|
|
_engine->_screens->loadMenuImage();
|
|
// load menu effect file only once
|
|
_plasmaEffectPtr = (uint8 *)malloc(kPlasmaEffectFilesize);
|
|
memset(_plasmaEffectPtr, 0, kPlasmaEffectFilesize);
|
|
|
|
CursorMan.pushCursor(cursorArrow, 11, 16, 1, 1, 3);
|
|
CursorMan.pushCursorPalette(cursorPalette, 0, 2);
|
|
return HQR::getEntry(_plasmaEffectPtr, Resources::HQR_RESS_FILE, RESSHQR_PLASMAEFFECT) > 0;
|
|
}
|
|
|
|
EngineState Menu::run() {
|
|
FrameMarker frame(_engine);
|
|
_engine->_text->initDial(TextBankId::Options_and_menus);
|
|
|
|
_engine->_sound->stopSamples();
|
|
if (_engine->isLBA1()) {
|
|
if (_engine->isCDROM()) {
|
|
_engine->_music->playCdTrack(9); // LBA's Theme
|
|
} else {
|
|
_engine->_music->playMidiFile(9); // LBA's Theme
|
|
}
|
|
} else {
|
|
_engine->_music->playMusic(6); // LBA2's Theme
|
|
}
|
|
|
|
ScopedCursor scoped(_engine);
|
|
switch (doGameMenu(&_mainMenuState)) {
|
|
case (int32)TextId::toNewGame:
|
|
case (int32)TextId::kNewGame: {
|
|
if (_engine->isLba1Classic()) {
|
|
if (newGameClassicMenu()) {
|
|
return EngineState::GameLoop;
|
|
}
|
|
break;
|
|
}
|
|
if (_engine->_menuOptions->newGameMenu()) {
|
|
return EngineState::GameLoop;
|
|
}
|
|
break;
|
|
}
|
|
case (int32)TextId::toContinueGame:
|
|
case (int32)TextId::kContinueGame: {
|
|
if (_engine->_menuOptions->continueGameMenu()) {
|
|
return EngineState::LoadedGame;
|
|
}
|
|
break;
|
|
}
|
|
case (int32)TextId::toOptions:
|
|
case (int32)TextId::kOptions: {
|
|
optionsMenu();
|
|
break;
|
|
}
|
|
case kDemoMenu: {
|
|
menuDemo();
|
|
break;
|
|
}
|
|
case (int32)TextId::kQuit:
|
|
case (int32)TextId::toQuit:
|
|
case kQuitEngine:
|
|
debug("quit the game");
|
|
return EngineState::QuitGame;
|
|
}
|
|
return EngineState::Menu;
|
|
}
|
|
|
|
int32 Menu::quitMenu() {
|
|
_engine->saveFrontBuffer();
|
|
_engine->_sound->pauseSamples();
|
|
|
|
MenuSettings *localMenu;
|
|
if (_engine->_cfgfile.UseAutoSaving) {
|
|
localMenu = &_giveUpMenuState;
|
|
} else {
|
|
localMenu = &_giveUpMenuWithSaveState;
|
|
}
|
|
|
|
ScopedCursor scoped(_engine);
|
|
|
|
int32 menuId;
|
|
do {
|
|
FrameMarker frame(_engine);
|
|
_engine->_text->initDial(TextBankId::Options_and_menus);
|
|
menuId = doGameMenu(localMenu);
|
|
switch (menuId) {
|
|
case (int32)TextId::kContinue:
|
|
_engine->_sound->resumeSamples();
|
|
break;
|
|
case (int32)TextId::kGiveUp:
|
|
_engine->_gameState->giveUp();
|
|
return 1;
|
|
case (int32)TextId::kCreateSaveGame:
|
|
_engine->_menuOptions->saveGameMenu();
|
|
break;
|
|
case kQuitEngine:
|
|
return kQuitEngine;
|
|
default:
|
|
warning("Unknown menu button handled: %i", menuId);
|
|
}
|
|
_engine->_text->initSceneTextBank();
|
|
} while (menuId != (int32)TextId::kGiveUp && menuId != (int32)TextId::kContinue && menuId != (int32)TextId::kCreateSaveGame);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Menu::drawHealthBar(int32 left, int32 right, int32 top, int32 barLeftPadding, int32 barHeight) {
|
|
_engine->_grid->drawSprite(left, top + 3, _engine->_resources->_spriteData[SPRITEHQR_LIFEPOINTS]);
|
|
const int32 barLeft = left + barLeftPadding;
|
|
const int32 healthBarRight = ruleThree32(barLeft, right, 50, _engine->_scene->_sceneHero->_lifePoint);
|
|
const int32 barBottom = top + barHeight;
|
|
_engine->_interface->box(Common::Rect(barLeft, top, healthBarRight, barBottom), COLOR_91);
|
|
_engine->_interface->box(Common::Rect(healthBarRight, top, left + 325, barBottom), COLOR_BLACK);
|
|
drawRectBorders(Common::Rect(barLeft, top, right, barBottom));
|
|
}
|
|
|
|
void Menu::drawCloverLeafs(int32 newBoxLeft, int32 boxRight, int32 top) {
|
|
// Clover leaf boxes
|
|
for (int32 i = 0; i < _engine->_gameState->_inventoryNumLeafsBox; i++) {
|
|
const int32 leftSpritePos = ruleThree32(newBoxLeft, boxRight, 10, i);
|
|
_engine->_grid->drawSprite(leftSpritePos, top + 58, _engine->_resources->_spriteData[SPRITEHQR_CLOVERLEAFBOX]);
|
|
}
|
|
|
|
// Clover leafs
|
|
for (int32 i = 0; i < _engine->_gameState->_inventoryNumLeafs; i++) {
|
|
const int32 leftSpritePos = ruleThree32(newBoxLeft, boxRight, 10, i);
|
|
_engine->_grid->drawSprite(leftSpritePos + 2, top + 60, _engine->_resources->_spriteData[SPRITEHQR_CLOVERLEAF]);
|
|
}
|
|
}
|
|
|
|
void Menu::drawMagicPointsBar(int32 left, int32 right, int32 top, int32 barLeftPadding, int32 barHeight) {
|
|
if (_engine->_gameState->inventoryDisabled()) {
|
|
return;
|
|
}
|
|
if (!_engine->_gameState->hasItem(InventoryItems::kiTunic)) {
|
|
return;
|
|
}
|
|
_engine->_grid->drawSprite(left, top + 1, _engine->_resources->_spriteData[SPRITEHQR_MAGICPOINTS]);
|
|
if (_engine->_gameState->_magicLevelIdx <= 0) {
|
|
return;
|
|
}
|
|
const int32 barLeft = left + barLeftPadding;
|
|
const int32 barBottom = top + barHeight;
|
|
// max magic level is 4
|
|
const int32 maxMagicPoints = 4 * 20;
|
|
const int32 barRight = ruleThree32(barLeft, right, maxMagicPoints, _engine->_gameState->_magicPoint);
|
|
const Common::Rect pointsRect(barLeft, top, barRight, barBottom);
|
|
_engine->_interface->box(pointsRect, COLOR_75);
|
|
|
|
for (int32 l = 0; l < _engine->_gameState->_magicLevelIdx; l++) {
|
|
const int32 x1 = ruleThree32(barLeft, right, 40, _engine->_gameState->_magicLevelIdx * 10);
|
|
_engine->_interface->drawLine(x1, top + barHeight, x1, top + 35 + 15 - 1, 0);
|
|
}
|
|
|
|
const int32 rectRight = ruleThree32(barLeft, right, 40, _engine->_gameState->_magicLevelIdx * 10);
|
|
drawRectBorders(barLeft, top, rectRight, barBottom);
|
|
}
|
|
|
|
void Menu::drawSpriteAndString(int32 left, int32 top, const SpriteData &spriteData, const Common::String &str, int32 color) {
|
|
_engine->_grid->drawSprite(left, top + 15, spriteData);
|
|
_engine->_text->setFontColor(color);
|
|
_engine->_text->drawText(left + 30, top + 5, str.c_str());
|
|
}
|
|
|
|
void Menu::drawCoins(int32 left, int32 top) {
|
|
const Common::String &inventoryNumKashes = Common::String::format("%d", _engine->_gameState->_goldPieces);
|
|
drawSpriteAndString(left, top, _engine->_resources->_spriteData[SPRITEHQR_KASHES], inventoryNumKashes);
|
|
}
|
|
|
|
void Menu::drawKeys(int32 left, int32 top) {
|
|
const Common::String &inventoryNumKeys = Common::String::format("%d", _engine->_gameState->_nbLittleKeys);
|
|
drawSpriteAndString(left, top, _engine->_resources->_spriteData[SPRITEHQR_KEY], inventoryNumKeys);
|
|
}
|
|
|
|
void Menu::drawInfoMenu(int16 left, int16 top, int16 width) {
|
|
_engine->_interface->unsetClip();
|
|
const int16 height = 80;
|
|
const Common::Rect rect(left, top, left + width, top + height);
|
|
drawRectBorders(rect);
|
|
Common::Rect filledRect(rect);
|
|
filledRect.grow(-1);
|
|
_engine->_interface->box(filledRect, COLOR_BLACK);
|
|
|
|
const int32 boxLeft = left + 9;
|
|
const int32 boxRight = left + 325;
|
|
const int32 barPadding = 25;
|
|
const int32 boxTop = top + 10;
|
|
const int32 barHeight = 14;
|
|
drawHealthBar(boxLeft, boxRight, boxTop, barPadding, barHeight);
|
|
drawMagicPointsBar(boxLeft, boxRight, boxTop + 25, barPadding, barHeight);
|
|
|
|
const int32 posLeft = left + 340;
|
|
drawCoins(posLeft, top);
|
|
drawKeys(posLeft, top + 35);
|
|
drawCloverLeafs(left + barPadding, boxRight, top);
|
|
|
|
_engine->copyBlockPhys(left, top, left + width, top + 135);
|
|
}
|
|
|
|
Common::Rect Menu::calcBehaviourRect(int32 left, int32 top, HeroBehaviourType behaviour) const {
|
|
const int border = 10;
|
|
const int32 padding = 11;
|
|
const int32 width = 99;
|
|
const int height = 119;
|
|
|
|
const int32 boxLeft = (int32)behaviour * (width + padding) + left + border;
|
|
const int32 boxRight = boxLeft + width;
|
|
const int32 boxTop = top + border;
|
|
const int32 boxBottom = boxTop + height;
|
|
return Common::Rect(boxLeft, boxTop, boxRight, boxBottom);
|
|
}
|
|
|
|
bool Menu::isBehaviourHovered(int32 left, int32 top, HeroBehaviourType behaviour) const {
|
|
if (!_engine->_cfgfile.Mouse) {
|
|
return false;
|
|
}
|
|
const Common::Rect &boxRect = calcBehaviourRect(left, top, behaviour);
|
|
return _engine->_input->isMouseHovering(boxRect);
|
|
}
|
|
|
|
void Menu::drawBehaviour(int32 left, int32 top, HeroBehaviourType behaviour, int32 angle, bool cantDrawBox) {
|
|
const Common::Rect &boxRect = calcBehaviourRect(left, top, behaviour);
|
|
|
|
const int animIdx = _engine->_actor->_heroAnimIdx[(byte)behaviour];
|
|
const AnimData ¤tAnimData = _engine->_resources->_animData[animIdx];
|
|
|
|
uint currentAnimState = _behaviourAnimState[(byte)behaviour];
|
|
|
|
if (_engine->_animations->setInterAnimObjet(currentAnimState, currentAnimData, *_behaviourEntity, &_behaviourAnimData[(byte)behaviour])) {
|
|
currentAnimState++; // keyframe
|
|
if (currentAnimState >= currentAnimData.getNbFramesAnim()) {
|
|
currentAnimState = currentAnimData.getLoopFrame();
|
|
}
|
|
_behaviourAnimState[(byte)behaviour] = currentAnimState;
|
|
}
|
|
|
|
if (!cantDrawBox) {
|
|
Common::Rect boxRectCopy(boxRect);
|
|
boxRectCopy.grow(1);
|
|
drawRectBorders(boxRectCopy);
|
|
}
|
|
|
|
_engine->_interface->memoClip();
|
|
_engine->_interface->unsetClip();
|
|
|
|
if (behaviour == _engine->_actor->_heroBehaviour) {
|
|
const int titleOffset = 10;
|
|
const int titleHeight = 40;
|
|
const int32 titleBoxLeft = left + 10;
|
|
const int32 titleBoxWidth = 430;
|
|
const int32 titleBoxCenter = titleBoxLeft + titleBoxWidth / 2;
|
|
const int32 titleBoxRight = titleBoxLeft + titleBoxWidth;
|
|
const int32 titleBoxTop = boxRect.bottom + titleOffset;
|
|
const int32 titleBoxBottom = titleBoxTop + titleHeight;
|
|
|
|
_engine->_interface->box(boxRect, COLOR_BRIGHT_BLUE2);
|
|
|
|
// behaviour menu title
|
|
const Common::Rect titleRect(titleBoxLeft, titleBoxTop, titleBoxRight, titleBoxBottom);
|
|
_engine->_interface->box(titleRect, COLOR_BLACK);
|
|
drawRectBorders(titleRect);
|
|
|
|
_engine->_text->setFontColor(COLOR_WHITE);
|
|
|
|
char dialText[256];
|
|
_engine->_text->getMenuText(_engine->_actor->getTextIdForBehaviour(), dialText, sizeof(dialText));
|
|
|
|
_engine->_text->drawText(titleBoxCenter - _engine->_text->sizeFont(dialText) / 2, titleBoxTop + 1, dialText);
|
|
} else {
|
|
_engine->_interface->box(boxRect, COLOR_BLACK);
|
|
}
|
|
|
|
_engine->_renderer->drawObj3D(boxRect, -600, angle, *_behaviourEntity, _moveMenu);
|
|
|
|
_engine->_interface->restoreClip();
|
|
}
|
|
|
|
void Menu::prepareAndDrawBehaviour(int32 left, int32 top, int32 angle, HeroBehaviourType behaviour) {
|
|
const int animIdx = _engine->_actor->_heroAnimIdx[(byte)behaviour];
|
|
_engine->_animations->setAnimObjet(_behaviourAnimState[(byte)behaviour], _engine->_resources->_animData[animIdx], *_behaviourEntity, &_behaviourAnimData[(byte)behaviour]);
|
|
drawBehaviour(left, top, behaviour, angle, false);
|
|
}
|
|
|
|
void Menu::drawBehaviourMenu(int32 left, int32 top, int32 angle) {
|
|
const int32 width = 450;
|
|
const int32 height = 190;
|
|
const int32 right = left + width;
|
|
const int32 bottom = top + height;
|
|
|
|
const Common::Rect titleRect(left, top, right, bottom);
|
|
drawRectBorders(titleRect);
|
|
|
|
Common::Rect boxRect(titleRect);
|
|
boxRect.grow(-1);
|
|
_engine->_interface->shadeBox(boxRect, 2);
|
|
|
|
prepareAndDrawBehaviour(left, top, angle, HeroBehaviourType::kNormal);
|
|
prepareAndDrawBehaviour(left, top, angle, HeroBehaviourType::kAthletic);
|
|
prepareAndDrawBehaviour(left, top, angle, HeroBehaviourType::kAggressive);
|
|
prepareAndDrawBehaviour(left, top, angle, HeroBehaviourType::kDiscrete);
|
|
|
|
_engine->copyBlockPhys(titleRect);
|
|
|
|
drawInfoMenu(titleRect.left, titleRect.bottom + 10, titleRect.width());
|
|
}
|
|
|
|
void Menu::processBehaviourMenu(bool behaviourMenu) {
|
|
if (_engine->_actor->_heroBehaviour == HeroBehaviourType::kProtoPack) {
|
|
_engine->_sound->stopSamples();
|
|
_engine->_actor->setBehaviour(HeroBehaviourType::kNormal);
|
|
}
|
|
|
|
_behaviourEntity = &_engine->_scene->_sceneHero->_entityDataPtr->getBody(_engine->_scene->_sceneHero->_body);
|
|
|
|
_engine->_actor->_heroAnimIdx[(byte)HeroBehaviourType::kNormal] = _engine->_actor->_heroAnimIdxNORMAL;
|
|
_engine->_actor->_heroAnimIdx[(byte)HeroBehaviourType::kAthletic] = _engine->_actor->_heroAnimIdxATHLETIC;
|
|
_engine->_actor->_heroAnimIdx[(byte)HeroBehaviourType::kAggressive] = _engine->_actor->_heroAnimIdxAGGRESSIVE;
|
|
_engine->_actor->_heroAnimIdx[(byte)HeroBehaviourType::kDiscrete] = _engine->_actor->_heroAnimIdxDISCRETE;
|
|
|
|
_engine->_movements->initRealAngle(_engine->_scene->_sceneHero->_beta, _engine->_scene->_sceneHero->_beta - LBAAngles::ANGLE_90, LBAAngles::ANGLE_17, &_moveMenu);
|
|
|
|
_engine->saveFrontBuffer();
|
|
|
|
TextBankId tmpTextBank = _engine->_scene->_sceneTextBank;
|
|
_engine->_scene->_sceneTextBank = TextBankId::None;
|
|
|
|
_engine->_text->initDial(TextBankId::Options_and_menus);
|
|
|
|
// quick actions to change behaviour don't show the menu in classic edition
|
|
if (!behaviourMenu && _engine->isLba1Classic()) {
|
|
char text[256];
|
|
_engine->_text->getMenuText(_engine->_actor->getTextIdForBehaviour(), text, sizeof(text));
|
|
_engine->_redraw->setRenderText(text);
|
|
} else {
|
|
const int32 left = _engine->width() / 2 - 220;
|
|
const int32 top = _engine->height() / 2 - 140;
|
|
drawBehaviourMenu(left, top, _engine->_scene->_sceneHero->_beta);
|
|
|
|
HeroBehaviourType tmpHeroBehaviour = _engine->_actor->_heroBehaviour;
|
|
|
|
const int animIdx = _engine->_actor->_heroAnimIdx[(byte)_engine->_actor->_heroBehaviour];
|
|
_engine->_animations->setAnimObjet(_behaviourAnimState[(byte)_engine->_actor->_heroBehaviour], _engine->_resources->_animData[animIdx], *_behaviourEntity, &_behaviourAnimData[(byte)_engine->_actor->_heroBehaviour]);
|
|
|
|
int32 tmpTime = _engine->timerRef;
|
|
|
|
ScopedCursor scopedCursor(_engine);
|
|
ScopedKeyMap scopedKeyMap(_engine, uiKeyMapId);
|
|
while (_engine->_input->isActionActive(TwinEActionType::BehaviourMenu) || _engine->_input->isQuickBehaviourActionActive()) {
|
|
FrameMarker frame(_engine, 50);
|
|
_engine->readKeys();
|
|
if (_engine->shouldQuit()) {
|
|
break;
|
|
}
|
|
|
|
if (isBehaviourHovered(left, top, HeroBehaviourType::kNormal)) {
|
|
_engine->_actor->_heroBehaviour = HeroBehaviourType::kNormal;
|
|
} else if (isBehaviourHovered(left, top, HeroBehaviourType::kAthletic)) {
|
|
_engine->_actor->_heroBehaviour = HeroBehaviourType::kAthletic;
|
|
} else if (isBehaviourHovered(left, top, HeroBehaviourType::kAggressive)) {
|
|
_engine->_actor->_heroBehaviour = HeroBehaviourType::kAggressive;
|
|
} else if (isBehaviourHovered(left, top, HeroBehaviourType::kDiscrete)) {
|
|
_engine->_actor->_heroBehaviour = HeroBehaviourType::kDiscrete;
|
|
}
|
|
|
|
int heroBehaviour = (int)_engine->_actor->_heroBehaviour;
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft)) {
|
|
heroBehaviour--;
|
|
} else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIRight)) {
|
|
heroBehaviour++;
|
|
}
|
|
|
|
if (heroBehaviour < (int)HeroBehaviourType::kNormal) {
|
|
heroBehaviour = (int)HeroBehaviourType::kDiscrete;
|
|
} else if (heroBehaviour >= (int)HeroBehaviourType::kProtoPack) {
|
|
heroBehaviour = (int)HeroBehaviourType::kNormal;
|
|
}
|
|
|
|
_engine->_actor->_heroBehaviour = (HeroBehaviourType)heroBehaviour;
|
|
|
|
if (tmpHeroBehaviour != _engine->_actor->_heroBehaviour) {
|
|
drawBehaviour(left, top, tmpHeroBehaviour, _engine->_scene->_sceneHero->_beta, true);
|
|
tmpHeroBehaviour = _engine->_actor->_heroBehaviour;
|
|
_engine->_movements->initRealAngle(_engine->_scene->_sceneHero->_beta, _engine->_scene->_sceneHero->_beta - LBAAngles::ANGLE_90, LBAAngles::ANGLE_17, &_moveMenu);
|
|
const int tmpAnimIdx = _engine->_actor->_heroAnimIdx[(byte)_engine->_actor->_heroBehaviour];
|
|
_engine->_animations->setAnimObjet(_behaviourAnimState[(byte)_engine->_actor->_heroBehaviour], _engine->_resources->_animData[tmpAnimIdx], *_behaviourEntity, &_behaviourAnimData[(byte)_engine->_actor->_heroBehaviour]);
|
|
}
|
|
|
|
drawBehaviour(left, top, _engine->_actor->_heroBehaviour, -1, true);
|
|
|
|
// increase the timer to play the animations
|
|
_engine->timerRef++;
|
|
debugC(3, kDebugLevels::kDebugTimers, "Behaviour time: %i", _engine->timerRef);
|
|
}
|
|
|
|
_engine->timerRef = tmpTime;
|
|
|
|
_engine->_gameState->init3DGame();
|
|
}
|
|
_engine->_actor->setBehaviour(_engine->_actor->_heroBehaviour);
|
|
|
|
_engine->_scene->_sceneTextBank = tmpTextBank;
|
|
_engine->_text->initSceneTextBank();
|
|
}
|
|
|
|
Common::Rect Menu::calcItemRect(int32 left, int32 top, int32 item, int32 *centerX, int32 *centerY) const {
|
|
const int32 itemWidth = SIZE_INV_OBJ_X;
|
|
const int32 itemHeight = SIZE_INV_OBJ_Y;
|
|
const int32 itemPadding = 11;
|
|
const int32 itemWidthHalf = itemWidth / 2;
|
|
const int32 itemHeightHalf = itemHeight / 2;
|
|
const int32 itemX = (item / 4) * (itemWidth + itemPadding) + left + itemWidthHalf + itemPadding - 1;
|
|
const int32 itemY = (item % 4) * (itemHeight + itemPadding) + top + itemHeightHalf + itemPadding - 1;
|
|
if (centerX) {
|
|
*centerX = itemX;
|
|
}
|
|
if (centerY) {
|
|
*centerY = itemY;
|
|
}
|
|
return Common::Rect(itemX - itemWidthHalf, itemY - itemHeightHalf, itemX + itemWidthHalf, itemY + itemHeightHalf);
|
|
}
|
|
|
|
void Menu::drawOneInventory(int32 left, int32 top, int32 item) {
|
|
int32 itemX, itemY;
|
|
const Common::Rect rect = calcItemRect(left, top, item, &itemX, &itemY);
|
|
const int32 color = _inventorySelectedItem == item ? _inventorySelectedColor : COLOR_BLACK;
|
|
|
|
_engine->_interface->box(rect, color);
|
|
|
|
if (item < NUM_INVENTORY_ITEMS && _engine->_gameState->hasItem((InventoryItems)item) && (!_engine->_gameState->inventoryDisabled() || item == InventoryItems::kiCloverLeaf)) {
|
|
_itemAngle[item] += LBAAngles::ANGLE_2;
|
|
_engine->_interface->setClip(rect);
|
|
_engine->_renderer->draw3dObject(itemX, itemY, _engine->_resources->_inventoryTable[item], _itemAngle[item], 15000);
|
|
_engine->_interface->unsetClip();
|
|
if (item == InventoryItems::kGasItem) {
|
|
_engine->_text->setFontColor(COLOR_WHITE);
|
|
const Common::String &inventoryNumGas = Common::String::format("%d", _engine->_gameState->_inventoryNumGas);
|
|
_engine->_text->drawText(rect.left + 3, rect.bottom - 32, inventoryNumGas.c_str());
|
|
}
|
|
}
|
|
|
|
drawRectBorders(rect);
|
|
}
|
|
|
|
void Menu::drawListInventory(int32 left, int32 top) {
|
|
constexpr int w = (SIZE_INV_OBJ_X + 10) * 7 + 10;
|
|
constexpr int h = (SIZE_INV_OBJ_Y + 10) * 4 + 10;
|
|
const Common::Rect rect(left, top, left + w, top + h);
|
|
_engine->_interface->shadeBox(rect, 4);
|
|
drawRectBorders(rect);
|
|
for (int32 item = 0; item < NUM_INVENTORY_ITEMS; item++) {
|
|
drawOneInventory(left, top, item);
|
|
}
|
|
_engine->_interface->unsetClip();
|
|
}
|
|
|
|
void Menu::inventory() {
|
|
int32 tmpAlphaLight = _engine->_scene->_alphaLight;
|
|
int32 tmpBetaLight = _engine->_scene->_betaLight;
|
|
|
|
_engine->saveFrontBuffer();
|
|
|
|
_engine->_renderer->setLightVector(LBAAngles::ANGLE_315, LBAAngles::ANGLE_334, LBAAngles::ANGLE_0);
|
|
|
|
_inventorySelectedColor = COLOR_SELECT_MENU;
|
|
|
|
if (_engine->_gameState->_inventoryNumLeafs > 0) {
|
|
_engine->_gameState->giveItem(InventoryItems::kiCloverLeaf);
|
|
} else {
|
|
_engine->_gameState->removeItem(InventoryItems::kiCloverLeaf);
|
|
}
|
|
|
|
const int32 left = _engine->width() / 2 - 303;
|
|
const int32 top = _engine->height() / 2 - 230;
|
|
drawListInventory(left, top);
|
|
|
|
_engine->_text->initDial(TextBankId::Inventory_Intro_and_Holomap);
|
|
|
|
_engine->_text->setFontCrossColor(COLOR_BRIGHT_BLUE);
|
|
_engine->_text->initDialWindow();
|
|
|
|
ProgressiveTextState dialstate = ProgressiveTextState::ContinueRunning;
|
|
bool updateItemText = true;
|
|
|
|
ScopedCursor scopedCursor(_engine);
|
|
ScopedKeyMap scopedKeyMap(_engine, uiKeyMapId);
|
|
for (;;) {
|
|
FrameMarker frame(_engine, 66);
|
|
_engine->readKeys();
|
|
int32 prevSelectedItem = _inventorySelectedItem;
|
|
|
|
if (_engine->_input->toggleAbortAction() || _engine->shouldQuit()) {
|
|
break;
|
|
}
|
|
|
|
for (int32 item = 0; item < NUM_INVENTORY_ITEMS; item++) {
|
|
const Common::Rect &rect = calcItemRect(left, top, item);
|
|
if (_engine->_input->isMouseHovering(rect)) {
|
|
_inventorySelectedItem = item;
|
|
drawOneInventory(left, top, prevSelectedItem);
|
|
updateItemText = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const bool cursorDown = _engine->_input->toggleActionIfActive(TwinEActionType::UIDown);
|
|
const bool cursorUp = _engine->_input->toggleActionIfActive(TwinEActionType::UIUp);
|
|
const bool cursorLeft = _engine->_input->toggleActionIfActive(TwinEActionType::UILeft);
|
|
const bool cursorRight = _engine->_input->toggleActionIfActive(TwinEActionType::UIRight);
|
|
|
|
if (cursorDown) {
|
|
_inventorySelectedItem++;
|
|
if (_inventorySelectedItem >= NUM_INVENTORY_ITEMS) {
|
|
_inventorySelectedItem = 0;
|
|
}
|
|
drawOneInventory(left, top, prevSelectedItem);
|
|
updateItemText = true;
|
|
} else if (cursorUp) {
|
|
_inventorySelectedItem--;
|
|
if (_inventorySelectedItem < 0) {
|
|
_inventorySelectedItem = NUM_INVENTORY_ITEMS - 1;
|
|
}
|
|
drawOneInventory(left, top, prevSelectedItem);
|
|
updateItemText = true;
|
|
} else if (cursorLeft) {
|
|
_inventorySelectedItem -= 4;
|
|
if (_inventorySelectedItem < 0) {
|
|
_inventorySelectedItem += NUM_INVENTORY_ITEMS;
|
|
}
|
|
drawOneInventory(left, top, prevSelectedItem);
|
|
updateItemText = true;
|
|
} else if (cursorRight) {
|
|
_inventorySelectedItem += 4;
|
|
if (_inventorySelectedItem >= NUM_INVENTORY_ITEMS) {
|
|
_inventorySelectedItem -= NUM_INVENTORY_ITEMS;
|
|
}
|
|
drawOneInventory(left, top, prevSelectedItem);
|
|
updateItemText = true;
|
|
}
|
|
|
|
if (updateItemText) {
|
|
_engine->_text->secondInitDialWindow();
|
|
if (_inventorySelectedItem < NUM_INVENTORY_ITEMS && _engine->_gameState->hasItem((InventoryItems)_inventorySelectedItem) && !_engine->_gameState->inventoryDisabled()) {
|
|
_engine->_text->initInventoryText((InventoryItems)_inventorySelectedItem);
|
|
} else {
|
|
_engine->_text->initInventoryText(InventoryItems::MaxInventoryItems);
|
|
}
|
|
dialstate = ProgressiveTextState::End;
|
|
updateItemText = false;
|
|
}
|
|
|
|
if (dialstate != ProgressiveTextState::NextPage) {
|
|
dialstate = _engine->_text->nextDialChar();
|
|
}
|
|
|
|
if (_engine->_input->toggleActionIfActive(TwinEActionType::UINextPage)) {
|
|
// restart the item description to appear from the beginning
|
|
if (dialstate == ProgressiveTextState::End) {
|
|
updateItemText = true;
|
|
}
|
|
if (dialstate == ProgressiveTextState::NextPage) {
|
|
_engine->_text->secondInitDialWindow();
|
|
dialstate = ProgressiveTextState::ContinueRunning;
|
|
}
|
|
}
|
|
|
|
drawOneInventory(left, top, _inventorySelectedItem);
|
|
|
|
if (_inventorySelectedItem < NUM_INVENTORY_ITEMS && _engine->_input->toggleActionIfActive(TwinEActionType::UIEnter) && _engine->_gameState->hasItem((InventoryItems)_inventorySelectedItem) && !_engine->_gameState->inventoryDisabled()) {
|
|
_engine->_loopInventoryItem = _inventorySelectedItem;
|
|
_inventorySelectedColor = COLOR_91;
|
|
drawOneInventory(left, top, _inventorySelectedItem);
|
|
break;
|
|
}
|
|
}
|
|
|
|
_engine->_text->closeDial();
|
|
|
|
_engine->_scene->_alphaLight = tmpAlphaLight;
|
|
_engine->_scene->_betaLight = tmpBetaLight;
|
|
|
|
_engine->_gameState->init3DGame();
|
|
|
|
_engine->_text->initSceneTextBank();
|
|
|
|
// this is a hack to 'fix' https://bugs.scummvm.org/ticket/13677
|
|
_engine->_input->toggleActionIfActive(TwinEActionType::RecenterScreenOnTwinsen);
|
|
}
|
|
|
|
} // namespace TwinE
|