diff --git a/src/Makefile.am b/src/Makefile.am index 96f0f7dbc..a3acd6004 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,6 +26,7 @@ libstatic_ssg_a_CXXFLAGS = @NOREGMOVE@ AM_CPPFLAGS=-DSUPERTUXKART_DATADIR="\"$(datadir)/games/@PACKAGE@/\"" supertuxkart_SOURCES = main.cpp \ + actionmap.cpp actionmap.hpp \ material.cpp material.hpp \ material_manager.cpp material_manager.hpp \ attachment.cpp attachment.hpp \ @@ -38,7 +39,7 @@ supertuxkart_SOURCES = main.cpp \ music_ogg.cpp music_ogg.hpp \ sfx_openal.cpp sfx_openal.hpp \ utils.cpp utils.hpp \ - inputmap.cpp inputmap.hpp \ + input.hpp \ isect.cpp isect.hpp \ track.cpp track.hpp \ herring.cpp herring.hpp \ diff --git a/src/inputmap.cpp b/src/actionmap.cpp similarity index 65% rename from src/inputmap.cpp rename to src/actionmap.cpp index a08ef7e30..de9ae5d3d 100644 --- a/src/inputmap.cpp +++ b/src/actionmap.cpp @@ -1,4 +1,4 @@ -// $Id: inputmap.cpp 1259 2007-09-24 12:28:19Z thebohemian $ +// $Id: actionmap.cpp 1259 2007-09-24 12:28:19Z thebohemian $ // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2007 Robert Schuster @@ -19,45 +19,43 @@ #include -#include "player.hpp" -#include "player_kart.hpp" -#include "inputmap.hpp" +#include "input.hpp" +#include "actionmap.hpp" using namespace std; void -InputMap::putEntry(PlayerKart *kart, KartActions kc) +ActionMap::putEntry(Input input, GameAction ga) { - Player *p = kart->getPlayer(); - const Input *i = p->getInput(kc); - - Entry *e = new Entry(); - e->kart = kart; - e->action = kc; - - inputMap[key(i->type, i->id0, i->id1, i->id2)] = e; + inputMap[key(input)] = ga; } -InputMap::Entry * -InputMap::getEntry(InputType it, int id0, int id1, int id2) +GameAction +ActionMap::getEntry(Input input) { - return inputMap[key(it, id0, id1, id2)]; + return inputMap[key(input)]; +} + +GameAction +ActionMap::getEntry(InputType type, int id0, int id1, int id2) +{ + return inputMap[key(type, id0, id1, id2)]; } void -InputMap::clear() +ActionMap::clear() { - for (map::iterator i = inputMap.begin(); - i != inputMap.end(); i++) - { - delete i->second; - } - inputMap.clear(); } -InputMap::Key -InputMap::key(InputType it, int id0, int id1, int id2) +ActionMap::Key +ActionMap::key(Input input) +{ + return key(input.type, input.id0, input.id1, input.id2); +} + +ActionMap::Key +ActionMap::key(InputType it, int id0, int id1, int id2) { /* * A short reminder on the bit distribution and their usage: diff --git a/src/inputmap.hpp b/src/actionmap.hpp similarity index 70% rename from src/inputmap.hpp rename to src/actionmap.hpp index 3e7f5e55e..18ed6df61 100644 --- a/src/inputmap.hpp +++ b/src/actionmap.hpp @@ -1,4 +1,4 @@ -// $Id: inputmap.hpp 1259 2007-09-24 12:28:19Z thebohemian $ +// $Id: actionmap.hpp 1259 2007-09-24 12:28:19Z thebohemian $ // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2007 Robert Schuster @@ -17,35 +17,33 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_INPUTMAP_H -#define HEADER_INPUTMAP_H +#ifndef HEADER_ACTIONMAP_H +#define HEADER_ACTIONMAP_H #include -#include "player.hpp" +#include "input.hpp" -class Playerkart; - -class InputMap +/** + * This thing is named after what is put in as values. + */ +class ActionMap { typedef std::pair Key; public: - typedef struct - { - PlayerKart *kart; - KartActions action; - } Entry; void clear(); - void putEntry(PlayerKart *, KartActions); + void putEntry(Input, GameAction); - Entry *getEntry(InputType, int, int, int); + GameAction getEntry(Input); + GameAction getEntry(InputType, int, int, int); private: + inline Key key(Input); Key key(InputType, int, int, int); - std::map inputMap; + std::map inputMap; }; diff --git a/src/gui/base_gui.cpp b/src/gui/base_gui.cpp index a62ed412c..bc2039c93 100644 --- a/src/gui/base_gui.cpp +++ b/src/gui/base_gui.cpp @@ -24,145 +24,66 @@ #include "world.hpp" #include "menu_manager.hpp" -void BaseGUI::input(InputType type, int id0, int id1, int id2, int value) +void +BaseGUI::animateWidget(const int PREV_SELECTED_WGT, const int SELECTED_WGT) { - switch (type) - { - case IT_KEYBOARD: - inputKeyboard(id0, value); - break; - - case IT_MOUSEMOTION: - { - const int PREV_SELECTED_WGT = widget_manager->get_selected_wgt(); - const int SELECTED_WGT = widget_manager->handle_mouse( id0, id1 ); - - //FIXME: I should take WGT_NONE out of the class. - if( SELECTED_WGT != WidgetManager::WGT_NONE ) - { - if( PREV_SELECTED_WGT != WidgetManager::WGT_NONE ) - { - widget_manager->darken_wgt_color( PREV_SELECTED_WGT ); - } - - widget_manager->lighten_wgt_color( SELECTED_WGT ); - widget_manager->pulse_wgt( SELECTED_WGT ); - } - -#ifdef ALT_MOUSE_HANDLING - if (id0 == 1 && value) - if (id1 == AD_NEGATIVE) - inputKeyboard(SDLK_UP, 1); - else - inputKeyboard(SDLK_DOWN, 1); -#endif - break; - } - - case IT_MOUSEBUTTON: - if (!value) // Act on button release only. - switch (id0) - { - case SDL_BUTTON_LEFT: - select(); - break; - case SDL_BUTTON_RIGHT: - inputKeyboard(SDLK_ESCAPE, 0); - break; - } - break; - - case IT_STICKMOTION: - if (id1 == 0) - { - // X-Axis - inputKeyboard((id2 == AD_NEGATIVE) ? SDLK_LEFT : SDLK_RIGHT, !value); - } - else if (id1 == 1) - { - // Y-Axis - inputKeyboard((id2 == AD_NEGATIVE) ? SDLK_UP : SDLK_DOWN, !value); - } - break; - - case IT_STICKBUTTON: - if( !value) // act on button release only - switch (id1) // Button no - { - case 0: - inputKeyboard(SDLK_RETURN, 0); - break; - case 1: - inputKeyboard(SDLK_ESCAPE, 0); - break; - } - break; - default: - break; - } + if( SELECTED_WGT != WidgetManager::WGT_NONE ) + { + if( PREV_SELECTED_WGT != WidgetManager::WGT_NONE ) + { + widget_manager->darken_wgt_color( PREV_SELECTED_WGT ); + } + widget_manager->lighten_wgt_color( SELECTED_WGT ); + widget_manager->pulse_wgt( SELECTED_WGT ); + } } //----------------------------------------------------------------------------- -/** - * Important note: One day the STK engine code will have no notion of SDL - * key, button, axes and so on. It will only know actions like menu up, menu - * down, enter menu, leave menu, ... - * - * However this requires some major reworking. Until this is done SDL's keys - * take the role of the actions. That is why joystick axes & buttons and mouse - * buttons are converted to key input (see BaseGUI::input). - * - * When the game actions are implemented not dealing with the input mechanisms - * gives more flexibility: - * - issue no game actions when input sensing is active - * - what issues a certain game action can be conveniently selected - * (at compile or runtime, depending on the need) - * - * Please keep this goal in mind when you work on the input stuff. - */ -void BaseGUI::inputKeyboard(int key, int pressed) +void +BaseGUI::handle(GameAction action, int value) { // Skip on keypress, act on keyrelease only. - if (pressed) + if (value) return; + + int previous = widget_manager->get_selected_wgt(); - switch ( key ) + switch ( action ) { - case SDLK_LEFT: - case SDLK_RIGHT: - case SDLK_UP: - case SDLK_DOWN: - { - const int PREV_SELECTED_WGT = widget_manager->get_selected_wgt(); - const int SELECTED_WGT = widget_manager->handle_keyboard( key ); - - if( SELECTED_WGT != WidgetManager::WGT_NONE ) - { - if( PREV_SELECTED_WGT != WidgetManager::WGT_NONE ) - { - widget_manager->darken_wgt_color( PREV_SELECTED_WGT ); - } - - widget_manager->lighten_wgt_color( SELECTED_WGT ); - widget_manager->pulse_wgt( SELECTED_WGT ); - } - break; - } - - case SDLK_PLUS: - case SDLK_MINUS: - case SDLK_PAGEUP: - case SDLK_PAGEDOWN: - widget_manager->handle_keyboard( key ); - break; - - case SDLK_SPACE: - case SDLK_RETURN: + case GA_CURSOR_LEFT: + animateWidget(previous, + widget_manager->handle_left()); + + break; + case GA_CURSOR_RIGHT: + animateWidget(previous, + widget_manager->handle_right()); + break; + case GA_CURSOR_UP: + animateWidget(previous, + widget_manager->handle_up()); + break; + case GA_CURSOR_DOWN: + animateWidget(previous, + widget_manager->handle_down()); + break; + case GA_INC_SCROLL_SPEED: + widget_manager->increase_scroll_speed(); + break; + case GA_INC_SCROLL_SPEED_FAST: + widget_manager->increase_scroll_speed(true); + break; + case GA_DEC_SCROLL_SPEED: + widget_manager->decrease_scroll_speed(); + break; + case GA_DEC_SCROLL_SPEED_FAST: + widget_manager->decrease_scroll_speed(true); + break; + case GA_ENTER: select(); break; - - case SDLK_ESCAPE: + case GA_LEAVE: if (menu_manager->getMenuStackSize() > 1) { if(menu_manager->isCurrentMenu(MENUID_RACEMENU)) @@ -175,13 +96,13 @@ void BaseGUI::inputKeyboard(int key, int pressed) default: break; } // switch -} // inputKeyboard +} // handle //----------------------------------------------------------------------------- void BaseGUI::inputPointer(int x, int y) { const int PREV_SELECTED_WGT = widget_manager->get_selected_wgt(); - const int SELECTED_WGT = widget_manager->handle_mouse( x, y ); + const int SELECTED_WGT = widget_manager->handle_pointer( x, y ); if( SELECTED_WGT != WidgetManager::WGT_NONE ) { @@ -196,7 +117,8 @@ BaseGUI::inputPointer(int x, int y) } //----------------------------------------------------------------------------- -void BaseGUI::update(float dt) +void +BaseGUI::update(float dt) { widget_manager->update(dt); #if 0 @@ -206,13 +128,22 @@ void BaseGUI::update(float dt) } // update //----------------------------------------------------------------------------- -void BaseGUI::TimeToString(const float TIME, char *s) +void +BaseGUI::TimeToString(const float TIME, char *s) { int min = (int) floor ( TIME / 60.0 ) ; int sec = (int) floor ( TIME - (double) ( 60 * min ) ) ; int tenths = (int) floor ( 10.0f * (TIME - (double)(sec + 60* min))); sprintf ( s, "%d:%02d:%d", min, sec, tenths ) ; } // TimeToString +//----------------------------------------------------------------------------- +void +BaseGUI::inputKeyboard(SDLKey, int) +{ + // This method is not supposed to be called since BaseGUI does not + // handle low-level keyboard input. + assert(false); +} //----------------------------------------------------------------------------- /* EOF */ diff --git a/src/gui/base_gui.hpp b/src/gui/base_gui.hpp index c8d79992b..90eadd49d 100644 --- a/src/gui/base_gui.hpp +++ b/src/gui/base_gui.hpp @@ -20,19 +20,24 @@ #ifndef HEADER_BASEGUI_H #define HEADER_BASEGUI_H +#include + #include "player.hpp" class BaseGUI { + void animateWidget(const int, const int); + public: BaseGUI() {} virtual ~BaseGUI() {} virtual void update(float dt); virtual void select() = 0; - - virtual void input(InputType type, int id0, int id1, int id2, int value); - virtual void inputKeyboard(int key, int pressed); + + virtual void handle(GameAction, int); + + virtual void inputKeyboard(SDLKey, int); void inputPointer(int x, int y); diff --git a/src/gui/help_menu.cpp b/src/gui/help_menu.cpp index edc8a179c..eef800d1b 100644 --- a/src/gui/help_menu.cpp +++ b/src/gui/help_menu.cpp @@ -228,74 +228,74 @@ get stuck or fall too far, use the rescue button to get back on track.")); widget_manager->add_wgt(WTOK_LEFT, 20, 5); widget_manager->set_wgt_round_corners(WTOK_LEFT, WGT_AREA_LFT ); - widget_manager->set_wgt_text(WTOK_LEFT, sKartAction2String[KC_LEFT]); + widget_manager->set_wgt_text(WTOK_LEFT, sKartAction2String[KA_LEFT]); widget_manager->add_wgt(WTOK_LEFTKEY, 20, 5); widget_manager->set_wgt_round_corners(WTOK_LEFTKEY, WGT_AREA_RGT ); widget_manager->set_wgt_text(WTOK_LEFTKEY, - user_config->getInputAsString(0, (KartActions)KC_LEFT).c_str()); + user_config->getMappingAsString(0, (KartAction)KA_LEFT).c_str()); widget_manager->break_line(); widget_manager->add_wgt(WTOK_RIGHT, 20, 5); widget_manager->set_wgt_round_corners(WTOK_RIGHT, WGT_AREA_LFT ); - widget_manager->set_wgt_text(WTOK_RIGHT, sKartAction2String[KC_RIGHT]); + widget_manager->set_wgt_text(WTOK_RIGHT, sKartAction2String[KA_RIGHT]); widget_manager->add_wgt(WTOK_RIGHTKEY, 20, 5); widget_manager->set_wgt_round_corners(WTOK_RIGHTKEY, WGT_AREA_RGT ); widget_manager->set_wgt_text(WTOK_RIGHTKEY, - user_config->getInputAsString(0, (KartActions)KC_RIGHT).c_str()); + user_config->getMappingAsString(0, (KartAction)KA_RIGHT).c_str()); widget_manager->break_line(); widget_manager->add_wgt(WTOK_ACCEL, 20, 5); widget_manager->set_wgt_round_corners(WTOK_ACCEL, WGT_AREA_LFT ); - widget_manager->set_wgt_text(WTOK_ACCEL, sKartAction2String[KC_ACCEL]); + widget_manager->set_wgt_text(WTOK_ACCEL, sKartAction2String[KA_ACCEL]); widget_manager->add_wgt(WTOK_ACCELKEY, 20, 5); widget_manager->set_wgt_round_corners(WTOK_ACCELKEY, WGT_AREA_RGT ); widget_manager->set_wgt_text(WTOK_ACCELKEY, - user_config->getInputAsString(0, (KartActions)KC_ACCEL).c_str()); + user_config->getMappingAsString(0, (KartAction)KA_ACCEL).c_str()); widget_manager->break_line(); widget_manager->add_wgt(WTOK_BRAKE, 20, 5); widget_manager->set_wgt_round_corners(WTOK_BRAKE, WGT_AREA_LFT ); - widget_manager->set_wgt_text(WTOK_BRAKE, sKartAction2String[KC_BRAKE]); + widget_manager->set_wgt_text(WTOK_BRAKE, sKartAction2String[KA_BRAKE]); widget_manager->add_wgt(WTOK_BRAKEKEY, 20, 5); widget_manager->set_wgt_round_corners(WTOK_BRAKEKEY, WGT_AREA_RGT ); widget_manager->set_wgt_text(WTOK_BRAKEKEY, - user_config->getInputAsString(0, (KartActions)KC_BRAKE).c_str()); + user_config->getMappingAsString(0, (KartAction)KA_BRAKE).c_str()); widget_manager->break_line(); widget_manager->add_wgt(WTOK_WHEELIE, 20, 5); widget_manager->set_wgt_round_corners(WTOK_WHEELIE, WGT_AREA_LFT ); - widget_manager->set_wgt_text(WTOK_WHEELIE, sKartAction2String[KC_WHEELIE]); + widget_manager->set_wgt_text(WTOK_WHEELIE, sKartAction2String[KA_WHEELIE]); widget_manager->add_wgt(WTOK_WHEELIEKEY, 20, 5); widget_manager->set_wgt_round_corners(WTOK_WHEELIEKEY, WGT_AREA_RGT ); widget_manager->set_wgt_text(WTOK_WHEELIEKEY, - user_config->getInputAsString(0, (KartActions)KC_WHEELIE).c_str()); + user_config->getMappingAsString(0, (KartAction)KA_WHEELIE).c_str()); widget_manager->break_line(); widget_manager->add_wgt(WTOK_RESCUE, 20, 5); widget_manager->set_wgt_round_corners(WTOK_RESCUE, WGT_AREA_LFT ); - widget_manager->set_wgt_text(WTOK_RESCUE, sKartAction2String[KC_RESCUE]); + widget_manager->set_wgt_text(WTOK_RESCUE, sKartAction2String[KA_RESCUE]); widget_manager->add_wgt(WTOK_RESCUEKEY, 20, 5); widget_manager->set_wgt_round_corners(WTOK_RESCUEKEY, WGT_AREA_RGT ); widget_manager->set_wgt_text(WTOK_RESCUEKEY, - user_config->getInputAsString(0, (KartActions)KC_RESCUE).c_str()); + user_config->getMappingAsString(0, (KartAction)KA_RESCUE).c_str()); widget_manager->break_line(); widget_manager->add_wgt(WTOK_FIRE, 20, 5); widget_manager->set_wgt_round_corners(WTOK_FIRE, WGT_AREA_LFT ); - widget_manager->set_wgt_text(WTOK_FIRE, sKartAction2String[KC_FIRE]); + widget_manager->set_wgt_text(WTOK_FIRE, sKartAction2String[KA_FIRE]); widget_manager->add_wgt(WTOK_FIREKEY, 20, 5); widget_manager->set_wgt_round_corners(WTOK_FIREKEY, WGT_AREA_RGT ); widget_manager->set_wgt_text(WTOK_FIREKEY, - user_config->getInputAsString(0, (KartActions)KC_FIRE).c_str()); + user_config->getMappingAsString(0, (KartAction)KA_FIRE).c_str()); widget_manager->break_line(); widget_manager->add_wgt(WTOK_JUMP, 20, 5); widget_manager->set_wgt_round_corners(WTOK_JUMP, WGT_AREA_LFT ); - widget_manager->set_wgt_text(WTOK_JUMP, sKartAction2String[KC_JUMP]); + widget_manager->set_wgt_text(WTOK_JUMP, sKartAction2String[KA_JUMP]); widget_manager->add_wgt(WTOK_JUMPKEY, 20, 5); widget_manager->set_wgt_round_corners(WTOK_JUMPKEY, WGT_AREA_RGT ); widget_manager->set_wgt_text(WTOK_JUMPKEY, - user_config->getInputAsString(0, (KartActions)KC_JUMP).c_str()); + user_config->getMappingAsString(0, (KartAction)KA_JUMP).c_str()); widget_manager->break_line(); /*Buttons at the bottom*/ diff --git a/src/gui/main_menu.cpp b/src/gui/main_menu.cpp index b6d1316f2..ce03037ff 100644 --- a/src/gui/main_menu.cpp +++ b/src/gui/main_menu.cpp @@ -144,18 +144,19 @@ void MainMenu::select() } //----------------------------------------------------------------------------- -void MainMenu::inputKeyboard(int key, int pressed) +void MainMenu::handle(GameAction ga, int value) { - switch ( key ) + switch ( ga ) { - case SDLK_ESCAPE: //ESC - if(!pressed) + case GA_LEAVE: + if(!value) break; + menu_manager->pushMenu(MENUID_EXITGAME); break; default: - BaseGUI::inputKeyboard(key, pressed); + BaseGUI::handle(ga, value); break; } } diff --git a/src/gui/main_menu.hpp b/src/gui/main_menu.hpp index 1807aa021..ff1e4092f 100644 --- a/src/gui/main_menu.hpp +++ b/src/gui/main_menu.hpp @@ -29,7 +29,8 @@ public: ~MainMenu(); void select(); - void inputKeyboard(int key, int pressed); + void handle(GameAction, int); + }; #endif diff --git a/src/gui/menu_manager.cpp b/src/gui/menu_manager.cpp index fe1aa5d04..2a963713f 100644 --- a/src/gui/menu_manager.cpp +++ b/src/gui/menu_manager.cpp @@ -47,6 +47,7 @@ #include "grand_prix_select.hpp" #include "sound_manager.hpp" #include "sdldrv.hpp" +#include "user_config.hpp" #include "widget_manager.hpp" MenuManager* menu_manager= new MenuManager(); @@ -95,11 +96,12 @@ void MenuManager::update() if (m_handled_size != m_menu_stack.size()) { - if(m_current_menu==m_RaceGUI) + if (m_RaceGUI + && m_current_menu == m_RaceGUI) { m_RaceGUI = 0; - drv_showPointer(); - } + drv_setMode(MENU); + } delete m_current_menu; m_current_menu= NULL; @@ -138,7 +140,7 @@ void MenuManager::update() m_current_menu= new NumPlayers(); break; case MENUID_RACE: - drv_hidePointer(); + drv_setMode(INGAME); m_current_menu = new RaceGUI(); m_RaceGUI = m_current_menu; break; diff --git a/src/gui/player_controls.cpp b/src/gui/player_controls.cpp index c74ddcea9..a856e21b7 100644 --- a/src/gui/player_controls.cpp +++ b/src/gui/player_controls.cpp @@ -57,18 +57,18 @@ enum WidgetTokens WTOK_QUIT }; -const char *sKartAction2String[KC_LAST+1] = {_("Left"), _("Right"), _("Accelerate"), +/** Limits the maximum length of the player name. */ +const size_t PlayerControls::PLAYER_NAME_MAX = 10; + +const char *sKartAction2String[KA_LAST+1] = {_("Left"), _("Right"), _("Accelerate"), _("Brake"), _("Wheelie"), _("Jump"), _("Rescue"), _("Fire"), _("Look back") }; PlayerControls::PlayerControls(int whichPlayer): - m_grab_id(WidgetManager::WGT_NONE), m_player_index(whichPlayer), + m_player_index(whichPlayer), m_grab_input(false) { - // We need the unicode character here, so enable the translation - SDL_EnableUNICODE(1); - const bool SHOW_RECT = true; const bool SHOW_TEXT = true; widget_manager->set_initial_rect_state(SHOW_RECT, WGT_AREA_ALL, WGT_TRANS_BLACK); @@ -89,14 +89,14 @@ PlayerControls::PlayerControls(int whichPlayer): widget_manager->activate_wgt( WTOK_PLYR_NAME1); widget_manager->break_line(); - KartActions control; - for(int i=0; i<=KC_LAST; i++) + KartAction control; + for(int i = KA_FIRST; i <= KA_LAST; i++) { widget_manager->add_wgt( WTOK_KEY0 + i, 30, 7); widget_manager->set_wgt_text( WTOK_KEY0 + i, sKartAction2String[i]); - control = (KartActions)i; - m_key_names[control] = user_config->getInputAsString(m_player_index, control); + control = (KartAction)i; + m_key_names[control] = user_config->getMappingAsString(m_player_index, control); widget_manager->add_wgt( WTOK_LEFT + i, 30, 7); widget_manager->set_wgt_text( WTOK_LEFT + i, m_key_names[control].c_str()); widget_manager->activate_wgt( WTOK_LEFT + i); @@ -117,128 +117,169 @@ PlayerControls::~PlayerControls() { widget_manager->delete_wgts(); // The unicode translation is not generally needed, so disable it again. - SDL_EnableUNICODE(0); } // ~PlayerControls //----------------------------------------------------------------------------- -void PlayerControls::select() +void +PlayerControls::select() { - if (m_grab_input) return; + const int selected = widget_manager->get_selected_wgt(); + switch (selected) + { + case WTOK_PLYR_NAME1: + // Switch to typing in the player's name. + widget_manager->set_wgt_text(WTOK_PLYR_NAME1, (m_name + "<").c_str()); + + drv_setMode(LOWLEVEL); + + break; + case WTOK_QUIT: + // Leave menu. + menu_manager->popMenu(); + + break; + default: + // Switch to input sensing. - m_grab_id = widget_manager->get_selected_wgt(); - if(m_grab_id == WTOK_PLYR_NAME1) - { - m_grab_input = true; - return; - } - - const int MENU_CHOICE = widget_manager->get_selected_wgt() - WTOK_LEFT; - - if(MENU_CHOICE == WTOK_QUIT) - { - menu_manager->popMenu(); - return; - } - m_edit_action = static_cast(MENU_CHOICE); - m_grab_input = true; - drv_hidePointer(); - - widget_manager->set_wgt_text(m_grab_id, _("Press key")); + // If the only remaining and not yet handled widgets are the ones + // that deal with the kart controls and the values are still in the + // correct order the assertion should hold. If not did something + // bad. + assert (selected >= WTOK_LEFT + && selected <= WTOK_LOOK_BACK); + + + m_edit_action = static_cast(selected - WTOK_LEFT); + widget_manager->set_wgt_text(selected, _("Press key")); + + drv_setMode(INPUT_SENSE); + + break; + } } // select - //----------------------------------------------------------------------------- -void PlayerControls::input(InputType type, int id0, int id1, int id2, int value) +void +PlayerControls::inputKeyboard(SDLKey key, int unicode) { - if (m_grab_input && value) - { - // Handle input of user name - // ------------------------- - if(m_grab_id == WTOK_PLYR_NAME1) - { - if(type==IT_KEYBOARD) - { - // Ignore shift, otherwise shift will disable input - // (making it impossible to enter upper case characters) - if(id0==SDLK_RSHIFT || id0==SDLK_LSHIFT) return; - // Handle backspace - if(id0==SDLK_BACKSPACE) - { - if(m_name.size()>=1) m_name.erase(m_name.size()-1,1); - } - // All other control characters are ignored and will end - // entering the name - else if(id0<32 || id0>255) - { - m_grab_input = false; - m_grab_id = WidgetManager::WGT_NONE; - user_config->m_player[m_player_index].setName(m_name); -// BaseGUI::input(type, id0, id1, id2, value); - return; - } - else // Add the character to the name - { - // For this menu only unicode translation is enabled. - // So we use the unicode character here, since this will - // take care of upper/lower case etc. - m_name = m_name + (char)id1; - } - widget_manager->set_wgt_text(WTOK_PLYR_NAME1, m_name.c_str()); - } - else - { - // Ignore all other events, e.g. when pressing the mouse - // button twice on the input field etc. - } - } - // Handle the definition of a key - // ------------------------------ - else - { - drv_showPointer(); - m_grab_input = false; + switch (key) + { + case SDLK_RSHIFT: + case SDLK_LSHIFT: + // Ignore shift, otherwise shift will disable input + // (making it impossible to enter upper case characters) + case SDLK_SPACE: + // Ignore space to prevent invisible names. + + // Note: This will never happen as long as SPACE has a mapping which + // causes GA_ENTER and therefore finishes the typing. Please leave this + // because I am not sure whether this is good behavior (that SPACE + // cannot reach inputKeyboard()) and with some changes to the input + // driver this code has suddenly a useful effect. + case SDLK_KP_ENTER: + case SDLK_RETURN: + case SDLK_ESCAPE: + // Ignore some control keys. What they could provide is implemented + // in the handle() method. + return; + case SDLK_BACKSPACE: + // Handle backspace. + if (m_name.size() >=1) + m_name.erase(m_name.size()-1, 1); + + widget_manager->set_wgt_text(WTOK_PLYR_NAME1, (m_name + "<").c_str()); + break; + break; + default: + // Adds the character to the name. + // For this menu only unicode translation is enabled. + // So we use the unicode character here, since this will + // take care of upper/lower case etc. + if (unicode && m_name.size() <= PLAYER_NAME_MAX) + m_name += (char) unicode; + widget_manager->set_wgt_text(WTOK_PLYR_NAME1, (m_name + "<").c_str()); + break; + } - // Do not accept pressing ESC as input. - if (type != IT_KEYBOARD || id0 != SDLK_ESCAPE) - { - // Since unicode translation is enabled, the value of id1 will - // be the unicode value. Since unicode is usually not enabled - // in the race we have to set this value to zero (unicode - // translation is only enabled here to help entering the name), - // otherwise the keys will not be recognised in the race!! - if(type==IT_KEYBOARD) id1=0; - user_config->m_player[m_player_index].setInput(m_edit_action, type, - id0, id1, id2); - } +} +//----------------------------------------------------------------------------- +void +PlayerControls::clearMapping() +{ + const int selected = widget_manager->get_selected_wgt(); + if (selected >= WTOK_LEFT && selected <= WTOK_LOOK_BACK) + { + user_config->clearInput(m_player_index, + (KartAction) (selected - WTOK_LEFT)); + updateAllKeyLabels(); + } +} +//----------------------------------------------------------------------------- +void PlayerControls::handle(GameAction ga, int value) +{ + if (value) + return; + + switch (ga) + { + case GA_CLEAR_MAPPING: + clearMapping(); - widget_manager->set_wgt_text(m_grab_id, m_key_names[m_edit_action].c_str()); - } - } - else - BaseGUI::input(type, id0, id1, id2, value); + break; + case GA_SENSE_COMPLETE: + // Updates the configuration with the newly sensed input. + user_config->setInput(m_player_index, + m_edit_action, + drv_getSensedInput()); + // Fall through to recover the widget labels. + case GA_SENSE_CANCEL: + drv_setMode(MENU); + + // Refresh all key labels since they mave changed because of + // conflicting bindings. + updateAllKeyLabels(); + break; + case GA_ENTER: + // If the user is typing her name this will be finished at this + // point. + if (drv_isInMode(LOWLEVEL)) + { + // Prevents zero-length names. + if (m_name.length() == 0) + m_name = _("Player ") + m_player_index; + user_config->m_player[m_player_index].setName(m_name); + widget_manager->set_wgt_text(WTOK_PLYR_NAME1, m_name.c_str()); + + drv_setMode(MENU); + } + else + select(); + break; + case GA_LEAVE: + // If the user is typing her name this will be cancelled at this + // point. + if (drv_isInMode(LOWLEVEL)) + { + m_name = user_config->m_player[m_player_index].getName(); + widget_manager->set_wgt_text(WTOK_PLYR_NAME1, m_name.c_str()); + + drv_setMode(MENU); + break; + } + // Fall through to reach the usual GA_LEAVE code (leave menu). + default: + BaseGUI::handle(ga, value); + } + } //----------------------------------------------------------------------------- -/*void PlayerControls::addKeyLabel(int CHANGE_ID, KartActions control, bool start) +void +PlayerControls::updateAllKeyLabels() { - - setKeyInfoString(control); - - if (start) - widgetSet -> start(CHANGE_ID, m_key_names[control].c_str(), GUI_MED, control); - else - widgetSet -> state(CHANGE_ID, m_key_names[control].c_str(), GUI_MED, control); -} // addKeyLabel - -//----------------------------------------------------------------------------- -void PlayerControls::changeKeyLabel(int m_grab_id, KartActions control) -{ - setKeyInfoString(control); - widgetSet -> set_label(m_grab_id, m_key_names[control].c_str()); - // widgetSet -> layout(m_menu_id, 0, 0); -} // changeKeyLabel - -//----------------------------------------------------------------------------- -void PlayerControls::setKeyInfoString(KartActions control) -{ -} // setKeyInfoString -*/ + for (int i = KA_FIRST; i <= KA_LAST; i++) + { + m_key_names[i] = user_config->getMappingAsString(m_player_index, + (KartAction) i); + widget_manager->set_wgt_text(WTOK_LEFT + i, m_key_names[i].c_str()); + } +} diff --git a/src/gui/player_controls.hpp b/src/gui/player_controls.hpp index 141facd4c..3e66aa7cd 100644 --- a/src/gui/player_controls.hpp +++ b/src/gui/player_controls.hpp @@ -24,6 +24,7 @@ #include "player.hpp" #include +#include class PlayerControls: public BaseGUI { @@ -32,18 +33,30 @@ public: ~PlayerControls(); void select(); - void input(InputType type, int id0, int id1, int id2, int value); + void clearMapping(); + + void handle(GameAction, int); + void inputKeyboard(SDLKey, int); + void addKeyLabel(int change_id, KartAction control, bool start); + void setKeyInfoString(KartAction control); private: - int m_grab_id; + void updateAllKeyLabels(); + int m_player_index; bool m_grab_input; - KartActions m_edit_action; - // Stores the heading - making this an attribute here avoids - // memory leaks or complicated memory management - char m_heading[60]; + + /** Stores the KartAction for which the input is being sensed. */ + KartAction m_edit_action; + + // Stores the heading - making this an attribute here avoids + // memory leaks or complicated memory management + char m_heading[60]; + std::string m_name; - std::string m_key_names[KC_LAST+1]; + std::string m_key_names[KC_COUNT]; + + static const size_t PLAYER_NAME_MAX; }; #endif diff --git a/src/gui/race_gui.cpp b/src/gui/race_gui.cpp index ee79196d3..17e2afd3b 100644 --- a/src/gui/race_gui.cpp +++ b/src/gui/race_gui.cpp @@ -18,30 +18,21 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include -#include "user_config.hpp" #include "race_gui.hpp" + +#include "input.hpp" +#include "sdldrv.hpp" + +#include "user_config.hpp" #include "history.hpp" #include "track.hpp" #include "material_manager.hpp" #include "menu_manager.hpp" -#include "sdldrv.hpp" #include "translation.hpp" #include "font.hpp" -#include "inputmap.hpp" -RaceGUI::RaceGUI(): m_input_map (new InputMap()), m_time_left(0.0) +RaceGUI::RaceGUI(): m_time_left(0.0) { - if(user_config->m_fullscreen) - { - SDL_ShowCursor(SDL_DISABLE); - } - - if(!user_config->m_profile) - { - updateInputMappings(); - } // if !user_config->m_profile - // FIXME: translation problem m_pos_string[0] = "?!?"; m_pos_string[1] = "1st"; @@ -74,49 +65,85 @@ RaceGUI::RaceGUI(): m_input_map (new InputMap()), m_time_left(0.0) //----------------------------------------------------------------------------- RaceGUI::~RaceGUI() { - delete m_input_map; - - if(user_config->m_fullscreen) - { - SDL_ShowCursor(SDL_ENABLE); - } //FIXME: does all that material stuff need freeing somehow? } // ~Racegui //----------------------------------------------------------------------------- -void RaceGUI::updateInputMappings() +void +RaceGUI::handle(GameAction ga, int value) { - m_input_map->clear(); + static int isWireframe = false; + + // The next lines find out the player and kartaction that belongs + // to a certain gameaction value (GameAction -> Player number, Kartaction). + // Since the numbers are fixed this can be done through computation + // (instead of using e.g. a separate data structure). + // Note that the kartaction enum value and their representatives in + // gameaction enum have the same order (Otherwise the stuff below would + // not work ...)! + if (ga >= GA_FIRST_KARTACTION && ga <= GA_LAST_KARTACTION) + { + // 'Pulls down' the gameaction value to make them multiples of the + // kartaction values. + int ka = ga - GA_FIRST_KARTACTION; + + int playerNo = ka / KC_COUNT; + ka = ka % KC_COUNT; + + world->getPlayerKart(playerNo)->action((KartAction) ka, value); + + return; + } + + if (value) + return; + + switch (ga) + { + case GA_DEBUG_ADD_MISSILE: + if (world->m_race_setup.getNumPlayers() ==1 ) + { + Kart* kart = world->getPlayerKart(0); + kart->setCollectable((rand()%2)?COLLECT_MISSILE :COLLECT_HOMING_MISSILE, 10000); + } + break; + case GA_DEBUG_TOGGLE_FPS: + user_config->m_display_fps = !user_config->m_display_fps; + if(user_config->m_display_fps) + { + m_fps_timer.reset(); + m_fps_timer.setMaxDelta(1000); + m_fps_counter=0; + } + break; +#ifdef BULLET + case GA_DEBUG_BULLET: + user_config->m_bullet_debug = !user_config->m_bullet_debug; + break; +#endif + case GA_DEBUG_TOGGLE_WIREFRAME: + glPolygonMode(GL_FRONT_AND_BACK, isWireframe ? GL_FILL : GL_LINE); + isWireframe = ! isWireframe; + break; +#ifndef WIN32 + // For now disable F9 toggling fullscreen, since windows requires + // to reload all textures, display lists etc. Fullscreen can + // be toggled from the main menu (options->display). + case GA_TOGGLE_FULLSCREEN: + drv_toggleFullscreen(0); // 0: do not reset textures + // Fall through to put the game into pause mode. +#endif + case GA_LEAVE_RACE: + world->pause(); + menu_manager->pushMenu(MENUID_RACEMENU); + break; + case GA_DEBUG_HISTORY: + history->Save(); + break; + default: + break; + } // switch - // Defines the mappings for player keys to kart and action - // To avoid looping over all players to find out what - // player control key was pressed, a special data structure - // is set up: keysToKart contains for each (player assigned) - // key which kart it applies to (and therefore which player), - // and typeForKey contains the assigned function of that key. - const int NUM = world->m_race_setup.getNumPlayers(); - for(int i=0; i < NUM; i++) - { - PlayerKart* kart = world->getPlayerKart(i); - - for(int ka=(int)KC_FIRST; ka < (int)KC_LAST+1; ka++) - m_input_map->putEntry(kart, (KartActions) ka); - } - -} // UpdateKeyControl - -//----------------------------------------------------------------------------- -bool RaceGUI::handleInput(InputType type, int id0, int id1, int id2, int value) -{ - InputMap::Entry *e = m_input_map->getEntry(type, id0, id1, id2); - - if (e) - { - e->kart->action(e->action, value); - return true; - } - else - return false; } //----------------------------------------------------------------------------- @@ -127,78 +154,6 @@ void RaceGUI::update(float dt) cleanupMessages(); } // update -//----------------------------------------------------------------------------- -void RaceGUI::input(InputType type, int id0, int id1, int id2, int value) -{ - switch (type) - { - case IT_KEYBOARD: - // Stuff that handleInput() does not care about are - // internal keyboard actions. - if (!handleInput(type, id0, id1, id2, value)) - inputKeyboard(id0, value); - break; - default: // no keyboard event - handleInput(type, id0, id1, id2, value); - break; - } - -} - -//----------------------------------------------------------------------------- -void RaceGUI::inputKeyboard(int key, int pressed) -{ - if (!pressed) - return; - - static int isWireframe = false ; - switch ( key ) - { - case SDLK_F7: - if(world->m_race_setup.getNumPlayers()==1) - { // ctrl-r - Kart* kart = world->getPlayerKart(0); - kart->setCollectable((rand()%2)?COLLECT_MISSILE :COLLECT_HOMING_MISSILE, 10000); - } - break; - case SDLK_F12: - user_config->m_display_fps = !user_config->m_display_fps; - if(user_config->m_display_fps) - { - m_fps_timer.reset(); - m_fps_timer.setMaxDelta(1000); - m_fps_counter=0; - } - break; -#ifdef BULLET - case SDLK_F2: - user_config->m_bullet_debug = !user_config->m_bullet_debug; - break; -#endif - case SDLK_F11: - glPolygonMode(GL_FRONT_AND_BACK, isWireframe ? GL_FILL : GL_LINE); - isWireframe = ! isWireframe; - break; -#ifndef WIN32 - // For now disable F9 toggling fullscreen, since windows requires - // to reload all textures, display lists etc. Fullscreen can - // be toggled from the main menu (options->display). - case SDLK_F9: - drv_toggleFullscreen(0); // 0: do not reset textures - // Fall through to put the game into pause mode. -#endif - case SDLK_ESCAPE: // ESC - world->pause(); - menu_manager->pushMenu(MENUID_RACEMENU); - break; - case SDLK_F10: - history->Save(); - break; - default: - break; - } // switch -} // inputKeyboard - //----------------------------------------------------------------------------- void RaceGUI::drawFPS () { diff --git a/src/gui/race_gui.hpp b/src/gui/race_gui.hpp index 996f7c556..cf017612d 100644 --- a/src/gui/race_gui.hpp +++ b/src/gui/race_gui.hpp @@ -66,13 +66,12 @@ public: ~RaceGUI(); void update(float dt); void select() {} - void input(InputType type, int id0, int id1, int id2, int value); - void handleKartAction(KartActions ka, int value); + void handle(GameAction, int); + void handleKartAction(KartAction ka, int value); void addMessage(const char *message, Kart *kart, float time, int fonst_size, int red=255, int green=0, int blue=255); private: - InputMap *m_input_map; ulClock m_fps_timer; int m_fps_counter; char m_fps_string[10]; @@ -95,9 +94,6 @@ private: void drawAllMessages (Kart* player_kart, int offset_x, int offset_y, float ratio_x, float ratio_y ); - void updateInputMappings(); - bool handleInput(InputType type, int id0, int id1, int id2, int value); - void inputKeyboard(int key, int pressed); void drawPlayerIcons (); void oldDrawPlayerIcons (); void drawGameOverText (const float dt); diff --git a/src/gui/race_menu.cpp b/src/gui/race_menu.cpp index 0f3253ad4..f44c6c341 100644 --- a/src/gui/race_menu.cpp +++ b/src/gui/race_menu.cpp @@ -134,19 +134,20 @@ void RaceMenu::select() } //----------------------------------------------------------------------------- -void RaceMenu::inputKeyboard(int key, int pressed) +void RaceMenu::handle(GameAction ga, int value) { - switch ( key ) + switch ( ga ) { - case SDLK_ESCAPE: //ESC - if(!pressed) + case GA_LEAVE: + if (value) break; + world->unpause(); menu_manager->popMenu(); break; default: - BaseGUI::inputKeyboard(key, pressed); + BaseGUI::handle(ga, value); break; } } diff --git a/src/gui/race_menu.hpp b/src/gui/race_menu.hpp index ec7e1a3bd..1f8927601 100644 --- a/src/gui/race_menu.hpp +++ b/src/gui/race_menu.hpp @@ -29,7 +29,7 @@ public: ~RaceMenu(); void select(); - void inputKeyboard(int key, int pressed); + void handle(GameAction ga, int value); }; #endif diff --git a/src/gui/race_results_gui.cpp b/src/gui/race_results_gui.cpp index 4fc763b48..fe490eb3e 100644 --- a/src/gui/race_results_gui.cpp +++ b/src/gui/race_results_gui.cpp @@ -204,14 +204,14 @@ void RaceResultsGUI::select() } // select //----------------------------------------------------------------------------- void -RaceResultsGUI::inputKeyboard(int key, int pressed) +RaceResultsGUI::handle(GameAction ga, int value) { // Attempts to close the menu are silently discarded // since they do not make sense at this point. - if (key == SDLK_ESCAPE) + if (ga == GA_LEAVE) return; else - BaseGUI::inputKeyboard(key, pressed); + BaseGUI::handle(ga, value); } /* EOF */ diff --git a/src/gui/race_results_gui.hpp b/src/gui/race_results_gui.hpp index 2c9783505..7d5f2e878 100644 --- a/src/gui/race_results_gui.hpp +++ b/src/gui/race_results_gui.hpp @@ -33,7 +33,7 @@ public: RaceResultsGUI(); ~RaceResultsGUI(); - void inputKeyboard(int, int); + void handle(GameAction, int); void select(); }; diff --git a/src/input.hpp b/src/input.hpp new file mode 100644 index 000000000..fe26b1ae8 --- /dev/null +++ b/src/input.hpp @@ -0,0 +1,200 @@ +#ifndef TUXKART_INPUT_H +#define TUXKART_INPUT_H + +#include + +enum AxisDirection { + AD_NEGATIVE, + AD_POSITIVE, + AD_NEUTRAL +}; + +enum InputType { + IT_NONE = 0, + IT_KEYBOARD, + IT_STICKMOTION, + IT_STICKBUTTON, + IT_STICKHAT, + IT_MOUSEMOTION, + IT_MOUSEBUTTON +}; +const int IT_LAST = IT_MOUSEBUTTON; + +struct Input +{ + InputType type; + int id0; + int id1; + int id2; + + // Esoteric C++ feature alarm: structs are classes where the fields and + // methods are public by default. I just needed some convenient constructors + // for this struct. + Input() + : type(IT_NONE), id0(0), id1(0), id2(0) + { + // Nothing to do. + } + + /** Creates an Input instance which represents an arbitrary way of getting + * game input using a type specifier and 3 integers. + * + * Meaning of the 3 integers for each InputType: + * IT_NONE: This means nothing. In certain cases this is regarded as an + * unset binding. + * IT_KEYBOARD: id0 is an SDLKey value. + * IT_STICKMOTION: id0 - stick index, id1 - axis index, id2 - axis direction + * (negative, positive). You can assume that axis 0 is the X-Axis where the + * negative direction is to the left and that axis 1 is the Y-Axis with the + * negative direction being upwards. + * IT_STICKBUTTON: id0 - stick index, id1 - button index. Button 0 and 1 are + * usually reached most easily. + * IT_STICKHAT: This is not yet implemented. + * IT_MOUSEMOTION: id0 - axis index (0 -> X, 1 -> Y). Mouse wheel is + * represented as buttons! + * IT_MOUSEBUTTON: id0 - button number (1 -> left, 2 -> middle, 3 -> right, + * ...) + * + * Note: For joystick bindings that are actice in the menu the joystick's + * index should be zero. The binding will react to all joysticks connected + * to the system. + */ + Input(InputType ntype, int nid0 , int nid1 = 0, int nid2= 0) + : type(ntype), id0(nid0), id1(nid1), id2(nid2) + { + // Nothing to do. + } +}; + +//When adding any action at the beginning or at the end, remember to update +//the KA_FIRST and/or KA_LAST constants. +enum KartAction { + KA_LEFT, + KA_RIGHT, + KA_ACCEL, + KA_BRAKE, + KA_WHEELIE, + KA_JUMP, + KA_RESCUE, + KA_FIRE, + KA_LOOK_BACK +}; +const int KA_FIRST = KA_LEFT; +const int KA_LAST = KA_LOOK_BACK; +const int KC_COUNT = (KA_LAST + 1); + +enum GameAction +{ + // Below this are synthetic game actions which are never triggered through + // an input device. + GA_NULL, // Nothing dummy entry. + GA_SENSE_CANCEL, // Input sensing canceled. + GA_SENSE_COMPLETE, // Input sensing successfully finished. + + // Below this point are the game actions which can happen while in menu + // mode. + + GA_ENTER, // Enter menu, acknowledge, ... + GA_LEAVE, // Leave a menu. + + GA_CLEAR_MAPPING, // Clear an input mapping. + + GA_INC_SCROLL_SPEED, + GA_INC_SCROLL_SPEED_FAST, + GA_DEC_SCROLL_SPEED, + GA_DEC_SCROLL_SPEED_FAST, + + GA_CURSOR_UP, + GA_CURSOR_DOWN, + GA_CURSOR_LEFT, + GA_CURSOR_RIGHT, + + // The following game actions occur when in ingame mode (= within a race). + + GA_P1_LEFT, + GA_P1_RIGHT, + GA_P1_ACCEL, + GA_P1_BRAKE, + GA_P1_WHEELIE, + GA_P1_JUMP, + GA_P1_RESCUE, + GA_P1_FIRE, + GA_P1_LOOK_BACK, + + GA_P2_LEFT, + GA_P2_RIGHT, + GA_P2_ACCEL, + GA_P2_BRAKE, + GA_P2_WHEELIE, + GA_P2_JUMP, + GA_P2_RESCUE, + GA_P2_FIRE, + GA_P2_LOOK_BACK, + + GA_P3_LEFT, + GA_P3_RIGHT, + GA_P3_ACCEL, + GA_P3_BRAKE, + GA_P3_WHEELIE, + GA_P3_JUMP, + GA_P3_RESCUE, + GA_P3_FIRE, + GA_P3_LOOK_BACK, + + GA_P4_LEFT, + GA_P4_RIGHT, + GA_P4_ACCEL, + GA_P4_BRAKE, + GA_P4_WHEELIE, + GA_P4_JUMP, + GA_P4_RESCUE, + GA_P4_FIRE, + GA_P4_LOOK_BACK, + + GA_TOGGLE_FULLSCREEN, // Switch between windowed/fullscreen mode + GA_LEAVE_RACE, // Switch from race to menu. + + GA_DEBUG_ADD_MISSILE, + GA_DEBUG_TOGGLE_FPS, + GA_DEBUG_BULLET, + GA_DEBUG_TOGGLE_WIREFRAME, + GA_DEBUG_HISTORY + +}; +/* Some constants to make future changes more easier to handle. If you use + * any of the GameAction constants to mark the beginning or end of a range + * or denote a count then something is wrong with your code. ;) + */ + +/** Generally the first GameAction constant. Unlikely to change. */ +const int GA_FIRST = GA_NULL; + +/** A usefull value for array allocations. Should always be to the + * last constant + 1. + */ +const int GA_COUNT = (GA_DEBUG_HISTORY + 1); + +/* The range of GameAction constants that is used while in menu mode. */ +const int GA_FIRST_MENU = GA_ENTER; +const int GA_LAST_MENU = GA_CURSOR_RIGHT; + +/* The range of GameAction constants that is used while in ingame (race) mode. */ +const int GA_FIRST_INGAME = GA_P1_LEFT; +const int GA_LAST_INGAME = GA_DEBUG_HISTORY; + +/* The range of GameAction constants which are used ingame but are considered + * fixed and their Inputs should not be used by the players. + */ +const int GA_FIRST_INGAME_FIXED = GA_TOGGLE_FULLSCREEN; +const int GA_LAST_INGAME_FIXED = GA_DEBUG_HISTORY; + +/** The range of GameAction constants that defines the mapping + for the players kart actions. Besides that these are the actions + whose mappings are changeable by the user (via menu & config file). + When looking for conflicting mappings only the user changeable + GameAction constants are regarded. +*/ +const int GA_FIRST_KARTACTION = GA_P1_LEFT; +const int GA_LAST_KARTACTION = GA_P4_LOOK_BACK; + +#endif diff --git a/src/main.cpp b/src/main.cpp index de28f52ce..44f615887 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -391,6 +391,7 @@ int main(int argc, char *argv[] ) //FIXME: this needs a better organization drv_init(); + game_manager = new GameManager (); // loadMaterials needs ssgLoadTextures (internally), which can // only be called after ssgInit (since this adds the actual loader) diff --git a/src/player.hpp b/src/player.hpp index dd52ec62f..f6c02e8b0 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -21,42 +21,16 @@ #define TUXKART_PLAYER_H #include +#include "input.hpp" -enum AxisDirection { AD_NEGATIVE, AD_POSITIVE }; +extern const char *sKartAction2String[KA_LAST+1]; -enum InputType { IT_NONE, IT_KEYBOARD, IT_STICKMOTION, IT_STICKBUTTON, IT_STICKHAT, IT_MOUSEMOTION, IT_MOUSEBUTTON }; -#define IT_LAST (IT_MOUSEBUTTON) - -typedef struct -{ - InputType type; - int id0; - int id1; - int id2; -} -Input; - -//When adding any action at the beginning or at the end, remember to update -//the KC_FIRST and/or KC_LAST constants. -enum KartActions { KC_LEFT, - KC_RIGHT, - KC_ACCEL, - KC_BRAKE, - KC_WHEELIE, - KC_JUMP, - KC_RESCUE, - KC_FIRE, - KC_LOOK_BACK }; -const int KC_FIRST = KC_LEFT; -const int KC_LAST = KC_LOOK_BACK; - -extern const char *sKartAction2String[KC_LAST+1]; /*class for managing player name and control configuration*/ class Player { private: std::string m_name; - Input m_action_map[KC_LAST+1]; + Input m_action_map[KA_LAST+1]; unsigned int m_last_kart_id; public: @@ -64,21 +38,8 @@ public: Player(const std::string &name_):m_name(name_){} void setName(const std::string &name_){m_name = name_;} - void setKey(KartActions action, int key) {} - void setButton(KartActions action, int button){ } - std::string getName() {return m_name;} - Input *getInput(KartActions action) { return &m_action_map[action]; } - void setInput(KartActions action, InputType type, int id0, int id1, int id2) - { - Input *i = &m_action_map[action]; - i->type = type; - i->id0 = id0; - i->id1 = id1; - i->id2 = id2; - } - unsigned int getLastKartId(){ return m_last_kart_id; } void setLastKartId(int newLastKartId){ m_last_kart_id = newLastKartId; } }; diff --git a/src/player_kart.cpp b/src/player_kart.cpp index be445cd7f..847905fff 100644 --- a/src/player_kart.cpp +++ b/src/player_kart.cpp @@ -30,11 +30,11 @@ #include "translation.hpp" #include "camera.hpp" -void PlayerKart::action(KartActions action, int value) +void PlayerKart::action(KartAction action, int value) { switch (action) { - case KC_LEFT: + case KA_LEFT: m_steer_val_l = -value; if (value) m_steer_val = -value; @@ -42,7 +42,7 @@ void PlayerKart::action(KartActions action, int value) m_steer_val = m_steer_val_r; break; - case KC_RIGHT: + case KA_RIGHT: m_steer_val_r = value; if (value) m_steer_val = value; @@ -50,27 +50,27 @@ void PlayerKart::action(KartActions action, int value) m_steer_val = m_steer_val_l; break; - case KC_ACCEL: + case KA_ACCEL: m_accel_val = value; break; - case KC_BRAKE: + case KA_BRAKE: if (value) m_accel_val = 0; m_controls.brake = value; break; - case KC_WHEELIE: + case KA_WHEELIE: m_controls.wheelie = value; break; - case KC_RESCUE: + case KA_RESCUE: m_controls.rescue = value; break; - case KC_FIRE: + case KA_FIRE: m_controls.fire = value; break; - case KC_LOOK_BACK: + case KA_LOOK_BACK: m_camera->setReverseHeading(value); break; - case KC_JUMP: + case KA_JUMP: m_controls.jump = value; break; } diff --git a/src/player_kart.hpp b/src/player_kart.hpp index dc66a4575..1469bbb6c 100644 --- a/src/player_kart.hpp +++ b/src/player_kart.hpp @@ -51,7 +51,7 @@ public: Player* getPlayer () {return m_player; } void update (float); void addMessages (); - void action (KartActions action, int value); + void action (KartAction action, int value); void forceCrash (); void handleZipper (); void collectedHerring (Herring* herring); diff --git a/src/sdldrv.cpp b/src/sdldrv.cpp index dc39ac701..f8730a448 100755 --- a/src/sdldrv.cpp +++ b/src/sdldrv.cpp @@ -17,10 +17,14 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + #include #include +#include "input.hpp" +#include "actionmap.hpp" #include "user_config.hpp" #include "sdldrv.hpp" #include "material_manager.hpp" @@ -34,17 +38,27 @@ #include "gui/menu_manager.hpp" #include "player.hpp" #include "gui/font.hpp" +#include "user_config.hpp" + +Input *sensedInput = 0; +ActionMap *actionMap = 0; SDL_Surface *mainSurface; long flags; -SDL_Joystick **sticks; +StickInfo **stickInfos = 0; -bool pointerVisible = true; +InputDriverMode mode = BOOTSTRAP; -#define DEADZONE_MOUSE 1 +#define DEADZONE_MOUSE 150 +#define DEADZONE_MOUSE_SENSE 200 #define DEADZONE_JOYSTICK 1000 #define MULTIPLIER_MOUSE 750 + +/** Helper values to store and track the relative mouse movements. If these + * values exceed the deadzone value the input is reported to the game. This + * Makes the mouse behave like an analog axis on a gamepad/joystick. + */ int mouseValX = 0; int mouseValY = 0; @@ -63,28 +77,29 @@ void drv_init() SDL_JoystickEventState(SDL_ENABLE); - const int NUM_STICKS = SDL_NumJoysticks(); - sticks = (SDL_Joystick **) malloc(sizeof(SDL_Joystick *) * NUM_STICKS); - for (int i=0;iisSomewhereOnStack(MENUID_RACE)) - drv_showPointer(); + showPointer(); } else if(menu_manager->isSomewhereOnStack(MENUID_RACE)) - drv_hidePointer(); + hidePointer(); SDL_FreeSurface(mainSurface); mainSurface = SDL_SetVideoMode(user_config->m_width, user_config->m_height, 0, flags); @@ -149,9 +164,9 @@ void drv_deinit() { const int NUM_STICKS = SDL_NumJoysticks(); for (int i=0;igetCurrentMenu(); - if (menu != NULL) - menu->input(type, id0, id1, id2, value); + BaseGUI* menu = menu_manager->getCurrentMenu(); + + GameAction ga = actionMap->getEntry(type, id0, id1, id2); + + if (menu != NULL) + { + // Act different in input sensing mode. + if (mode == INPUT_SENSE) + { + // Input sensing should be canceled. + if (ga == GA_LEAVE) + { + menu->handle(GA_SENSE_CANCEL, value); + } + // Stores the sensed input when the button/key/axes/ is + // released only and is not used in a fixed mapping. + else if (!(value || user_config->isFixedInput(type, id0, id1, id2))) + { + sensedInput->type = type; + sensedInput->id0 = id0; + sensedInput->id1 = id1; + sensedInput->id2 = id2; + + // Notify the completion of the input sensing. + menu->handle(GA_SENSE_COMPLETE, 0); + } + } + else if (ga != GA_NULL) + { + // Lets the currently active menu handle the GameAction. + menu->handle(ga, value); + } + } } //----------------------------------------------------------------------------- +/** Reads the SDL event loop, does some tricks with certain values and calls + * input() is appropriate. + * + * Digital inputs get the value of 32768 when pressed (key/button press, + * digital axis) because this is what SDL provides. Relative mouse inputs + * which do not fit into this scheme are converted to match. This is done to + * relieve the KartAction implementor from the need to think about different + * input devices and how SDL treats them. The same input gets the value of 0 + * when released. + * + * Analog axes can have any value from [0, 32768]. + * + * 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). + * + */ void drv_loop() { SDL_Event ev; @@ -180,36 +254,61 @@ void drv_loop() break; case SDL_KEYUP: - input(IT_KEYBOARD, ev.key.keysym.sym, ev.key.keysym.unicode, 0, 0); + input(IT_KEYBOARD, ev.key.keysym.sym, 0, 0, 0); break; - case SDL_KEYDOWN: - input(IT_KEYBOARD, ev.key.keysym.sym, ev.key.keysym.unicode, 0, 32768); + if (mode == LOWLEVEL) + { + // Unicode translation in SDL is only done for keydown events. + // Therefore for lowlevel keyboard handling we provide no notion + // of whether a key was pressed or released. + menu_manager->getCurrentMenu() + ->inputKeyboard(ev.key.keysym.sym, + ev.key.keysym.unicode); + } + input(IT_KEYBOARD, ev.key.keysym.sym, + ev.key.keysym.unicode, 0, 32768); + break; case SDL_MOUSEMOTION: - if (pointerVisible) - { - BaseGUI* menu= menu_manager->getCurrentMenu(); - if (menu != NULL) - menu->inputPointer(ev.motion.x, mainSurface->h - ev.motion.y); + // Reports absolute pointer values on a separate path to the menu + // system to avoid the trouble that arises because all other input + // methods have only one value to inspect (pressed/release, + // axis value) while the pointer has two. + if (!mode) + { + BaseGUI* menu = menu_manager->getCurrentMenu(); + if (menu != NULL) + menu->inputPointer(ev.motion.x, mainSurface->h - ev.motion.y); + } + // If sensing input mouse movements are made less sensitive in order + // to avoid it being detected unwantedly. + else if (mode == INPUT_SENSE) + { + if (ev.motion.xrel <= -DEADZONE_MOUSE_SENSE) + input(IT_MOUSEMOTION, 0, AD_NEGATIVE, 0, 0); + else if (ev.motion.xrel >= DEADZONE_MOUSE_SENSE) + input(IT_MOUSEMOTION, 0, AD_POSITIVE, 0, 0); - // Reset parameters for relative mouse handling to make sure we are - // in a valid state when returning to relative mouse mode - mouseValX = mouseValY = 0; - } - else - { - mouseValX = std::max(-32768, - std::min(32768, mouseValX - + ev.motion.xrel - * MULTIPLIER_MOUSE)); - mouseValY = std::max(-32768, - std::min(32768, mouseValY - + ev.motion.yrel - * MULTIPLIER_MOUSE)); - } - break; + if (ev.motion.yrel <= -DEADZONE_MOUSE_SENSE) + input(IT_MOUSEMOTION, 1, AD_NEGATIVE, 0, 0); + else if (ev.motion.yrel >= DEADZONE_MOUSE_SENSE) + input(IT_MOUSEMOTION, 1, AD_POSITIVE, 0, 0); + } + else + { + // Calculates new values for the mouse helper variables. It + // keeps them in the [-32768, 32768] range. The same values are + // used by SDL for stick axes. + mouseValX = std::max(-32768, std::min(32768, + mouseValX + ev.motion.xrel + * MULTIPLIER_MOUSE)); + mouseValY = std::max(-32768, + std::min(32768, mouseValY + ev.motion.yrel + * MULTIPLIER_MOUSE)); + } + break; case SDL_MOUSEBUTTONUP: input(IT_MOUSEBUTTON, ev.button.button, 0, 0, 0); break; @@ -219,37 +318,266 @@ void drv_loop() break; case SDL_JOYAXISMOTION: + // If the joystick axis exceeds the deadzone report the input. + // In menu mode (mode = MENU = 0) the joystick number is reported + // to be zero in all cases. This has the neat effect that all + // joysticks can be used to control the menu. if(ev.jaxis.value <= -DEADZONE_JOYSTICK) - input(IT_STICKMOTION, ev.jaxis.which, ev.jaxis.axis, AD_NEGATIVE, -ev.jaxis.value); - else if(ev.jaxis.value <= 0) - input(IT_STICKMOTION, ev.jaxis.which, ev.jaxis.axis, AD_NEGATIVE, 0); - - if(ev.jaxis.value >= DEADZONE_JOYSTICK) - input(IT_STICKMOTION, ev.jaxis.which, ev.jaxis.axis, AD_POSITIVE, ev.jaxis.value); - else if(ev.jaxis.value >= 0) - input(IT_STICKMOTION, ev.jaxis.which, ev.jaxis.axis, AD_POSITIVE, 0); + { + input(IT_STICKMOTION, !mode ? 0 : ev.jaxis.which, + ev.jaxis.axis, AD_NEGATIVE, -ev.jaxis.value); + stickInfos[ev.jaxis.which]->prevAxisDirections[ev.jaxis.axis] + = AD_NEGATIVE; + } + else if(ev.jaxis.value >= DEADZONE_JOYSTICK) + { + input(IT_STICKMOTION, !mode ? 0 : ev.jaxis.which, + ev.jaxis.axis, AD_POSITIVE, ev.jaxis.value); + stickInfos[ev.jaxis.which]->prevAxisDirections[ev.jaxis.axis] + = AD_POSITIVE; + } + else + { + // Axis stands still: This is reported once for digital axes and + // can be called multipled times for analog ones. Uses the + // previous direction in which the axis was triggered to + // determine which one has to be brought into the released + // state. This allows us to regard two directions of an axis + // as completely independent input variants (as if they where + // two buttons). + if (stickInfos[ev.jaxis.which] + ->prevAxisDirections[ev.jaxis.axis] == AD_NEGATIVE) + input(IT_STICKMOTION, !mode ? 0 : ev.jaxis.which, + ev.jaxis.axis, AD_NEGATIVE, 0); + else if (stickInfos[ev.jaxis.which] + ->prevAxisDirections[ev.jaxis.axis] == AD_POSITIVE) + input(IT_STICKMOTION, !mode ? 0 : ev.jaxis.which, + ev.jaxis.axis, AD_POSITIVE, 0); + + stickInfos[ev.jaxis.which]->prevAxisDirections[ev.jaxis.axis] + = AD_NEUTRAL; + } break; case SDL_JOYBUTTONUP: - input(IT_STICKBUTTON, ev.jbutton.which, ev.jbutton.button, 0, + // See the SDL_JOYAXISMOTION case label because of !mode thingie. + input(IT_STICKBUTTON, !mode ? 0 : ev.jbutton.which, ev.jbutton.button, 0, 0); break; case SDL_JOYBUTTONDOWN: - input(IT_STICKBUTTON, ev.jbutton.which, ev.jbutton.button, 0, + // See the SDL_JOYAXISMOTION case label because of !mode thingie. + input(IT_STICKBUTTON, !mode ? 0 : ev.jbutton.which, ev.jbutton.button, 0, 32768); break; } // switch } // while (SDL_PollEvent()) // Makes mouse behave like an analog axis. - if (mouseValX <= DEADZONE_MOUSE) - input(IT_MOUSEMOTION, 0, AD_NEGATIVE, 0, -mouseValX); - else if (mouseValX >= DEADZONE_MOUSE) - input(IT_MOUSEMOTION, 0, AD_POSITIVE, 0, mouseValX); + if (mouseValX <= -DEADZONE_MOUSE) + input(IT_MOUSEMOTION, 0, AD_NEGATIVE, 0, -mouseValX); + else if (mouseValX >= DEADZONE_MOUSE) + input(IT_MOUSEMOTION, 0, AD_POSITIVE, 0, mouseValX); + else + mouseValX = 0; - if (mouseValY <= DEADZONE_MOUSE) - input(IT_MOUSEMOTION, 1, AD_NEGATIVE, 0, -mouseValY); - else if (mouseValY >= DEADZONE_MOUSE) - input(IT_MOUSEMOTION, 1, AD_POSITIVE, 0, mouseValY); + if (mouseValY <= -DEADZONE_MOUSE) + input(IT_MOUSEMOTION, 1, AD_NEGATIVE, 0, -mouseValY); + else if (mouseValY >= DEADZONE_MOUSE) + input(IT_MOUSEMOTION, 1, AD_POSITIVE, 0, mouseValY); + else + mouseValY = 0; } + +//----------------------------------------------------------------------------- +/** Retrieves the Input instance that has been prepared in the input sense + * mode. + * + * The Instance has valid values of the last input sensing operation *only* + * if called immediately after a BaseGUI::handle() implementation received + * GA_SENSE_COMPLETE. + * + * It is wrong to call it when not in input sensing mode anymore. + */ +Input & +drv_getSensedInput() +{ + assert (mode == INPUT_SENSE); + + // sensedInput should be available in input sense mode. + assert (sensedInput); + + return *sensedInput; +} + +//----------------------------------------------------------------------------- +/** Queries the input driver whether it is in the given expected mode. + */ +bool +drv_isInMode(InputDriverMode expMode) +{ + return mode == expMode; +} +//----------------------------------------------------------------------------- +/** 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 menu mode the pointer is visible (and reports absolute values through + * BaseGUI::inputKeyboard()) and the BaseGUI::handle() implementations can + * receive GameAction values from GA_FIRST_MENU to GA_LAST_MENU. + * + * In ingame mode the pointer is invisible (and reports relative values) + * and the BaseGUI::handle() implementations can receive GameAction values + * from GA_FIRST_INGAME to GA_LAST_INGAME. + * + * 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_getSensedInput() *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. + * + * In lowlevel mode the pointer is invisible (and reports relative values - + * this is just a side effect). BaseGUI::handle() can receive GameAction + * values from GA_FIRST_MENU to GA_LAST_MENU. Additionally each key press is + * distributed through BaseGUI::inputKeyboard(). This happens *before* the + * same keypress is processed to be distributed as a GameAction. This was done + * to make the effects of changing the input driver's mode from + * BaseGUI::handle() implementations less strange. The way it is implemented + * makes sure that such a change only affects the next keypress or keyrelease. + * The same is not true for mode changes from within a BaseGUI::inputKeyboard() + * implementation. It is therefore discouraged. + * + * 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 +drv_setMode(InputDriverMode newMode) +{ + switch (newMode) + { + case MENU: + switch (mode) + { + case INGAME: + // Leaving ingame mode. + + if (actionMap) + delete actionMap; + + // Reset the helper values for the relative mouse movement + // supresses to the notification of them as an input. + mouseValX = mouseValY = 0; + + showPointer(); + + // Fall through expected. + case BOOTSTRAP: + // Leaving boot strap mode. + + // Installs the action map for the menu. + actionMap = user_config->newMenuActionMap(); + + mode = MENU; + + break; + case INPUT_SENSE: + // Leaving input sense mode. + + showPointer(); + + // The order is deliberate just in case someone starts to make + // STK multithreaded: sensedInput must not be 0 when + // mode == INPUT_SENSE. + mode = MENU; + + delete sensedInput; + sensedInput = 0; + + break; + case LOWLEVEL: + // Leaving lowlevel mode. + + SDL_EnableUNICODE(SDL_DISABLE); + + showPointer(); + + mode = MENU; + + break; + default: + // Something is broken. + assert (false); + } + + break; + case INGAME: + // We must be in menu mode now in order to switch. + assert (mode == MENU); + + if (actionMap) + delete actionMap; + + // Installs the action map for the ingame mode. + actionMap = user_config->newIngameActionMap(); + + hidePointer(); + + mode = INGAME; + + break; + case INPUT_SENSE: + // We must be in menu mode now in order to switch. + assert (mode == MENU); + + // Reset the helper values for the relative mouse movement supresses to + // the notification of them as an input. + mouseValX = mouseValY = 0; + + sensedInput = new Input(); + + hidePointer(); + + mode = INPUT_SENSE; + + break; + case LOWLEVEL: + // We must be in menu mode now in order to switch. + assert (mode == MENU); + + SDL_EnableUNICODE(SDL_ENABLE); + + hidePointer(); + + mode = LOWLEVEL; + + break; + default: + // Invalid mode. + assert(false); + } +} + +StickInfo::StickInfo(int index) +{ + sdlJoystick = SDL_JoystickOpen(index); + const int count = SDL_JoystickNumAxes(sdlJoystick); + prevAxisDirections = new AxisDirection[count]; + + for (int i = 0; i < count; i++) + prevAxisDirections[i] = AD_NEUTRAL; +} + +StickInfo::~StickInfo() +{ + delete prevAxisDirections; + + SDL_JoystickClose(sdlJoystick); +} diff --git a/src/sdldrv.hpp b/src/sdldrv.hpp index 2c22eb978..fc25589f8 100755 --- a/src/sdldrv.hpp +++ b/src/sdldrv.hpp @@ -20,16 +20,41 @@ #ifndef HEADER_SDLDRV_H #define HEADER_SDLDRV_H +#include + +#include "input.hpp" + +class ActionMap; + enum MouseState { INITIAL, MOVED, RESET_NEEDED }; +enum InputDriverMode { + MENU = 0, + INGAME, + INPUT_SENSE, + LOWLEVEL, + BOOTSTRAP +}; + +struct StickInfo { + SDL_Joystick *sdlJoystick; + AxisDirection *prevAxisDirections; + + StickInfo(int); + ~StickInfo(); +}; + void drv_init(); void drv_deinit(); -void drv_showPointer(); -void drv_hidePointer(); - void drv_toggleFullscreen(int resetTextures=1); void drv_loop(); +void drv_setMode(InputDriverMode); +bool drv_isInMode(InputDriverMode); + +Input &drv_getSensedInput(); + + #endif diff --git a/src/user_config.cpp b/src/user_config.cpp index d7ddb076e..d3636e187 100644 --- a/src/user_config.cpp +++ b/src/user_config.cpp @@ -21,6 +21,7 @@ #include #include +#include // for mkdir: #if !defined(WIN32) || defined(__CYGWIN__) @@ -33,6 +34,7 @@ #include #include +#include "actionmap.hpp" #include "user_config.hpp" #include "lisp/lisp.hpp" #include "lisp/parser.hpp" @@ -42,6 +44,9 @@ # define snprintf _snprintf #endif +using namespace std; +using namespace lisp; + UserConfig *user_config; UserConfig::UserConfig() @@ -65,7 +70,7 @@ UserConfig::~UserConfig() // ----------------------------------------------------------------------------- std::string UserConfig::getConfigDir() { - std::string DIRNAME; + string DIRNAME; #ifdef WIN32 // For now the old windows config way is used: store a config file // in the current directory (in other OS a special subdirectory is created) @@ -141,51 +146,152 @@ void UserConfig::setDefaults() { m_player[i].setName(m_username); } + + // Clear every entry. + memset(inputMap, sizeof(inputMap), 0); + + /* general game input settings */ + set(GA_ENTER, + Input(IT_KEYBOARD, SDLK_RETURN), + Input(IT_KEYBOARD, SDLK_SPACE), + Input(IT_STICKBUTTON, 0, 0), + Input(IT_MOUSEBUTTON, 1)); + set(GA_LEAVE, + Input(IT_KEYBOARD, SDLK_ESCAPE), + Input(IT_STICKBUTTON, 0, 1), + Input(IT_MOUSEBUTTON, 2), + Input(IT_MOUSEBUTTON, 3)); + set(GA_CURSOR_UP, + Input(IT_KEYBOARD, SDLK_UP), + Input(IT_STICKMOTION, 0, 1, AD_NEGATIVE)); + + set(GA_CURSOR_DOWN, + Input(IT_KEYBOARD, SDLK_DOWN), + Input(IT_STICKMOTION, 0, 1, AD_POSITIVE)); + + set(GA_CURSOR_LEFT, + Input(IT_KEYBOARD, SDLK_LEFT), + Input(IT_STICKMOTION, 0, 0, AD_NEGATIVE)); + set(GA_CURSOR_RIGHT, + Input(IT_KEYBOARD, SDLK_RIGHT), + Input(IT_STICKMOTION, 0, 0, AD_POSITIVE)); - /*player 1 default keyboard settings*/ - m_player[0].setInput(KC_LEFT, IT_KEYBOARD, SDLK_LEFT, 0, 0); - m_player[0].setInput(KC_RIGHT, IT_KEYBOARD, SDLK_RIGHT, 0, 0); - m_player[0].setInput(KC_ACCEL, IT_KEYBOARD, SDLK_UP, 0, 0); - m_player[0].setInput(KC_BRAKE, IT_KEYBOARD, SDLK_DOWN, 0, 0); - m_player[0].setInput(KC_WHEELIE,IT_KEYBOARD, SDLK_RSHIFT, 0, 0); - m_player[0].setInput(KC_JUMP, IT_KEYBOARD, SDLK_MINUS, 0, 0); - m_player[0].setInput(KC_RESCUE, IT_KEYBOARD, SDLK_BACKSPACE, 0, 0); - m_player[0].setInput(KC_FIRE, IT_KEYBOARD, SDLK_RCTRL, 0, 0); - m_player[0].setInput(KC_LOOK_BACK, IT_KEYBOARD, SDLK_RALT, 0, 0); + set(GA_CLEAR_MAPPING, + Input(IT_KEYBOARD, SDLK_BACKSPACE), + Input(IT_STICKBUTTON, 0, 2)); - /*player 2 default keyboard settings*/ - m_player[1].setInput(KC_LEFT, IT_KEYBOARD, SDLK_a, 0, 0); - m_player[1].setInput(KC_RIGHT, IT_KEYBOARD, SDLK_d, 0, 0); - m_player[1].setInput(KC_ACCEL, IT_KEYBOARD, SDLK_w, 0, 0); - m_player[1].setInput(KC_BRAKE, IT_KEYBOARD, SDLK_s, 0, 0); - m_player[1].setInput(KC_WHEELIE,IT_KEYBOARD, SDLK_LSHIFT, 0, 0); - m_player[1].setInput(KC_JUMP, IT_KEYBOARD, SDLK_CAPSLOCK, 0, 0); - m_player[1].setInput(KC_RESCUE, IT_KEYBOARD, SDLK_q, 0, 0); - m_player[1].setInput(KC_FIRE, IT_KEYBOARD, SDLK_LCTRL, 0, 0); - m_player[1].setInput(KC_LOOK_BACK, IT_KEYBOARD, SDLK_LALT, 0, 0); + set(GA_INC_SCROLL_SPEED, + Input(IT_KEYBOARD, SDLK_PLUS)); + set(GA_INC_SCROLL_SPEED_FAST, + Input(IT_KEYBOARD, SDLK_PAGEDOWN)); - /*player 3 default keyboard settings*/ - m_player[2].setInput(KC_LEFT, IT_KEYBOARD, SDLK_f, 0, 0); - m_player[2].setInput(KC_RIGHT, IT_KEYBOARD, SDLK_h, 0, 0); - m_player[2].setInput(KC_ACCEL, IT_KEYBOARD, SDLK_t, 0, 0); - m_player[2].setInput(KC_BRAKE, IT_KEYBOARD, SDLK_g, 0, 0); - m_player[2].setInput(KC_WHEELIE,IT_KEYBOARD, SDLK_c, 0, 0); - m_player[2].setInput(KC_JUMP, IT_KEYBOARD, SDLK_v, 0, 0); - m_player[2].setInput(KC_RESCUE, IT_KEYBOARD, SDLK_r, 0, 0); - m_player[2].setInput(KC_FIRE, IT_KEYBOARD, SDLK_b, 0, 0); - m_player[2].setInput(KC_LOOK_BACK, IT_KEYBOARD, SDLK_n, 0, 0); + set(GA_DEC_SCROLL_SPEED, + Input(IT_KEYBOARD, SDLK_MINUS)); + set(GA_DEC_SCROLL_SPEED_FAST, + Input(IT_KEYBOARD, SDLK_PAGEUP)); + + set(GA_TOGGLE_FULLSCREEN, + Input(IT_KEYBOARD, SDLK_F9)); + set(GA_LEAVE_RACE, + Input(IT_KEYBOARD, SDLK_ESCAPE)); + + set(GA_DEBUG_ADD_MISSILE, + Input(IT_KEYBOARD, SDLK_F7)); + set(GA_DEBUG_TOGGLE_FPS, + Input(IT_KEYBOARD, SDLK_F12)); + set(GA_DEBUG_TOGGLE_WIREFRAME, + Input(IT_KEYBOARD, SDLK_F11)); + set(GA_DEBUG_HISTORY, + Input(IT_KEYBOARD, SDLK_F10)); + set(GA_DEBUG_BULLET, + Input(IT_KEYBOARD, SDLK_F2)); + + // TODO: The following should become a static + // array. This allows: + // a) resetting to default values + // b) prevent loading those defaults if config file contains any bindings + + /* Player 1 default input settings */ + set(GA_P1_LEFT, + Input(IT_KEYBOARD, SDLK_LEFT)); + set(GA_P1_RIGHT, + Input(IT_KEYBOARD, SDLK_RIGHT)); + set(GA_P1_ACCEL, + Input(IT_KEYBOARD, SDLK_UP)); + set(GA_P1_BRAKE, + Input(IT_KEYBOARD, SDLK_DOWN)); + set(GA_P1_WHEELIE, + Input(IT_KEYBOARD, SDLK_RSHIFT)); + set(GA_P1_JUMP, + Input(IT_KEYBOARD, SDLK_MINUS)); + set(GA_P1_RESCUE, + Input(IT_KEYBOARD, SDLK_BACKSPACE)); + set(GA_P1_FIRE, + Input(IT_KEYBOARD, SDLK_RCTRL)); + set(GA_P1_LOOK_BACK, + Input(IT_KEYBOARD, SDLK_RALT)); + + /* Player 2 default input settings */ + set(GA_P2_LEFT, + Input(IT_KEYBOARD, SDLK_a)); + set(GA_P2_RIGHT, + Input(IT_KEYBOARD, SDLK_d)); + set(GA_P2_ACCEL, + Input(IT_KEYBOARD, SDLK_w)); + set(GA_P2_BRAKE, + Input(IT_KEYBOARD, SDLK_s)); + set(GA_P2_WHEELIE, + Input(IT_KEYBOARD, SDLK_LSHIFT)); + set(GA_P2_JUMP, + Input(IT_KEYBOARD, SDLK_CAPSLOCK)); + set(GA_P2_RESCUE, + Input(IT_KEYBOARD, SDLK_q)); + set(GA_P2_FIRE, + Input(IT_KEYBOARD, SDLK_LCTRL)); + set(GA_P2_LOOK_BACK, + Input(IT_KEYBOARD, SDLK_LALT)); + + /* Player 3 default input settings */ + set(GA_P3_LEFT, + Input(IT_KEYBOARD, SDLK_f)); + set(GA_P3_RIGHT, + Input(IT_KEYBOARD, SDLK_h)); + set(GA_P3_ACCEL, + Input(IT_KEYBOARD, SDLK_t)); + set(GA_P3_BRAKE, + Input(IT_KEYBOARD, SDLK_g)); + set(GA_P3_WHEELIE, + Input(IT_KEYBOARD, SDLK_c)); + set(GA_P3_JUMP, + Input(IT_KEYBOARD, SDLK_v)); + set(GA_P3_RESCUE, + Input(IT_KEYBOARD, SDLK_r)); + set(GA_P3_FIRE, + Input(IT_KEYBOARD, SDLK_b)); + set(GA_P3_LOOK_BACK, + Input(IT_KEYBOARD, SDLK_n)); + + /* Player 4 default input settings */ + set(GA_P4_LEFT, + Input(IT_KEYBOARD, SDLK_j)); + set(GA_P4_RIGHT, + Input(IT_KEYBOARD, SDLK_l)); + set(GA_P4_ACCEL, + Input(IT_KEYBOARD, SDLK_i)); + set(GA_P4_BRAKE, + Input(IT_KEYBOARD, SDLK_k)); + set(GA_P4_WHEELIE, + Input(IT_KEYBOARD, SDLK_m)); + set(GA_P4_JUMP, + Input(IT_KEYBOARD, SDLK_COMMA)); + set(GA_P4_RESCUE, + Input(IT_KEYBOARD, SDLK_u)); + set(GA_P4_FIRE, + Input(IT_KEYBOARD, SDLK_PERIOD)); + set(GA_P4_LOOK_BACK, + Input(IT_KEYBOARD, SDLK_SLASH)); - /*player 4 default keyboard settings*/ - m_player[3].setInput(KC_LEFT, IT_KEYBOARD, SDLK_j, 0, 0); - m_player[3].setInput(KC_RIGHT, IT_KEYBOARD, SDLK_l, 0, 0); - m_player[3].setInput(KC_ACCEL, IT_KEYBOARD, SDLK_i, 0, 0); - m_player[3].setInput(KC_BRAKE, IT_KEYBOARD, SDLK_k, 0, 0); - m_player[3].setInput(KC_WHEELIE,IT_KEYBOARD, SDLK_m, 0, 0); - m_player[3].setInput(KC_JUMP, IT_KEYBOARD, SDLK_COMMA, 0, 0); - m_player[3].setInput(KC_RESCUE, IT_KEYBOARD, SDLK_u, 0, 0); - m_player[3].setInput(KC_FIRE, IT_KEYBOARD, SDLK_PERIOD, 0, 0); - m_player[3].setInput(KC_LOOK_BACK, IT_KEYBOARD, SDLK_SLASH, 0, 0); } // setDefaults @@ -347,7 +453,7 @@ void UserConfig::loadConfig(const std::string& filename) } std::string name; reader->get("name", name); - if(configFileVersion <=3) + if (configFileVersion <= 3) { // For older config files, replace the default player // names "Player %d" with the user name @@ -365,16 +471,16 @@ void UserConfig::loadConfig(const std::string& filename) m_player[i].setLastKartId(lastKartId); // Retrieves a player's INPUT configuration - readInput(reader, "left", KC_LEFT, m_player[i]); - readInput(reader, "right", KC_RIGHT, m_player[i]); - readInput(reader, "accel", KC_ACCEL, m_player[i]); - readInput(reader, "brake", KC_BRAKE, m_player[i]); + readPlayerInput(reader, "left", KA_LEFT, i); + readPlayerInput(reader, "right", KA_RIGHT, i); + readPlayerInput(reader, "accel", KA_ACCEL, i); + readPlayerInput(reader, "brake", KA_BRAKE, i); - readInput(reader, "wheelie", KC_WHEELIE, m_player[i]); - readInput(reader, "jump", KC_JUMP, m_player[i]); - readInput(reader, "rescue", KC_RESCUE, m_player[i]); - readInput(reader, "fire", KC_FIRE, m_player[i]); - readInput(reader, "lookBack", KC_LOOK_BACK, m_player[i]); + readPlayerInput(reader, "wheelie", KA_WHEELIE, i); + readPlayerInput(reader, "jump", KA_JUMP, i); + readPlayerInput(reader, "rescue", KA_RESCUE, i); + readPlayerInput(reader, "fire", KA_FIRE, i); + readPlayerInput(reader, "lookBack", KA_LOOK_BACK, i); } } catch(std::exception& e) @@ -385,72 +491,75 @@ void UserConfig::loadConfig(const std::string& filename) } delete root; } // loadConfig - // ----------------------------------------------------------------------------- -void UserConfig::readInput(const lisp::Lisp* &r, - const char *node, - KartActions action, - Player& player) +void +UserConfig::readPlayerInput(const lisp::Lisp *r, const char *node, + KartAction ka, int playerIndex) { - std::string inputTypeName; - const lisp::Lisp* subReader = r->getLisp(node); - InputType it=IT_KEYBOARD; + readInput(r, node, (GameAction) (playerIndex * KC_COUNT + ka + GA_P1_LEFT)); +} +// ----------------------------------------------------------------------------- +void +UserConfig::readInput(const lisp::Lisp* r, + const char *node, + GameAction action) +{ + string inputTypeName; + + const Lisp* nodeReader = r->getLisp(node); + if (!nodeReader) + return; + // Every unused id variable *must* be set to // something different than -1. Otherwise - // the restored mapping will not be applied - // to the player. - int id0 = -1, id1 = -1, id2 = -1; + // the restored mapping will not be applied. + Input input = Input(IT_NONE, -1, -1, -1); - // If config file contains no such entry should - // create an empty input mapping. - if (!subReader) - { - player.setInput(action, IT_NONE, 0, 0, 0); - return; - } - - subReader->get("type", inputTypeName); + nodeReader->get("type", inputTypeName); if (inputTypeName == "keyboard") { - it = IT_KEYBOARD; - subReader->get("key", id0); - id1 = id2 = 0; + input.type = IT_KEYBOARD; + nodeReader->get("key", input.id0); + input.id1 = input.id2 = 0; } else if (inputTypeName == "stickaxis") { - it = IT_STICKMOTION; - subReader->get("stick", id0); - subReader->get("axis", id1); - subReader->get("direction", id2); + input.type = IT_STICKMOTION; + nodeReader->get("stick", input.id0); + nodeReader->get("axis", input.id1); + nodeReader->get("direction", input.id2); } else if (inputTypeName == "stickbutton") { - it = IT_STICKBUTTON; - subReader->get("stick", id0); - subReader->get("button", id1); - id2 = 0; + input.type = IT_STICKBUTTON; + nodeReader->get("stick", input.id0); + nodeReader->get("button", input.id1); + input.id2 = 0; } else if (inputTypeName == "stickhat") { - it = IT_STICKHAT; + input.type = IT_STICKHAT; // TODO: Implement me } else if (inputTypeName == "mouseaxis") { - it = IT_MOUSEMOTION; - subReader->get("axis", id0); - subReader->get("direction", id1); - id2 = 0; + input.type = IT_MOUSEMOTION; + nodeReader->get("axis", input.id0); + nodeReader->get("direction", input.id1); + input.id2 = 0; } else if (inputTypeName == "mousebutton") { - it = IT_MOUSEBUTTON; - subReader->get("button", id0); - id1 = id2 = 0; + input.type = IT_MOUSEBUTTON; + nodeReader->get("button", input.id0); + input.id1 = input.id2 = 0; } - if (id0 != -1 && id1 != -1 && id2 != -1) - player.setInput(action, it, id0, id1, id2); + if (input.id0 != -1 && input.id1 != -1 && input.id2 != -1) + { + setInput(action, input); + } + } // ----------------------------------------------------------------------------- @@ -475,42 +584,44 @@ void UserConfig::saveConfig(const std::string& filename) // and we can exit here without any further messages. if (DIR_EXIST == 0) return; + Writer *writer = new Writer(filename); try { - lisp::Writer writer(filename); + writer->beginList("tuxkart-config"); + writer->writeComment("If the game's supported config file version is higher than this number the configuration is discarded."); + writer->write("configFileVersion\t", CURRENT_CONFIG_VERSION); - writer.beginList("tuxkart-config"); - writer.writeComment("If the game's supported config file version is higher than this number the configuration is discarded."); - writer.write("configFileVersion\t", CURRENT_CONFIG_VERSION); + writer->writeComment("the following options can be set to #t or #f:"); + writer->write("sfx\t", !(m_sfx==UC_DISABLE)); + writer->write("music\t", !(m_music==UC_DISABLE)); + writer->write("smoke\t", m_smoke); + writer->writeComment("Display frame per seconds"); + writer->write("displayFPS\t", m_display_fps); + writer->writeComment("Name of the .herring file to use."); + writer->write("herringStyle\t", m_herring_style); + writer->writeComment("Background music file to use,"); + writer->write("background-music\t", m_background_music); + writer->writeComment("Allow players to disable a magnet"); + writer->write("disableMagnet\t", m_disable_magnet); + writer->writeComment("Use of kilometers per hours (km/h) instead of mph"); + writer->write("useKPH\t", m_use_kph); + writer->writeComment("With improved physics the gravity on a non-horizontal"); + writer->writeComment("plane will add an accelerating force on the kart"); + writer->write("improvedPhysics\t", m_improved_physics); - writer.writeComment("the following options can be set to #t or #f:"); - writer.write("sfx\t", !(m_sfx==UC_DISABLE)); - writer.write("music\t", !(m_music==UC_DISABLE)); - writer.write("smoke\t", m_smoke); - writer.writeComment("Display frame per seconds"); - writer.write("displayFPS\t", m_display_fps); - writer.writeComment("Name of the .herring file to use."); - writer.write("herringStyle\t", m_herring_style); - writer.writeComment("Background music file to use,"); - writer.write("background-music\t", m_background_music); - writer.writeComment("Allow players to disable a magnet"); - writer.write("disableMagnet\t", m_disable_magnet); - writer.writeComment("Use of kilometers per hours (km/h) instead of mph"); - writer.write("useKPH\t", m_use_kph); - writer.writeComment("With improved physics the gravity on a non-horizontal"); - writer.writeComment("plane will add an accelerating force on the kart"); - writer.write("improvedPhysics\t", m_improved_physics); + writer->writeComment("screen resolution and windowing mode"); + writer->write("width\t", m_width); + writer->write("height\t", m_height); + writer->write("fullscreen\t", m_fullscreen); - writer.writeComment("screen resolution and windowing mode"); - writer.write("width\t", m_width); - writer.write("height\t", m_height); - writer.write("fullscreen\t", m_fullscreen); + writer->writeComment("number of karts. -1 means use all"); + writer->write("karts\t", m_karts); - writer.writeComment("number of karts. -1 means use all"); - writer.write("karts\t", m_karts); + writer->writeComment("number of karts. -1 means use all"); + writer->write("karts\t", m_karts); - writer.writeComment("error logging to log (true) or stderr (false)"); - writer.write("log-errors\t", m_log_errors); + writer->writeComment("error logging to log (true) or stderr (false)"); + writer->write("log-errors\t", m_log_errors); /* write player configurations */ for(i=0; iwriteComment(temp); temp = "player-"; temp += i+'1'; - writer.beginList(temp); + writer->beginList(temp); - writer.write("name\t", m_player[i].getName()); + writer->write("name\t", m_player[i].getName()); - writer.writeComment("optional"); - writer.write("lastKartId", m_player[i].getLastKartId()); + writer->writeComment("optional"); + writer->write("lastKartId", m_player[i].getLastKartId()); - writeInput(writer, "left\t", KC_LEFT, m_player[i]); - writeInput(writer, "right\t", KC_RIGHT, m_player[i]); - writeInput(writer, "accel\t", KC_ACCEL, m_player[i]); - writeInput(writer, "brake\t", KC_BRAKE, m_player[i]); - writeInput(writer, "wheelie\t", KC_WHEELIE, m_player[i]); - writeInput(writer, "jump\t", KC_JUMP, m_player[i]); - writeInput(writer, "rescue\t", KC_RESCUE, m_player[i]); - writeInput(writer, "fire\t", KC_FIRE, m_player[i]); - writeInput(writer, "lookBack\t", KC_LOOK_BACK, m_player[i]); + writePlayerInput(writer, "left\t", KA_LEFT, i); + writePlayerInput(writer, "right\t", KA_RIGHT, i); + writePlayerInput(writer, "accel\t", KA_ACCEL, i); + writePlayerInput(writer, "brake\t", KA_BRAKE, i); + writePlayerInput(writer, "wheelie\t", KA_WHEELIE, i); + writePlayerInput(writer, "jump\t", KA_JUMP, i); + writePlayerInput(writer, "rescue\t", KA_RESCUE, i); + writePlayerInput(writer, "fire\t", KA_FIRE, i); + writePlayerInput(writer, "lookBack\t", KA_LOOK_BACK, i); - writer.endList(temp); + writer->endList(temp); } // for i - writer.endList("tuxkart-config"); + writer->endList("tuxkart-config"); } catch(std::exception& e) { @@ -549,88 +660,103 @@ void UserConfig::saveConfig(const std::string& filename) fprintf(stderr, e.what()); fprintf(stderr, "\n"); } + + delete writer; } // saveConfig // ----------------------------------------------------------------------------- -void UserConfig::writeInput(lisp::Writer &writer, const char *node, KartActions action, Player& player) +void +UserConfig::writePlayerInput(lisp::Writer *writer, const char *node, + KartAction ka, int playerIndex) { - const Input *INPUT = player.getInput(action); - - // Write no entry if the input has no mapping. - if (INPUT->type == IT_NONE) - return; - - writer.beginList(node); - - switch (INPUT->type) - { - case IT_KEYBOARD: - writer.write("type", "keyboard"); - writer.write("key", INPUT->id0); - break; - case IT_STICKMOTION: - writer.write("type", "stickaxis"); - writer.write("stick", INPUT->id0); - writer.write("axis", INPUT->id1); - writer.writeComment("0 is negative/left/up, 1 is positive/right/down"); - writer.write("direction", INPUT->id2); - break; - case IT_STICKBUTTON: - writer.write("type", "stickbutton"); - writer.write("stick", INPUT->id0); - writer.write("button", INPUT->id1); - break; - case IT_STICKHAT: - // TODO: Implement me - break; - case IT_MOUSEMOTION: - writer.write("type", "mouseaxis"); - writer.write("axis", INPUT->id0); - writer.writeComment("0 is negative/left/up, 1 is positive/right/down"); - writer.write("direction", INPUT->id1); - break; - case IT_MOUSEBUTTON: - writer.write("type", "mousebutton"); - writer.write("button", INPUT->id0); - break; - default: - break; - } - - writer.endList(node); + writeInput(writer, node, (GameAction) (playerIndex * KC_COUNT + ka + GA_P1_LEFT)); } - // ----------------------------------------------------------------------------- -std::string UserConfig::getInputAsString(int player_index, KartActions control) +void +UserConfig::writeInput(lisp::Writer *writer, + const char *node, + GameAction action) +{ + writer->beginList(node); + + if (inputMap[action].count) + { + + const Input input = inputMap[action].inputs[0]; + + if (input.type != IT_NONE) + { + switch (input.type) + { + case IT_NONE: + break; + case IT_KEYBOARD: + writer->write("type", "keyboard"); + writer->write("key", input.id0); + break; + case IT_STICKMOTION: + writer->write("type", "stickaxis"); + writer->write("stick", input.id0); + writer->write("axis", input.id1); + writer->writeComment("0 is negative/left/up, 1 is positive/right/down"); + writer->write("direction", input.id2); + break; + case IT_STICKBUTTON: + writer->write("type", "stickbutton"); + writer->write("stick", input.id0); + writer->write("button", input.id1); + break; + case IT_STICKHAT: + // TODO: Implement me + break; + case IT_MOUSEMOTION: + writer->write("type", "mouseaxis"); + writer->write("axis", input.id0); + writer->writeComment("0 is negative/left/up, 1 is positive/right/down"); + writer->write("direction", input.id1); + break; + case IT_MOUSEBUTTON: + writer->write("type", "mousebutton"); + writer->writeComment("0 is left, 1 is middle, 2 is right, 3 is wheel up, 4 is wheel down"); + writer->writeComment("other values denote auxillary buttons"); + writer->write("button", input.id0); + break; + } + } + } + + writer->endList(node); +} +// ----------------------------------------------------------------------------- +std::string UserConfig::getInputAsString(Input &input) { - const Input *INPUT = m_player[player_index].getInput(control); char msg[MAX_MESSAGE_LENGTH]; std::ostringstream stm; - switch (INPUT->type) + switch (input.type) { case IT_NONE: snprintf(msg, sizeof(msg), _("not set")); break; case IT_KEYBOARD: - snprintf(msg, sizeof(msg), _("%s"), SDL_GetKeyName((SDLKey) INPUT->id0)); + snprintf(msg, sizeof(msg), _("%s"), SDL_GetKeyName((SDLKey) input.id0)); break; case IT_STICKMOTION: snprintf(msg, sizeof(msg), _("joy %d axis %d %c"), - INPUT->id0, INPUT->id1, (INPUT->id2 == AD_NEGATIVE) ? '-' : '+'); + input.id0, input.id1, (input.id2 == AD_NEGATIVE) ? '-' : '+'); break; case IT_STICKBUTTON: - snprintf(msg, sizeof(msg), _("joy %d btn %d"), INPUT->id0, INPUT->id1); + snprintf(msg, sizeof(msg), _("joy %d btn %d"), input.id0, input.id1); break; case IT_STICKHAT: - snprintf(msg, sizeof(msg), _("joy %d hat %d"), INPUT->id0, INPUT->id1); + snprintf(msg, sizeof(msg), _("joy %d hat %d"), input.id0, input.id1); break; case IT_MOUSEBUTTON: - snprintf(msg, sizeof(msg), _("mouse btn %d"), INPUT->id0); + snprintf(msg, sizeof(msg), _("mouse btn %d"), input.id0); break; case IT_MOUSEMOTION: snprintf(msg, sizeof(msg), _("mouse axis %d %c"), - INPUT->id0, ((INPUT->id1 == AD_NEGATIVE) ? '-' : '+')); + input.id0, ((input.id1 == AD_NEGATIVE) ? '-' : '+')); break; default: snprintf(msg, sizeof(msg), _("Invalid")); @@ -642,5 +768,165 @@ std::string UserConfig::getInputAsString(int player_index, KartActions control) } // GetKeyAsString // ----------------------------------------------------------------------------- +string +UserConfig::getMappingAsString(GameAction ga) +{ + if (inputMap[ga].count + && inputMap[ga].inputs[0].type) + { + stringstream s; + s << getInputAsString(inputMap[ga].inputs[0]); + + return s.str(); + } + else + { + return string(_("not set")); + } +} +// ----------------------------------------------------------------------------- +string +UserConfig::getMappingAsString(int playerIndex, KartAction ka) +{ + return getMappingAsString((GameAction) (GA_FIRST_KARTACTION + + playerIndex * KC_COUNT + ka)); +} +// ----------------------------------------------------------------------------- + +void +UserConfig::unsetDuplicates (GameAction ga, Input &i) +{ + for (int cga = GA_FIRST_KARTACTION; cga <= GA_LAST_KARTACTION; cga++) + { + if (cga != ga) + { + // If the input occurs in any other mapping + // delete it properly from there. + + if (inputMap[cga].count + && inputMap[cga].inputs[0].type == i.type + && inputMap[cga].inputs[0].id0 == i.id0 + && inputMap[cga].inputs[0].id1 == i.id1 + && inputMap[cga].inputs[0].id2 == i.id2) + { + // Delete it. + inputMap[cga].inputs[0].type = IT_NONE; + } + } + } +} +// ----------------------------------------------------------------------------- +void +UserConfig::set(GameAction ga, Input i) +{ + inputMap[ga].count = 1; + inputMap[ga].inputs = new Input[1]; + inputMap[ga].inputs[0] = i; +} +// ----------------------------------------------------------------------------- +void +UserConfig::set(GameAction ga, Input i0, Input i1) +{ + inputMap[ga].count = 2; + inputMap[ga].inputs = new Input[2]; + inputMap[ga].inputs[0] = i0; + inputMap[ga].inputs[1] = i1; +} +// ----------------------------------------------------------------------------- +void +UserConfig::set(GameAction ga, Input i0, Input i1, Input i2) +{ + inputMap[ga].count = 3; + inputMap[ga].inputs = new Input[3]; + inputMap[ga].inputs[0] = i0; + inputMap[ga].inputs[1] = i1; + inputMap[ga].inputs[2] = i2; +} +// ----------------------------------------------------------------------------- +void +UserConfig::set(GameAction ga, Input i0, Input i1, Input i2, Input i3) +{ + inputMap[ga].count = 4; + inputMap[ga].inputs = new Input[4]; + inputMap[ga].inputs[0] = i0; + inputMap[ga].inputs[1] = i1; + inputMap[ga].inputs[2] = i2; + inputMap[ga].inputs[3] = i3; +} + +// ----------------------------------------------------------------------------- +void +UserConfig::setInput(GameAction ga, Input &input) +{ + // Removes the input from all mappings where it occurs. + unsetDuplicates(ga, input); + + set(ga, input); +} +// ----------------------------------------------------------------------------- +void +UserConfig::setInput(int playerIndex, KartAction ka, Input &input) +{ + setInput((GameAction) (GA_FIRST_KARTACTION + + playerIndex * KC_COUNT + ka), + input); +} +// ----------------------------------------------------------------------------- +void +UserConfig::clearInput(int playerIndex, KartAction ka) +{ + inputMap[(GameAction) (GA_FIRST_KARTACTION + playerIndex * KC_COUNT + ka)] + .count = 0; +} +// ----------------------------------------------------------------------------- +ActionMap * +UserConfig::newActionMap(const int from, const int to) +{ + ActionMap *am = new ActionMap(); + + for (int i = from; i <= to; i++) + { + const int count = inputMap[i].count; + for (int j = 0;j < count; j++) + am->putEntry(inputMap[i].inputs[j], (GameAction) i); + } + + return am; +} +// ----------------------------------------------------------------------------- +ActionMap * +UserConfig::newMenuActionMap() +{ + return newActionMap(GA_FIRST_MENU, GA_LAST_MENU); +} +// ----------------------------------------------------------------------------- +ActionMap * +UserConfig::newIngameActionMap() +{ + ActionMap *am = newActionMap(GA_FIRST_INGAME, GA_LAST_INGAME); + + return am; +} +// ----------------------------------------------------------------------------- +/** Determines whether the given Input is used in a mapping where it is marked + * as fixed. This allows the input driver to discard the mapping and not + * allow the user to use it. + */ +bool +UserConfig::isFixedInput(InputType type, int id0, int id1, int id2) +{ + for (int i = GA_FIRST_INGAME_FIXED; i <= GA_LAST_INGAME_FIXED; i++) + { + const int count = inputMap[i].count; + for (int j = 0;j < count; j++) + if (inputMap[i].inputs[j].type == type + && inputMap[i].inputs[j].id0 == id0 + && inputMap[i].inputs[j].id1 == id1 + && inputMap[i].inputs[j].id2 == id2) + return true; + } + + return false; +} /*EOF*/ diff --git a/src/user_config.hpp b/src/user_config.hpp index de9ce7771..8de99ee5e 100644 --- a/src/user_config.hpp +++ b/src/user_config.hpp @@ -37,6 +37,7 @@ #define SUPPORTED_CONFIG_VERSION 3 #include +#include "input.hpp" #include "player.hpp" #include "lisp/lisp.hpp" #include "lisp/parser.hpp" @@ -44,12 +45,28 @@ #define CONFIGDIR ".supertuxkart" +class ActionMap; /*class for managing general tuxkart configuration data*/ class UserConfig { private: + typedef struct + { + int count; + Input *inputs; + } InputMapEntry; + std::string filename; + + /** Stores the GameAction->Input mappings in a way that is suitable for + * quick modification of the mappings. Internally this allows multiple + * Input instances per GameAction but the public methods allow only one + * mapping. + * + * It is named after what is put in as values. + */ + InputMapEntry inputMap[GA_COUNT]; void setFilename (); int CheckAndCreateDir(); @@ -58,15 +75,57 @@ private: int m_sfx; int m_music; - void readInput(const lisp::Lisp* &r, - const char *node, - KartActions action, - Player& player); + void readPlayerInput(const lisp::Lisp *, + const char *, + KartAction ka, + int); + + void readInput(const lisp::Lisp *, + const char *, + GameAction); + + void writeInput(lisp::Writer *, + const char *, + GameAction); + + void writePlayerInput(lisp::Writer *, + const char *, + KartAction, + int); + + /** Iterates through the input mapping and unsets all + * where the given input occurs. + * + * This makes sure an input is not bound multiple times. + */ + void unsetDuplicates(GameAction, Input &); + + /** Creates an GameAction->Input mapping with one Input */ + void set(GameAction, Input); + + /** Creates an GameAction->Input mapping with two Inputs */ + void set(GameAction, Input, Input); + + /** Creates an GameAction->Input mapping with three Inputs */ + void set(GameAction, Input, Input, Input); + + /** Creates an GameAction->Input mapping with four Inputs */ + void set(GameAction, Input, Input, Input, Input); + + std::string getInputAsString(Input &); + + /** Creates an ActionMap for the GameAction values of the specified + * range. + */ + ActionMap *newActionMap(const int, const int); + + /** Sets the Input for the given GameAction. Includes a check for + * duplicates and automatic removing of the other candidate(s). + * + * For use when reading from file. + */ + void setInput(GameAction, Input &); - void writeInput(lisp::Writer &writer, - const char *node, - KartActions action, - Player& player); public: enum UC_Mode {UC_ENABLE, UC_DISABLE, UC_TEMPORARY_DISABLE}; @@ -106,7 +165,31 @@ public: void loadConfig(const std::string& filename); void saveConfig(); void saveConfig(const std::string& filename); - std::string getInputAsString(int player_index, KartActions control); + + /** Retrieves a human readable string of the mapping for a GameAction */ + std::string getMappingAsString(GameAction); + /** Retrieves a human readable string of the mapping for the given + * player and KartAction. + */ + std::string getMappingAsString(int, KartAction); + + /** Sets the Input for the given Player and KartAction. Includes a check + * for duplicates and automatic removing of the other candidate(s). + * + * For use when sensing input. + */ + void setInput(int, KartAction, Input &); + + /** Clears the mapping for a given Player and KartAction. */ + void clearInput(int, KartAction); + + bool isFixedInput(InputType, int, int, int); + + /** Creates ActionMap for use in menu mode. */ + ActionMap *newMenuActionMap(); + + /** Creates ActionMap for use in ingame mode. */ + ActionMap *newIngameActionMap(); }; diff --git a/src/widget_manager.cpp b/src/widget_manager.cpp index b7996bd7c..4090e6839 100644 --- a/src/widget_manager.cpp +++ b/src/widget_manager.cpp @@ -20,9 +20,6 @@ #include "user_config.hpp" -//TODO: this include should not be necesary? -#include - #include "gui/font.hpp" //TEMP @@ -887,11 +884,11 @@ void WidgetManager::darken_wgt_color(const int TOKEN) else std::cerr << "Tried to darken an unexistant widget with token " << TOKEN << '\n'; } -/** The handle_mouse() function returns the current widget under the mouse +/** The handle_pointer() function returns the current widget under the * pointer, if it's different from the selected widget. If the widget under - * the mouse is the selected widget, it returns WGT_NONE. + * the pointer is the selected widget, it returns WGT_NONE. */ -int WidgetManager::handle_mouse(const int X, const int Y ) +int WidgetManager::handle_pointer(const int X, const int Y ) { //Search if the given x and y positions are on top of any widget. Please //note that the bounding box for each widget is used instead of the @@ -932,131 +929,74 @@ int WidgetManager::handle_mouse(const int X, const int Y ) return WGT_NONE; } -/** The handle_keyboard() function stores the current widget under the cursor +/** The handle_*() function stores the current widget under the cursor * after receiving input from a key. */ -int WidgetManager::handle_keyboard(const int KEY) +int +WidgetManager::handle_left() { if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE; - - int next_wgt = find_id(m_selected_wgt_token); - //FIXME: eventually, the keys should not be hard coded - switch (KEY) - { - case SDLK_LEFT: - next_wgt = find_left_widget(find_id(m_selected_wgt_token)); - break; - - case SDLK_RIGHT: - next_wgt = find_right_widget(find_id(m_selected_wgt_token)); - break; - - case SDLK_UP: - next_wgt = find_top_widget(find_id(m_selected_wgt_token)); - break; - - case SDLK_DOWN: - next_wgt = find_bottom_widget(find_id(m_selected_wgt_token)); - break; - - //FIXME: apparently, there are different codes for the + and - - //near the numlock. - case SDLK_PLUS: - { - const int ID = find_id(m_selected_wgt_token); - if( m_widgets[ID].widget->m_enable_scroll ) - { - //FIXME: these increases shouldn't be in pixels, but in percentages. - //This should increase it by 1%, and the page buttons by 5%. - m_widgets[ID].widget->m_scroll_speed_y -= 1; - } - break; - } - - case SDLK_MINUS: - { - const int ID = find_id(m_selected_wgt_token); - if( m_widgets[ID].widget->m_enable_scroll ) - { - m_widgets[ID].widget->m_scroll_speed_y += 1; - } - break; - } - - case SDLK_PAGEUP: - { - const int ID = find_id(m_selected_wgt_token); - if( m_widgets[ID].widget->m_enable_scroll ) - { - m_widgets[ID].widget->m_scroll_speed_y -= 5; - } - break; - } - - case SDLK_PAGEDOWN: - { - const int ID = find_id(m_selected_wgt_token); - if( m_widgets[ID].widget->m_enable_scroll ) - { - m_widgets[ID].widget->m_scroll_speed_y += 5; - } - return WGT_NONE; - } - - default: return WGT_NONE; - } - - if( next_wgt == WGT_NONE) return WGT_NONE; - - m_selected_wgt_token = m_widgets[next_wgt].token; - return m_selected_wgt_token; + + return handle_finish(find_left_widget(find_id(m_selected_wgt_token))); } -/** The handle_joystick() function stores the current widget under the cursor - * after receiving input from the joystick. - */ -//FIXME: shouldn't direction and value be merged? -int WidgetManager::handle_joystick -( - const int axis, - const int direction, - int value -) +int +WidgetManager::handle_right() { if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE; + + return handle_finish(find_right_widget(find_id(m_selected_wgt_token))); +} - int next_wgt = WGT_NONE; //This asignment is to prevent a compiler warning - switch (axis) - { - case 0: - if( direction == 0 ) - { - next_wgt = find_left_widget(find_id(m_selected_wgt_token)); - } - else if( direction == 1 ) - { - next_wgt = find_right_widget(find_id(m_selected_wgt_token)); - } - break; +int +WidgetManager::handle_up() +{ + if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE; + + return handle_finish(find_top_widget(find_id(m_selected_wgt_token))); +} - case 1: - if( direction == 0 ) - { - next_wgt = find_top_widget(find_id(m_selected_wgt_token)); - } - else if( direction == 1 ) - { - next_wgt = find_bottom_widget(find_id(m_selected_wgt_token)); - } - break; +int +WidgetManager::handle_down() +{ + if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE; + + return handle_finish(find_bottom_widget(find_id(m_selected_wgt_token))); +} - default: return WGT_NONE; - } +int +WidgetManager::handle_finish(const int next_wgt) +{ + if( next_wgt == WGT_NONE) + return WGT_NONE; + + m_selected_wgt_token = m_widgets[next_wgt].token; + + return m_selected_wgt_token; +} - if( next_wgt == find_id(m_selected_wgt_token) ) return WGT_NONE; +void +WidgetManager::increase_scroll_speed(const bool fast) +{ + const int ID = find_id(m_selected_wgt_token); + if( m_widgets[ID].widget->m_enable_scroll ) + { + //FIXME: these increases shouldn't be in pixels, but in percentages. + //This should increase it by 1%, and the page buttons by 5%. + m_widgets[ID].widget->m_scroll_speed_y -= (fast) ? 5 : 1; + } +} - m_selected_wgt_token = m_widgets[next_wgt].token; - return m_selected_wgt_token; +void +WidgetManager::decrease_scroll_speed(const bool fast) +{ + const int ID = find_id(m_selected_wgt_token); + if( m_widgets[ID].widget->m_enable_scroll ) + { + //FIXME: these increases shouldn't be in pixels, but in percentages. + //This should increase it by 1%, and the page buttons by 5%. + m_widgets[ID].widget->m_scroll_speed_y += (fast) ? 5 : 1; + } } //FIXME: find_left_widget() doesn't works properly yet diff --git a/src/widget_manager.hpp b/src/widget_manager.hpp index 8b6f11aac..d52083fb7 100644 --- a/src/widget_manager.hpp +++ b/src/widget_manager.hpp @@ -100,6 +100,8 @@ class WidgetManager int find_top_widget(const int START_WGT) const; int find_bottom_widget(const int START_WGT) const; + int handle_finish(const int); + public: //FIXME: maybe I should get this out of this class? static const int WGT_NONE; @@ -190,9 +192,15 @@ public: void darken_wgt_color(const int TOKEN); /* Input device handling. */ - int handle_mouse( const int X, const int Y ); - int handle_keyboard( const int KEY ); - int handle_joystick( int axis, int dir, int value ); + int handle_pointer( const int X, const int Y ); + int handle_left(); + int handle_right(); + int handle_up(); + int handle_down(); + + /* Scrolling modification. */ + void increase_scroll_speed(bool = false); + void decrease_scroll_speed(bool = false); }; extern WidgetManager *widget_manager;