diff --git a/engines/twine/input.cpp b/engines/twine/input.cpp index 2812f792e7a..3cc30fe7e75 100644 --- a/engines/twine/input.cpp +++ b/engines/twine/input.cpp @@ -164,4 +164,12 @@ bool Input::isMouseHovering(int32 left, int32 top, int32 right, int32 bottom) co return point.x >= left && point.x <= right && point.y >= top && point.y <= bottom; } +bool Input::isMouseHovering(const Common::Rect &rect) const { + if (!_engine->cfgfile.Mouse) { + return false; + } + Common::Point point = g_system->getEventManager()->getMousePos(); + return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom; +} + } // namespace TwinE diff --git a/engines/twine/input.h b/engines/twine/input.h index e5e670cea19..22da5d5bb28 100644 --- a/engines/twine/input.h +++ b/engines/twine/input.h @@ -27,6 +27,7 @@ #include "common/keyboard.h" #include "common/scummsys.h" #include "common/util.h" +#include "common/rect.h" namespace TwinE { @@ -129,6 +130,7 @@ public: bool isActionActive(TwinEActionType actionType, bool onlyFirstTime = true) const; bool isMouseHovering(int32 left, int32 top, int32 right, int32 bottom) const; + bool isMouseHovering(const Common::Rect &rect) const; /** * @brief If the action is active, the internal state is reset and a following call of this method won't return diff --git a/engines/twine/interface.cpp b/engines/twine/interface.cpp index 1527837517c..d596b05b0bc 100644 --- a/engines/twine/interface.cpp +++ b/engines/twine/interface.cpp @@ -228,7 +228,11 @@ void Interface::drawTransparentBox(int32 left, int32 top, int32 right, int32 bot } while (height2 > 0); } -void Interface::drawSplittedBox(int32 left, int32 top, int32 right, int32 bottom, uint8 e) { // Box +void Interface::drawSplittedBox(const Common::Rect &rect, uint8 colorIndex) { + drawSplittedBox(rect.left, rect.top, rect.right, rect.bottom, colorIndex); +} + +void Interface::drawSplittedBox(int32 left, int32 top, int32 right, int32 bottom, uint8 colorIndex) { // Box if (left > SCREEN_TEXTLIMIT_RIGHT) { return; } @@ -249,7 +253,7 @@ void Interface::drawSplittedBox(int32 left, int32 top, int32 right, int32 bottom for (int32 x = top; x < bottom; x++) { for (int32 y = left; y < right; y++) { - *(ptr++) = e; + *(ptr++) = colorIndex; } ptr += offset; } diff --git a/engines/twine/interface.h b/engines/twine/interface.h index 269e07229c6..c8ced700422 100644 --- a/engines/twine/interface.h +++ b/engines/twine/interface.h @@ -24,6 +24,7 @@ #define TWINE_INTERFACE_H #include "common/scummsys.h" +#include "common/rect.h" namespace Graphics { class ManagedSurface; @@ -91,7 +92,8 @@ public: */ void drawTransparentBox(int32 left, int32 top, int32 right, int32 bottom, int32 colorAdj); - void drawSplittedBox(int32 left, int32 top, int32 right, int32 bottom, uint8 e); + void drawSplittedBox(int32 left, int32 top, int32 right, int32 bottom, uint8 colorIndex); + void drawSplittedBox(const Common::Rect &rect, uint8 colorIndex); void setClip(int32 left, int32 top, int32 right, int32 bottom); void saveClip(); // saveTextWindow diff --git a/engines/twine/menu.cpp b/engines/twine/menu.cpp index 47929b3a2f2..84381ae618d 100644 --- a/engines/twine/menu.cpp +++ b/engines/twine/menu.cpp @@ -30,6 +30,7 @@ #include "common/scummsys.h" #include "common/system.h" #include "common/util.h" +#include "graphics/cursorman.h" #include "twine/actor.h" #include "twine/animations.h" #include "twine/gamestate.h" @@ -231,11 +232,15 @@ void Menu::processPlasmaEffect(int32 left, int32 top, int32 color) { } } +void Menu::drawBox(const Common::Rect &rect) { + _engine->_interface->drawLine(rect.left, rect.top, rect.right, rect.top, 79); // top line + _engine->_interface->drawLine(rect.left, rect.top, rect.left, rect.bottom, 79); // left line + _engine->_interface->drawLine(rect.right, rect.top + 1, rect.right, rect.bottom, 73); // right line + _engine->_interface->drawLine(rect.left + 1, rect.bottom, rect.right, rect.bottom, 73); // bottom line +} + void Menu::drawBox(int32 left, int32 top, int32 right, int32 bottom) { - _engine->_interface->drawLine(left, top, right, top, 79); // top line - _engine->_interface->drawLine(left, top, left, bottom, 79); // left line - _engine->_interface->drawLine(right, top + 1, right, bottom, 73); // right line - _engine->_interface->drawLine(left + 1, bottom, right, bottom, 73); // bottom line + drawBox(Common::Rect(left, top, right, bottom)); } void Menu::drawButtonGfx(const MenuSettings *menuSettings, int32 left, int32 top, int32 right, int32 bottom, int32 buttonId, const char *dialText, bool hover) { @@ -506,19 +511,20 @@ int32 Menu::processMenu(MenuSettings *menuSettings) { } if (buttonsNeedRedraw) { - menuSettings->setActiveButton(currentButton); - // draw all buttons const int16 mouseButtonHovered = drawButtons(menuSettings, false); if (mouseButtonHovered != -1) { currentButton = mouseButtonHovered; } - buttonsNeedRedraw = false; + 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; } @@ -543,6 +549,7 @@ int32 Menu::advoptionsMenu() { _engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer); _engine->flip(); + ScopedCursor scoped(_engine); for (;;) { switch (processMenu(&advOptionsMenuState)) { case TextId::kReturnMenu: { @@ -567,6 +574,7 @@ int32 Menu::savemanageMenu() { _engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer); _engine->flip(); + ScopedCursor scoped(_engine); for (;;) { switch (processMenu(&saveManageMenuState)) { case TextId::kReturnMenu: @@ -592,6 +600,7 @@ int32 Menu::volumeMenu() { _engine->_screens->copyScreen(_engine->workVideoBuffer, _engine->frontVideoBuffer); _engine->flip(); + ScopedCursor scoped(_engine); for (;;) { switch (processMenu(&volumeMenuState)) { case TextId::kReturnMenu: @@ -631,6 +640,7 @@ int32 Menu::optionsMenu() { _engine->_sound->stopSamples(); //_engine->_music->playCDtrack(9); + ScopedCursor scoped(_engine); for (;;) { switch (processMenu(&optionsMenuState)) { case TextId::kReturnGame: @@ -659,10 +669,37 @@ int32 Menu::optionsMenu() { 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() { // 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; } @@ -673,6 +710,7 @@ EngineState Menu::run() { _engine->_music->playTrackMusic(9); // LBA's Theme _engine->_sound->stopSamples(); + ScopedCursor scoped(_engine); switch (processMenu(&mainMenuState)) { case TextId::kNewGame: { if (_engine->_menuOptions->newGameMenu()) { @@ -714,6 +752,8 @@ int32 Menu::giveupMenu() { localMenu = &giveUpMenuWithSaveState; } + ScopedCursor scoped(_engine); + int32 menuId; do { _engine->_text->initTextBank(TextBankId::Options_and_menus); @@ -800,8 +840,7 @@ void Menu::drawInfoMenu(int16 left, int16 top) { _engine->copyBlockPhys(left, top, left + 450, top + 135); } -// TODO: convert cantDrawBox to bool -void Menu::drawBehaviour(HeroBehaviourType behaviour, int32 angle, int16 cantDrawBox) { +Common::Rect Menu::calcBehaviourRect(HeroBehaviourType behaviour) const { const int border = 110; const int32 padding = 11; const int32 width = 99; @@ -811,15 +850,24 @@ void Menu::drawBehaviour(HeroBehaviourType behaviour, int32 angle, int16 cantDra const int32 boxRight = boxLeft + width; const int32 boxTop = border; const int32 boxBottom = boxTop + height; + return Common::Rect(boxLeft, boxTop, boxRight, boxBottom); +} +bool Menu::isBehaviourHovered(HeroBehaviourType behaviour) const { + const Common::Rect &boxRect = calcBehaviourRect(behaviour); + return _engine->_input->isMouseHovering(boxRect); +} + +void Menu::drawBehaviour(HeroBehaviourType behaviour, int32 angle, bool cantDrawBox) { + const Common::Rect &boxRect = calcBehaviourRect(behaviour); const int titleOffset = 10; const int titleHeight = 40; - const int32 titleBoxLeft = border; + const int32 titleBoxLeft = 110; const int32 titleBoxRight = 540; - const int32 titleBoxTop = boxBottom + titleOffset; + const int32 titleBoxTop = boxRect.bottom + titleOffset; const int32 titleBoxBottom = titleBoxTop + titleHeight; - uint8 *currentAnim = _engine->_resources->animTable[_engine->_actor->heroAnimIdx[(byte)behaviour]]; + const uint8 *currentAnim = _engine->_resources->animTable[_engine->_actor->heroAnimIdx[(byte)behaviour]]; int16 currentAnimState = behaviourAnimState[(byte)behaviour]; if (_engine->_animations->setModelAnimation(currentAnimState, currentAnim, behaviourEntity, &behaviourAnimData[(byte)behaviour])) { @@ -830,17 +878,19 @@ void Menu::drawBehaviour(HeroBehaviourType behaviour, int32 angle, int16 cantDra behaviourAnimState[(byte)behaviour] = currentAnimState; } - if (cantDrawBox == 0) { - drawBox(boxLeft - 1, boxTop - 1, boxRight + 1, boxBottom + 1); + if (!cantDrawBox) { + Common::Rect boxRectCopy(boxRect); + boxRectCopy.grow(1); + drawBox(boxRectCopy); } _engine->_interface->saveClip(); _engine->_interface->resetClip(); if (behaviour != _engine->_actor->heroBehaviour) { // unselected - _engine->_interface->drawSplittedBox(boxLeft, boxTop, boxRight, boxBottom, 0); + _engine->_interface->drawSplittedBox(boxRect, 0); } else { // selected - _engine->_interface->drawSplittedBox(boxLeft, boxTop, boxRight, boxBottom, 69); + _engine->_interface->drawSplittedBox(boxRect, 69); // behaviour menu title _engine->_interface->drawSplittedBox(titleBoxLeft, titleBoxTop, titleBoxRight, titleBoxBottom, 0); @@ -851,12 +901,12 @@ void Menu::drawBehaviour(HeroBehaviourType behaviour, int32 angle, int16 cantDra char dialText[256]; _engine->_text->getMenuText(_engine->_actor->getTextIdForBehaviour(), dialText, sizeof(dialText)); - _engine->_text->drawText((650 - _engine->_text->getTextSize(dialText)) / 2, titleBoxTop + 1, dialText); + _engine->_text->drawText(SCREEN_WIDTH / 2 - _engine->_text->getTextSize(dialText) / 2, titleBoxTop + 1, dialText); } - _engine->_renderer->renderBehaviourModel(boxLeft, boxTop, boxRight, boxBottom, -600, angle, behaviourEntity); + _engine->_renderer->renderBehaviourModel(boxRect, -600, angle, behaviourEntity); - _engine->copyBlockPhys(boxLeft, boxTop, boxRight, boxBottom); + _engine->copyBlockPhys(boxRect); _engine->copyBlockPhys(titleBoxLeft, titleBoxTop, titleBoxRight, titleBoxBottom); _engine->_interface->loadClip(); @@ -864,7 +914,7 @@ void Menu::drawBehaviour(HeroBehaviourType behaviour, int32 angle, int16 cantDra void Menu::prepareAndDrawBehaviour(int32 angle, HeroBehaviourType behaviour) { _engine->_animations->setAnimAtKeyframe(behaviourAnimState[(byte)behaviour], _engine->_resources->animTable[_engine->_actor->heroAnimIdx[(byte)behaviour]], behaviourEntity, &behaviourAnimData[(byte)behaviour]); - drawBehaviour(behaviour, angle, 0); + drawBehaviour(behaviour, angle, false); } void Menu::drawBehaviourMenu(int32 angle) { @@ -911,10 +961,25 @@ void Menu::processBehaviourMenu() { int32 tmpTime = _engine->lbaTime; - ScopedKeyMap scoped(_engine, uiKeyMapId); +#if 0 + ScopedCursor scopedCursor(_engine); +#endif + ScopedKeyMap scopedKeyMap(_engine, uiKeyMapId); while (_engine->_input->isActionActive(TwinEActionType::BehaviourMenu) || _engine->_input->isQuickBehaviourActionActive()) { _engine->readKeys(); +#if 0 + if (isBehaviourHovered(HeroBehaviourType::kNormal)) { + _engine->_actor->heroBehaviour = HeroBehaviourType::kNormal; + } else if (isBehaviourHovered(HeroBehaviourType::kAthletic)) { + _engine->_actor->heroBehaviour = HeroBehaviourType::kAthletic; + } else if (isBehaviourHovered(HeroBehaviourType::kAggressive)) { + _engine->_actor->heroBehaviour = HeroBehaviourType::kAggressive; + } else if (isBehaviourHovered(HeroBehaviourType::kDiscrete)) { + _engine->_actor->heroBehaviour = HeroBehaviourType::kDiscrete; + } +#endif + int heroBehaviour = (int)_engine->_actor->heroBehaviour; if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft)) { heroBehaviour--; @@ -931,13 +996,13 @@ void Menu::processBehaviourMenu() { _engine->_actor->heroBehaviour = (HeroBehaviourType)heroBehaviour; if (tmpHeroBehaviour != _engine->_actor->heroBehaviour) { - drawBehaviour(tmpHeroBehaviour, _engine->_scene->sceneHero->angle, 1); + drawBehaviour(tmpHeroBehaviour, _engine->_scene->sceneHero->angle, true); tmpHeroBehaviour = _engine->_actor->heroBehaviour; _engine->_movements->setActorAngleSafe(_engine->_scene->sceneHero->angle, _engine->_scene->sceneHero->angle - ANGLE_90, 50, &moveMenu); _engine->_animations->setAnimAtKeyframe(behaviourAnimState[(byte)_engine->_actor->heroBehaviour], _engine->_resources->animTable[_engine->_actor->heroAnimIdx[(byte)_engine->_actor->heroBehaviour]], behaviourEntity, &behaviourAnimData[(byte)_engine->_actor->heroBehaviour]); } - drawBehaviour(_engine->_actor->heroBehaviour, -1, 1); + drawBehaviour(_engine->_actor->heroBehaviour, -1, true); _engine->_system->delayMillis(1000 / 50); _engine->lbaTime++; @@ -1023,9 +1088,10 @@ void Menu::processInventoryMenu() { _engine->_text->setFontCrossColor(4); _engine->_text->initDialogueBox(); - Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper(); - keymapper->getKeymap(uiKeyMapId)->setEnabled(true); - +#if 0 + ScopedCursor scopedCursor(_engine); +#endif + ScopedKeyMap scopedKeyMap(_engine, uiKeyMapId); for (;;) { _engine->readKeys(); int32 prevSelectedItem = inventorySelectedItem; @@ -1114,8 +1180,6 @@ void Menu::processInventoryMenu() { _engine->_system->delayMillis(1); } - keymapper->getKeymap(uiKeyMapId)->setEnabled(false); - _engine->_text->_hasValidTextHandle = false; _engine->_scene->alphaLight = tmpAlphaLight; diff --git a/engines/twine/menu.h b/engines/twine/menu.h index 05239b8d3fb..63258e125b6 100644 --- a/engines/twine/menu.h +++ b/engines/twine/menu.h @@ -165,7 +165,9 @@ private: /** Used to run the save game management menu */ int32 savemanageMenu(); void drawInfoMenu(int16 left, int16 top); - void drawBehaviour(HeroBehaviourType behaviour, int32 angle, int16 cantDrawBox); + Common::Rect calcBehaviourRect(HeroBehaviourType behaviour) const; + bool isBehaviourHovered(HeroBehaviourType behaviour) const; + void drawBehaviour(HeroBehaviourType behaviour, int32 angle, bool cantDrawBox); void drawInventoryItems(); void prepareAndDrawBehaviour(int32 angle, HeroBehaviourType behaviour); void drawBehaviourMenu(int32 angle); @@ -218,7 +220,7 @@ public: * @param bottom end height to draw the button */ void drawBox(int32 left, int32 top, int32 right, int32 bottom); - + void drawBox(const Common::Rect &rect); /** * Where the main menu options are processed * @param menuSettings menu settings array with the information to build the menu options diff --git a/engines/twine/menuoptions.cpp b/engines/twine/menuoptions.cpp index d318bc1e325..ef94dd2673c 100644 --- a/engines/twine/menuoptions.cpp +++ b/engines/twine/menuoptions.cpp @@ -42,9 +42,6 @@ namespace TwinE { -#define ONSCREENKEYBOARD_WIDTH 14 -#define ONSCREENKEYBOARD_HEIGHT 5 - static const char allowedCharIndex[] = " ABCDEFGHIJKLM.NOPQRSTUVWXYZ-abcdefghijklm?nopqrstuvwxyz!0123456789\040\b\r\0"; void MenuOptions::newGame() { @@ -121,19 +118,34 @@ void MenuOptions::showCredits() { _engine->setPalette(_engine->_screens->paletteRGBA); } -void MenuOptions::drawSelectableCharacter(int32 x, int32 y, bool selected) { - char buffer[2]; +void MenuOptions::drawSelectableCharacter(int32 x, int32 y) { + const int32 borderTop = 200; + const int32 borderLeft = 25; + const int32 halfButtonHeight = 25; + const int32 halfButtonWidth = 20; + const int32 buttonDistanceX = halfButtonWidth * 2 + 5; + const int32 buttonDistanceY = halfButtonHeight * 2 + 5; + const int32 centerX = x * buttonDistanceX + borderLeft; + const int32 centerY = y * buttonDistanceY + borderTop; + const int32 left = centerX - halfButtonWidth; + const int32 right = centerX + halfButtonWidth; + const int32 top = centerY - halfButtonHeight; + const int32 bottom = centerY + halfButtonHeight; - buffer[0] = allowedCharIndex[y + x * ONSCREENKEYBOARD_WIDTH]; - buffer[1] = '\0'; + if (_engine->_input->isMouseHovering(left, top, right, bottom)) { + setOnScreenKeyboard(x, y); + } - const int32 centerX = y * 45 + 25; - const int32 left = centerX - 20; - const int32 right = centerX + 20; - const int32 top = x * 56 + 200 - 25; - const int32 centerY = x * 56 + 200; - const int32 bottom = x * 56 + 200 + 25; + const int idx = x + y * ONSCREENKEYBOARD_WIDTH; + if (_onScreenKeyboardDirty[idx] == 0) { + return; + } + --_onScreenKeyboardDirty[idx]; + + const char buffer[] { allowedCharIndex[idx], '\0' }; + + const bool selected = _onScreenKeyboardX == x && _onScreenKeyboardY == y; if (selected) { _engine->_interface->drawSplittedBox(left, top, right, bottom, 91); } else { @@ -144,15 +156,44 @@ void MenuOptions::drawSelectableCharacter(int32 x, int32 y, bool selected) { _engine->_menu->drawBox(left, top, right, bottom); _engine->_text->setFontColor(15); - _engine->_text->drawText(centerX - _engine->_text->getTextSize(buffer) / 2, centerY - 18, buffer); + const uint8 character = (uint8)allowedCharIndex[idx]; + const int32 textX = centerX - _engine->_text->getCharWidth(character) / 2; + const int32 textY = centerY - _engine->_text->getCharHeight(character) / 2; + _engine->_text->drawText(textX, textY, buffer); _engine->copyBlockPhys(left, top, right, bottom); } +void MenuOptions::setOnScreenKeyboard(int x, int y) { + if (x < 0) { + x = ONSCREENKEYBOARD_WIDTH - 1; + } else if (x >= ONSCREENKEYBOARD_WIDTH) { + x = 0; + } + + if (y < 0) { + y = ONSCREENKEYBOARD_HEIGHT - 1; + } else if (y >= ONSCREENKEYBOARD_HEIGHT) { + y = 0; + } + + if (_onScreenKeyboardX == x && _onScreenKeyboardY == y) { + return; + } + + ++_onScreenKeyboardDirty[_onScreenKeyboardX + _onScreenKeyboardY * ONSCREENKEYBOARD_WIDTH]; + ++_onScreenKeyboardDirty[x + y * ONSCREENKEYBOARD_WIDTH]; + + _onScreenKeyboardX = x; + _onScreenKeyboardY = y; + + _onScreenKeyboardLeaveViaOkButton = true; +} + void MenuOptions::drawSelectableCharacters() { - for (int8 x = 0; x < ONSCREENKEYBOARD_HEIGHT; x++) { - for (int8 y = 0; y < ONSCREENKEYBOARD_WIDTH; y++) { - drawSelectableCharacter(x, y, _onScreenKeyboardY == x && _onScreenKeyboardX == y); + for (int8 x = 0; x < ONSCREENKEYBOARD_WIDTH; x++) { + for (int8 y = 0; y < ONSCREENKEYBOARD_HEIGHT; y++) { + drawSelectableCharacter(x, y); } } } @@ -202,6 +243,7 @@ bool MenuOptions::enterPlayerName(int32 textIdx) { _engine->copyBlockPhys(0, 0, SCREEN_WIDTH - 1, 99); _engine->flip(); + Common::fill(&_onScreenKeyboardDirty[0], &_onScreenKeyboardDirty[ARRAYSIZE(_onScreenKeyboardDirty)], 1); ScopedFeatureState scopedVirtualKeyboard(OSystem::kFeatureVirtualKeyboard, true); for (;;) { Common::Event event; @@ -246,30 +288,14 @@ bool MenuOptions::enterPlayerName(int32 textIdx) { return false; } if (_engine->_input->toggleActionIfActive(TwinEActionType::UILeft)) { - --_onScreenKeyboardX; - if (_onScreenKeyboardX < 0) { - _onScreenKeyboardX = ONSCREENKEYBOARD_WIDTH - 1; - } - _onScreenKeyboardLeaveViaOkButton = true; + setOnScreenKeyboard(_onScreenKeyboardX - 1, _onScreenKeyboardY); } else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIRight)) { - ++_onScreenKeyboardX; - if (_onScreenKeyboardX >= ONSCREENKEYBOARD_WIDTH) { - _onScreenKeyboardX = 0; - } - _onScreenKeyboardLeaveViaOkButton = true; + setOnScreenKeyboard(_onScreenKeyboardX + 1, _onScreenKeyboardY); } if (_engine->_input->toggleActionIfActive(TwinEActionType::UIUp)) { - --_onScreenKeyboardY; - if (_onScreenKeyboardY < 0) { - _onScreenKeyboardY = ONSCREENKEYBOARD_HEIGHT - 1; - } - _onScreenKeyboardLeaveViaOkButton = true; + setOnScreenKeyboard(_onScreenKeyboardX, _onScreenKeyboardY - 1); } else if (_engine->_input->toggleActionIfActive(TwinEActionType::UIDown)) { - ++_onScreenKeyboardY; - if (_onScreenKeyboardY >= ONSCREENKEYBOARD_HEIGHT) { - _onScreenKeyboardY = 0; - } - _onScreenKeyboardLeaveViaOkButton = true; + setOnScreenKeyboard(_onScreenKeyboardX, _onScreenKeyboardY + 1); } break; diff --git a/engines/twine/menuoptions.h b/engines/twine/menuoptions.h index 9054eb13098..3b703234751 100644 --- a/engines/twine/menuoptions.h +++ b/engines/twine/menuoptions.h @@ -23,6 +23,9 @@ #ifndef TWINE_MENUOPTIONS_H #define TWINE_MENUOPTIONS_H +#define ONSCREENKEYBOARD_WIDTH 14 +#define ONSCREENKEYBOARD_HEIGHT 5 + #include "common/scummsys.h" #include "twine/actor.h" @@ -32,14 +35,18 @@ class MenuOptions { private: TwinEEngine *_engine; + uint8 _onScreenKeyboardDirty[ONSCREENKEYBOARD_WIDTH * ONSCREENKEYBOARD_HEIGHT] { 0 }; + int _onScreenKeyboardX = 0; int _onScreenKeyboardY = 0; bool _onScreenKeyboardLeaveViaOkButton = false; + void setOnScreenKeyboard(int x, int y); + bool enterPlayerName(int32 textIdx); void drawSelectableCharacters(); void drawPlayerName(int32 centerx, int32 top, int32 type); - void drawSelectableCharacter(int32 x, int32 y, bool selected); + void drawSelectableCharacter(int32 x, int32 y); int chooseSave(int textIdx, bool showEmptySlots = false); public: diff --git a/engines/twine/metaengine.cpp b/engines/twine/metaengine.cpp index d61be15cafe..cfdc4f07ee5 100644 --- a/engines/twine/metaengine.cpp +++ b/engines/twine/metaengine.cpp @@ -128,6 +128,13 @@ static const ExtraGuiOption OptMovies = { true }; +static const ExtraGuiOption OptMouse = { + _s("Enable mouse"), + _s("Enable the mouse for the UI"), + "mouse", + true +}; + static const ExtraGuiOption OptUSAVersion = { _s("Use the USA version"), _s("Enable the USA specific version flags"), @@ -147,6 +154,7 @@ const ExtraGuiOptions TwinEMetaEngine::getExtraGuiOptions(const Common::String & options.push_back(OptUSAVersion); options.push_back(OptVoices); options.push_back(OptText); + options.push_back(OptMouse); return options; } @@ -335,11 +343,13 @@ Common::KeymapArray TwinEMetaEngine::initKeymaps(const char *target) const { act->setCustomEngineActionEvent(TwinEActionType::UIEnter); act->addDefaultInputMapping("RETURN"); act->addDefaultInputMapping("KP_ENTER"); + act->addDefaultInputMapping("MOUSE_LEFT"); uiKeyMap->addAction(act); act = new Action("ABORT", _("Abort")); act->setCustomEngineActionEvent(TwinEActionType::UIAbort); act->addDefaultInputMapping("ESCAPE"); + act->addDefaultInputMapping("MOUSE_RIGHT"); uiKeyMap->addAction(act); act = new Action("UP", _("Up")); @@ -358,12 +368,14 @@ Common::KeymapArray TwinEMetaEngine::initKeymaps(const char *target) const { act->setCustomEngineActionEvent(TwinEActionType::UIRight); act->addDefaultInputMapping("RIGHT"); act->addDefaultInputMapping("KP6"); + act->addDefaultInputMapping("MOUSE_WHEEL_UP"); uiKeyMap->addAction(act); act = new Action("LEFT", _("Left")); act->setCustomEngineActionEvent(TwinEActionType::UILeft); act->addDefaultInputMapping("LEFT"); act->addDefaultInputMapping("KP4"); + act->addDefaultInputMapping("MOUSE_WHEEL_DOWN"); uiKeyMap->addAction(act); act = new Action("NEXTPAGE", _("Next Page")); diff --git a/engines/twine/renderer.cpp b/engines/twine/renderer.cpp index b9520f91b5d..1a4246b8364 100644 --- a/engines/twine/renderer.cpp +++ b/engines/twine/renderer.cpp @@ -1688,6 +1688,10 @@ void Renderer::copyActorInternAnim(const uint8 *bodyPtrSrc, uint8 *bodyPtrDest) } } +void Renderer::renderBehaviourModel(const Common::Rect &rect, int32 y, int32 angle, uint8 *entityPtr) { + renderBehaviourModel(rect.left, rect.top, rect.right, rect.bottom, y, angle, entityPtr); +} + void Renderer::renderBehaviourModel(int32 boxLeft, int32 boxTop, int32 boxRight, int32 boxBottom, int32 y, int32 angle, uint8 *entityPtr) { int32 tmpBoxRight = boxRight; diff --git a/engines/twine/renderer.h b/engines/twine/renderer.h index 61c180293ed..c1faeb3ecae 100644 --- a/engines/twine/renderer.h +++ b/engines/twine/renderer.h @@ -24,6 +24,7 @@ #define TWINE_RENDERER_H #include "common/scummsys.h" +#include "common/rect.h" #define POLYGONTYPE_FLAT 0 #define POLYGONTYPE_COPPER 1 @@ -240,6 +241,7 @@ public: void copyActorInternAnim(const uint8 *bodyPtrSrc, uint8 *bodyPtrDest); void renderBehaviourModel(int32 boxLeft, int32 boxTop, int32 boxRight, int32 boxBottom, int32 y, int32 angle, uint8 *entityPtr); + void renderBehaviourModel(const Common::Rect &rect, int32 y, int32 angle, uint8 *entityPtr); void renderInventoryItem(int32 x, int32 y, uint8 *itemBodyPtr, int32 angle, int32 param); }; diff --git a/engines/twine/text.h b/engines/twine/text.h index f125f4361d3..2e375bcc2ef 100644 --- a/engines/twine/text.h +++ b/engines/twine/text.h @@ -140,8 +140,6 @@ private: * @param counter The amount of characters to handle - max 32 */ void fadeInCharacters(int32 counter, int32 fontColor); - int32 getCharWidth(uint8 chr) const; - int32 getCharHeight(uint8 chr) const; /** * Copy dialogue text * @param src source text buffer @@ -254,6 +252,8 @@ public: * @param dialogue ascii text to display */ int32 getTextSize(const char *dialogue); + int32 getCharWidth(uint8 chr) const; + int32 getCharHeight(uint8 chr) const; void initDialogueBox(); void initInventoryDialogueBox(); diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp index cd6e316a1ac..1c512fdf6ab 100644 --- a/engines/twine/twine.cpp +++ b/engines/twine/twine.cpp @@ -35,6 +35,7 @@ #include "engines/metaengine.h" #include "engines/util.h" #include "graphics/colormasks.h" +#include "graphics/cursorman.h" #include "graphics/fontman.h" #include "graphics/font.h" #include "graphics/managed_surface.h" @@ -81,6 +82,14 @@ ScopedEngineFreeze::~ScopedEngineFreeze() { _engine->unfreezeTime(); } +ScopedCursor::ScopedCursor(const TwinEEngine* engine) { + CursorMan.showMouse(engine->cfgfile.Mouse); +} + +ScopedCursor::~ScopedCursor() { + CursorMan.showMouse(false); +} + TwinEEngine::TwinEEngine(OSystem *system, Common::Language language, uint32 flags, TwineGameType gameType) : Engine(system), _gameType(gameType), _gameLang(language), _gameFlags(flags), _rnd("twine") { // Add default file directories @@ -323,6 +332,7 @@ void TwinEEngine::initConfigurations() { cfgfile.Movie = ConfGetIntOrDefault("movie", CONF_MOVIE_FLA); cfgfile.Fps = ConfGetIntOrDefault("fps", DEFAULT_FRAMES_PER_SECOND); cfgfile.Debug = ConfGetBoolOrDefault("debug", false); + cfgfile.Mouse = ConfGetIntOrDefault("mouse", true); cfgfile.UseAutoSaving = ConfGetBoolOrDefault("useautosaving", false); cfgfile.CrossFade = ConfGetBoolOrDefault("crossfade", false); @@ -941,6 +951,10 @@ void TwinEEngine::flip() { g_system->updateScreen(); } +void TwinEEngine::copyBlockPhys(const Common::Rect &rect) { + copyBlockPhys(rect.left, rect.top, rect.right, rect.bottom); +} + void TwinEEngine::copyBlockPhys(int32 left, int32 top, int32 right, int32 bottom) { assert(left <= right); assert(top <= bottom); diff --git a/engines/twine/twine.h b/engines/twine/twine.h index af98f283998..6ea81aa596d 100644 --- a/engines/twine/twine.h +++ b/engines/twine/twine.h @@ -25,6 +25,7 @@ #include "backends/keymapper/keymap.h" #include "common/random.h" +#include "common/rect.h" #include "engines/engine.h" #include "graphics/managed_surface.h" @@ -111,6 +112,7 @@ struct ConfigFile { bool WallCollision = false; /** Use original autosaving system or save when you want */ bool UseAutoSaving = false; + bool Mouse = false; // these settings can be changed in-game - and must be persisted /** Shadow mode type, value: all, character only, none */ @@ -161,6 +163,11 @@ struct ScopedEngineFreeze { ~ScopedEngineFreeze(); }; +struct ScopedCursor { + ScopedCursor(const TwinEEngine* engine); + ~ScopedCursor(); +}; + class TwinEEngine : public Engine { private: int32 isTimeFreezed = 0; @@ -298,6 +305,7 @@ public: * @param bottom bottom position to start copy */ void copyBlockPhys(int32 left, int32 top, int32 right, int32 bottom); + void copyBlockPhys(const Common::Rect &rect); /** Cross fade feature * @param buffer screen buffer