void OptionsScreenInput::filterInput(Input::InputType type, int deviceID, int btnID, int axisDir, int axisRange, int value) { if (type == Input::IT_STICKMOTION || type == Input::IT_STICKBUTTON) { GamePadDevice* gamepad = input_manager->getDeviceList()->getGamePadFromIrrID(deviceID); if (gamepad != NULL && gamepad->getConfiguration() != NULL) { //printf("'%s'\n", gamepad->getConfiguration()->getName().c_str()); ListWidget* devices = this->getWidget<ListWidget>("devices"); assert(devices != NULL); std::string internal_name; const int gpad_config_count = input_manager->getDeviceList()->getGamePadConfigAmount(); for (int i = 0; i < gpad_config_count; i++) { GamepadConfig *config = input_manager->getDeviceList()->getGamepadConfig(i); // Don't display the configuration if a matching device is not available if (config == gamepad->getConfiguration()) { std::ostringstream gpname; gpname << "gamepad" << i; internal_name = gpname.str(); } } if (internal_name.size() > 0 && abs(value) > Input::MAX_VALUE/2) { devices->markItemRed(internal_name.c_str()); m_highlights[internal_name] = 0.25f; } } } }
InputDevice *DeviceManager::mapGamepadInput(Input::InputType type, int device_id, int button_id, int axis_dir, int *value /* inout */, InputManager::InputDriverMode mode, StateManager::ActivePlayer **player /* out */, PlayerAction *action /* out */) { GamePadDevice *gPad = getGamePadFromIrrID(device_id); if (gPad != NULL) { if (gPad->processAndMapInput(type, button_id, mode, action, value)) { if (m_single_player != NULL) { *player = m_single_player; // in single-player mode, assign the gamepad as needed if (gPad->getPlayer() != m_single_player) gPad->setPlayer(m_single_player); } else if (m_assign_mode == NO_ASSIGN) // Don't set the player in NO_ASSIGN mode { *player = NULL; } else { *player = gPad->getPlayer(); } } else gPad = NULL; // If no bind was found, return NULL } return gPad; } // mapGamepadInput
/** * Called on keyboard events [indirectly] by irrLicht * * Analog axes can have any value from [-32768, 32767]. * * There are no negative values. Instead this is reported as an axis with a * negative direction. This simplifies input configuration and allows greater * flexibility (= treat 4 directions as four buttons). * * Returns whether to halt the event's propagation here */ EventPropagation InputManager::input(const SEvent& event) { if (event.EventType == EET_JOYSTICK_INPUT_EVENT) { // Axes - FIXME, instead of checking all of them, ask the bindings // which ones to poll for (int axis_id=0; axis_id<SEvent::SJoystickEvent::NUMBER_OF_AXES ; axis_id++) { int value = event.JoystickEvent.Axis[axis_id]; if (UserConfigParams::m_gamepad_debug) { printf("axis motion: gamepad_id=%d axis=%d value=%d\n", event.JoystickEvent.Joystick, axis_id, value); } dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, axis_id, Input::AD_NEUTRAL, value); } if (event.JoystickEvent.POV == 65535) { dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_H_ID, Input::AD_NEUTRAL, 0); dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_V_ID, Input::AD_NEUTRAL, 0); } else { // *0.017453925f is to convert degrees to radians dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_H_ID, Input::AD_NEUTRAL, (int)(cos(event.JoystickEvent.POV*0.017453925f/100.0f) *Input::MAX_VALUE)); dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_V_ID, Input::AD_NEUTRAL, (int)(sin(event.JoystickEvent.POV*0.017453925f/100.0f) *Input::MAX_VALUE)); } GamePadDevice* gp = getDeviceList()->getGamePadFromIrrID(event.JoystickEvent.Joystick); if (gp == NULL) { // Prevent null pointer crash return EVENT_BLOCK; } for(int i=0; i<gp->m_button_count; i++) { const bool isButtonPressed = event.JoystickEvent.IsButtonPressed(i); // Only report button events when the state of the button changes if ((!gp->isButtonPressed(i) && isButtonPressed) || (gp->isButtonPressed(i) && !isButtonPressed) ) { if (UserConfigParams::m_gamepad_debug) { printf("button %i, status=%i\n", i, isButtonPressed); } dispatchInput(Input::IT_STICKBUTTON, event.JoystickEvent.Joystick, i, Input::AD_POSITIVE, isButtonPressed ? Input::MAX_VALUE : 0); } gp->setButtonPressed(i, isButtonPressed); } } else if (event.EventType == EET_KEY_INPUT_EVENT) { // On some systems (linux esp.) certain keys (e.g. [] ) have a 0 // Key value, but do have a value defined in the Char field. // So to distinguish them (otherwise [] would both be mapped to // the same value 0, which means we can't distinguish which key // was actually pressed anymore). We set bit 10 which should // allow us to distinguish those artifical keys from the // 'real' keys. const int key = event.KeyInput.Key ? event.KeyInput.Key : event.KeyInput.Char+1024; if (event.KeyInput.PressedDown) { // escape is a little special if (key == KEY_ESCAPE) { StateManager::get()->escapePressed(); return EVENT_BLOCK; } // 'backspace' in a text control must never be mapped, since user // can be in a text area trying to erase text (and if it's mapped // to rescue that would dismiss the dialog instead of erasing a // single letter). Same for spacebar. Same for letters. if (GUIEngine::isWithinATextBox()) { if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT) { return EVENT_LET; } if (key >= KEY_KEY_0 && key <= KEY_KEY_Z) { return EVENT_LET; } } const bool wasInTextBox = GUIEngine::isWithinATextBox(); dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key, Input::AD_POSITIVE, Input::MAX_VALUE); // if this action took us into a text box, don't let event continue // (FIXME not the cleanest solution) if (!wasInTextBox && GUIEngine::isWithinATextBox()) { return EVENT_BLOCK; } } else { // 'backspace' in a text control must never be mapped, since user // can be in a text area trying to erase text (and if it's mapped // to rescue that would dismiss the dialog instead of erasing a // single letter). Same for spacebar. Same for letters. if (GUIEngine::isWithinATextBox()) { if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT) { return EVENT_LET; } if (key >= KEY_KEY_0 && key <= KEY_KEY_Z) { return EVENT_LET; } } dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key, Input::AD_POSITIVE, 0); return EVENT_BLOCK; // Don't propagate key up events } } #if 0 // in case we ever use mouse in-game... else if(event.EventType == EET_MOUSE_INPUT_EVENT) { const int type = event.MouseInput.Event; if(type == EMIE_MOUSE_MOVED) { // m_mouse_x = event.MouseInput.X; // m_mouse_y = event.MouseInput.Y; //const int wheel = event.MouseInput.Wheel; } /* EMIE_LMOUSE_PRESSED_DOWN Left mouse button was pressed down. EMIE_RMOUSE_PRESSED_DOWN Right mouse button was pressed down. EMIE_MMOUSE_PRESSED_DOWN Middle mouse button was pressed down. EMIE_LMOUSE_LEFT_UP Left mouse button was left up. EMIE_RMOUSE_LEFT_UP Right mouse button was left up. EMIE_MMOUSE_LEFT_UP Middle mouse button was left up. EMIE_MOUSE_MOVED The mouse cursor changed its position. EMIE_MOUSE_WHEEL The mouse wheel was moved. Use Wheel value in event data to find out in what direction and how fast. */ } #endif // block events in all modes but initial menus (except in text boxes to // allow typing, and except in modal dialogs in-game) // FIXME: 1) that's awful logic 2) that's not what the code below does, // events are never blocked in menus if (getDeviceList()->getAssignMode() != NO_ASSIGN && !GUIEngine::isWithinATextBox() && (!GUIEngine::ModalDialog::isADialogActive() && StateManager::get()->getGameState() == GUIEngine::GAME)) { return EVENT_BLOCK; } else { return EVENT_LET; } }
void OptionsScreenInput2::gotSensedInput(const Input& sensed_input) { const bool keyboard = (m_config->isKeyboard() && sensed_input.m_type == Input::IT_KEYBOARD); const bool gamepad = (sensed_input.m_type == Input::IT_STICKMOTION || sensed_input.m_type == Input::IT_STICKBUTTON) && m_config->isGamePad(); if (keyboard) { if (UserConfigParams::logMisc()) { Log::info("OptionsScreenInput2", "Binding %s: setting to keyboard key %d", KartActionStrings[binding_to_set].c_str(), sensed_input.m_button_id); } KeyboardConfig* keyboard = (KeyboardConfig*)m_config; keyboard->setBinding(binding_to_set, Input::IT_KEYBOARD, sensed_input.m_button_id, Input::AD_NEUTRAL, Input::AR_HALF, sensed_input.m_character); // refresh display updateInputButtons(); } else if (gamepad) { if (UserConfigParams::logMisc()) { Log::info("OptionsScreenInput2", "Binding %s: setting to gamepad #%d", KartActionStrings[binding_to_set].c_str(), sensed_input.m_device_id); if (sensed_input.m_type == Input::IT_STICKMOTION) { Log::info("OptionsScreenInput2", "Axis %d; direction %s", sensed_input.m_button_id, sensed_input.m_axis_direction == Input::AD_NEGATIVE ? "-" : "+"); } else if (sensed_input.m_type == Input::IT_STICKBUTTON) { Log::info("OptionsScreenInput2", "Button %d", sensed_input.m_button_id); } else { Log::info("OptionsScreenInput2", "Sensed unknown gamepad event type??"); } } GamePadDevice *gpad = input_manager->getDeviceManager() ->getGamePadFromIrrID(sensed_input.m_device_id); std::string gamepad_name = gpad ? gpad->getName() : "UNKNOWN DEVICE"; if (m_config->getName() == gamepad_name) { GamepadConfig* config = (GamepadConfig*)m_config; config->setBinding(binding_to_set, sensed_input.m_type, sensed_input.m_button_id, (Input::AxisDirection)sensed_input.m_axis_direction, (Input::AxisRange)sensed_input.m_axis_range); // refresh display updateInputButtons(); } } else if (sensed_input.m_type == Input::IT_NONE) { if (UserConfigParams::logMisc()) { Log::info("OptionsScreenInput2", "Binding %s: setting to keyboard key NONE", KartActionStrings[binding_to_set].c_str()); } KeyboardConfig* keyboard = (KeyboardConfig*)m_config; keyboard->setBinding(binding_to_set, Input::IT_NONE, sensed_input.m_button_id, Input::AD_NEUTRAL, Input::AR_HALF, sensed_input.m_character); // refresh display updateInputButtons(); } else { return; } ModalDialog::dismiss(); input_manager->setMode(InputManager::MENU); if (keyboard && (sensed_input.m_button_id == irr::KEY_SHIFT || sensed_input.m_button_id == irr::KEY_LSHIFT || sensed_input.m_button_id == irr::KEY_RSHIFT)) { new MessageDialog(_("Warning, 'Shift' is not a recommended key : when " "shift is pressed down, all keys that contain a " "character that is different in upper-case will " "stop working.")); } // re-select the previous button (TODO!) //ButtonWidget* btn = // getWidget<ButtonWidget>(binding_to_set_button.c_str()); //if(btn != NULL) btn->setFocusForPlayer(PLAYER_ID_GAME_MASTER); // save new binding to file input_manager->getDeviceManager()->save(); } // gotSensedInput
/** * Called on keyboard events [indirectly] by irrLicht * * Analog axes can have any value from [-32768, 32767]. * * There are no negative values. Instead this is reported as an axis with a * negative direction. This simplifies input configuration and allows greater * flexibility (= treat 4 directions as four buttons). * * Returns whether to halt the event's propagation here */ EventPropagation InputManager::input(const SEvent& event) { if (event.EventType == EET_JOYSTICK_INPUT_EVENT) { // Axes - FIXME, instead of checking all of them, ask the bindings // which ones to poll for (int axis_id=0; axis_id<SEvent::SJoystickEvent::NUMBER_OF_AXES ; axis_id++) { int value = event.JoystickEvent.Axis[axis_id]; if (UserConfigParams::m_gamepad_debug) { Log::info("InputManager", "axis motion: gamepad_id=%d axis=%d value=%d", event.JoystickEvent.Joystick, axis_id, value); } dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, axis_id, Input::AD_NEUTRAL, value); } if (event.JoystickEvent.POV == 65535) { dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_H_ID, Input::AD_NEUTRAL, 0); dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_V_ID, Input::AD_NEUTRAL, 0); } else { // *0.017453925f is to convert degrees to radians dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_H_ID, Input::AD_NEUTRAL, (int)(cos(event.JoystickEvent.POV*0.017453925f/100.0f) *Input::MAX_VALUE)); dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_V_ID, Input::AD_NEUTRAL, (int)(sin(event.JoystickEvent.POV*0.017453925f/100.0f) *Input::MAX_VALUE)); } GamePadDevice* gp = getDeviceManager()->getGamePadFromIrrID(event.JoystickEvent.Joystick); if (gp == NULL) { // Prevent null pointer crash return EVENT_BLOCK; } for(int i=0; i<gp->getNumberOfButtons(); i++) { const bool isButtonPressed = event.JoystickEvent.IsButtonPressed(i); // Only report button events when the state of the button changes if ((!gp->isButtonPressed(i) && isButtonPressed) || (gp->isButtonPressed(i) && !isButtonPressed) ) { if (UserConfigParams::m_gamepad_debug) { Log::info("InputManager", "button %i, status=%i", i, isButtonPressed); } dispatchInput(Input::IT_STICKBUTTON, event.JoystickEvent.Joystick, i, Input::AD_POSITIVE, isButtonPressed ? Input::MAX_VALUE : 0); } gp->setButtonPressed(i, isButtonPressed); } } else if (event.EventType == EET_KEY_INPUT_EVENT) { // On some systems (linux esp.) certain keys (e.g. [] ) have a 0 // Key value, but do have a value defined in the Char field. // So to distinguish them (otherwise [] would both be mapped to // the same value 0, which means we can't distinguish which key // was actually pressed anymore), we set bit 10 which should // allow us to distinguish those artifical keys from the // 'real' keys. const int key = event.KeyInput.Key ? event.KeyInput.Key : event.KeyInput.Char+1024; if (event.KeyInput.PressedDown) { // escape is a little special if (key == KEY_ESCAPE) { StateManager::get()->escapePressed(); return EVENT_BLOCK; } // 'backspace' in a text control must never be mapped, since user // can be in a text area trying to erase text (and if it's mapped // to rescue that would dismiss the dialog instead of erasing a // single letter). Same for spacebar. Same for letters. if (GUIEngine::isWithinATextBox()) { if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT) { return EVENT_LET; } if (key >= KEY_KEY_0 && key <= KEY_KEY_Z) { return EVENT_LET; } } const bool wasInTextBox = GUIEngine::isWithinATextBox(); dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key, Input::AD_POSITIVE, Input::MAX_VALUE, event.KeyInput.Shift); // if this action took us into a text box, don't let event continue // (FIXME not the cleanest solution) if (!wasInTextBox && GUIEngine::isWithinATextBox()) { return EVENT_BLOCK; } } else { // 'backspace' in a text control must never be mapped, since user // can be in a text area trying to erase text (and if it's mapped // to rescue that would dismiss the dialog instead of erasing a // single letter). Same for spacebar. Same for letters. if (GUIEngine::isWithinATextBox()) { if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT) { return EVENT_LET; } if (key >= KEY_KEY_0 && key <= KEY_KEY_Z) { return EVENT_LET; } } dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key, Input::AD_POSITIVE, 0, event.KeyInput.Shift); return EVENT_BLOCK; // Don't propagate key up events } } // Use the mouse to change the looking direction when first person view is activated else if (event.EventType == EET_MOUSE_INPUT_EVENT) { const int type = event.MouseInput.Event; if (type == EMIE_MOUSE_MOVED) { if (Camera::isFPS()) { Camera *cam = Camera::getActiveCamera(); // Center of the screen core::vector2df screen_size = irr_driver->getCurrentScreenSize(); int mid_x = (int) screen_size.X / 2; int mid_y = (int) screen_size.Y / 2; // Relative mouse movement int diff_x = event.MouseInput.X - m_mouse_val_x; int diff_y = event.MouseInput.Y - m_mouse_val_y; float mouse_x = ((float) diff_x) * UserConfigParams::m_fpscam_direction_speed; float mouse_y = ((float) diff_y) * -UserConfigParams::m_fpscam_direction_speed; // No movement the first time it's used // At the moment there's also a hard limit because the mouse // gets reset to the middle of the screen and sometimes there // are more events fired than expected. if (m_mouse_val_x != -1 && (diff_x + diff_y) < 100 && (diff_x + diff_y) > -100) { // Rotate camera cam->applyMouseMovement(mouse_x, mouse_y); // Reset mouse position to the middle of the screen when // the mouse is far away if (event.MouseInput.X < mid_x / 2 || event.MouseInput.X > (mid_x + mid_x / 2) || event.MouseInput.Y < mid_y / 2 || event.MouseInput.Y > (mid_y + mid_y / 2)) { irr_driver->getDevice()->getCursorControl()->setPosition(mid_x, mid_y); m_mouse_val_x = mid_x; m_mouse_val_y = mid_y; } else { m_mouse_val_x = event.MouseInput.X; m_mouse_val_y = event.MouseInput.Y; } } else { m_mouse_val_x = event.MouseInput.X; m_mouse_val_y = event.MouseInput.Y; } return EVENT_BLOCK; } else // Reset mouse position m_mouse_val_x = m_mouse_val_y = -1; } else if (type == EMIE_MOUSE_WHEEL) { if (Camera::isFPS()) { // Use scrolling to change the maximum speed // Only test if it's more or less than 0 as it seems to be not // reliable accross more platforms. Camera *cam = Camera::getActiveCamera(); if (event.MouseInput.Wheel < 0) { float vel = cam->getMaximumVelocity() - 3; if (vel < 0.0f) vel = 0.0f; cam->setMaximumVelocity(vel); } else if (event.MouseInput.Wheel > 0) { cam->setMaximumVelocity(cam->getMaximumVelocity() + 3); } } } /* EMIE_LMOUSE_PRESSED_DOWN Left mouse button was pressed down. EMIE_RMOUSE_PRESSED_DOWN Right mouse button was pressed down. EMIE_MMOUSE_PRESSED_DOWN Middle mouse button was pressed down. EMIE_LMOUSE_LEFT_UP Left mouse button was left up. EMIE_RMOUSE_LEFT_UP Right mouse button was left up. EMIE_MMOUSE_LEFT_UP Middle mouse button was left up. EMIE_MOUSE_MOVED The mouse cursor changed its position. EMIE_MOUSE_WHEEL The mouse wheel was moved. Use Wheel value in event data to find out in what direction and how fast. */ } // block events in all modes but initial menus (except in text boxes to // allow typing, and except in modal dialogs in-game) // FIXME: 1) that's awful logic 2) that's not what the code below does, // events are never blocked in menus if (getDeviceManager()->getAssignMode() != NO_ASSIGN && !GUIEngine::isWithinATextBox() && (!GUIEngine::ModalDialog::isADialogActive() && StateManager::get()->getGameState() == GUIEngine::GAME)) { return EVENT_BLOCK; } else { return EVENT_LET; } }
/** 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, bool shift_mask) { // Act different in input sensing mode. if (m_mode == INPUT_SENSE_KEYBOARD || m_mode == INPUT_SENSE_GAMEPAD) { // Do not pick disabled gamepads for input sensing if (type == Input::IT_STICKBUTTON || type == Input::IT_STICKMOTION) { GamePadDevice *gPad = m_device_manager->getGamePadFromIrrID(deviceID); // This can happen in case of automatically ignored accelerator // devices, which are not part of stk's gamepad mapping. if (!gPad) return; DeviceConfig *conf = gPad->getConfiguration(); if (!conf->isEnabled()) return; } 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; else if (button == KEY_TAB) { if (shift_mask) { action = PA_MENU_UP; } else { action = PA_MENU_DOWN; } } 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::getRunningInstance()->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) { //Log::info("InputManager", "New Player Joining with Key %d", button); 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::getRunningInstance()->joinPlayer(device); } } 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() && !race_manager->isWatchingReplay() ) { if (player == NULL) { // Prevent null pointer crash return; } // Find the corresponding PlayerKart from our ActivePlayer instance AbstractKart* pk = player->getKart(); if (pk == NULL) { Log::error("InputManager::dispatchInput", "Trying to process " "action for an unknown player"); return; } Controller* controller = pk->getController(); if (controller != NULL) controller->action(action, abs(value)); } else if (race_manager->isWatchingReplay()) { // Get the first ghost kart World::getWorld()->getKart(0) ->getController()->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 = getDeviceManager()->getGamePadFromIrrID(deviceID); // Check for deadzone if (gp != NULL && gp->moved(value)) { //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!", core::stringw(gp->getName().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