/** * @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 "land.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_prev", "target_nearest", "target_hostile", /* Fighting. */ "primary", "face", "board", /* Secondary weapons. */ "secondary", "secondary_next", /* Escorts. */ "e_attack", "e_hold", "e_return", "e_clear", /* Space Navigation. */ "autonav", "target_planet", "land", "thyperspace","starmap", "jump", /* Communication. */ "hail", /* Misc. */ "mapzoomin", "mapzoomout", "screenshot", "pause", "speed", "menu", "info", "end" /* Must terminate at the end. */ }; /* Keybinding descriptions. Should match in position of the names. */ const char* keybindDescription[] = { /* Movement. */ "Makes your ship accelerate forward.", "Makes your ship turn left.", "Makes your ship turn right.", "Makes your ship turn around and face the direction you're moving from. Good for braking.", "Makes your ship afterburn if you have an afterburner installed.", /* Targetting. */ "Cycles through ship targets.", "Cycles backwards through ship targets.", "Targets the nearest non-disabled ship.", "Targets the nearest hostile ship.", /* Fighting. */ "Fires your primary weapons.", "Faces your target (ship target if you have one, otherwise planet target).", "Attempts to board your target ship.", /* Secondary weapons. */ "Fires your secondary weapon.", "Cycles through secondary weapons.", /* Escorts. */ "Tells your escorts to attack your target.", "Tells your escorts to hold their posistion.", "Tells your escorts to return to your ships hanger.", "Clears your escorts commands.", /* Space navigation. */ "Initializes the autonavigation system.", "Cycles through planet targets.", "Attempt to land on your targetted planet or targets nearest landable planet. \ Requests for landing if you don't have permission yet.", "Cycles through hyperspace targets.", "Opens the Star Map.", "Attempt to jump to your hyperspace target.", /* Communication. */ "Attempts to initialize communication with your taretted ship.", /* Misc. */ "Zooms in on your radar.", "Zooms out on your radar.", "Takes a screenshot.", "Toggles 2x speed modifier.", "Pauses the game.", "Opens the small ingame menu.", "Opens the information menu." }; /* 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; static char* keyconv[SDLK_LAST]; /**< Key conversion table. */ static void input_keyConvGen(void); static void input_keyConvDestroy(void); /** * @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_NULL, 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_prev", KEYBIND_KEYBOARD, SDLK_TAB, KMOD_RCTRL, 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_1, KMOD_NONE, 0); input_setKeybind("e_hold", KEYBIND_KEYBOARD, SDLK_2, KMOD_NONE, 0); input_setKeybind("e_return", KEYBIND_KEYBOARD, SDLK_3, KMOD_NONE, 0); input_setKeybind("e_clear", KEYBIND_KEYBOARD, SDLK_4, KMOD_NONE, 0); /* Secondary weapon. */ input_setKeybind("secondary", KEYBIND_KEYBOARD, SDLK_LALT, 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); /* Communication. */ input_setKeybind("hail", KEYBIND_KEYBOARD, SDLK_y, 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("speed", KEYBIND_KEYBOARD, SDLK_BACKQUOTE, 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; } input_keyConvGen(); } /** * @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); input_keyConvDestroy(); } /** * @brief Create the key conversion table. */ static void input_keyConvGen(void) { SDLKey k; for(k == SDLK_FIRST; k < SDLK_LAST; k++) keyconv[k] = SDL_GetKeyName(k); } /** * @brief Destroy the key conversion table. */ static void input_keyConvDestroy(void) { } /** * @brief Get the key id from its name. * @param name Name of the key to get id from. * @return ID of the key. */ SDLKey input_keyConv(char* name) { SDLKey k; size_t l; char buf[2]; l = strlen(name); buf[0] = tolower(name[0]); buf[1] = '\0'; for(k = SDLK_FIRST; k < SDLK_LAST; k++) if(strcmp((l==1) ? buf : name, keyconv[k])==0) return k; WARN("Keyname '%s' doesn't match any key.", name); return SDLK_UNKNOWN; } /** * @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); } /** * @brief Get the value of a keybind. */ SDLKey input_getKeybind(const 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 (SDLKey)-1; } /** * @brief Get the description of the keybinding */ const char* input_getKeybindDescription(char* keybind) { int i; for(i = 0; strcmp(keybindNames[i], "end"); i++) if(strcmp(keybind, input_keybinds[i]->name)==0) return keybindDescription[i]; WARN("Unable to key keybinding description '%s', that cmmand doesn't exist", keybind); return NULL; } /** * @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. */ #define NODEAD() (player) /**< Player isn't dead. */ #define NOLAND() (!landed) /**< Player isn't landed. */ static void input_key(int keynum, double value, int kabs) { unsigned int t; /* Accelerating. */ if(KEY("accel")) { if(kabs) { player_abortAutonav(NULL); player_accel(value); } else { /* Prevent it from getting stuck. */ if(value == KEY_PRESS) { player_abortAutonav(NULL); 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(NULL); 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(NULL); 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(NULL); 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(NULL); player_setFlag(PLAYER_PRIMARY); } else if(value == KEY_RELEASE) { player_rmFlag(PLAYER_PRIMARY); } } /* Targetting. */ else if(INGAME() && NODEAD() && KEY("target")) { if(value == KEY_PRESS) player_targetNext(); } else if(INGAME() && NODEAD() && KEY("target_prev")) { if(value == KEY_PRESS) player_targetPrev(); } else if(INGAME() && NODEAD() && KEY("target_nearest")) { if(value == KEY_PRESS) player_targetNearest(); } else if(INGAME() && NODEAD() && KEY("target_hostile")) { if(value == KEY_PRESS) player_targetHostile(); } /* Face the target. */ else if(KEY("face")) { if(value == KEY_PRESS) { player_abortAutonav(NULL); 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(NULL); player_board(); } } /* Escorts. */ else if(INGAME() && NODEAD() && KEY("e_attack")) { if(value == KEY_PRESS) escorts_attack(player); } else if(INGAME() && NODEAD() && KEY("e_hold")) { if(value == KEY_PRESS) escorts_hold(player); } else if(INGAME() && NODEAD() && KEY("e_return")) { if(value == KEY_PRESS) escorts_return(player); } else if(INGAME() && NODEAD() && 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(NULL); player_land(); } } else if(KEY("thyperspace") && NOHYP() && NOLAND() && NODEAD()) { if(value == KEY_PRESS) { player_abortAutonav(NULL); player_targetHyperspace(); } } else if(KEY("starmap") && NOHYP() && NODEAD()) { if(value == KEY_PRESS) map_open(); } else if(KEY("jump") && INGAME()) { if(value == KEY_PRESS) { player_abortAutonav(NULL); player_jump(); } } /* Communication. */ else if(KEY("hail") && INGAME() && NOHYP()) { if(value == KEY_PRESS) { player_hail(); } } /* 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(); } } /* Toggle speed mode. */ else if(KEY("speed")) { if(value == KEY_PRESS) { if(dt_mod == 1.) pause_setSpeed(2.); else pause_setSpeed(1.); } } /* 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; } }