- committed input abstraction patch - as seen on list

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@1305 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
thebohemian 2007-11-26 14:28:15 +00:00
parent 456e205494
commit 44a2338368
28 changed files with 1682 additions and 907 deletions

View File

@ -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 \

View File

@ -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 <map>
#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<Key, Entry *>::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:

View File

@ -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 <map>
#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<int, int> 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<Key, Entry *> inputMap;
std::map<Key, GameAction> inputMap;
};

View File

@ -24,20 +24,9 @@
#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 )
@ -48,121 +37,53 @@ void BaseGUI::input(InputType type, int id0, int id1, int id2, int value)
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;
}
}
//-----------------------------------------------------------------------------
/**
* 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;
switch ( key )
{
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 );
int previous = widget_manager->get_selected_wgt();
if( SELECTED_WGT != WidgetManager::WGT_NONE )
switch ( action )
{
if( PREV_SELECTED_WGT != WidgetManager::WGT_NONE )
{
widget_manager->darken_wgt_color( PREV_SELECTED_WGT );
}
case GA_CURSOR_LEFT:
animateWidget(previous,
widget_manager->handle_left());
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 );
case GA_CURSOR_RIGHT:
animateWidget(previous,
widget_manager->handle_right());
break;
case SDLK_SPACE:
case SDLK_RETURN:
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 */

View File

@ -20,10 +20,14 @@
#ifndef HEADER_BASEGUI_H
#define HEADER_BASEGUI_H
#include <SDL/SDL.h>
#include "player.hpp"
class BaseGUI
{
void animateWidget(const int, const int);
public:
BaseGUI() {}
virtual ~BaseGUI() {}
@ -31,8 +35,9 @@ public:
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);

View File

@ -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*/

View File

@ -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;
}
}

View File

@ -29,7 +29,8 @@ public:
~MainMenu();
void select();
void inputKeyboard(int key, int pressed);
void handle(GameAction, int);
};
#endif

View File

@ -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,10 +96,11 @@ 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;
@ -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;

View File

@ -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;
m_grab_id = widget_manager->get_selected_wgt();
if(m_grab_id == WTOK_PLYR_NAME1)
const int selected = widget_manager->get_selected_wgt();
switch (selected)
{
m_grab_input = true;
return;
}
case WTOK_PLYR_NAME1:
// Switch to typing in the player's name.
widget_manager->set_wgt_text(WTOK_PLYR_NAME1, (m_name + "<").c_str());
const int MENU_CHOICE = widget_manager->get_selected_wgt() - WTOK_LEFT;
drv_setMode(LOWLEVEL);
if(MENU_CHOICE == WTOK_QUIT)
{
break;
case WTOK_QUIT:
// Leave menu.
menu_manager->popMenu();
return;
break;
default:
// Switch to input sensing.
// 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<KartAction>(selected - WTOK_LEFT);
widget_manager->set_wgt_text(selected, _("Press key"));
drv_setMode(INPUT_SENSE);
break;
}
m_edit_action = static_cast<KartActions>(MENU_CHOICE);
m_grab_input = true;
drv_hidePointer();
widget_manager->set_wgt_text(m_grab_id, _("Press key"));
} // 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)
switch (key)
{
case SDLK_RSHIFT:
case SDLK_LSHIFT:
// 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);
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;
}
else // Add the character to the name
{
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.
m_name = m_name + (char)id1;
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;
}
}
//-----------------------------------------------------------------------------
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();
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());
}
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;
// Do not accept pressing ESC as input.
if (type != IT_KEYBOARD || id0 != SDLK_ESCAPE)
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))
{
// 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);
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);
}
widget_manager->set_wgt_text(m_grab_id, m_key_names[m_edit_action].c_str());
}
}
else
BaseGUI::input(type, id0, id1, id2, 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)
for (int i = KA_FIRST; i <= KA_LAST; i++)
{
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
*/
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());
}
}

View File

@ -24,6 +24,7 @@
#include "player.hpp"
#include <string>
#include <SDL/SDL.h>
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 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

View File

@ -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 <SDL/SDL.h>
#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,94 +65,49 @@ 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;
// 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++)
// 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)
{
PlayerKart* kart = world->getPlayerKart(i);
// 'Pulls down' the gameaction value to make them multiples of the
// kartaction values.
int ka = ga - GA_FIRST_KARTACTION;
for(int ka=(int)KC_FIRST; ka < (int)KC_LAST+1; ka++)
m_input_map->putEntry(kart, (KartActions) ka);
int playerNo = ka / KC_COUNT;
ka = ka % KC_COUNT;
world->getPlayerKart(playerNo)->action((KartAction) ka, value);
return;
}
} // 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;
}
//-----------------------------------------------------------------------------
void RaceGUI::update(float dt)
{
assert(world != NULL);
drawStatusText(world->m_race_setup, 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)
if (value)
return;
static int isWireframe = false ;
switch ( key )
switch (ga)
{
case SDLK_F7:
case GA_DEBUG_ADD_MISSILE:
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:
case GA_DEBUG_TOGGLE_FPS:
user_config->m_display_fps = !user_config->m_display_fps;
if(user_config->m_display_fps)
{
@ -171,11 +117,11 @@ void RaceGUI::inputKeyboard(int key, int pressed)
}
break;
#ifdef BULLET
case SDLK_F2:
case GA_DEBUG_BULLET:
user_config->m_bullet_debug = !user_config->m_bullet_debug;
break;
#endif
case SDLK_F11:
case GA_DEBUG_TOGGLE_WIREFRAME:
glPolygonMode(GL_FRONT_AND_BACK, isWireframe ? GL_FILL : GL_LINE);
isWireframe = ! isWireframe;
break;
@ -183,21 +129,30 @@ void RaceGUI::inputKeyboard(int key, int pressed)
// 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:
case GA_TOGGLE_FULLSCREEN:
drv_toggleFullscreen(0); // 0: do not reset textures
// Fall through to put the game into pause mode.
#endif
case SDLK_ESCAPE: // ESC
case GA_LEAVE_RACE:
world->pause();
menu_manager->pushMenu(MENUID_RACEMENU);
break;
case SDLK_F10:
case GA_DEBUG_HISTORY:
history->Save();
break;
default:
break;
} // switch
} // inputKeyboard
}
//-----------------------------------------------------------------------------
void RaceGUI::update(float dt)
{
assert(world != NULL);
drawStatusText(world->m_race_setup, dt);
cleanupMessages();
} // update
//-----------------------------------------------------------------------------
void RaceGUI::drawFPS ()

View File

@ -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);

View File

@ -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;
}
}

View File

@ -29,7 +29,7 @@ public:
~RaceMenu();
void select();
void inputKeyboard(int key, int pressed);
void handle(GameAction ga, int value);
};
#endif

View File

@ -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 */

View File

@ -33,7 +33,7 @@ public:
RaceResultsGUI();
~RaceResultsGUI();
void inputKeyboard(int, int);
void handle(GameAction, int);
void select();
};

200
src/input.hpp Normal file
View File

@ -0,0 +1,200 @@
#ifndef TUXKART_INPUT_H
#define TUXKART_INPUT_H
#include <string>
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

View File

@ -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)

View File

@ -21,42 +21,16 @@
#define TUXKART_PLAYER_H
#include <string>
#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; }
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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 <map>
#include <SDL/SDL.h>
#include <plib/ssg.h>
#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;i<NUM_STICKS;i++)
sticks[i] = SDL_JoystickOpen(i);
const int numSticks = SDL_NumJoysticks();
stickInfos = new StickInfo *[numSticks];
for (int i = 0; i < numSticks; i++)
stickInfos[i] = new StickInfo(i);
SDL_WM_SetCaption("SuperTuxKart", NULL);
ssgInit () ;
// Get into menu mode initially.
drv_setMode(MENU);
}
//-----------------------------------------------------------------------------
void
drv_showPointer()
showPointer()
{
pointerVisible = true;
SDL_ShowCursor(SDL_ENABLE);
}
//-----------------------------------------------------------------------------
void
drv_hidePointer()
hidePointer()
{
pointerVisible = false;
SDL_ShowCursor(SDL_DISABLE);
}
//-----------------------------------------------------------------------------
@ -99,10 +114,10 @@ void drv_toggleFullscreen(int resetTextures)
flags |= SDL_FULLSCREEN;
if(menu_manager->isSomewhereOnStack(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;i<NUM_STICKS;i++)
SDL_JoystickClose(sticks[i]);
delete stickInfos[i];
free(sticks);
delete stickInfos;
SDL_FreeSurface(mainSurface);
@ -159,14 +174,73 @@ void drv_deinit()
}
//-----------------------------------------------------------------------------
/** Handles the conversion from some input to a GameAction and its distribution
* to the currently active menu.
* It also handles whether the game is currently sensing input. It does so by
* suppressing the distribution of the input as a GameAction. Instead the
* input is stored in 'sensedInput' and GA_SENSE_COMPLETE is distributed. If
* however the input in question has resolved to GA_LEAVE this is treated as
* an attempt of the user to cancel the sensing. In that case GA_SENSE_CANCEL
* is distributed.
*
* Note: It is the obligation of the called menu to switch of the sense mode.
*
*/
void input(InputType type, int id0, int id1, int id2, int value)
{
BaseGUI* menu = menu_manager->getCurrentMenu();
GameAction ga = actionMap->getEntry(type, id0, id1, id2);
if (menu != NULL)
menu->input(type, id0, id1, id2, value);
{
// 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/<whatever> 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,33 +254,58 @@ 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)
// 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;
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
{
mouseValX = std::max(-32768,
std::min(32768, mouseValX
+ ev.motion.xrel
// 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
std::min(32768, mouseValY + ev.motion.yrel
* MULTIPLIER_MOUSE));
}
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);
{
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);
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);
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)
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)
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);
}

View File

@ -20,16 +20,41 @@
#ifndef HEADER_SDLDRV_H
#define HEADER_SDLDRV_H
#include <SDL/SDL.h>
#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

View File

@ -21,6 +21,7 @@
#include <stdexcept>
#include <sstream>
#include <string>
// for mkdir:
#if !defined(WIN32) || defined(__CYGWIN__)
@ -33,6 +34,7 @@
#include <SDL/SDL.h>
#include <plib/ul.h>
#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)
@ -142,50 +147,151 @@ void UserConfig::setDefaults()
m_player[i].setName(m_username);
}
// Clear every entry.
memset(inputMap, sizeof(inputMap), 0);
/*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);
/* 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));
/*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_CURSOR_DOWN,
Input(IT_KEYBOARD, SDLK_DOWN),
Input(IT_STICKMOTION, 0, 1, AD_POSITIVE));
/*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_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));
set(GA_CLEAR_MAPPING,
Input(IT_KEYBOARD, SDLK_BACKSPACE),
Input(IT_STICKBUTTON, 0, 2));
set(GA_INC_SCROLL_SPEED,
Input(IT_KEYBOARD, SDLK_PLUS));
set(GA_INC_SCROLL_SPEED_FAST,
Input(IT_KEYBOARD, SDLK_PAGEDOWN));
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
@ -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 (input.id0 != -1 && input.id1 != -1 && input.id2 != -1)
{
setInput(action, input);
}
if (id0 != -1 && id1 != -1 && id2 != -1)
player.setInput(action, it, id0, id1, id2);
}
// -----------------------------------------------------------------------------
@ -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; i<PLAYERS; ++i)
@ -518,30 +629,30 @@ void UserConfig::saveConfig(const std::string& filename)
temp = "player ";
temp += i+'1';
temp += " settings";
writer.writeComment(temp);
writer->writeComment(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)
writeInput(writer, node, (GameAction) (playerIndex * KC_COUNT + ka + GA_P1_LEFT));
}
// -----------------------------------------------------------------------------
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);
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);
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);
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);
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:
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);
}
}
writer->endList(node);
}
// -----------------------------------------------------------------------------
std::string UserConfig::getInputAsString(int player_index, KartActions control)
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*/

View File

@ -37,6 +37,7 @@
#define SUPPORTED_CONFIG_VERSION 3
#include <string>
#include "input.hpp"
#include "player.hpp"
#include "lisp/lisp.hpp"
#include "lisp/parser.hpp"
@ -44,13 +45,29 @@
#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();
};

View File

@ -20,9 +20,6 @@
#include "user_config.hpp"
//TODO: this include should not be necesary?
#include <SDL/SDL.h>
#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)
return handle_finish(find_left_widget(find_id(m_selected_wgt_token)));
}
int
WidgetManager::handle_right()
{
case SDLK_LEFT:
next_wgt = find_left_widget(find_id(m_selected_wgt_token));
break;
if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE;
case SDLK_RIGHT:
next_wgt = find_right_widget(find_id(m_selected_wgt_token));
break;
return handle_finish(find_right_widget(find_id(m_selected_wgt_token)));
}
case SDLK_UP:
next_wgt = find_top_widget(find_id(m_selected_wgt_token));
break;
int
WidgetManager::handle_up()
{
if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE;
case SDLK_DOWN:
next_wgt = find_bottom_widget(find_id(m_selected_wgt_token));
break;
return handle_finish(find_top_widget(find_id(m_selected_wgt_token)));
}
//FIXME: apparently, there are different codes for the + and -
//near the numlock.
case SDLK_PLUS:
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)));
}
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;
}
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 -= 1;
m_widgets[ID].widget->m_scroll_speed_y -= (fast) ? 5 : 1;
}
break;
}
case SDLK_MINUS:
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 )
{
m_widgets[ID].widget->m_scroll_speed_y += 1;
//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;
}
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;
}
/** 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
)
{
if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE;
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;
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;
default: return WGT_NONE;
}
if( next_wgt == find_id(m_selected_wgt_token) ) return WGT_NONE;
m_selected_wgt_token = m_widgets[next_wgt].token;
return m_selected_wgt_token;
}
//FIXME: find_left_widget() doesn't works properly yet

View File

@ -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;