1401 lines
52 KiB
C++
1401 lines
52 KiB
C++
// SuperTuxKart - a fun racing game with go-kart
|
|
//
|
|
// Copyright (C) 2012-2015 SuperTuxKart-Team
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 3
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#include "input/input_manager.hpp"
|
|
|
|
#include "config/user_config.hpp"
|
|
#include "graphics/camera_fps.hpp"
|
|
#include "graphics/irr_driver.hpp"
|
|
#include "guiengine/engine.hpp"
|
|
#include "guiengine/event_handler.hpp"
|
|
#include "guiengine/modaldialog.hpp"
|
|
#include "guiengine/screen.hpp"
|
|
#include "guiengine/screen_keyboard.hpp"
|
|
#include "input/device_manager.hpp"
|
|
#include "input/gamepad_device.hpp"
|
|
#include "input/input.hpp"
|
|
#include "input/keyboard_device.hpp"
|
|
#include "input/multitouch_device.hpp"
|
|
#include "input/wiimote_manager.hpp"
|
|
#include "karts/controller/controller.hpp"
|
|
#include "karts/abstract_kart.hpp"
|
|
#include "modes/demo_world.hpp"
|
|
#include "modes/profile_world.hpp"
|
|
#include "modes/world.hpp"
|
|
#include "network/network_config.hpp"
|
|
#include "network/rewind_manager.hpp"
|
|
#include "physics/physics.hpp"
|
|
#include "race/history.hpp"
|
|
#include "replay/replay_recorder.hpp"
|
|
#include "states_screens/dialogs/splitscreen_player_dialog.hpp"
|
|
#include "states_screens/kart_selection.hpp"
|
|
#include "states_screens/main_menu_screen.hpp"
|
|
#include "states_screens/options_screen_device.hpp"
|
|
#include "states_screens/state_manager.hpp"
|
|
#include "utils/debug.hpp"
|
|
#include "utils/string_utils.hpp"
|
|
|
|
#include <ISceneManager.h>
|
|
#include <ICameraSceneNode.h>
|
|
#include <ISceneNode.h>
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
|
|
InputManager *input_manager;
|
|
|
|
using GUIEngine::EventPropagation;
|
|
using GUIEngine::EVENT_LET;
|
|
using GUIEngine::EVENT_BLOCK;
|
|
|
|
#define INPUT_MODE_DEBUG 0
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Initialise input
|
|
*/
|
|
InputManager::InputManager() : m_mode(BOOTSTRAP),
|
|
m_mouse_val_x(-1), m_mouse_val_y(-1)
|
|
{
|
|
m_device_manager = new DeviceManager();
|
|
m_device_manager->initialize();
|
|
|
|
m_timer_in_use = false;
|
|
m_master_player_only = false;
|
|
m_timer = 0;
|
|
|
|
}
|
|
// -----------------------------------------------------------------------------
|
|
void InputManager::update(float dt)
|
|
{
|
|
#ifdef ENABLE_WIIUSE
|
|
wiimote_manager->update();
|
|
#endif
|
|
|
|
if(m_timer_in_use)
|
|
{
|
|
m_timer -= dt;
|
|
if(m_timer < 0) m_timer_in_use = false;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Destructor. Frees all data structures.
|
|
*/
|
|
InputManager::~InputManager()
|
|
{
|
|
delete m_device_manager;
|
|
} // ~InputManager
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void InputManager::handleStaticAction(int key, int value)
|
|
{
|
|
static bool control_is_pressed = false;
|
|
static bool shift_is_pressed = false;
|
|
|
|
World *world = World::getWorld();
|
|
|
|
// When no players... a cutscene
|
|
if (race_manager->getNumPlayers() == 0 && world != NULL && value > 0 &&
|
|
(key == IRR_KEY_SPACE || key == IRR_KEY_RETURN ||
|
|
key == IRR_KEY_BUTTON_A))
|
|
{
|
|
world->onFirePressed(NULL);
|
|
}
|
|
|
|
|
|
if (world != NULL && UserConfigParams::m_artist_debug_mode &&
|
|
control_is_pressed && value > 0)
|
|
{
|
|
if (Debug::handleStaticAction(key))
|
|
return;
|
|
}
|
|
|
|
// TODO: move debug shortcuts to Debug::handleStaticAction
|
|
switch (key)
|
|
{
|
|
#ifdef DEBUG
|
|
// Special debug options for profile mode: switch the
|
|
// camera to show a different kart.
|
|
case IRR_KEY_1:
|
|
case IRR_KEY_2:
|
|
case IRR_KEY_3:
|
|
case IRR_KEY_4:
|
|
case IRR_KEY_5:
|
|
case IRR_KEY_6:
|
|
case IRR_KEY_7:
|
|
case IRR_KEY_8:
|
|
case IRR_KEY_9:
|
|
{
|
|
if(!ProfileWorld::isProfileMode() || !world) break;
|
|
int kart_id = key - IRR_KEY_1;
|
|
if(kart_id<0 || kart_id>=(int)world->getNumKarts()) break;
|
|
Camera::getCamera(0)->setKart(world->getKart(kart_id));
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case IRR_KEY_CONTROL:
|
|
case IRR_KEY_RCONTROL:
|
|
case IRR_KEY_LCONTROL:
|
|
case IRR_KEY_RMENU:
|
|
case IRR_KEY_LMENU:
|
|
case IRR_KEY_LWIN:
|
|
control_is_pressed = value!=0;
|
|
break;
|
|
case IRR_KEY_LSHIFT:
|
|
case IRR_KEY_RSHIFT:
|
|
case IRR_KEY_SHIFT:
|
|
shift_is_pressed = value!=0; break;
|
|
|
|
// Flying up and down
|
|
case IRR_KEY_I:
|
|
{
|
|
if (!world || !UserConfigParams::m_artist_debug_mode) break;
|
|
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
if (kart == NULL) break;
|
|
|
|
kart->flyUp();
|
|
break;
|
|
}
|
|
case IRR_KEY_K:
|
|
{
|
|
if (!world || !UserConfigParams::m_artist_debug_mode) break;
|
|
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
if (kart == NULL) break;
|
|
|
|
kart->flyDown();
|
|
break;
|
|
}
|
|
// Moving the first person camera
|
|
case IRR_KEY_W:
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (world && UserConfigParams::m_artist_debug_mode && cam )
|
|
{
|
|
core::vector3df vel(cam->getLinearVelocity());
|
|
vel.Z = value ? cam->getMaximumVelocity() : 0;
|
|
cam->setLinearVelocity(vel);
|
|
}
|
|
break;
|
|
}
|
|
case IRR_KEY_S:
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (world && UserConfigParams::m_artist_debug_mode && cam)
|
|
{
|
|
core::vector3df vel(cam->getLinearVelocity());
|
|
vel.Z = value ? -cam->getMaximumVelocity() : 0;
|
|
cam->setLinearVelocity(vel);
|
|
}
|
|
break;
|
|
}
|
|
case IRR_KEY_D:
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (world && !UserConfigParams::m_artist_debug_mode && cam)
|
|
{
|
|
core::vector3df vel(cam->getLinearVelocity());
|
|
vel.X = value ? -cam->getMaximumVelocity() : 0;
|
|
cam->setLinearVelocity(vel);
|
|
}
|
|
break;
|
|
}
|
|
case IRR_KEY_A:
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (world && UserConfigParams::m_artist_debug_mode && cam)
|
|
{
|
|
core::vector3df vel(cam->getLinearVelocity());
|
|
vel.X = value ? cam->getMaximumVelocity() : 0;
|
|
cam->setLinearVelocity(vel);
|
|
}
|
|
break;
|
|
}
|
|
case IRR_KEY_R:
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (world && UserConfigParams::m_artist_debug_mode && cam)
|
|
{
|
|
core::vector3df vel(cam->getLinearVelocity());
|
|
vel.Y = value ? cam->getMaximumVelocity() : 0;
|
|
cam->setLinearVelocity(vel);
|
|
}
|
|
break;
|
|
}
|
|
case IRR_KEY_F:
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (world && UserConfigParams::m_artist_debug_mode && cam)
|
|
{
|
|
core::vector3df vel(cam->getLinearVelocity());
|
|
vel.Y = value ? -cam->getMaximumVelocity() : 0;
|
|
cam->setLinearVelocity(vel);
|
|
}
|
|
break;
|
|
}
|
|
// Rotating the first person camera
|
|
case IRR_KEY_Q:
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (world && UserConfigParams::m_artist_debug_mode && cam )
|
|
{
|
|
cam->setAngularVelocity(value ?
|
|
UserConfigParams::m_fpscam_max_angular_velocity : 0.0f);
|
|
}
|
|
break;
|
|
}
|
|
case IRR_KEY_E:
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (world && UserConfigParams::m_artist_debug_mode && cam)
|
|
{
|
|
cam->setAngularVelocity(value ?
|
|
-UserConfigParams::m_fpscam_max_angular_velocity : 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IRR_KEY_SNAPSHOT:
|
|
case IRR_KEY_PRINT:
|
|
// on windows we don't get a press event, only release. So
|
|
// save on release only (to avoid saving twice on other platforms)
|
|
if (value == 0)
|
|
{
|
|
if (control_is_pressed)
|
|
{
|
|
const bool is_recording = irr_driver->isRecording();
|
|
irr_driver->setRecording(!is_recording);
|
|
}
|
|
else
|
|
{
|
|
irr_driver->requestScreenshot();
|
|
}
|
|
}
|
|
break;
|
|
case IRR_KEY_F11:
|
|
if(value && shift_is_pressed && world && RewindManager::isEnabled())
|
|
{
|
|
printf("Enter rewind to time in ticks:");
|
|
char s[256];
|
|
fgets(s, 256, stdin);
|
|
int t;
|
|
StringUtils::fromString(s,t);
|
|
RewindManager::get()->rewindTo(t, world->getTimeTicks());
|
|
Log::info("Rewind", "Rewinding from %d to %d",
|
|
world->getTimeTicks(), t);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
else if (UserConfigParams::m_artist_debug_mode && world)
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
|
|
if (control_is_pressed)
|
|
kart->setPowerup(PowerupManager::POWERUP_SWATTER, 10000);
|
|
else
|
|
kart->setPowerup(PowerupManager::POWERUP_RUBBERBALL, 10000);
|
|
|
|
#ifdef FORCE_RESCUE_ON_FIRST_KART
|
|
// Can be useful for debugging places where the AI gets into
|
|
// a rescue loop: rescue, drive, crash, rescue to same place
|
|
world->getKart(0)->forceRescue();
|
|
#endif
|
|
}
|
|
break;
|
|
case IRR_KEY_F2:
|
|
if (UserConfigParams::m_artist_debug_mode && world)
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
|
|
kart->setPowerup(PowerupManager::POWERUP_PLUNGER, 10000);
|
|
}
|
|
break;
|
|
case IRR_KEY_F3:
|
|
if (UserConfigParams::m_artist_debug_mode && world)
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
kart->setPowerup(PowerupManager::POWERUP_CAKE, 10000);
|
|
}
|
|
break;
|
|
case IRR_KEY_F4:
|
|
if (UserConfigParams::m_artist_debug_mode && world)
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
kart->setPowerup(PowerupManager::POWERUP_SWITCH, 10000);
|
|
}
|
|
break;
|
|
case IRR_KEY_F5:
|
|
if (UserConfigParams::m_artist_debug_mode && world)
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
kart->setPowerup(PowerupManager::POWERUP_BOWLING, 10000);
|
|
}
|
|
break;
|
|
case IRR_KEY_F6:
|
|
if (UserConfigParams::m_artist_debug_mode && world)
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
kart->setPowerup(PowerupManager::POWERUP_BUBBLEGUM, 10000);
|
|
}
|
|
break;
|
|
case IRR_KEY_F7:
|
|
if (UserConfigParams::m_artist_debug_mode && world)
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
kart->setPowerup(PowerupManager::POWERUP_ZIPPER, 10000);
|
|
}
|
|
break;
|
|
case IRR_KEY_F8:
|
|
if (UserConfigParams::m_artist_debug_mode && value && world)
|
|
{
|
|
if (control_is_pressed)
|
|
{
|
|
RaceGUIBase* gui = world->getRaceGUI();
|
|
if (gui != NULL) gui->m_enabled = !gui->m_enabled;
|
|
|
|
const int count = World::getWorld()->getNumKarts();
|
|
for (int n=0; n<count; n++)
|
|
{
|
|
if(World::getWorld()->getKart(n)->getController()->isPlayerController())
|
|
World::getWorld()->getKart(n)->getNode()
|
|
->setVisible(gui->m_enabled);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
kart->setEnergy(100.0f);
|
|
}
|
|
}
|
|
break;
|
|
case IRR_KEY_F9:
|
|
if (UserConfigParams::m_artist_debug_mode && world)
|
|
{
|
|
AbstractKart* kart = world->getLocalPlayerKart(0);
|
|
if(control_is_pressed && race_manager->getMinorMode()!=
|
|
RaceManager::MINOR_MODE_3_STRIKES)
|
|
kart->setPowerup(PowerupManager::POWERUP_RUBBERBALL,
|
|
10000);
|
|
else
|
|
kart->setPowerup(PowerupManager::POWERUP_SWATTER, 10000);
|
|
}
|
|
break;
|
|
*/
|
|
case IRR_KEY_F10:
|
|
if(world && value)
|
|
{
|
|
if(control_is_pressed)
|
|
ReplayRecorder::get()->save();
|
|
else
|
|
history->Save();
|
|
}
|
|
break;
|
|
/*
|
|
case IRR_KEY_F11:
|
|
if (UserConfigParams::m_artist_debug_mode && value &&
|
|
control_is_pressed && world)
|
|
{
|
|
world->getPhysics()->nextDebugMode();
|
|
}
|
|
break;
|
|
*/
|
|
case IRR_KEY_F12:
|
|
if(value)
|
|
UserConfigParams::m_display_fps =
|
|
!UserConfigParams::m_display_fps;
|
|
break;
|
|
default:
|
|
break;
|
|
} // switch
|
|
} // handleStaticAction
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/**
|
|
* Handles input when an input sensing mode (when configuring input)
|
|
*/
|
|
void InputManager::inputSensing(Input::InputType type, int deviceID,
|
|
int button, Input::AxisDirection axisDirection,
|
|
int value)
|
|
{
|
|
#if INPUT_MODE_DEBUG
|
|
Log::info("InputManager::inputSensing", "Start sensing input");
|
|
#endif
|
|
|
|
// don't store if we're trying to do something like bindings keyboard
|
|
// keys on a gamepad
|
|
if (m_mode == INPUT_SENSE_KEYBOARD && type != Input::IT_KEYBOARD)
|
|
return;
|
|
if (m_mode == INPUT_SENSE_GAMEPAD && type != Input::IT_STICKMOTION &&
|
|
type != Input::IT_STICKBUTTON)
|
|
return;
|
|
|
|
#if INPUT_MODE_DEBUG
|
|
Log::info("InputManager::inputSensing", store_new ? "storing it" : "ignoring it");
|
|
#endif
|
|
|
|
|
|
switch(type)
|
|
{
|
|
case Input::IT_KEYBOARD:
|
|
if (value > Input::MAX_VALUE/2)
|
|
{
|
|
m_sensed_input_high_kbd.insert(button);
|
|
break;
|
|
}
|
|
if (value != 0) break; // That shouldn't happen
|
|
// only notify on key release
|
|
if (m_sensed_input_high_kbd.find(button)
|
|
!= m_sensed_input_high_kbd.end())
|
|
{
|
|
Input sensed_input;
|
|
sensed_input.m_type = Input::IT_KEYBOARD;
|
|
sensed_input.m_device_id = deviceID;
|
|
sensed_input.m_button_id = button;
|
|
sensed_input.m_character = deviceID;
|
|
OptionsScreenDevice::getInstance()->gotSensedInput(sensed_input);
|
|
return;
|
|
}
|
|
break;
|
|
case Input::IT_STICKBUTTON:
|
|
if (abs(value) > Input::MAX_VALUE/2.0f)
|
|
{
|
|
Input sensed_input;
|
|
sensed_input.m_type = Input::IT_STICKBUTTON;
|
|
sensed_input.m_device_id = deviceID;
|
|
sensed_input.m_button_id = button;
|
|
sensed_input.m_character = deviceID;
|
|
OptionsScreenDevice::getInstance()->gotSensedInput(sensed_input);
|
|
return;
|
|
}
|
|
break;
|
|
case Input::IT_STICKMOTION:
|
|
{
|
|
// We have to save the direction in which the axis was moved.
|
|
// This is done by storing it as a sign (and since button can
|
|
// be zero, we add one before changing the sign).
|
|
int input_button_id = value>=0 ? 1+button : -(1+button);
|
|
std::tuple<int, int> input_id(deviceID, input_button_id);
|
|
std::tuple<int, int> input_id_inv(deviceID, -input_button_id);
|
|
|
|
bool id_was_high = m_sensed_input_high_gamepad.find(input_id)
|
|
!= m_sensed_input_high_gamepad.end();
|
|
bool inverse_id_was_high = m_sensed_input_high_gamepad.find(input_id_inv)
|
|
!= m_sensed_input_high_gamepad.end();
|
|
bool id_was_zero = m_sensed_input_zero_gamepad.find(button)
|
|
!= m_sensed_input_zero_gamepad.end();
|
|
|
|
// A stick was pushed far enough (for the first time) to count as
|
|
// 'triggered' - save the axis (coded with direction in the button
|
|
// value) for later, so that it can be registered when the stick is
|
|
// released again.
|
|
// This is mostly legacy behaviour, it is probably good enough
|
|
// to register this as soon as the value is high enough.
|
|
if (!id_was_high && abs(value) > Input::MAX_VALUE*6.0f/7.0f)
|
|
{
|
|
if(inverse_id_was_high && !id_was_zero) {
|
|
Input sensed_input;
|
|
sensed_input.m_type = type;
|
|
sensed_input.m_device_id = deviceID;
|
|
sensed_input.m_button_id = button;
|
|
sensed_input.m_axis_direction = (value>=0) ? Input::AD_POSITIVE
|
|
: Input::AD_NEGATIVE;
|
|
sensed_input.m_axis_range = Input::AR_FULL;
|
|
sensed_input.m_character = deviceID;
|
|
OptionsScreenDevice::getInstance()->gotSensedInput(sensed_input);
|
|
|
|
}
|
|
else m_sensed_input_high_gamepad.insert(input_id);
|
|
}
|
|
// At least with xbox controller they can come to a 'rest' with a value of
|
|
// around 6000! So in order to detect that an axis was released, we need to
|
|
// test with a rather high deadzone value
|
|
else if ( abs(value) < Input::MAX_VALUE/3.0f )
|
|
{
|
|
if( id_was_high )
|
|
{
|
|
Input sensed_input;
|
|
sensed_input.m_type = type;
|
|
sensed_input.m_device_id = deviceID;
|
|
sensed_input.m_button_id = button;
|
|
sensed_input.m_axis_direction = (value>=0) == id_was_zero
|
|
? Input::AD_POSITIVE
|
|
: Input::AD_NEGATIVE;
|
|
sensed_input.m_axis_range = id_was_zero ? Input::AR_HALF
|
|
: Input::AR_FULL;
|
|
sensed_input.m_character = deviceID;
|
|
OptionsScreenDevice::getInstance()->gotSensedInput(sensed_input);
|
|
}
|
|
else if( inverse_id_was_high )
|
|
{
|
|
Input sensed_input;
|
|
sensed_input.m_type = type;
|
|
sensed_input.m_device_id = deviceID;
|
|
sensed_input.m_button_id = button;
|
|
// Since the inverse direction was high (i.e. stick went from
|
|
// +30000 to -100), we have to inverse the sign
|
|
sensed_input.m_axis_direction = (value>=0) == id_was_zero
|
|
? Input::AD_NEGATIVE
|
|
: Input::AD_POSITIVE;
|
|
sensed_input.m_axis_range = id_was_zero ? Input::AR_HALF
|
|
: Input::AR_FULL;
|
|
sensed_input.m_character = deviceID;
|
|
OptionsScreenDevice::getInstance()->gotSensedInput(sensed_input);
|
|
}
|
|
else
|
|
{
|
|
m_sensed_input_zero_gamepad.insert(button);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Input::IT_NONE:
|
|
case Input::IT_MOUSEMOTION:
|
|
case Input::IT_MOUSEBUTTON:
|
|
// uninteresting (but we keep them here to explicitely state we do
|
|
// nothing with them, and thus to fix warnings)
|
|
break;
|
|
} // switch
|
|
} // inputSensing
|
|
|
|
//-----------------------------------------------------------------------------
|
|
int InputManager::getPlayerKeyboardID() const
|
|
{
|
|
// In no-assign mode, just return the GUI player ID (devices not
|
|
// assigned yet)
|
|
if (m_device_manager->getAssignMode() == NO_ASSIGN)
|
|
return PLAYER_ID_GAME_MASTER;
|
|
|
|
// Otherwise, after devices are assigned, we can check in more depth
|
|
// Return the first keyboard that is actually being used
|
|
const int amount = m_device_manager->getKeyboardAmount();
|
|
for (int k=0; k<amount; k++)
|
|
{
|
|
if (m_device_manager->getKeyboard(k) != NULL &&
|
|
m_device_manager->getKeyboard(k)->getPlayer() != NULL)
|
|
{
|
|
return m_device_manager->getKeyboard(k)->getPlayer()->getID();
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
/** 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 == IRR_KEY_UP) action = PA_MENU_UP;
|
|
else if (button == IRR_KEY_DOWN) action = PA_MENU_DOWN;
|
|
else if (button == IRR_KEY_LEFT) action = PA_MENU_LEFT;
|
|
else if (button == IRR_KEY_RIGHT) action = PA_MENU_RIGHT;
|
|
else if (button == IRR_KEY_SPACE) action = PA_MENU_SELECT;
|
|
else if (button == IRR_KEY_RETURN) action = PA_MENU_SELECT;
|
|
else if (button == IRR_KEY_BUTTON_UP) action = PA_MENU_DOWN;
|
|
else if (button == IRR_KEY_BUTTON_DOWN) action = PA_MENU_UP;
|
|
else if (button == IRR_KEY_BUTTON_LEFT) action = PA_MENU_LEFT;
|
|
else if (button == IRR_KEY_BUTTON_RIGHT) action = PA_MENU_RIGHT;
|
|
else if (button == IRR_KEY_BUTTON_A) action = PA_MENU_SELECT;
|
|
else if (button == IRR_KEY_TAB)
|
|
{
|
|
if (shift_mask)
|
|
{
|
|
action = PA_MENU_UP;
|
|
}
|
|
else
|
|
{
|
|
action = PA_MENU_DOWN;
|
|
}
|
|
}
|
|
|
|
if (button == IRR_KEY_RETURN || button == IRR_KEY_BUTTON_A)
|
|
{
|
|
if (GUIEngine::ModalDialog::isADialogActive() &&
|
|
!GUIEngine::ScreenKeyboard::isActive())
|
|
{
|
|
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)
|
|
{
|
|
if (NetworkConfig::get()->isNetworking() &&
|
|
NetworkConfig::get()->isAddingNetworkPlayers())
|
|
{
|
|
// Ignore release event
|
|
if (value == 0)
|
|
return;
|
|
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 && (action == PA_FIRE || action == PA_MENU_SELECT))
|
|
{
|
|
if (!GUIEngine::ModalDialog::isADialogActive())
|
|
new SplitscreenPlayerDialog(device);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 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, NULL/*player profile*/);
|
|
}
|
|
}
|
|
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() &&
|
|
!GUIEngine::ScreenKeyboard::isActive() &&
|
|
!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() && !GUIEngine::ModalDialog::isADialogActive())
|
|
{
|
|
// 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
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void InputManager::setMasterPlayerOnly(bool enabled)
|
|
{
|
|
#if INPUT_MODE_DEBUG
|
|
Log::info("InputManager::setMasterPlayerOnly", enabled ? "enabled" : "disabled");
|
|
#endif
|
|
m_master_player_only = enabled;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Returns whether only the master player should be allowed to perform changes
|
|
* in menus */
|
|
bool InputManager::masterPlayerOnly() const
|
|
{
|
|
return m_device_manager->getAssignMode() == ASSIGN && m_master_player_only;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* 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 == IRR_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 == IRR_KEY_BACK || key == IRR_KEY_SPACE ||
|
|
key == IRR_KEY_SHIFT)
|
|
{
|
|
return EVENT_LET;
|
|
}
|
|
if (key >= IRR_KEY_0 && key <= IRR_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 == IRR_KEY_BACK || key == IRR_KEY_SPACE ||
|
|
key == IRR_KEY_SHIFT)
|
|
{
|
|
return EVENT_LET;
|
|
}
|
|
if (key >= IRR_KEY_0 && key <= IRR_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
|
|
}
|
|
}
|
|
else if (event.EventType == EET_TOUCH_INPUT_EVENT)
|
|
{
|
|
MultitouchDevice* device = m_device_manager->getMultitouchDevice();
|
|
unsigned int id = (unsigned int)event.TouchInput.ID;
|
|
|
|
if (device != NULL && id < device->m_events.size())
|
|
{
|
|
device->m_events[id].id = id;
|
|
device->m_events[id].x = event.TouchInput.X;
|
|
device->m_events[id].y = event.TouchInput.Y;
|
|
|
|
if (event.TouchInput.Event == ETIE_PRESSED_DOWN)
|
|
{
|
|
device->m_events[id].touched = true;
|
|
}
|
|
else if (event.TouchInput.Event == ETIE_LEFT_UP)
|
|
{
|
|
device->m_events[id].touched = false;
|
|
}
|
|
|
|
m_device_manager->updateMultitouchDevice();
|
|
device->updateDeviceState(id);
|
|
}
|
|
}
|
|
// 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)
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (cam)
|
|
{
|
|
// Center of the screen
|
|
core::dimension2du screen_size = irr_driver->getActualScreenSize();
|
|
int mid_x = (int) screen_size.Width / 2;
|
|
int mid_y = (int) screen_size.Height / 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)
|
|
{
|
|
CameraFPS *cam = dynamic_cast<CameraFPS*>(Camera::getActiveCamera());
|
|
if (cam)
|
|
{
|
|
// 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.
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Simulate touch event on non-android devices
|
|
#if !defined(ANDROID)
|
|
MultitouchDevice* device = m_device_manager->getMultitouchDevice();
|
|
|
|
if (device != NULL && (type == EMIE_LMOUSE_PRESSED_DOWN ||
|
|
type == EMIE_LMOUSE_LEFT_UP || type == EMIE_MOUSE_MOVED))
|
|
{
|
|
device->m_events[0].id = 0;
|
|
device->m_events[0].x = event.MouseInput.X;
|
|
device->m_events[0].y = event.MouseInput.Y;
|
|
|
|
if (type == EMIE_LMOUSE_PRESSED_DOWN)
|
|
{
|
|
device->m_events[0].touched = true;
|
|
}
|
|
else if (type == EMIE_LMOUSE_LEFT_UP)
|
|
{
|
|
device->m_events[0].touched = false;
|
|
}
|
|
|
|
m_device_manager->updateMultitouchDevice();
|
|
device->updateDeviceState(0);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
}
|
|
else if (event.EventType == EET_ACCELEROMETER_EVENT)
|
|
{
|
|
MultitouchDevice* device = m_device_manager->getMultitouchDevice();
|
|
|
|
if (device && device->isAccelerometerActive())
|
|
{
|
|
m_device_manager->updateMultitouchDevice();
|
|
|
|
float factor = UserConfigParams::m_multitouch_tilt_factor;
|
|
factor = std::max(factor, 0.1f);
|
|
device->updateAxisX(float(event.AccelerometerEvent.Y) / factor);
|
|
}
|
|
}
|
|
|
|
// 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() &&
|
|
!GUIEngine::ScreenKeyboard::isActive() &&
|
|
StateManager::get()->getGameState() == GUIEngine::GAME))
|
|
{
|
|
return EVENT_BLOCK;
|
|
}
|
|
else
|
|
{
|
|
return EVENT_LET;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Queries the input driver whether it is in the given expected mode.
|
|
*/
|
|
bool InputManager::isInMode(InputDriverMode expMode)
|
|
{
|
|
return m_mode == expMode;
|
|
} // isInMode
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Sets the mode of the input driver.
|
|
*
|
|
* Switching of the input driver's modes is only needed for special menus
|
|
* (those who need typing or input sensing) and the MenuManager (switch to
|
|
* ingame/menu mode). Therefore there is a limited amount of legal combinations
|
|
* of current and next input driver modes: From the menu mode you can switch
|
|
* to any other mode and from any other mode only back to the menu mode.
|
|
*
|
|
* In input sense mode the pointer is invisible (any movement reports are
|
|
* suppressed). If an input happens it is stored internally and can be
|
|
* retrieved through drv_getm_sensed_input() *after* GA_SENSE_COMPLETE has been
|
|
* distributed to a menu (Normally the menu that received GA_SENSE_COMPLETE
|
|
* will request the sensed input ...). If GA_SENSE_CANCEL is received instead
|
|
* the user decided to cancel input sensing. No other game action values are
|
|
* distributed in this mode.
|
|
*
|
|
* And there is the bootstrap mode. You cannot switch to it and only leave it
|
|
* once per application instance. It is the state the input driver is first.
|
|
*
|
|
*/
|
|
void InputManager::setMode(InputDriverMode new_mode)
|
|
{
|
|
if (new_mode == m_mode) return; // no change
|
|
|
|
switch (new_mode)
|
|
{
|
|
case MENU:
|
|
#if INPUT_MODE_DEBUG
|
|
Log::info("InputManager::setMode", "MENU");
|
|
#endif
|
|
switch (m_mode)
|
|
{
|
|
case INGAME:
|
|
// Leaving ingame mode.
|
|
|
|
//if (m_action_map)
|
|
// delete m_action_map;
|
|
|
|
// Reset the helper values for the relative mouse movement
|
|
// supresses to the notification of them as an input.
|
|
m_mouse_val_x = m_mouse_val_y = -1;
|
|
|
|
//irr_driver->showPointer();
|
|
m_mode = MENU;
|
|
break;
|
|
|
|
case BOOTSTRAP:
|
|
// Leaving boot strap mode.
|
|
|
|
// Installs the action map for the menu.
|
|
// m_action_map = UserConfigParams::newMenuActionMap();
|
|
|
|
m_mode = MENU;
|
|
m_device_manager->setAssignMode(NO_ASSIGN);
|
|
|
|
break;
|
|
case INPUT_SENSE_KEYBOARD:
|
|
case INPUT_SENSE_GAMEPAD:
|
|
// Leaving input sense mode.
|
|
|
|
//irr_driver->showPointer();
|
|
m_sensed_input_high_gamepad.clear();
|
|
m_sensed_input_zero_gamepad.clear();
|
|
m_sensed_input_high_kbd.clear();
|
|
|
|
// The order is deliberate just in case someone starts
|
|
// to make STK multithreaded: m_sensed_input must not be
|
|
// 0 when mode == INPUT_SENSE_PREFER_{AXIS,BUTTON}.
|
|
m_mode = MENU;
|
|
|
|
break;
|
|
|
|
/*
|
|
case LOWLEVEL:
|
|
// Leaving lowlevel mode.
|
|
//irr_driver->showPointer();
|
|
|
|
m_mode = MENU;
|
|
|
|
break;
|
|
*/
|
|
default:
|
|
;
|
|
// Something is broken.
|
|
//assert (false);
|
|
}
|
|
|
|
break;
|
|
case INGAME:
|
|
#if INPUT_MODE_DEBUG
|
|
Log::info("InputManager::setMode", "INGAME");
|
|
#endif
|
|
// We must be in menu mode now in order to switch.
|
|
assert (m_mode == MENU);
|
|
|
|
//if (m_action_map)
|
|
// delete m_action_map;
|
|
|
|
// Installs the action map for the ingame mode.
|
|
// m_action_map = UserConfigParams::newIngameActionMap();
|
|
|
|
//irr_driver->hidePointer();
|
|
|
|
m_mode = INGAME;
|
|
|
|
break;
|
|
case INPUT_SENSE_KEYBOARD:
|
|
case INPUT_SENSE_GAMEPAD:
|
|
#if INPUT_MODE_DEBUG
|
|
Log::info("InputManager::setMode", "INPUT_SENSE_*");
|
|
#endif
|
|
// We must be in menu mode now in order to switch.
|
|
assert (m_mode == MENU);
|
|
|
|
// Reset the helper values for the relative mouse movement
|
|
// supresses to the notification of them as an input.
|
|
m_mouse_val_x = m_mouse_val_y = -1;
|
|
m_mode = new_mode;
|
|
|
|
break;
|
|
/*
|
|
case LOWLEVEL:
|
|
#if INPUT_MODE_DEBUG
|
|
Log::info("InputManager::setMode", "LOWLEVEL");
|
|
#endif
|
|
// We must be in menu mode now in order to switch.
|
|
assert (m_mode == MENU);
|
|
|
|
//irr_driver->hidePointer();
|
|
|
|
m_mode = LOWLEVEL;
|
|
|
|
break;
|
|
*/
|
|
default:
|
|
// Invalid mode.
|
|
assert(false);
|
|
}
|
|
}
|
|
|