Lephisto/src/input.c
2013-12-27 23:19:20 +00:00

479 lines
15 KiB
C

/**
* @file input.c
*
* @brief Handle all the keybindings and input.
*/
#include "lephisto.h"
#include "log.h"
#include "player.h"
#include "pause.h"
#include "toolkit.h"
#include "menu.h"
#include "board.h"
#include "map.h"
#include "input.h"
#define KEY_PRESS ( 1.) /**< Key is pressed. */
#define KEY_RELEASE (-1.) /**< Key is released. */
/* Keybind structure. */
typedef struct Keybind_ {
char* name; /**< Keybinding name, taken from keybindNames[] */
KeybindType type; /**< type, defined in player.h. */
unsigned int key; /**< Key/axis/button event number. */
double reverse; /**< 1. if normal, -1 if reversed, only useful for joystick axis. */
SDLMod mod; /**< Key modifiers (where applicable). */
} Keybind;
static Keybind** input_keybinds; /**< Contains the players keybindings. */
/* Name of each keybinding. */
const char* keybindNames[] = {
/* Movement. */
"accel", "left", "right", "reverse", "afterburn",
/* Targetting. */
"target", "target_nearest", "target_hostile",
/* Fighting. */
"primary", "face", "board",
/* Secondary weapons. */
"secondary", "secondary_next",
/* Space Navigation. */
"autonav", "target_planet", "land", "thyperspace","starmap", "jump",
/* Misc. */
"mapzoomin", "mapzoomout", "screenshot", "pause", "menu", "info",
"end" /* Must terminate at the end. */
};
/* Accel hacks. */
static unsigned int input_accelLast = 0; /**< Used to see if double tap. */
unsigned int input_afterburnSensibility = 200; /**< ms between taps to afterburn. */
/* From player.c */
extern double player_turn;
extern unsigned int player_target;
/**
* @fn void input_setDefault(void)
*
* @brief Set the default input keys.
*/
void input_setDefault(void) {
/* Movement. */
input_setKeybind("accel", KEYBIND_KEYBOARD, SDLK_w, KMOD_ALL, 0);
input_setKeybind("afterburn", KEYBIND_KEYBOARD, SDLK_UNKNOWN, KMOD_ALL, 0);
input_setKeybind("left", KEYBIND_KEYBOARD, SDLK_a, KMOD_ALL, 0);
input_setKeybind("right", KEYBIND_KEYBOARD, SDLK_d, KMOD_ALL, 0);
input_setKeybind("reverse", KEYBIND_KEYBOARD, SDLK_s, KMOD_ALL, 0);
/* Targetting. */
input_setKeybind("target", KEYBIND_KEYBOARD, SDLK_TAB, KMOD_NONE, 0);
input_setKeybind("target_nearest", KEYBIND_KEYBOARD, SDLK_t, KMOD_NONE, 0);
input_setKeybind("target_hostile", KEYBIND_KEYBOARD, SDLK_r, KMOD_NONE, 0);
/* Combat. */
input_setKeybind("primary", KEYBIND_KEYBOARD, SDLK_SPACE, KMOD_NONE, 0);
input_setKeybind("face", KEYBIND_KEYBOARD, SDLK_f, KMOD_NONE, 0);
input_setKeybind("board", KEYBIND_KEYBOARD, SDLK_b, KMOD_NONE, 0);
/* Secondary weapon. */
input_setKeybind("secondary", KEYBIND_KEYBOARD, SDLK_LSHIFT, KMOD_ALL, 0);
input_setKeybind("secondary_next", KEYBIND_KEYBOARD, SDLK_e, KMOD_NONE, 0);
/* Space */
input_setKeybind("autonav", KEYBIND_KEYBOARD, SDLK_j, KMOD_LCTRL, 0);
input_setKeybind("target_planet", KEYBIND_KEYBOARD, SDLK_p, KMOD_NONE, 0);
input_setKeybind("land", KEYBIND_KEYBOARD, SDLK_l, KMOD_NONE, 0);
input_setKeybind("thyperspace", KEYBIND_KEYBOARD, SDLK_h, KMOD_NONE, 0);
input_setKeybind("starmap", KEYBIND_KEYBOARD, SDLK_m, KMOD_NONE, 0);
input_setKeybind("jump", KEYBIND_KEYBOARD, SDLK_j, KMOD_NONE, 0);
/* Misc. */
input_setKeybind("mapzoomin", KEYBIND_KEYBOARD, SDLK_KP_PLUS, KMOD_NONE, 0);
input_setKeybind("mapzoomout", KEYBIND_KEYBOARD, SDLK_KP_MINUS, KMOD_NONE, 0);
input_setKeybind("screenshot", KEYBIND_KEYBOARD, SDLK_KP_MULTIPLY, KMOD_NONE, 0);
input_setKeybind("pause", KEYBIND_KEYBOARD, SDLK_F1, KMOD_NONE, 0);
input_setKeybind("menu", KEYBIND_KEYBOARD, SDLK_ESCAPE, KMOD_NONE, 0);
input_setKeybind("info", KEYBIND_KEYBOARD, SDLK_i, KMOD_NONE, 0);
}
/**
* @fn void input_init(void)
*
* @brief Initialize the input subsystem (does not set keys).
*/
void input_init(void) {
Keybind* tmp;
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++); /* Get number of bindings. */
input_keybinds = malloc(i*sizeof(Keybind*));
/* Create a null keybinding for each. */
for(i = 0; strcmp(keybindNames[i], "end"); i++) {
tmp = MALLOC_L(Keybind);
tmp->name = (char*)keybindNames[i];
tmp->type = KEYBIND_NULL;
tmp->key = SDLK_UNKNOWN;
tmp->mod = KMOD_NONE;
tmp->reverse = 1.;
input_keybinds[i] = tmp;
}
}
/**
* @fn void input_exit(void)
*
* @brief Exit the input subsystem.
*/
void input_exit(void) {
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++)
free(input_keybinds[i]);
free(input_keybinds);
}
/**
* @fn void input_setKeybind(char* keybind, KeybindType type, int key, int reverse)
*
* @brief Bind a key of type to action keybind.
* @param keybind The name of the keybind defined above.
* @param type The type of the keybind.
* @param key The key to bind to.
* @param mod Modifiers to check for.
* @param reverse Whether to reverse it or not.
*/
void input_setKeybind(char* keybind, KeybindType type, int key,
SDLMod mod, int reverse) {
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++)
if(strcmp(keybind, input_keybinds[i]->name)==0) {
input_keybinds[i]->type = type;
input_keybinds[i]->key = key;
/* Non-keyboards get mod KMOD_ALL to always match. */
input_keybinds[i]->mod = (type == KEYBIND_KEYBOARD) ? mod : KMOD_ALL;
input_keybinds[i]->reverse = reverse ? -1. : 1.;
return;
}
WARN("Unable to set keybind '%s', That command does not exist.", keybind);
}
/* @fn int input_getKeybind(char* keybind, KeybindType* type, int* reverse)
*
* @brief Get the value of a keybind.
*/
int input_getKeybind(char* keybind, KeybindType* type, SDLMod* mod, int* reverse) {
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++)
if(strcmp(keybind, input_keybinds[i]->name)==0) {
if(type != NULL) (*type) = input_keybinds[i]->type;
if(mod != NULL) (*mod) = input_keybinds[i]->mod;
if(reverse != NULL) (*reverse) = input_keybinds[i]->reverse;
return input_keybinds[i]->key;
}
WARN("Unable to get keybinding '%s', that command doesn't exist", keybind);
return -1;
}
/**
* @fn static void input_key(int keynum, double value, int kabs)
*
* @brief Run the input command.
* @param keynum The index of the keybind.
* @param value The value of the keypress (defined above).
* @param abs Whether or not it's an absolute value (for the joystick).
*/
#define KEY(s) (((input_keybinds[keynum]->mod == mod) || \
(input_keybinds[keynum]->mod==KMOD_ALL)) && \
(strcmp(input_keybinds[keynum]->name, s)==0)) /**< Shortcut for ease. */
#define INGAME() (!toolkit) /**< Make sure player is in game. */
#define NOHYP() \
(player && !pilot_isFlag(player, PILOT_HYP_PREP) && \
!pilot_isFlag(player, PILOT_HYP_BEGIN) && \
!pilot_isFlag(player, PILOT_HYPERSPACE)) /**< Make sure player isn't jumping. */
static void input_key(int keynum, double value, int kabs) {
unsigned int t;
SDLMod mod;
mod = SDL_GetModState(); /* Yes, we always get it just in case. */
mod &= ~(KMOD_CAPS | KMOD_NUM | KMOD_MODE); /* We want to ignore "global" modifiers. */
/* Accelerating. */
if(KEY("accel")) {
if(kabs) {
player_abortAutonav();
player_accel(value);
}
else {
/* Prevent it from getting stuck. */
if(value == KEY_PRESS) {
player_accel(1.);
player_abortAutonav();
}
else if(value == KEY_RELEASE) player_accelOver();
}
/* Double tap accel = afterburn! */
t = SDL_GetTicks();
if((value == KEY_PRESS) && INGAME() && NOHYP() &&
(t-input_accelLast <= input_afterburnSensibility))
player_afterburn();
else if((value == KEY_RELEASE) && player_isFlag(PLAYER_AFTERBURNER))
player_afterburnOver();
if(value == KEY_PRESS) input_accelLast = t;
}
else if(KEY("afterburn") && INGAME() && NOHYP()) {
if(value == KEY_PRESS) player_afterburn();
else if(value == KEY_RELEASE) player_afterburnOver();
}
/* Turning left. */
else if(KEY("left")) {
/* Set flags for facing correction. */
if(value == KEY_PRESS) {
player_setFlag(PLAYER_TURN_LEFT);
player_abortAutonav();
}
else if(value == KEY_RELEASE) { player_rmFlag(PLAYER_TURN_LEFT); }
if(kabs) { player_turn = -value; }
else { player_turn -= value; }
if(player_turn < -1.) player_turn = -1.; /* Make sure value is sane. */
}
/* Turning right. */
else if(KEY("right")) {
/* Set flags for facing correction. */
if(value == KEY_PRESS) {
player_setFlag(PLAYER_TURN_RIGHT);
player_abortAutonav();
}
else if(value == KEY_RELEASE) { player_rmFlag(PLAYER_TURN_RIGHT); }
if(kabs) { player_turn = value; }
else { player_turn += value; }
if(player_turn < -1.) { player_turn = -1.; } /* Make sure value is sane. */
}
/* Turn around to face vel. */
else if(KEY("reverse")) {
if(value == KEY_PRESS) {
player_setFlag(PLAYER_REVERSE);
player_abortAutonav();
}
else if(value == KEY_RELEASE) {
player_rmFlag(PLAYER_REVERSE);
player_turn = 0; /* Turning corrections. */
if(player_isFlag(PLAYER_TURN_LEFT)) { player_turn -= 1; }
if(player_isFlag(PLAYER_TURN_RIGHT)) { player_turn += 1; }
}
}
/* Shoot primary weapon. BOOM BOOM. */
else if(KEY("primary")) {
if(value == KEY_PRESS) {
player_setFlag(PLAYER_PRIMARY);
player_abortAutonav();
}
else if(value == KEY_RELEASE) { player_rmFlag(PLAYER_PRIMARY); }
}
/* Targetting. */
else if(INGAME() && KEY("target")) {
if(value == KEY_PRESS) player_targetNext();
}
else if(INGAME() && KEY("target_nearest")) {
if(value == KEY_PRESS) player_targetNearest();
}
else if(INGAME() && KEY("target_hostile")) {
if(value == KEY_PRESS) player_targetHostile();
}
/* Face the target. */
else if(KEY("face")) {
if(value == KEY_PRESS) {
player_setFlag(PLAYER_FACE);
player_abortAutonav();
}
else if(value == KEY_RELEASE) {
player_rmFlag(PLAYER_FACE);
/* Turning corrections. */
player_turn = 0;
if(player_isFlag(PLAYER_TURN_LEFT)) { player_turn -= 1; }
if(player_isFlag(PLAYER_TURN_RIGHT)) { player_turn += 1; }
}
}
/* Board those ships. */
else if(KEY("board") && INGAME() && NOHYP()) {
if(value == KEY_PRESS) {
player_board();
player_abortAutonav();
}
}
/* Shooting secondary weapon. */
else if(KEY("secondary") && NOHYP()) {
if(value == KEY_PRESS) { player_setFlag(PLAYER_SECONDARY); }
else if(value == KEY_RELEASE) { player_rmFlag(PLAYER_SECONDARY); }
}
/* Selecting secondary weapon. */
else if(KEY("secondary_next") && INGAME()) {
if(value == KEY_PRESS) player_secondaryNext();
}
/* Space. */
else if(KEY("autonav") && INGAME() && NOHYP()) {
if(value == KEY_PRESS) player_startAutonav();
}
/* Target planet (cycles just like target). */
else if(KEY("target_planet") && INGAME() && NOHYP()) {
if(value == KEY_PRESS) player_targetPlanet();
}
/* Target nearest planet or attempt to land. */
else if(KEY("land") && INGAME() && NOHYP()) {
if(value == KEY_PRESS) {
player_land();
player_abortAutonav();
}
}
else if(KEY("thyperspace") && INGAME() && NOHYP()) {
if(value == KEY_PRESS) {
player_targetHyperspace();
player_abortAutonav();
}
}
else if(KEY("starmap") && NOHYP()) {
if(value == KEY_PRESS) map_open();
}
else if(KEY("jump") && INGAME()) {
if(value == KEY_PRESS) {
player_jump();
player_abortAutonav();
}
}
/* Zoom in. */
else if(KEY("mapzoomin") && INGAME()) {
if(value == KEY_PRESS) player_setRadarRel(1);
}
/* Zoom out. */
else if(KEY("mapzoomout") && INGAME()) {
if(value == KEY_PRESS) player_setRadarRel(-1);
}
/* Take a screenshot. */
else if(KEY("screenshot") && INGAME()) {
if(value == KEY_PRESS) player_screenshot();
}
/* Pause the game. */
else if(KEY("pause") && NOHYP()) {
if(value == KEY_PRESS) {
if(!toolkit) {
if(paused) unpause_game();
} else pause_game();
}
}
/* Opens a menu. */
else if(KEY("menu")) {
if(value == KEY_PRESS) menu_small();
}
/* Show pilot information. */
else if(KEY("info") && NOHYP()) {
if(value == KEY_PRESS) menu_info();
}
}
#undef KEY
/* --Events-- */
static void input_joyaxis(const unsigned int axis, const int value);
static void input_joydown(const unsigned int button);
static void input_joyup(const unsigned int button);
static void input_keydown(SDLKey key);
static void input_keyup(SDLKey key);
/* Joystick. */
/* Axis. */
static void input_joyaxis(const unsigned int axis, const int value) {
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++)
if(input_keybinds[i]->type == KEYBIND_JAXIS &&
input_keybinds[i]->key == axis)
input_key(i, -(input_keybinds[i]->reverse) * (double)value / 32767., 1);
}
/* Joystick button down. */
static void input_joydown(const unsigned int button) {
int i;
for(i = 0; strcmp(keybindNames[i], "end");i++)
if(input_keybinds[i]->type == KEYBIND_JBUTTON &&
input_keybinds[i]->key == button)
input_key(i, KEY_RELEASE, 0);
}
/* Joystick button up. */
static void input_joyup(const unsigned int button) {
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++)
if(input_keybinds[i]->type == KEYBIND_JBUTTON &&
input_keybinds[i]->key == button)
input_key(i, KEY_RELEASE, 0);
}
/* Keyboard. */
/* Key down. */
static void input_keydown(SDLKey key) {
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++)
if(input_keybinds[i]->type == KEYBIND_KEYBOARD &&
input_keybinds[i]->key == key)
input_key(i, KEY_PRESS, 0);
}
/* Key up. */
static void input_keyup(SDLKey key) {
int i;
for(i = 0; strcmp(keybindNames[i], "end"); i++)
if(input_keybinds[i]->type == KEYBIND_KEYBOARD &&
input_keybinds[i]->key == key)
input_key(i, KEY_RELEASE, 0);
}
/**
* @fn void input_handle(SDL_Event* event)
*
* @brief Handle global input.
*
* Basically seperates the event types.
* @param event Incoming SDL_Event.
*/
void input_handle(SDL_Event* event) {
/* Pause the game if it is unfocused. */
if(event->type == SDL_ACTIVEEVENT) {
if(event->active.state != SDL_APPMOUSEFOCUS) {
/* We don't need mouse focus. */
if((event->active.gain == 0) && !paused) pause_game();
else if((event->active.gain == 1) && paused) unpause_game();
return;
}
}
if(toolkit)
/* Toolkit is handled seperately. */
if(toolkit_input(event))
return; /* We don't process it if toolkit grabs it. */
switch(event->type) {
case SDL_JOYAXISMOTION:
input_joyaxis(event->jaxis.axis, event->jaxis.value);
break;
case SDL_JOYBUTTONDOWN:
input_joydown(event->jbutton.button);
break;
case SDL_JOYBUTTONUP:
input_joyup(event->jbutton.button);
break;
case SDL_KEYDOWN:
input_keydown(event->key.keysym.sym);
break;
case SDL_KEYUP:
input_keyup(event->key.keysym.sym);
break;
}
}