Better player detection. Still getting some problems with irrlicht widgets : they internally catch some events without asking me. For instance, in 'press fire to join', if player has 'fire' assiged to the spacebar, pressing the spacebar results in irrlicht pressing the currently button - not sure how to get around this.

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/irrlicht@3726 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
auria
2009-07-11 01:56:07 +00:00
parent 3c6ba08857
commit 8eaef3f63b
9 changed files with 229 additions and 99 deletions

View File

@@ -40,7 +40,12 @@ using namespace GUIEngine;
namespace StateManager
{
class PlayerKart : public Widget
class PlayerKartWidget;
// ref only since we're adding them to a Screen, and the Screen will take ownership of these widgets
ptr_vector<PlayerKartWidget, REF> g_player_karts;
class PlayerKartWidget : public Widget
{
public:
LabelWidget* playerID;
@@ -48,7 +53,12 @@ namespace StateManager
ModelViewWidget* modelView;
LabelWidget* kartName;
PlayerKart(Widget* area) : Widget()
int player_id_x, player_id_y, player_id_w, player_id_h;
int player_name_x, player_name_y, player_name_w, player_name_h;
int model_x, model_y, model_w, model_h;
int kart_name_x, kart_name_y, kart_name_w, kart_name_h;
PlayerKartWidget(Widget* area) : Widget()
{
this->m_properties[PROP_ID] = "@p1";
this->x = area->x;
@@ -56,24 +66,25 @@ namespace StateManager
this->w = area->w;
this->h = area->h;
setSize(area);
playerID = new LabelWidget();
playerID->m_properties[PROP_TEXT] = _("Player 1 (keyboard)");
playerID->m_properties[PROP_TEXT_ALIGN] = "center";
playerID->m_properties[PROP_ID] = "@p1_label";
playerID->x = area->x;
playerID->y = area->y;
playerID->w = area->w;
playerID->h = 25;
playerID->x = player_id_x;
playerID->y = player_id_y;
playerID->w = player_id_w;
playerID->h = player_id_h;
//playerID->setParent(this);
m_children.push_back(playerID);
const int playerAmount = UserConfigParams::m_player.size();
const int spinnerWidth = std::min(400, area->w/3 /* FIXME : replace by number of players */);
playerName = new SpinnerWidget();
playerName->x = area->x + area->w/2 - spinnerWidth/2;
playerName->y = area->y + 25;
playerName->w = spinnerWidth;
playerName->h = 40;
playerName->x = player_name_x;
playerName->y = player_name_y;
playerName->w = player_name_w;
playerName->h = player_name_h;
playerName->m_properties[PROP_MIN_VALUE] = "0";
playerName->m_properties[PROP_MAX_VALUE] = (playerAmount-1);
@@ -83,28 +94,23 @@ namespace StateManager
modelView = new ModelViewWidget();
const int modelY = area->y + 65;
const int modelMaxHeight = area->h - 25 - 65;
const int modelMaxWidth = area->w;
const int bestSize = std::min(modelMaxWidth, modelMaxHeight);
modelView->x = area->x + area->w/2 - bestSize*1.2/2;
modelView->y = modelY + modelMaxHeight/2 - bestSize/2;
modelView->w = bestSize*1.2; // FIXME : for some reason, it looks better this way, though full square should be ok
modelView->h = bestSize;
modelView->x = model_x;
modelView->y = model_y;
modelView->w = model_w;
modelView->h = model_h;
modelView->m_properties[PROP_ID] = "@p1_model";
//modelView->setParent(this);
m_children.push_back(modelView);
kartName = new LabelWidget();
kartName->m_properties[PROP_TEXT] = _("Tux");
kartName->m_properties[PROP_TEXT_ALIGN] = "center";
kartName->m_properties[PROP_ID] = "@p1_kartname";
kartName->x = area->x;
kartName->y = area->y + area->h - 25;
kartName->w = area->w;
kartName->h = 25;
kartName->x = kart_name_x;
kartName->y = kart_name_y;
kartName->w = kart_name_w;
kartName->h = kart_name_h;
//kartName->setParent(this);
m_children.push_back(kartName);
}
@@ -114,13 +120,36 @@ namespace StateManager
playerName->add();
modelView->add();
kartName->add();
}
}
void setSize(Widget* area)
{
player_id_x = area->x;
player_id_y = area->y;
player_id_w = area->w;
player_id_h = 25;
player_name_w = std::min(400, area->w);
player_name_x = area->x + area->w/2 - player_name_w/2;
player_name_y = area->y + 25;
player_name_h = 40;
const int modelMaxHeight = area->h - 25 - 65;
const int modelMaxWidth = area->w;
const int bestSize = std::min(modelMaxWidth, modelMaxHeight);
const int modelY = area->y + 65;
model_x = area->x + area->w/2 - bestSize*1.2/2;
model_y = modelY + modelMaxHeight/2 - bestSize/2;
model_w = bestSize*1.2; // FIXME : for some reason, it looks better this way, though full square should be ok
model_h = bestSize;
kart_name_x = area->x;
kart_name_y = area->y + area->h - 25;;
kart_name_w = area->w;
kart_name_h = 25;
}
};
// ref only since we're adding them to a Screen, and the Screen will take ownership of these widgets
ptr_vector<PlayerKart, REF> g_player_karts;
class KartHoverListener : public RibbonGridHoverListener
{
public:
@@ -152,19 +181,40 @@ class KartHoverListener : public RibbonGridHoverListener
};
KartHoverListener* karthoverListener = NULL;
void firePressedOnNewDevice(InputDevice* device)
{
std::cout << "===== firePressedOnNewDevice =====\n";
if(device == NULL)
{
std::cout << "I don't know which device was pressed :'(\n";
return;
}
else if(device->getType() == DT_KEYBOARD)
{
std::cout << "Fire was pressed on a keyboard\n";
}
else if(device->getType() == DT_GAMEPAD)
{
std::cout << "Fire was pressed on a gamepad\n";
}
}
void setPlayer0Device(InputDevice* device)
{
std::cout << "===== setPlayer0Device =====\n";
if(device == NULL)
{
std::cout << "I don't know which device to assign to player 0 :'(\n";
return;
}
if(device->getType() == DT_KEYBOARD)
else if(device->getType() == DT_KEYBOARD)
{
std::cout << "Player 0 is using a keyboard\n";
}
if(device->getType() == DT_GAMEPAD)
else if(device->getType() == DT_GAMEPAD)
{
std::cout << "Player 0 is using a gamepad\n";
}
@@ -172,7 +222,7 @@ void setPlayer0Device(InputDevice* device)
// TODO : support more than 1 player
StateManager::addActivePlayer( UserConfigParams::m_player.get(0) );
UserConfigParams::m_player[0].setDevice(device);
input_manager->getDeviceList()->setNoAssignMode(false);
input_manager->getDeviceList()->setAssignMode(DETECT_NEW);
// TODO : fall back in no-assign mode when aborting a game and going back to the menu
// how to revert assign mode :
@@ -202,7 +252,7 @@ void menuEventKarts(Widget* widget, std::string& name)
Widget* area = getCurrentScreen()->getWidget("playerskarts");
PlayerKart* playerKart1 = new PlayerKart(area);
PlayerKartWidget* playerKart1 = new PlayerKartWidget(area);
getCurrentScreen()->manualAddWidget(playerKart1);
playerKart1->add();
g_player_karts.push_back(playerKart1);
@@ -250,6 +300,8 @@ void menuEventKarts(Widget* widget, std::string& name)
race_manager->setLocalKartInfo(0, w->getSelectionIDString());
input_manager->getDeviceList()->setAssignMode(ASSIGN);
StateManager::pushMenu("racesetup.stkgui");
}
}

View File

@@ -28,5 +28,7 @@ class InputDevice;
namespace StateManager
{
void setPlayer0Device(InputDevice* device);
void firePressedOnNewDevice(InputDevice* device);
void menuEventKarts(GUIEngine::Widget* widget, std::string& name);
}

View File

@@ -432,11 +432,36 @@ Widget* Screen::getLastWidget(ptr_vector<Widget>* within_vector)
#pragma mark irrLicht events
#endif
#define MAX_VALUE 32768
bool Screen::onWidgetActivated(Widget* w)
{
if(ModalDialog::isADialogActive() && w->m_event_handler == NULL)
{
ModalDialog::getCurrent()->processEvent(w->m_properties[PROP_ID]);
return false;
}
Widget* parent = w->m_event_handler;
if(w->m_event_handler != NULL)
{
/* Find topmost parent. Stop looping if a widget event handler's is itself, to not fall
in an infinite loop (this can happen e.g. in checkboxes, where they need to be
notified of clicks onto themselves so they can toggle their state. ) */
while(parent->m_event_handler != NULL && parent->m_event_handler != parent)
parent = parent->m_event_handler;
/* notify the found event event handler, and also notify the main callback if the
parent event handler says so */
if(parent->transmitEvent(w, w->m_properties[PROP_ID]))
transmitEvent(parent, parent->m_properties[PROP_ID]);
}
else transmitEvent(w, w->m_properties[PROP_ID]);
return true;
}
void Screen::processAction(const int action, const unsigned int value, Input::InputType type)
{
const bool pressedDown = value > MAX_VALUE*2/3;
const bool pressedDown = value > Input::MAX_VALUE*2/3;
if(!pressedDown && type == Input::IT_STICKMOTION) return;
@@ -628,6 +653,15 @@ void Screen::processAction(const int action, const unsigned int value, Input::In
case PA_FIRE:
if(type == Input::IT_STICKBUTTON)
{
if (pressedDown)
{
IGUIElement* element = GUIEngine::getGUIEnv()->getFocus();
Widget* w = getWidget( element->getID() );
if(w == NULL) break;
onWidgetActivated( w );
}
/*
// simulate a 'enter' key press
irr::SEvent::SKeyInput evt;
evt.PressedDown = pressedDown;
@@ -638,6 +672,7 @@ void Screen::processAction(const int action, const unsigned int value, Input::In
wrapper.EventType = EET_KEY_INPUT_EVENT;
GUIEngine::getDevice()->postEventFromUser(wrapper);
*/
}
break;
default:
@@ -663,28 +698,7 @@ bool Screen::OnEvent(const SEvent& event)
Widget* w = getWidget(id);
if(w == NULL) break;
if(ModalDialog::isADialogActive() && w->m_event_handler == NULL)
{
ModalDialog::getCurrent()->processEvent(w->m_properties[PROP_ID]);
return false;
}
Widget* parent = w->m_event_handler;
if(w->m_event_handler != NULL)
{
/* Find topmost parent. Stop looping if a widget event handler's is itself, to not fall
in an infinite loop (this can happen e.g. in checkboxes, where they need to be
notified of clicks onto themselves so they can toggle their state. ) */
while(parent->m_event_handler != NULL && parent->m_event_handler != parent)
parent = parent->m_event_handler;
/* notify the found event event handler, and also notify the main callback if the
parent event handler says so */
if(parent->transmitEvent(w, w->m_properties[PROP_ID]))
transmitEvent(parent, parent->m_properties[PROP_ID]);
}
else transmitEvent(w, w->m_properties[PROP_ID]);
break;
return onWidgetActivated(w);
}
case EGET_ELEMENT_HOVERED:
{

View File

@@ -52,6 +52,7 @@ namespace GUIEngine
static void addWidgetsRecursively(ptr_vector<Widget>& widgets, Widget* parent=NULL);
void calculateLayout(ptr_vector<Widget>& widgets, Widget* parent=NULL);
bool onWidgetActivated(Widget* w);
public:
// current mouse position, read-only...
int m_mouse_x, m_mouse_y;

View File

@@ -6,6 +6,7 @@
#include "config/player.hpp"
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "gui/kart_selection.hpp"
#include "gui/state_manager.hpp"
#include "io/file_manager.hpp"
@@ -14,7 +15,7 @@ DeviceManager::DeviceManager()
m_keyboard_amount = 0;
m_gamepad_amount = 0;
m_latest_used_device = NULL;
m_no_assign_mode = true;
m_assign_mode = NO_ASSIGN;
}
// -----------------------------------------------------------------------------
bool DeviceManager::initGamePadSupport()
@@ -45,12 +46,12 @@ bool DeviceManager::initGamePadSupport()
}
// -----------------------------------------------------------------------------
void DeviceManager::setNoAssignMode(const bool noAssignMode)
void DeviceManager::setAssignMode(const PlayerAssignMode assignMode)
{
m_no_assign_mode = noAssignMode;
m_assign_mode = assignMode;
// when going back to no-assign mode, do some cleanup
if(noAssignMode)
if(assignMode == NO_ASSIGN)
{
for(unsigned int i=0; i<m_gamepad_amount; i++)
{
@@ -117,7 +118,7 @@ bool DeviceManager::mapInputToPlayerAndAction( Input::InputType type, int device
const bool programaticallyGenerated, int* player /* out */,
PlayerAction* action /* out */ )
{
if(m_no_assign_mode)
if(m_assign_mode == NO_ASSIGN)
{
*player = -1;
}
@@ -131,7 +132,7 @@ bool DeviceManager::mapInputToPlayerAndAction( Input::InputType type, int device
{
// We found which device was triggered.
if(m_no_assign_mode)
if(m_assign_mode == NO_ASSIGN)
{
// In no-assign mode, simply keep track of which device is used
if(!programaticallyGenerated) m_latest_used_device = m_keyboards.get(n);
@@ -154,9 +155,26 @@ bool DeviceManager::mapInputToPlayerAndAction( Input::InputType type, int device
return true;
}
}
// no active player has this binding
// no active player has this binding. if we want to check for new players trying to join,
// check now
if (m_assign_mode == DETECT_NEW)
{
for(unsigned int n=0; n<m_keyboard_amount; n++)
{
PlayerAction localaction = PA_FIRST; // none
if (m_keyboards[n].hasBinding(btnID, &localaction) && localaction == PA_FIRE)
{
if (value > Input::MAX_VALUE/2) StateManager::firePressedOnNewDevice( m_keyboards.get(n) );
*action = PA_FIRST; // FIXME : returning PA_FIRST is quite a hackish way to tell input was handled internally
return true;
}
} // end for
} // end if assign_mode == DETECT_NEW
return false;
}
} // end if/else NO_ASSIGN mode
return true;
@@ -173,23 +191,49 @@ bool DeviceManager::mapInputToPlayerAndAction( Input::InputType type, int device
GamePadDevice* gamepad = getGamePadFromIrrID(deviceID);
if(m_no_assign_mode)
if(m_assign_mode == NO_ASSIGN)
{
// In no-assign mode, simply keep track of which device is used
if(!programaticallyGenerated)
m_latest_used_device = gamepad;
if(gamepad->hasBinding(type, btnID /* axis or button */, value, *player, action /* out */) )
{
// In no-assign mode, simply keep track of which device is used.
// Only assign for buttons, since axes may send some small values even when
// not actively used by user
if(!programaticallyGenerated && type == Input::IT_STICKBUTTON)
{
m_latest_used_device = gamepad;
}
return true;
}
}
else
{
if(gamepad->m_player_id != -1)
{
*player = gamepad->m_player_id;
}
else
{
// no active player has this binding. if we want to check for new players trying to join,
// check now
if (m_assign_mode == DETECT_NEW)
{
for(unsigned int n=0; n<m_gamepad_amount; n++)
{
PlayerAction localaction = PA_FIRST; // none
if (m_gamepads[n].hasBinding(type, btnID, value, -1, &localaction) && localaction == PA_FIRE)
{
if (value > Input::MAX_VALUE/2) StateManager::firePressedOnNewDevice( m_gamepads.get(n) );
*action = PA_FIRST;
return true;
}
} // end for
}
return false; // no player mapped to this device
}
if(gamepad->hasBinding(type, btnID /* axis or button */, value, *player, action /* out */) )
{

View File

@@ -4,6 +4,13 @@
#include "input/input_device.hpp"
#include "utils/ptr_vector.hpp"
enum PlayerAssignMode
{
NO_ASSIGN, // react to all devices
DETECT_NEW, // notify the manager when an inactive device is being asked to activate with fire
ASSIGN // only react to assigned devices
};
class DeviceManager
{
ptr_vector<KeyboardDevice, HOLD> m_keyboards;
@@ -16,7 +23,7 @@ class DeviceManager
InputDevice* m_latest_used_device;
bool m_no_assign_mode;
PlayerAssignMode m_assign_mode;
public:
DeviceManager();
@@ -35,14 +42,15 @@ public:
* Switching back to no-assign mode will also clear anything in devices that was associated with
* players in assign mode.
*/
bool noAssignMode() const { return m_no_assign_mode; }
void setNoAssignMode(const bool noAssignMode);
PlayerAssignMode playerAssignMode() const { return m_assign_mode; }
void setAssignMode(const PlayerAssignMode assignMode);
int getKeyboardAmount() const { return m_keyboard_amount; }
KeyboardDevice* getKeyboard(const int i) { return m_keyboards.get(i); }
/** Given some input, finds to which device it belongs and, using the corresponding device object,
maps this input to the corresponding player and game action. returns false if player/action could not be set */
maps this input to the corresponding player and game action. returns false if player/action could not be set.
Special case : can return true but set action to PA_FIRST if the input was used but is not associated to an action and a player */
bool mapInputToPlayerAndAction( Input::InputType type, int id0, int id1, int id2, int value, const bool programaticallyGenerated,
int* player /* out */, PlayerAction* action /* out */ );

View File

@@ -29,6 +29,8 @@ const int MULTIPLIER_MOUSE = 750;
struct Input
{
static const int MAX_VALUE = 32768;
enum AxisDirection {
AD_NEGATIVE,
AD_POSITIVE,

View File

@@ -317,33 +317,39 @@ void GamePadDevice::resetAxisDirection(const int axis, Input::AxisDirection dire
}
}
// -----------------------------------------------------------------------------
/**
* Player ID can either be a player ID or -1. If -1, the method only returns whether a binding exists for this player.
* If it's a player name, it also handles axis resets, direction changes, etc.
*/
bool GamePadDevice::hasBinding(Input::InputType type, const int id, const int value, const int player, PlayerAction* action /* out */)
{
if(m_prevAxisDirections == NULL) return false; // device not open
//if(player != m_player_id && player != -1) return false; // device open, but belongs to another player
if(type == Input::IT_STICKMOTION)
{
if(id >= m_axis_count) return false; // this gamepad doesn't even have that many axes
// going to negative from positive
if (value < 0 && m_prevAxisDirections[id] == Input::AD_POSITIVE)
if (player != -1)
{
// set positive id to 0
resetAxisDirection(id, Input::AD_POSITIVE, player);
}
// going to positive from negative
else if (value > 0 && m_prevAxisDirections[id] == Input::AD_NEGATIVE)
{
// set negative id to 0
resetAxisDirection(id, Input::AD_NEGATIVE, player);
// going to negative from positive
if (value < 0 && m_prevAxisDirections[id] == Input::AD_POSITIVE)
{
// set positive id to 0
resetAxisDirection(id, Input::AD_POSITIVE, player);
}
// going to positive from negative
else if (value > 0 && m_prevAxisDirections[id] == Input::AD_NEGATIVE)
{
// set negative id to 0
resetAxisDirection(id, Input::AD_NEGATIVE, player);
}
}
if(value > 0) m_prevAxisDirections[id] = Input::AD_POSITIVE;
else if(value < 0) m_prevAxisDirections[id] = Input::AD_NEGATIVE;
// check if within deadzone
if(value > -m_deadzone && value < m_deadzone)
if(value > -m_deadzone && value < m_deadzone && player != -1)
{
// Axis stands still: This is reported once for digital axes and
// can be called multipled times for analog ones. Uses the

View File

@@ -97,9 +97,6 @@ InputManager::~InputManager()
delete m_device_manager;
} // ~InputManager
#define MAX_VALUE 32768
void InputManager::handleStaticAction(int key, int value)
{
static int isWireframe = false;
@@ -186,7 +183,7 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, int btnID,
if(m_mode == INPUT_SENSE_GAMEPAD && type != Input::IT_STICKMOTION && type != Input::IT_STICKBUTTON) store_new = false;
// only store axes when they're pushed quite far
if(m_mode == INPUT_SENSE_GAMEPAD && type == Input::IT_STICKMOTION && abs(value) < MAX_VALUE *2/3) store_new = false;
if(m_mode == INPUT_SENSE_GAMEPAD && type == Input::IT_STICKMOTION && abs(value) < Input::MAX_VALUE *2/3) store_new = false;
// for axis bindings, we request at least 2 different values bhefore accepting (ignore non-moving axes
// as some devices have special axes that are at max value at rest)
@@ -223,7 +220,7 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, int btnID,
m_max_sensed_type = type;
// don't notify on first axis value (unless the axis is pushed to the maximum)
if(m_mode == INPUT_SENSE_GAMEPAD && type == Input::IT_STICKMOTION && first_value && abs(value) != MAX_VALUE)
if(m_mode == INPUT_SENSE_GAMEPAD && type == Input::IT_STICKMOTION && first_value && abs(value) != Input::MAX_VALUE)
{
std::cout << "not notifying on first value\n";
return;
@@ -231,7 +228,7 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, int btnID,
}
// Notify the completion of the input sensing when key is released
if( abs(value) < MAX_VALUE/2 && m_sensed_input->deviceID == deviceID &&
if( abs(value) < Input::MAX_VALUE/2 && m_sensed_input->deviceID == deviceID &&
m_sensed_input->btnID == btnID)
{
StateManager::gotSensedInput(m_sensed_input);
@@ -260,9 +257,12 @@ void InputManager::input(Input::InputType type, int deviceID, int btnID, int axi
bool action_found = m_device_manager->mapInputToPlayerAndAction( type, deviceID, btnID, axisDirection,
value, programaticallyGenerated, &player, &action );
if (action_found && action == PA_FIRST) return; // input handled internally by the device manager
// in menus, some keyboard keys are standard (before each player selected his device)
// FIXME: should enter always work to accept for a player using keyboard?
if(!StateManager::isGameState() && type == Input::IT_KEYBOARD && m_mode == MENU && m_device_manager->noAssignMode())
if(!StateManager::isGameState() && type == Input::IT_KEYBOARD && m_mode == MENU &&
m_device_manager->playerAssignMode() == NO_ASSIGN)
{
action = PA_FIRST;
@@ -311,7 +311,7 @@ void InputManager::input(Input::InputType type, int deviceID, int btnID, int axi
// menu input
if(!m_timer_in_use)
{
if(abs(value) > MAX_VALUE*2/3)
if(abs(value) > Input::MAX_VALUE*2/3)
{
m_timer_in_use = true;
m_timer = 0.25;
@@ -342,7 +342,7 @@ bool InputManager::input(const SEvent& event)
{
//const bool programaticallyGenerated = (event.UserEvent.UserData1 == 666 && event.UserEvent.UserData1 == 999);
const bool programaticallyGenerated = event.EventType == EET_KEY_INPUT_EVENT && (event.KeyInput.Char == 666);
const bool programaticallyGenerated = false; //event.EventType == EET_KEY_INPUT_EVENT && (event.KeyInput.Char == 666);
if(event.EventType == EET_JOYSTICK_INPUT_EVENT)
{
@@ -376,7 +376,8 @@ bool InputManager::input(const SEvent& event)
const bool isButtonPressed = event.JoystickEvent.IsButtonPressed(i);
if(gp->isButtonPressed(i) || isButtonPressed)
input(Input::IT_STICKBUTTON, event.JoystickEvent.Joystick, i, 0, isButtonPressed ? MAX_VALUE : 0, programaticallyGenerated);
input(Input::IT_STICKBUTTON, event.JoystickEvent.Joystick, i, 0,
isButtonPressed ? Input::MAX_VALUE : 0, programaticallyGenerated);
gp->setButtonPressed(i, isButtonPressed);
}
@@ -418,7 +419,7 @@ bool InputManager::input(const SEvent& event)
#else
ev.key.keysym.unicode,
#endif
MAX_VALUE, programaticallyGenerated);
Input::MAX_VALUE, programaticallyGenerated);
}
else