From 548c0c747d80db7f7155e08b44db7079b2ca3430 Mon Sep 17 00:00:00 2001 From: Emil Kosz Date: Tue, 9 Sep 2025 07:20:38 +0200 Subject: [PATCH] Game stage system --- src/gfx_context.c | 43 +++++++++++++++++++++++ src/gfx_context.h | 17 +++++++++ src/main.c | 50 +++++++++++--------------- src/stage/stage.c | 48 +++++++++++++++++++++++++ src/stage/stage.h | 39 +++++++++++++++++++++ src/stage/stage_main_menu.c | 25 +++++++++++++ src/stage/stage_main_menu.h | 5 +++ src/stage/stage_manager.c | 70 +++++++++++++++++++++++++++++++++++++ src/stage/stage_manager.h | 21 +++++++++++ src/util.h | 4 +++ 10 files changed, 292 insertions(+), 30 deletions(-) create mode 100644 src/gfx_context.c create mode 100644 src/gfx_context.h create mode 100644 src/stage/stage.c create mode 100644 src/stage/stage.h create mode 100644 src/stage/stage_main_menu.c create mode 100644 src/stage/stage_main_menu.h create mode 100644 src/stage/stage_manager.c create mode 100644 src/stage/stage_manager.h diff --git a/src/gfx_context.c b/src/gfx_context.c new file mode 100644 index 0000000..2443ba8 --- /dev/null +++ b/src/gfx_context.c @@ -0,0 +1,43 @@ +#include "gfx_context.h" + +GfxContext * +GfxContext_new () +{ + GfxContext *self = g_new0 (GfxContext, 1); + + self->window = SDL_CreateWindow (GAME_NAME, 800, 600, SDL_WINDOW_RESIZABLE); + + if (!self->window) + { + g_critical ("Couldn't create window for context %p: %s", self, SDL_GetError ()); + } + + self->renderer = SDL_CreateRenderer (self->window, NULL); + + if (!self->window) + { + g_critical ("Couldn't create renderer for context %p: %s", self, SDL_GetError ()); + } + + return self; +} + +void +GfxContext_prerender (GfxContext *self) +{ + SDL_RenderClear (self->renderer); +} + +void +GfxContext_postrender (GfxContext *self) +{ + SDL_RenderPresent (self->renderer); +} + +void +GfxContext_free (GfxContext *self) +{ + SDL_DestroyRenderer (self->renderer); + SDL_DestroyWindow (self->window); + g_free (self); +} diff --git a/src/gfx_context.h b/src/gfx_context.h new file mode 100644 index 0000000..da41892 --- /dev/null +++ b/src/gfx_context.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include "util.h" + +typedef struct GfxContext +{ + SDL_Window *window; + SDL_Renderer *renderer; +} GfxContext; + +GfxContext *GfxContext_new (); +void GfxContext_prerender (GfxContext *self); +void GfxContext_postrender (GfxContext *self); +void GfxContext_free (GfxContext *self); diff --git a/src/main.c b/src/main.c index 35de745..deaf2fb 100644 --- a/src/main.c +++ b/src/main.c @@ -5,51 +5,39 @@ #include #include "util.h" +#include "gfx_context.h" +#include "stage/stage_manager.h" -#include "world.h" - -static SDL_Window *sdl_window; -static SDL_Renderer *sdl_renderer; -/* static World *world; */ +static GfxContext *gfx_ctx; +static StageManager *stageman; SDL_AppResult SDL_AppInit (void **appstate, int argc, char **argv) { UNUSED (appstate, argc, argv); + /* Makes GLib use less dumb useless shit. */ setenv ("GIO_USE_VFS", "local", 1); setenv ("GSETTINGS_BACKEND", "memory", 1); - + + /* This is set in the Makefile. By default it's 1, so we get all the + debug messages printed. */ #ifdef DEBUG setenv ("G_MESSAGES_DEBUG", "all", 1); #endif - - SDL_SetAppMetadata ("LibreFront", "0.1", - "https://git.cfpi-fpsi.eu:8443/chopin-interactive/librefront"); - g_info ("Welcome to LibreFront"); + SDL_SetAppMetadata (GAME_NAME, GAME_VERSION, GAME_URL); if (!SDL_Init (SDL_INIT_VIDEO)) { g_error ("Couldn't initialize SDL: %s", SDL_GetError ()); } - sdl_window = SDL_CreateWindow ("LibreFront", 800, 600, SDL_WINDOW_RESIZABLE); + stageman = StageManager_new (); + gfx_ctx = GfxContext_new (); - if (!sdl_window) - { - g_error ("Couldn't create window: %s", SDL_GetError ()); - } + StageManager_switch (stageman, "main_menu"); - sdl_renderer = SDL_CreateRenderer (sdl_window, NULL); - - if (!sdl_renderer) - { - g_error ("Couldn't create renderer: %s", SDL_GetError ()); - } - - /* world = World_random (128, 128); */ - return SDL_APP_CONTINUE; } @@ -63,6 +51,8 @@ SDL_AppEvent (void *appstate, SDL_Event *event) return SDL_APP_SUCCESS; } + StageManager_on_SDL_Event (stageman, event); + return SDL_APP_CONTINUE; } @@ -71,10 +61,10 @@ SDL_AppIterate (void *appstate) { UNUSED (appstate); - /* World_update (world); */ - SDL_RenderClear (sdl_renderer); - /* World_render (world, sdl_renderer); */ - SDL_RenderPresent (sdl_renderer); + StageManager_update (stageman); + GfxContext_prerender (gfx_ctx); + StageManager_render (stageman, gfx_ctx); + GfxContext_postrender (gfx_ctx); return SDL_APP_CONTINUE; } @@ -84,6 +74,6 @@ SDL_AppQuit (void *appstate, SDL_AppResult result) { UNUSED (appstate, result); - SDL_DestroyRenderer (sdl_renderer); - SDL_DestroyWindow (sdl_window); + GfxContext_free (gfx_ctx); + StageManager_free (stageman); } diff --git a/src/stage/stage.c b/src/stage/stage.c new file mode 100644 index 0000000..6c980e3 --- /dev/null +++ b/src/stage/stage.c @@ -0,0 +1,48 @@ +#include "stage.h" + +Stage * +Stage_new () +{ + Stage *g = g_new0 (Stage, 1); + return g; +} + +void +Stage_add_event_handler (Stage *self, int priority, unsigned int on_event, + gboolean (*predicate) (SDL_Event *sdl_event, void *userdata), void *predicate_data, + gboolean (*callback) (SDL_Event *sdl_event, void *userdata), void *callback_data) +{ + EventHandler *h = g_new (EventHandler, 1); + h->on_event = on_event; + h->priority = priority; + h->predicate_data = predicate_data; + h->callback_data = callback_data; + h->predicate = predicate; + h->callback = callback; + + GList *iter = self->event_handlers; + while (iter && ((EventHandler *) iter->data)->priority >= priority) + { + iter = iter->next; + } + + self->event_handlers = g_list_insert_before (self->event_handlers, iter, h); +} + +void +Stage_handle_SDL_Event (Stage *self, SDL_Event *event) +{ + for (GList *l = self->event_handlers; l != NULL; l = l->next) + { + EventHandler *h = (EventHandler *) l->data; + + if ((h->on_event == 0 || h->on_event == event->type) + && (!h->predicate || h->predicate (event, h->predicate_data))) + { + if (h->callback && h->callback (event, h->callback_data)) + { + break; + } + } + } +} diff --git a/src/stage/stage.h b/src/stage/stage.h new file mode 100644 index 0000000..c937ea2 --- /dev/null +++ b/src/stage/stage.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "../util.h" +#include "../gfx_context.h" + +typedef struct { + unsigned int on_event; + int priority; + + void *predicate_data, *callback_data; + gboolean (*predicate) (SDL_Event *sdl_event, void *userdata); + gboolean (*callback) (SDL_Event *sdl_event, void *userdata); +} EventHandler; + +typedef struct Stage +{ + /* There should be like an UI context here, the World, and so + on. Both could be NULL. */ + + GList *event_handlers; + + void (*on_enter) (struct Stage *); + void (*on_exit) (struct Stage *); + void (*on_update) (struct Stage *); + void (*on_render) (struct Stage *, GfxContext *); +} Stage; + +Stage *Stage_new (); +void Stage_add_event_handler (Stage *self, int priority, unsigned int on_event, + gboolean (*predicate) (SDL_Event *, void *), void *predicate_data, + gboolean (*callback) (SDL_Event *, void *), void *callback_data); +void Stage_handle_SDL_Event (Stage *self, SDL_Event *event); + +gboolean predicate_key (SDL_Event *event, void *data); + +gboolean callback_switch_stage (SDL_Event *event, void *data); diff --git a/src/stage/stage_main_menu.c b/src/stage/stage_main_menu.c new file mode 100644 index 0000000..5cf6058 --- /dev/null +++ b/src/stage/stage_main_menu.c @@ -0,0 +1,25 @@ +#include "stage_main_menu.h" + +static void +_on_enter (Stage *self) +{ + UNUSED (self); +} + +static void +_on_render (Stage *self, GfxContext *gfx_ctx) +{ + UNUSED (self); + + SDL_SetRenderDrawColor (gfx_ctx->renderer, 255, 0, 0, 255); +} + +Stage * +Stage_main_menu_new () +{ + Stage *self = Stage_new (); + self->on_enter = _on_enter; + self->on_render = _on_render; + + return self; +} diff --git a/src/stage/stage_main_menu.h b/src/stage/stage_main_menu.h new file mode 100644 index 0000000..751f0b0 --- /dev/null +++ b/src/stage/stage_main_menu.h @@ -0,0 +1,5 @@ +#pragma once + +#include "stage.h" + +Stage *Stage_main_menu_new (); diff --git a/src/stage/stage_manager.c b/src/stage/stage_manager.c new file mode 100644 index 0000000..47a7937 --- /dev/null +++ b/src/stage/stage_manager.c @@ -0,0 +1,70 @@ +#include "stage_manager.h" + +StageManager * +StageManager_new () +{ + StageManager *self = g_new0 (StageManager, 1); + self->stages = g_hash_table_new (g_str_hash, g_str_equal); + + StageManager_add (self, "main_menu", Stage_main_menu_new ()); + + return self; +} + +void +StageManager_add (StageManager *self, const char *name, Stage *stage) +{ + g_hash_table_replace (self->stages, g_strdup (name), stage); +} + +void +StageManager_switch (StageManager *self, const char *name) +{ + Stage *next_stage = g_hash_table_lookup (self->stages, name); + + if (self->current && self->current->on_exit) + { + self->current->on_exit (self->current); + } + + if (next_stage && next_stage->on_enter) + { + next_stage->on_enter (next_stage); + self->current = next_stage; + } +} + +void +StageManager_free (StageManager *self) +{ + g_free (self->stages); + g_free (self); +} + +void +StageManager_update (StageManager *self) +{ + if (self->current && self->current->on_update) + { + self->current->on_update (self->current); + } +} + +void +StageManager_on_SDL_Event (StageManager *self, SDL_Event *event) +{ + if (self->current && self->current->event_handlers) + { + Stage_handle_SDL_Event (self->current, event); + } +} + +void +StageManager_render (StageManager *self, GfxContext *ctx) +{ + if (self->current && self->current->on_render) + { + self->current->on_render (self->current, ctx); + } +} + diff --git a/src/stage/stage_manager.h b/src/stage/stage_manager.h new file mode 100644 index 0000000..01269c4 --- /dev/null +++ b/src/stage/stage_manager.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "stage_main_menu.h" + +typedef struct StageManager +{ + GHashTable *stages; + Stage *current, *global; +} StageManager; + +StageManager *StageManager_new (); +void StageManager_add (StageManager *self, const char *name, Stage *stage); +void StageManager_switch (StageManager *self, const char *name); +void StageManager_free (StageManager *self); + +void StageManager_update (StageManager *self); +void StageManager_on_SDL_Event (StageManager *self, SDL_Event *event); +void StageManager_render (StageManager *self, GfxContext *ctx); diff --git a/src/util.h b/src/util.h index a3dd55c..50ae263 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,9 @@ #pragma once +#define GAME_NAME "LibreFront" +#define GAME_VERSION "0.1" +#define GAME_URL "https://git.cfpi-fpsi.eu:8443/chopin-interactive/librefront" + #define UNUSED(...) (UNUSED_1 (__VA_ARGS__, 0)) #define UNUSED_1(arg, ...) (void) arg, UNUSED_2 (__VA_ARGS__, 0) #define UNUSED_2(arg, ...) (void) arg, UNUSED_3 (__VA_ARGS__, 0)