bool AgiEngine::showPredictiveDialog() { GUI::PredictiveDialog predictiveDialog; inGameTimerPause(); predictiveDialog.runModal(); inGameTimerResume(); Common::String predictiveResult(predictiveDialog.getResult()); uint16 predictiveResultLen = predictiveResult.size(); if (predictiveResult.size()) { // User actually entered something for (int16 resultPos = 0; resultPos < predictiveResultLen; resultPos++) { keyEnqueue(predictiveResult[resultPos]); } if (!cycleInnerLoopIsActive()) { if (_text->promptIsEnabled()) { // add ENTER, when the input is probably meant for the prompt keyEnqueue(AGI_KEY_ENTER); } } else { switch (_game.cycleInnerLoopType) { case CYCLE_INNERLOOP_GETSTRING: case CYCLE_INNERLOOP_GETNUMBER: // add ENTER, when the input is probably meant for GetString/GetNumber keyEnqueue(AGI_KEY_ENTER); break; default: break; } } return true; } return false; }
void AgiEngine::processScummVMEvents() { Common::Event event; int key = 0; while (_eventMan->pollEvent(event)) { switch (event.type) { case Common::EVENT_PREDICTIVE_DIALOG: showPredictiveDialog(); break; case Common::EVENT_LBUTTONDOWN: if (_game.mouseEnabled) { key = AGI_MOUSE_BUTTON_LEFT; _mouse.button = kAgiMouseButtonLeft; keyEnqueue(key); _mouse.pos.x = event.mouse.x; _mouse.pos.y = event.mouse.y; } break; case Common::EVENT_RBUTTONDOWN: if (_game.mouseEnabled) { key = AGI_MOUSE_BUTTON_RIGHT; _mouse.button = kAgiMouseButtonRight; keyEnqueue(key); _mouse.pos.x = event.mouse.x; _mouse.pos.y = event.mouse.y; } break; case Common::EVENT_WHEELUP: if (_game.mouseEnabled) { key = AGI_MOUSE_WHEEL_UP; keyEnqueue(key); } break; case Common::EVENT_WHEELDOWN: if (_game.mouseEnabled) { key = AGI_MOUSE_WHEEL_DOWN; keyEnqueue(key); } break; case Common::EVENT_MOUSEMOVE: if (_game.mouseEnabled) { _mouse.pos.x = event.mouse.x; _mouse.pos.y = event.mouse.y; if (!_game.mouseFence.isEmpty()) { if (_mouse.pos.x < _game.mouseFence.left) _mouse.pos.x = _game.mouseFence.left; if (_mouse.pos.x > _game.mouseFence.right) _mouse.pos.x = _game.mouseFence.right; if (_mouse.pos.y < _game.mouseFence.top) _mouse.pos.y = _game.mouseFence.top; if (_mouse.pos.y > _game.mouseFence.bottom) _mouse.pos.y = _game.mouseFence.bottom; g_system->warpMouse(_mouse.pos.x, _mouse.pos.y); } } break; case Common::EVENT_LBUTTONUP: case Common::EVENT_RBUTTONUP: if (_game.mouseEnabled) { _mouse.button = kAgiMouseButtonUp; _mouse.pos.x = event.mouse.x; _mouse.pos.y = event.mouse.y; } break; case Common::EVENT_KEYDOWN: if (event.kbd.hasFlags(Common::KBD_CTRL | Common::KBD_SHIFT) && event.kbd.keycode == Common::KEYCODE_d) { _console->attach(); break; } key = event.kbd.ascii; if (event.kbd.keycode >= Common::KEYCODE_KP0 && event.kbd.keycode <= Common::KEYCODE_KP9) { if (!(event.kbd.flags & Common::KBD_NUM)) { // HACK: Num-Lock not enabled // We shouldn't get a valid ascii code in these cases. We fix it here, so that cursor keys // on the numpad work properly. key = 0; } } if ((key) && (key <= 0xFF)) { // No special key, directly accept it // Is ISO-8859-1, we need lower 128 characters only, which is plain ASCII, so no mapping required if (Common::isAlpha(key)) { // Key is A-Z. // Map Ctrl-A to 1, Ctrl-B to 2, etc. if (event.kbd.flags & Common::KBD_CTRL) { key = toupper(key) - 'A' + 1; } else if (event.kbd.flags & Common::KBD_ALT) { // Map Alt-A, Alt-B etc. to special scancode values according to an internal scancode table. key = scancodeTable[toupper(key) - 'A'] << 8; } } } else { key = 0; switch (event.kbd.keycode) { case Common::KEYCODE_LEFT: case Common::KEYCODE_KP4: if (_allowSynthetic || !event.synthetic) key = AGI_KEY_LEFT; break; case Common::KEYCODE_RIGHT: case Common::KEYCODE_KP6: if (_allowSynthetic || !event.synthetic) key = AGI_KEY_RIGHT; break; case Common::KEYCODE_UP: case Common::KEYCODE_KP8: if (_allowSynthetic || !event.synthetic) key = AGI_KEY_UP; break; case Common::KEYCODE_DOWN: case Common::KEYCODE_KP2: if (_allowSynthetic || !event.synthetic) key = AGI_KEY_DOWN; break; case Common::KEYCODE_PAGEUP: case Common::KEYCODE_KP9: if (_allowSynthetic || !event.synthetic) key = AGI_KEY_UP_RIGHT; break; case Common::KEYCODE_PAGEDOWN: case Common::KEYCODE_KP3: if (_allowSynthetic || !event.synthetic) key = AGI_KEY_DOWN_RIGHT; break; case Common::KEYCODE_HOME: case Common::KEYCODE_KP7: if (_allowSynthetic || !event.synthetic) key = AGI_KEY_UP_LEFT; break; case Common::KEYCODE_END: case Common::KEYCODE_KP1: if (_allowSynthetic || !event.synthetic) key = AGI_KEY_DOWN_LEFT; break; case Common::KEYCODE_KP5: key = AGI_KEY_STATIONARY; break; case Common::KEYCODE_F1: key = AGI_KEY_F1; break; case Common::KEYCODE_F2: key = AGI_KEY_F2; break; case Common::KEYCODE_F3: key = AGI_KEY_F3; break; case Common::KEYCODE_F4: key = AGI_KEY_F4; break; case Common::KEYCODE_F5: key = AGI_KEY_F5; break; case Common::KEYCODE_F6: key = AGI_KEY_F6; break; case Common::KEYCODE_F7: key = AGI_KEY_F7; break; case Common::KEYCODE_F8: key = AGI_KEY_F8; break; case Common::KEYCODE_F9: key = AGI_KEY_F9; break; case Common::KEYCODE_F10: key = AGI_KEY_F10; break; case Common::KEYCODE_F11: key = AGI_KEY_F11; break; case Common::KEYCODE_F12: key = AGI_KEY_F12; break; case Common::KEYCODE_KP_ENTER: key = AGI_KEY_ENTER; break; default: break; } } if (key) keyEnqueue(key); break; case Common::EVENT_KEYUP: if (_keyHoldMode) { // Original AGI actually created direction events in here // We don't do that, that's why we create a stationary event instead, which will // result in a direction change to 0 in handleController(). switch (event.kbd.keycode) { case Common::KEYCODE_LEFT: case Common::KEYCODE_RIGHT: case Common::KEYCODE_UP: case Common::KEYCODE_DOWN: case Common::KEYCODE_HOME: case Common::KEYCODE_END: case Common::KEYCODE_PAGEUP: case Common::KEYCODE_PAGEDOWN: case Common::KEYCODE_KP4: case Common::KEYCODE_KP6: case Common::KEYCODE_KP8: case Common::KEYCODE_KP2: case Common::KEYCODE_KP9: case Common::KEYCODE_KP3: case Common::KEYCODE_KP7: case Common::KEYCODE_KP1: keyEnqueue(AGI_KEY_STATIONARY); break; default: break; } } break; default: break; } } }
int AgiEngine::handleController(int key) { VtEntry *v = &_game.viewTable[0]; int i; // AGI 3.149 games, The Black Cauldron and King's Quest 4 need KEY_ESCAPE to use menus // Games with the GF_ESCPAUSE flag need KEY_ESCAPE to pause the game if (key == 0 || (key == KEY_ESCAPE && getVersion() != 0x3149 && getGameID() != GID_BC && getGameID() != GID_KQ4 && !(getFeatures() & GF_ESCPAUSE)) ) return false; if ((getGameID() == GID_MH1 || getGameID() == GID_MH2) && (key == KEY_ENTER) && (_game.inputMode == INPUT_NONE)) { key = 0x20; // Set Enter key to Space in Manhunter when there's no text input } debugC(3, kDebugLevelInput, "key = %04x", key); for (i = 0; i < MAX_CONTROLLERS; i++) { if (_game.controllers[i].keycode == key) { debugC(3, kDebugLevelInput, "event %d: key press", _game.controllers[i].controller); _game.controllerOccured[_game.controllers[i].controller] = true; return true; } } if (key == BUTTON_LEFT) { if ((getflag(fMenusWork) || (getFeatures() & GF_MENUS)) && _mouse.y <= CHAR_LINES) { newInputMode(INPUT_MENU); return true; } } // Show predictive dialog if the user clicks on input area if (key == BUTTON_LEFT && (int)_mouse.y >= _game.lineUserInput * CHAR_LINES && (int)_mouse.y <= (_game.lineUserInput + 1) * CHAR_LINES) { GUI::PredictiveDialog _predictiveDialog; _predictiveDialog.runModal(); strcpy(_predictiveResult, _predictiveDialog.getResult()); if (strcmp(_predictiveResult, "")) { if (_game.inputMode == INPUT_NONE) { for (int n = 0; _predictiveResult[n]; n++) keyEnqueue(_predictiveResult[n]); } else { strcpy((char *)_game.inputBuffer, _predictiveResult); handleKeys(KEY_ENTER); } } /* if (predictiveDialog()) { if (_game.inputMode == INPUT_NONE) { for (int n = 0; _predictiveResult[n]; n++) keyEnqueue(_predictiveResult[n]); } else { strcpy((char *)_game.inputBuffer, _predictiveResult); handleKeys(KEY_ENTER); } } */ return true; } if (_game.playerControl) { int d = 0; if (!KEY_ASCII(key)) { switch (key) { case KEY_UP: d = 1; break; case KEY_DOWN: d = 5; break; case KEY_LEFT: d = 7; break; case KEY_RIGHT: d = 3; break; case KEY_UP_RIGHT: d = 2; break; case KEY_DOWN_RIGHT: d = 4; break; case KEY_UP_LEFT: d = 8; break; case KEY_DOWN_LEFT: d = 6; break; } } if (!(getFeatures() & GF_AGIMOUSE)) { // Handle mouse button events if (key == BUTTON_LEFT) { if (getGameID() == GID_PQ1 && _game.vars[vCurRoom] == 116) { // WORKAROUND: Special handling for mouse clicks in the newspaper // screen of PQ1. Fixes bug #3018770. d = 3; // fake a right arrow key (next page) } else { // Click-to-walk mouse interface v->flags |= fAdjEgoXY; v->parm1 = WIN_TO_PIC_X(_mouse.x); v->parm2 = WIN_TO_PIC_Y(_mouse.y); return true; } } } if (d || key == KEY_STATIONARY) { v->flags &= ~fAdjEgoXY; v->direction = v->direction == d ? 0 : d; return true; } } return false; }
int AgiEngine::selectSlot() { int i, key, active = 0; int rc = -1; int hm = 1, vm = 3; // box margins int xmin, xmax, slotClicked; char desc[NUM_VISIBLE_SLOTS][40]; int textCenter, buttonLength, buttonX[2], buttonY; const char *buttonText[] = { " OK ", "Cancel", NULL }; _noSaveLoadAllowed = true; for (i = 0; i < NUM_VISIBLE_SLOTS; i++) { getSavegameDescription(_firstSlot + i, desc[i]); } textCenter = GFX_WIDTH / CHAR_LINES / 2; buttonLength = 6; buttonX[0] = (textCenter - 3 * buttonLength / 2) * CHAR_COLS; buttonX[1] = (textCenter + buttonLength / 2) * CHAR_COLS; buttonY = (vm + 17) * CHAR_LINES; for (i = 0; i < 2; i++) _gfx->drawCurrentStyleButton(buttonX[i], buttonY, buttonText[i], false, false, i == 0); AllowSyntheticEvents on(this); int oldFirstSlot = _firstSlot + 1; int oldActive = active + 1; while (!(shouldQuit() || _restartGame)) { int sbPos = 0; // Use the extreme scrollbar positions only if the extreme // slots are in sight. (We have to calculate this even if we // don't redraw the save slots, because it's also used for // clicking in the scrollbar. if (_firstSlot == 0) sbPos = 1; else if (_firstSlot == NUM_SLOTS - NUM_VISIBLE_SLOTS) sbPos = NUM_VISIBLE_SLOTS - 2; else { sbPos = 2 + (_firstSlot * (NUM_VISIBLE_SLOTS - 4)) / (NUM_SLOTS - NUM_VISIBLE_SLOTS - 1); if (sbPos >= NUM_VISIBLE_SLOTS - 3) sbPos = NUM_VISIBLE_SLOTS - 3; } if (oldFirstSlot != _firstSlot || oldActive != active) { char dstr[64]; for (i = 0; i < NUM_VISIBLE_SLOTS; i++) { sprintf(dstr, "[%2d. %-28.28s]", i + _firstSlot, desc[i]); printText(dstr, 0, hm + 1, vm + 4 + i, (40 - 2 * hm) - 1, i == active ? MSG_BOX_COLOR : MSG_BOX_TEXT, i == active ? MSG_BOX_TEXT : MSG_BOX_COLOR); } char upArrow[] = "^"; char downArrow[] = "v"; char scrollBar[] = " "; for (i = 1; i < NUM_VISIBLE_SLOTS - 1; i++) printText(scrollBar, 35, hm + 1, vm + 4 + i, 1, MSG_BOX_COLOR, 7, true); printText(upArrow, 35, hm + 1, vm + 4, 1, 8, 7); printText(downArrow, 35, hm + 1, vm + 4 + NUM_VISIBLE_SLOTS - 1, 1, 8, 7); printText(scrollBar, 35, hm + 1, vm + 4 + sbPos, 1, MSG_BOX_COLOR, MSG_BOX_TEXT); oldActive = active; oldFirstSlot = _firstSlot; } pollTimer(); key = doPollKeyboard(); // It may happen that somebody will open GMM while // this dialog is open, and load a game // We are processing it here, effectively jumping // out of the dead loop if (getflag(fRestoreJustRan)) { rc = -2; goto getout; } switch (key) { case KEY_ENTER: rc = active; strncpy(_game.strings[MAX_STRINGS], desc[i], MAX_STRINGLEN); goto press; case KEY_ESCAPE: rc = -1; goto getout; case BUTTON_LEFT: if (_gfx->testButton(buttonX[0], buttonY, buttonText[0])) { rc = active; strncpy(_game.strings[MAX_STRINGS], desc[i], MAX_STRINGLEN); goto press; } if (_gfx->testButton(buttonX[1], buttonY, buttonText[1])) { rc = -1; goto getout; } slotClicked = ((int)_mouse.y - 1) / CHAR_COLS - (vm + 4); xmin = (hm + 1) * CHAR_COLS; xmax = xmin + CHAR_COLS * 34; if ((int)_mouse.x >= xmin && (int)_mouse.x <= xmax) { if (slotClicked >= 0 && slotClicked < NUM_VISIBLE_SLOTS) active = slotClicked; } xmin = (hm + 36) * CHAR_COLS; xmax = xmin + CHAR_COLS; if ((int)_mouse.x >= xmin && (int)_mouse.x <= xmax) { if (slotClicked >= 0 && slotClicked < NUM_VISIBLE_SLOTS) { if (slotClicked == 0) keyEnqueue(KEY_UP); else if (slotClicked == NUM_VISIBLE_SLOTS - 1) keyEnqueue(KEY_DOWN); else if (slotClicked < sbPos) keyEnqueue(KEY_UP_RIGHT); else if (slotClicked > sbPos) keyEnqueue(KEY_DOWN_RIGHT); } } break; case KEY_DOWN: active++; if (active >= NUM_VISIBLE_SLOTS) { if (_firstSlot + NUM_VISIBLE_SLOTS < NUM_SLOTS) { _firstSlot++; for (i = 1; i < NUM_VISIBLE_SLOTS; i++) memcpy(desc[i - 1], desc[i], sizeof(desc[0])); getSavegameDescription(_firstSlot + NUM_VISIBLE_SLOTS - 1, desc[NUM_VISIBLE_SLOTS - 1]); } active = NUM_VISIBLE_SLOTS - 1; } break; case KEY_UP: active--; if (active < 0) { active = 0; if (_firstSlot > 0) { _firstSlot--; for (i = NUM_VISIBLE_SLOTS - 1; i > 0; i--) memcpy(desc[i], desc[i - 1], sizeof(desc[0])); getSavegameDescription(_firstSlot, desc[0]); } } break; // Page Up/Down and mouse wheel scrolling all leave 'active' // unchanged so that a visible slot will remain selected. case WHEEL_DOWN: if (_firstSlot < NUM_SLOTS - NUM_VISIBLE_SLOTS) { _firstSlot++; for (i = 1; i < NUM_VISIBLE_SLOTS; i++) memcpy(desc[i - 1], desc[i], sizeof(desc[0])); getSavegameDescription(_firstSlot + NUM_VISIBLE_SLOTS - 1, desc[NUM_VISIBLE_SLOTS - 1]); } break; case WHEEL_UP: if (_firstSlot > 0) { _firstSlot--; for (i = NUM_VISIBLE_SLOTS - 1; i > 0; i--) memcpy(desc[i], desc[i - 1], sizeof(desc[0])); getSavegameDescription(_firstSlot, desc[0]); } break; case KEY_DOWN_RIGHT: // This is probably triggered by Page Down. _firstSlot += NUM_VISIBLE_SLOTS; if (_firstSlot > NUM_SLOTS - NUM_VISIBLE_SLOTS) { _firstSlot = NUM_SLOTS - NUM_VISIBLE_SLOTS; } for (i = 0; i < NUM_VISIBLE_SLOTS; i++) getSavegameDescription(_firstSlot + i, desc[i]); break; case KEY_UP_RIGHT: // This is probably triggered by Page Up. _firstSlot -= NUM_VISIBLE_SLOTS; if (_firstSlot < 0) { _firstSlot = 0; } for (i = 0; i < NUM_VISIBLE_SLOTS; i++) getSavegameDescription(_firstSlot + i, desc[i]); break; } _gfx->doUpdate(); } press: debugC(8, kDebugLevelMain | kDebugLevelInput, "Button pressed: %d", rc); getout: closeWindow(); _noSaveLoadAllowed = false; return rc; }