#include "townengine/input/internal_api.h" #include "townengine/context.h" #include "townengine/util.h" #include #include #include #include static void update_action_pressed_state(struct input_state *input, struct action *action) { for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) { switch (action->bindings[i].source) { case BUTTON_SOURCE_NOT_SET: break; case BUTTON_SOURCE_KEYBOARD_PHYSICAL: /* not pressed */ if (input->keyboard_state[action->bindings[i].code.scancode] == 0) { action->just_changed = action->is_pressed; action->is_pressed = false; } /* pressed */ else { action->just_changed = !action->is_pressed; action->is_pressed = true; return; } break; case BUTTON_SOURCE_MOUSE: /* not pressed */ if ((input->mouse_state & action->bindings[i].code.mouse_button) == 0) { action->just_changed = action->is_pressed; action->is_pressed = false; } /* pressed */ else { action->just_changed = !action->is_pressed; action->is_pressed = true; action->position.x = (float)input->mouse_window_position.x; action->position.x = (float)input->mouse_window_position.x; /* TODO: */ /* * SDL_RenderWindowToLogical will turn window mouse * coords into a position inside the logical render * area. this has to be done to get an accurate point * that can actually be used in game logic */ // SDL_RenderWindowToLogical(input->renderer, // input->mouse_window_position.x, // input->mouse_window_position.y, // &action->position.x, // &action->position.y); return; } break; default: break; } } } static void input_bind_code_to_action(struct input_state *input, char *action_name, enum button_source source, union button_code code) { struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name); if (action_item == NULL) { log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); return; } struct action *action = &action_item->value; /* check every binding to make sure this code isn't already bound */ for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) { struct button *binding = &action->bindings[i]; if (binding->source != source) break; bool is_already_bound = false; switch (binding->source) { case BUTTON_SOURCE_NOT_SET: break; case BUTTON_SOURCE_KEYBOARD_PHYSICAL: is_already_bound = binding->code.scancode == code.scancode; break; case BUTTON_SOURCE_KEYBOARD_CHARACTER: is_already_bound = binding->code.keycode == code.keycode; break; case BUTTON_SOURCE_GAMEPAD: is_already_bound = binding->code.gamepad_button == code.gamepad_button; break; case BUTTON_SOURCE_MOUSE: is_already_bound = binding->code.mouse_button == code.mouse_button; break; } if (is_already_bound) { log_warn("(%s) Code already bound to action \"%s\".", __func__, action_name); return; } } /* if we're at max bindings, forget the first element and shift the rest */ if (action->num_bindings == SDL_arraysize(action->bindings)) { --action->num_bindings; size_t shifted_size = (sizeof action->bindings) - (sizeof action->bindings[0]); memmove(action->bindings, action->bindings + 1, shifted_size); } action->bindings[action->num_bindings++] = (struct button) { .source = source, .code = code, }; } static void input_unbind_code_from_action(struct input_state *input, char *action_name, enum button_source source, union button_code code) { struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name); if (action_item == NULL) { log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); return; } struct action *action = &action_item->value; /* check every binding to make sure this code is bound */ size_t index = 0; bool is_bound = false; for (index = 0; index < SDL_arraysize(action->bindings); ++index) { struct button *binding = &action->bindings[index]; if (binding->source != source) continue; switch (binding->source) { case BUTTON_SOURCE_NOT_SET: break; case BUTTON_SOURCE_KEYBOARD_PHYSICAL: is_bound = binding->code.scancode == code.scancode; break; case BUTTON_SOURCE_KEYBOARD_CHARACTER: is_bound = binding->code.keycode == code.keycode; break; case BUTTON_SOURCE_GAMEPAD: is_bound = binding->code.gamepad_button == code.gamepad_button; break; case BUTTON_SOURCE_MOUSE: is_bound = binding->code.mouse_button == code.mouse_button; break; } if (is_bound) break; } if (!is_bound) { log_warn("(%s) Code is not bound to action \"%s\".", __func__, action_name); return; } /* remove the element to unbind and shift the rest so there isn't a gap */ if (action->num_bindings == SDL_arraysize(action->bindings)) { size_t elements_after_index = action->num_bindings - index; size_t shifted_size = elements_after_index * (sizeof action->bindings[0]); memmove(action->bindings + index, action->bindings + index + 1, shifted_size); --action->num_bindings; } } void input_state_init(struct input_state *input) { sh_new_strdup(input->action_hash); } void input_state_deinit(struct input_state *input) { shfree(input->action_hash); } void input_state_update(struct input_state *input) { input->keyboard_state = SDL_GetKeyboardState(NULL); input->mouse_state = SDL_GetMouseState(&input->mouse_window_position.x, &input->mouse_window_position.y); SDL_GetRelativeMouseState(&input->mouse_relative_position.x, &input->mouse_relative_position.y); for (size_t i = 0; i < shlenu(input->action_hash); ++i) { struct action *action = &input->action_hash[i].value; update_action_pressed_state(input, action); } } void input_bind_action_scancode(struct input_state *input, char *action_name, SDL_Scancode scancode) { input_bind_code_to_action(input, action_name, BUTTON_SOURCE_KEYBOARD_PHYSICAL, (union button_code) { .scancode = scancode }); } void input_unbind_action_scancode(struct input_state *input, char *action_name, SDL_Scancode scancode) { input_unbind_code_from_action(input, action_name, BUTTON_SOURCE_KEYBOARD_PHYSICAL, (union button_code) { .scancode = scancode }); } void input_bind_action_mouse(struct input_state *input, char *action_name, uint8_t mouse_button) { input_bind_code_to_action(input, action_name, BUTTON_SOURCE_MOUSE, (union button_code) { .mouse_button = mouse_button}); } void input_unbind_action_mouse(struct input_state *input, char *action_name, uint8_t mouse_button) { input_unbind_code_from_action(input, action_name, BUTTON_SOURCE_MOUSE, (union button_code) { .mouse_button = mouse_button}); } void input_add_action(struct input_state *input, char *action_name) { if (shgeti(input->action_hash, action_name) >= 0) { log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name); return; } shput(input->action_hash, action_name, (struct action) { 0 }); } void input_delete_action(struct input_state *input, char *action_name) { if (shdel(input->action_hash, action_name) == 0) log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name); } bool input_is_action_pressed(struct input_state *input, char *action_name) { struct action_hash_item *action = shgetp_null(input->action_hash, action_name); if (action == NULL) { log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); return false; } return action->value.is_pressed; } bool input_is_action_just_pressed(struct input_state *input, char *action_name) { struct action_hash_item *action = shgetp_null(input->action_hash, action_name); if (action == NULL) { log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); return false; } return action->value.is_pressed && action->value.just_changed; } bool input_is_action_just_released(struct input_state *input, char *action_name) { struct action_hash_item *action = shgetp_null(input->action_hash, action_name); if (action == NULL) { log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); return false; } return !action->value.is_pressed && action->value.just_changed; } t_fvec2 input_get_action_position(struct input_state *input, char *action_name) { struct action_hash_item *action = shgetp_null(input->action_hash, action_name); if (action == NULL) { log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name); return (t_fvec2) { 0 }; } return action->value.position; } void input_set_mouse_captured(struct input_state *input, bool enabled) { (void)input; /* TODO: returns -1 if not supported, but like... do we care? */ SDL_SetRelativeMouseMode(enabled); } bool input_is_mouse_captured(struct input_state *input) { (void)input; return SDL_GetRelativeMouseMode(); } void input_reset_state(struct input_state *input) { stbds_shfree(input->action_hash); }