/** Handles the conversion from some input to a GameAction and its distribution * to the currently active menu. * It also handles whether the game is currently sensing input. It does so by * suppressing the distribution of the input as a GameAction. Instead the * input is stored in 'm_sensed_input' and GA_SENSE_COMPLETE is distributed. If * however the input in question has resolved to GA_LEAVE this is treated as * an attempt of the user to cancel the sensing. In that case GA_SENSE_CANCEL * is distributed. * * Note: It is the obligation of the called menu to switch of the sense mode. * */ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button, Input::AxisDirection axisDirection, int value) { // Act different in input sensing mode. if (m_mode == INPUT_SENSE_KEYBOARD || m_mode == INPUT_SENSE_GAMEPAD) { inputSensing(type, deviceID, button, axisDirection, value); return; } // Abort demo mode if a key is pressed during the race in demo mode if(dynamic_cast<DemoWorld*>(World::getWorld())) { race_manager->exitRace(); StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance()); return; } StateManager::ActivePlayer* player = NULL; PlayerAction action; bool action_found = m_device_manager->translateInput(type, deviceID, button, axisDirection, value, m_mode, &player, &action); // in menus, some keyboard keys are standard (before each player selected // his device). So if a key could not be mapped to any known binding, // fall back to check the defaults. if (!action_found && StateManager::get()->getGameState() != GUIEngine::GAME && type == Input::IT_KEYBOARD && m_mode == MENU && m_device_manager->getAssignMode() == NO_ASSIGN) { action = PA_BEFORE_FIRST; if (button == KEY_UP) action = PA_MENU_UP; else if (button == KEY_DOWN) action = PA_MENU_DOWN; else if (button == KEY_LEFT) action = PA_MENU_LEFT; else if (button == KEY_RIGHT) action = PA_MENU_RIGHT; else if (button == KEY_SPACE) action = PA_MENU_SELECT; else if (button == KEY_RETURN) action = PA_MENU_SELECT; if (button == KEY_RETURN && GUIEngine::ModalDialog::isADialogActive()) { GUIEngine::ModalDialog::onEnterPressed(); } if (action != PA_BEFORE_FIRST) { action_found = true; player = NULL; } } // do something with the key if it matches a binding if (action_found) { // If we're in the kart menu awaiting new players, do special things // when a device presses fire or rescue if (m_device_manager->getAssignMode() == DETECT_NEW) { // Player is unjoining if ((player != NULL) && (action == PA_RESCUE || action == PA_MENU_CANCEL ) ) { // returns true if the event was handled if (KartSelectionScreen::getInstance()->playerQuit( player )) { return; // we're done here } } /* The way this is currently structured, any time an event is received from an input device that is not associated with a player and the device manager is in DETECT_NEW mode, the event is ignored, unless it is a PA_FIRE event (a player is joining) perhaps it will be good to let unassigned devices back out of the kart selection menu? */ else if (player == NULL) { // New player is joining if (action == PA_FIRE || action == PA_MENU_SELECT) { InputDevice *device = NULL; if (type == Input::IT_KEYBOARD) { //std::cout << "==== New Player Joining with Key " << // button << " ====" << std::endl; device = m_device_manager->getKeyboardFromBtnID(button); } else if (type == Input::IT_STICKBUTTON || type == Input::IT_STICKMOTION ) { device = m_device_manager->getGamePadFromIrrID(deviceID); } if (device != NULL) { KartSelectionScreen::getInstance()->playerJoin(device, false ); } } return; // we're done here, ignore devices that aren't // associated with players } } // ... when in-game if (StateManager::get()->getGameState() == GUIEngine::GAME && !GUIEngine::ModalDialog::isADialogActive() ) { if (player == NULL) { // Prevent null pointer crash return; } // Find the corresponding PlayerKart from our ActivePlayer instance AbstractKart* pk = player->getKart(); if (pk == NULL) { std::cerr << "Error, trying to process action for an unknown player\n"; return; } Controller* controller = pk->getController(); if (controller != NULL) controller->action(action, abs(value)); } // ... when in menus else { // reset timer when released if (abs(value) == 0 && type == Input::IT_STICKBUTTON) { m_timer_in_use = false; m_timer = 0; } // When in master-only mode, we can safely assume that players // are set up, contrarly to early menus where we accept every // input because players are not set-up yet if (m_master_player_only && player == NULL) { if (type == Input::IT_STICKMOTION || type == Input::IT_STICKBUTTON) { GamePadDevice* gp = getDeviceList()->getGamePadFromIrrID(deviceID); if (gp != NULL && abs(value)>gp->m_deadzone) { //I18N: message shown when an input device is used but // is not associated to any player GUIEngine::showMessage( _("Ignoring '%s', you needed to join earlier to play!", irr::core::stringw(gp->m_name.c_str()).c_str()) ); } } return; } // menu input if (!m_timer_in_use) { if (abs(value) > Input::MAX_VALUE*2/3) { m_timer_in_use = true; m_timer = 0.25; } // player may be NULL in early menus, before player setup has // been performed int playerID = (player == NULL ? 0 : player->getID()); // If only the master player can act, and this player is not // the master, ignore his input if (m_device_manager->getAssignMode() == ASSIGN && m_master_player_only && playerID != PLAYER_ID_GAME_MASTER) { //I18N: message shown when a player that isn't game master //I18N: tries to modify options that only the game master //I18N: is allowed to GUIEngine::showMessage( _("Only the Game Master may act at this point!")); return; } // all is good, pass the translated input event on to the // event handler GUIEngine::EventHandler::get() ->processGUIAction(action, deviceID, abs(value), type, playerID); } } } else if (type == Input::IT_KEYBOARD) { // keyboard press not handled by device manager / bindings. // Check static bindings... handleStaticAction( button, value ); } } // input