/** * @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 "escort.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. */ SDLKey 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", /* Escorts. */ "e_attack", "e_hold", "e_return", "e_clear", /* 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; /** * @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); /* Escorts. */ input_setKeybind("e_attack", KEYBIND_KEYBOARD, SDLK_f, KMOD_NONE, 0); input_setKeybind("e_hold", KEYBIND_KEYBOARD, SDLK_g, KMOD_NONE, 0); input_setKeybind("e_return", KEYBIND_KEYBOARD, SDLK_r, KMOD_NONE, 0); input_setKeybind("e_clear", KEYBIND_KEYBOARD, SDLK_c, 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) (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; /* Accelerating. */ if(KEY("accel")) { if(kabs) { player_abortAutonav(); player_accel(value); } else { /* Prevent it from getting stuck. */ if(value == KEY_PRESS) { player_abortAutonav(); player_accel(1.); } 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_abortAutonav(); player_setFlag(PLAYER_TURN_LEFT); } 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_abortAutonav(); player_setFlag(PLAYER_TURN_RIGHT); } 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_abortAutonav(); player_setFlag(PLAYER_REVERSE); } 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_abortAutonav(); player_setFlag(PLAYER_PRIMARY); } 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_abortAutonav(); player_setFlag(PLAYER_FACE); } 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_abortAutonav(); player_board(); } } /* Escorts. */ else if(INGAME() && KEY("e_attack")) { if(value == KEY_PRESS) escorts_attack(player); } else if(INGAME() && KEY("e_hold")) { if(value == KEY_PRESS) escorts_hold(player); } else if(INGAME() && KEY("e_return")) { if(value == KEY_PRESS) escorts_return(player); } else if(INGAME() && KEY("e_clear")) { if(value == KEY_PRESS) escorts_clear(player); } /* 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_abortAutonav(); player_land(); } } else if(KEY("thyperspace") && INGAME() && NOHYP()) { if(value == KEY_PRESS) { player_abortAutonav(); player_targetHyperspace(); } } else if(KEY("starmap") && NOHYP()) { if(value == KEY_PRESS) map_open(); } else if(KEY("jump") && INGAME()) { if(value == KEY_PRESS) { player_abortAutonav(); player_jump(); } } /* 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_joyevent(int event, const unsigned int button); static void input_keyevent(int event, SDLKey key, SDLMod mod); /* 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 event. */ static void input_joyevent(int event, 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, event, 0); } /* Keyboard. */ /* Key event. */ static void input_keyevent(int event, SDLKey key, SDLMod mod) { int i; mod &= ~(KMOD_CAPS | KMOD_NUM | KMOD_MODE); /* We want to ignore "global" modifiers. */ for(i = 0; strcmp(keybindNames[i], "end"); i++) if((input_keybinds[i]->type == KEYBIND_KEYBOARD) && (input_keybinds[i]->key == key) && ((input_keybinds[i]->mod == mod) || (input_keybinds[i]->mod == KMOD_ALL))) input_key(i, event, 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_joyevent(KEY_PRESS, event->jbutton.button); break; case SDL_JOYBUTTONUP: input_joyevent(KEY_RELEASE, event->jbutton.button); break; case SDL_KEYDOWN: input_keyevent(KEY_PRESS, event->key.keysym.sym, event->key.keysym.mod); break; case SDL_KEYUP: input_keyevent(KEY_RELEASE, event->key.keysym.sym, event->key.keysym.mod); break; } }