partially done work on total source tree rework, separation of engine context and game context, generalization of renderer for different backends as well as web platform target
This commit is contained in:
320
src/twn_input.c
Normal file
320
src/twn_input.c
Normal file
@ -0,0 +1,320 @@
|
||||
#include "townengine/input/internal_api.h"
|
||||
#include "townengine/context.h"
|
||||
#include "townengine/util.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <stb_ds.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
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);
|
||||
}
|
Reference in New Issue
Block a user