TWINE: implement ui control via mouse

Also optimized the on-screen keyboard rendering a lot, renamed
parameters and started to use Common::Rect
This commit is contained in:
Martin Gerhardy 2020-11-24 18:17:12 +01:00
parent 0c3fb36127
commit e8fcff6e6d
14 changed files with 228 additions and 73 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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"));

View File

@ -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;

View File

@ -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);
};

View File

@ -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();

View File

@ -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);

View File

@ -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