diff --git a/src/fencer.c b/src/fencer.c index 2777243..c2a0bf8 100644 --- a/src/fencer.c +++ b/src/fencer.c @@ -1,11 +1,63 @@ +#include "camera.h" +#include "input.h" #include "program.h" +#include "spritesheet.h" +#include "sprite.h" #include "tilemap.h" +#include + +static Spritesheet* spr_player_standing = NULL; +static Sprite* player = NULL; +static Transform player_trans = IdentityTransform; + +static Level* level = NULL; + +static Vector cam_speed = ZeroVector; + +static +void cam_move_h(int val) { + cam_speed.x = -val * 0.01f; +} +static +void cam_move_v(int val) { + cam_speed.y = val * 0.01f; +} + +static +void play() { + spr_player_standing = spritesheet_load("assets/sprites/player.png", (IVector){128, 128}); + player = sprite_from_spritesheet(spr_player_standing, 0); + sprite_set_origin(player, (Vector){0.5f, 1.0f}); + player_trans.scale = OneVector; + assert(player != NULL && "Player failed to load"); + + level = level_load("level_0"); + assert(level != NULL && "Level failed to load"); + + input_add_axis_action(SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, &cam_move_h); + input_add_axis_action(SDL_SCANCODE_DOWN, SDL_SCANCODE_UP, &cam_move_v); + g_camera.width = 20; +} + +static +void tick() { + g_camera.centre = vaddf(g_camera.centre, cam_speed); +} + +static +void draw() { + level_draw(level); + sprite_draw(player, player_trans); +} int main(int argc, char* argv[]) { struct ProgramSettings config = { .target_fps = 0, // unbounded speed .title = "fencer", - .view_resolution = {1920, 1080} + .view_resolution = {1920, 1080}, + .on_play = &play, + .on_tick = &tick, + .on_draw = &draw }; return program_run(&config); } diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..ccfcffc --- /dev/null +++ b/src/input.c @@ -0,0 +1,115 @@ +#include "input.h" +#include "debug.h" +#include + +typedef struct InputAction { + const uint8_t* positive; + const uint8_t* negative; + int last; + InputActionDelegate delegate; +} InputAction; + +static InputAction* _actions = NULL; +static size_t _actions_len = 0; +static size_t _actions_cap = 0; + +static const uint8_t* _keys; + +void input_init() { + _actions = malloc(8 * sizeof(InputAction)); + if(_actions == NULL) { + LOG_ERROR("Failed to allocate memory for input actions array"); + return; + } + _actions_cap = 8; + + _keys = SDL_GetKeyboardState(NULL); +} + +void input_clean() { + free(_actions); + _actions_len = 0; + _actions_cap = 0; +} + +static +void _key_changed_event(SDL_Event evt) { + const uint8_t* keyptr = _keys + evt.key.keysym.scancode; + for(InputAction* action = _actions; action < _actions + _actions_len; ++action) { + if(keyptr == action->positive || keyptr == action->negative) { + int val = *action->positive - *action->negative; + if(val != action->last) { + action->last = val; + action->delegate(val); + } + } + } +} + +void input_handle_event(SDL_Event event) { + switch(event.type) { + default:return; + case SDL_KEYDOWN: + case SDL_KEYUP: + _key_changed_event(event); + break; + } +} + +static +void _resize_actions_if_needed(size_t needed_amount) { + if(_actions_cap > needed_amount) + return; + + size_t next_amount = _actions_cap; + while(next_amount <= needed_amount) { + next_amount *= 2; + } + + InputAction* new = realloc(_actions, next_amount); + if(new == NULL) { + LOG_ERROR("Failed to allocate enough space for adding a new input actions"); + return; + } + _actions = new; + _actions_cap = next_amount; +} + +void input_add_axis_action(SDL_Scancode negative, SDL_Scancode positive, InputActionDelegate delegate) { + _resize_actions_if_needed(_actions_len + 1); + _actions[_actions_len] = (InputAction){ + .negative = _keys + negative, + .positive = _keys + positive, + .delegate = delegate + }; + ++_actions_len; +} + +void input_add_key_action(SDL_Scancode key, InputActionDelegate delegate) { + _resize_actions_if_needed(_actions_len + 1); + _actions[_actions_len] = (InputAction) { + .negative = NULL, + .positive = _keys + key, + .delegate = delegate + }; + ++_actions_len; +} + +static +void _remove_element(InputAction* item) { + InputAction* next = item + 1; + + if(next < _actions + _actions_len) { + memmove(item, next, (_actions + _actions_len) - next); + } + + --_actions_len; +} + +void input_remove_actions(InputActionDelegate delegate) { + for(InputAction* action = _actions; action < _actions + _actions_len; ++action) { + if(action->delegate == delegate) { + _remove_element(action); + } + } +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..c550526 --- /dev/null +++ b/src/input.h @@ -0,0 +1,19 @@ +#ifndef _fencer_input_h +#define _fencer_input_h + +#include +#include +#include + +typedef void (*InputActionDelegate)(int value); + +extern void input_init(); +extern void input_clean(); + +extern void input_handle_event(SDL_Event event); + +extern void input_add_axis_action(SDL_Scancode negative, SDL_Scancode positive, InputActionDelegate delegate); +extern void input_add_key_action(SDL_Scancode key, InputActionDelegate delegate); +extern void input_remove_actions(InputActionDelegate delegate); + +#endif // !_fencer_input_h diff --git a/src/program.c b/src/program.c index 969dbb2..a044811 100644 --- a/src/program.c +++ b/src/program.c @@ -1,18 +1,44 @@ #include "program.h" -#include "tilemap.h" #include "camera.h" #include "sprite.h" +#include "time.h" +#include "assets.h" +#include "debug.h" +#include "input.h" +#include #include #include SDL_Window* g_window; -double g_delta_time; -double g_frame_start; +static double _delta_time; +static double _frame_start; +static double _game_start_time; #define INITFLAGS SDL_INIT_EVENTS | SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER +static inline +double tstos(struct timespec ts) { + return (double)ts.tv_sec + (double)ts.tv_nsec * 1E-09; +} + +struct timespec get_time() { + struct timespec ts; + timespec_get(&ts, TIME_UTC); + return ts; +} + +double get_time_s() { + return tstos(get_time()); +} + int program_run(const struct ProgramSettings* settings) { + LOG_INFO("Starting program..."); + float target_dt = 1.0f/settings->target_fps; + if(settings->target_fps <= 0) { + target_dt = 0; + } + _game_start_time = _frame_start = get_time_s(); SDL_Init(INITFLAGS); g_window = SDL_CreateWindow( @@ -21,26 +47,41 @@ int program_run(const struct ProgramSettings* settings) { SDL_WINDOWPOS_CENTERED_DISPLAY(0), settings->view_resolution.x, settings->view_resolution.y, - SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE); + SDL_WINDOW_MAXIMIZED | SDL_WINDOW_RESIZABLE); render_init(g_window, settings); + assets_init(); camera_init(); + input_init(); - Spritesheet* sheet = spritesheet_from_texture("resources/player.png", (IVector){64, 64}); - Sprite* sprite = sprite_from_spritesheet(sheet); + LOG_INFO("settings->on_play"); + settings->on_play(); + LOG_INFO("Starting program loop"); for(;;) { - SDL_Delay(1); - program_handle_events(); - sprite_draw(sprite, IdentityTransform); render_present(); + double current; + do { + current = get_time_s(); + _delta_time = current - _frame_start; + SDL_Delay(1); + program_handle_events(); + } while(_delta_time <= target_dt); + + _frame_start = current; + + settings->on_tick(); + settings->on_draw(); } - return 1; + LOG_ERROR("Failed to exit"); + abort(); } void program_quit() { + assets_clean(); render_clean(); + input_clean(); SDL_DestroyWindow(g_window); SDL_Quit(); exit(0); @@ -51,6 +92,10 @@ void program_handle_events() { while(SDL_PollEvent(&event)) { switch(event.type) { default: break; + case SDL_KEYUP: + case SDL_KEYDOWN: + input_handle_event(event); + break; case SDL_WINDOWEVENT: if(event.window.windowID == SDL_GetWindowID(g_window)) { program_handle_windowevent(&event.window); diff --git a/src/program.h b/src/program.h index b9ae660..7c1b355 100644 --- a/src/program.h +++ b/src/program.h @@ -6,15 +6,21 @@ #include "vmath.h" #include "render.h" +typedef void(*TickCallback)(); +typedef void(*PlayCallback)(); +typedef void(*DrawCallback)(); + struct ProgramSettings { const char* title; IVector view_resolution; int target_fps; + + TickCallback on_tick; + PlayCallback on_play; + DrawCallback on_draw; }; extern SDL_Window* g_window; -extern double g_delta_time; -extern double g_frame_start; extern int program_run(const struct ProgramSettings* settings); extern void program_quit();