tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE
commit 5450a7938bd6073a2aa9ec67fe63bdff500c3998
parent 7bcd6d8843a79892f006c4d2482eec185a67ab09
Author: Connor Thomson <connor@gnufault.org>
Date:   Sun, 18 Oct 2026 14:00:11 -0700

Add loading screen & more

Diffstat:
MMakefile | 2+-
Afiles/images/loading_logo.tai | 0
Ainclude/screens/ta_screen_loading.h | 8++++++++
Ainclude/screens/ta_screen_main_menu.h | 8++++++++
Minclude/ta_api.h | 3---
Minclude/ta_files.h | 2++
Minclude/ta_image.h | 4+++-
Ainclude/ta_main.h | 6++++++
Ainclude/ta_screen.h | 16++++++++++++++++
Mplatforms/x11/Makefile | 2+-
Asrc/screens/ta_screen_loading.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/screens/ta_screen_main_menu.c | 24++++++++++++++++++++++++
Msrc/ta_button.c | 2++
Msrc/ta_image.c | 120++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/ta_main.c | 20++++++++------------
Asrc/ta_screen.c | 42++++++++++++++++++++++++++++++++++++++++++
16 files changed, 275 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile @@ -30,7 +30,7 @@ CFLAGS := -march=$(MARCH) -mtune=$(MARCH) -m$(BITS) $(ARCH_DEFINE) -I$(INCL ARFLAGS := rcs -SRCS := $(wildcard $(SRC_DIR)/*.c) +SRCS := $(shell find $(SRC_DIR) -type f -name '*.c') ASSETS := $(shell find $(FILES_DIR) -type f -not -name 'LICENSE') ASSET_PATHS := $(patsubst $(FILES_DIR)/%,%,$(ASSETS)) GENERATED_SRCS := $(addprefix $(BUILD_DIR)/,$(addsuffix .c,$(basename $(ASSET_PATHS)))) diff --git a/files/images/loading_logo.tai b/files/images/loading_logo.tai Binary files differ. diff --git a/include/screens/ta_screen_loading.h b/include/screens/ta_screen_loading.h @@ -0,0 +1,8 @@ +#ifndef TA_SCREEN_LOADING_H +#define TA_SCREEN_LOADING_H + +void ta_screen_loading_init(void); +void ta_screen_loading_update(void); +void ta_screen_loading_destroy(void); + +#endif // TA_SCREEN_LOADING_H diff --git a/include/screens/ta_screen_main_menu.h b/include/screens/ta_screen_main_menu.h @@ -0,0 +1,8 @@ +#ifndef TA_SCREEN_MAIN_MENU_H +#define TA_SCREEN_MAIN_MENU_H + +void ta_screen_main_menu_init(void); +void ta_screen_main_menu_update(void); +void ta_screen_main_menu_destroy(void); + +#endif // TA_SCREEN_MAIN_MENU_H diff --git a/include/ta_api.h b/include/ta_api.h @@ -5,9 +5,6 @@ #include "ta_types.h" #include "ta_fb.h" #include "ta_keyboard.h" -#include "ta_text.h" -#include "ta_button.h" -#include "ta_image.h" #ifdef __cplusplus extern "C" { diff --git a/include/ta_files.h b/include/ta_files.h @@ -5,5 +5,7 @@ extern unsigned char ta_file_dejavu_sans[]; extern unsigned int ta_file_dejavu_sans_len; extern unsigned char ta_file_bg[]; extern unsigned int ta_file_bg_len; +extern unsigned char ta_file_loading_logo[]; +extern unsigned int ta_file_loading_logo_len; #endif diff --git a/include/ta_image.h b/include/ta_image.h @@ -3,7 +3,9 @@ #include "ta_types.h" -ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, ta_uint32 x, ta_uint32 y, ta_uint32 width, ta_uint32 height, ta_uint16 frame); +ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, float x, float y, float width, float height, ta_uint16 frame); +ta_bool ta_image_init(const ta_uint8 *data, ta_uint32 length); +ta_bool ta_image_get_size(const ta_uint8 *data, ta_uint32 length, ta_uint32 *width, ta_uint32 *height); void ta_image_release(void); #endif diff --git a/include/ta_main.h b/include/ta_main.h @@ -0,0 +1,6 @@ +#ifndef TA_MAIN_H +#define TA_MAIN_H + +void ta_main_destroy(void); + +#endif // TA_MAIN_H diff --git a/include/ta_screen.h b/include/ta_screen.h @@ -0,0 +1,15 @@ +#ifndef TA_SCREEN_H +#define TA_SCREEN_H + +typedef enum { + TA_SCREEN_LOADING, + TA_SCEEEN_MAIN_MENU +} ta_screens; + +extern ta_screens ta_screen_current; + +void ta_screen_init(void); +void ta_screen_update(void); +void ta_screen_destroy(void); + +#endif // TA_SCREEN_H +\ No newline at end of file diff --git a/platforms/x11/Makefile b/platforms/x11/Makefile @@ -11,7 +11,7 @@ X11_SOURCE := $(MAKEFILE_DIR)x11.c X11_BUILD_DIR := $(ROOT_DIR)/$(X11_BUILD) X11_LIBRARY := $(X11_BUILD_DIR)/tuxanci2.a TARGET := $(MAKEFILE_DIR)tuxanci2 -ROOT_INPUTS := $(wildcard $(ROOT_DIR)/src/*.c) \ +ROOT_INPUTS := $(shell find $(ROOT_DIR)/src -type f -name '*.c') \ $(wildcard $(ROOT_DIR)/include/*.h) \ $(ROOT_DIR)/include/ta_config.h.in \ $(shell find $(ROOT_DIR)/files -type f) \ diff --git a/src/screens/ta_screen_loading.c b/src/screens/ta_screen_loading.c @@ -0,0 +1,60 @@ +#include "ta_screen.h" +#include "ta_files.h" +#include "ta_fb.h" +#include "ta_image.h" + +#define TA_LOADING_LOGO_SIZE 0.3f + +static ta_bool logo_loaded; +static ta_bool logo_shown; +static ta_bool background_loaded; + +static void draw_loading_logo(void) { + ta_uint32 width; + ta_uint32 height; + + if (!ta_image_get_size(ta_file_loading_logo, ta_file_loading_logo_len, &width, &height)) { + return; + } + + float scale_x = (float)ta_fb.width * TA_LOADING_LOGO_SIZE / width; + float scale_y = (float)ta_fb.height * TA_LOADING_LOGO_SIZE / height; + float scale = scale_x < scale_y ? scale_x : scale_y; + float draw_width = (float)width * scale / ta_fb.width; + float draw_height = (float)height * scale / ta_fb.height; + float x = (1.0f - draw_width) * 0.5f; + float y = (1.0f - draw_height) * 0.5f; + + ta_image_draw(ta_file_loading_logo, ta_file_loading_logo_len, x, y, draw_width, draw_height, 0); +} + +void ta_screen_loading_init(void) { + logo_loaded = ta_false; + logo_shown = ta_false; + background_loaded = ta_false; +} + +void ta_screen_loading_update(void) { + if (!logo_loaded) { + logo_loaded = ta_image_init(ta_file_loading_logo, ta_file_loading_logo_len); + return; + } + + draw_loading_logo(); + + if (!logo_shown) { + logo_shown = ta_true; + return; + } + + if (!background_loaded) { + background_loaded = ta_image_init(ta_file_bg, ta_file_bg_len); + return; + } + + ta_screen_current = TA_SCEEEN_MAIN_MENU; +} + +void ta_screen_loading_destroy(void) { + +} diff --git a/src/screens/ta_screen_main_menu.c b/src/screens/ta_screen_main_menu.c @@ -0,0 +1,24 @@ +#include "ta_screen.h" +#include "ta_files.h" +#include "ta_fb.h" +#include "ta_button.h" +#include "ta_image.h" +#include "ta_main.h" + +static ta_button_lists menu; + +void ta_screen_main_menu_init(void) { + ta_button_lists_init(&menu); + ta_button_lists_add(&menu, "Play", 0); + ta_button_lists_add(&menu, "Quit", ta_main_destroy); +} + +void ta_screen_main_menu_update(void) { + ta_image_draw(ta_file_bg, ta_file_bg_len, 0.0f, 0.0f, 1.0f, 1.0f, 0); + ta_button_lists_update(&menu); + ta_button_lists_draw(&menu); +} + +void ta_screen_main_menu_destroy(void) { + ta_button_lists_destroy(&menu); +} diff --git a/src/ta_button.c b/src/ta_button.c @@ -1,4 +1,6 @@ #include "ta_api.h" +#include "ta_button.h" +#include "ta_text.h" #define TA_BUTTON_ANIMATION_FRAMES 8 #define TA_BUTTON_FONT_SIZE 32 diff --git a/src/ta_image.c b/src/ta_image.c @@ -5,7 +5,7 @@ #define TAIF_FLAG_ANIMATED 0x04 #define TAIF_WINDOW_SIZE 4096 -typedef struct { +typedef struct ta_image_cache { const ta_uint8 *data; ta_uint32 length; ta_uint32 width; @@ -18,9 +18,10 @@ typedef struct { ta_uint8 flags; ta_uint8 palette[256][3]; ta_uint8 *pixels; + struct ta_image_cache *next; } ta_image_cache; -static ta_image_cache cached_image; +static ta_image_cache *image_cache; static ta_uint16 read_le16(const ta_uint8 *data) { return (ta_uint16)((ta_uint16)data[0] | ((ta_uint16)data[1] << 8)); @@ -234,59 +235,122 @@ static ta_bool decode_image(const ta_uint8 *data, ta_uint32 length, ta_uint16 re return ta_true; } -static ta_bool ensure_cached_image(const ta_uint8 *data, ta_uint32 length, ta_uint16 frame) { - if (cached_image.data == data && cached_image.length == length && cached_image.requested_frame == frame && cached_image.pixels != 0) { +static ta_image_cache *find_image(const ta_uint8 *data, ta_uint32 length) { + for (ta_image_cache *image = image_cache; image != 0; image = image->next) { + if (image->data == data && image->length == length) { + return image; + } + } + + return 0; +} + +ta_bool ta_image_init(const ta_uint8 *data, ta_uint32 length) { + if (find_image(data, length) != 0) { return ta_true; } - ta_image_cache image = {0}; + ta_image_cache *image = (ta_image_cache *)ta_malloc(sizeof(*image)); + if (image == 0) { + return ta_false; + } - if (!decode_image(data, length, frame, &image)) { + *image = (ta_image_cache){0}; + if (!decode_image(data, length, 0, image)) { + ta_free(image); return ta_false; } - ta_free(cached_image.pixels); - cached_image = image; + image->next = image_cache; + image_cache = image; + return ta_true; +} +ta_bool ta_image_get_size(const ta_uint8 *data, ta_uint32 length, ta_uint32 *width, ta_uint32 *height) { + ta_image_cache *image = find_image(data, length); + if (image == 0 || width == 0 || height == 0) { + return ta_false; + } + + *width = image->width; + *height = image->height; return ta_true; } -ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, ta_uint32 x, ta_uint32 y, ta_uint32 width, ta_uint32 height, ta_uint16 frame) { - if (data == 0 || length == 0 || width == 0 || height == 0 || ta_fb.buffers[1] == 0 || (ta_fb.bpp != 8 && ta_fb.bpp != 32) || width > ta_fb.width || height > ta_fb.height || !ensure_cached_image(data, length, frame)) { +static ta_image_cache *get_image(const ta_uint8 *data, ta_uint32 length, ta_uint16 frame) { + if (!ta_image_init(data, length)) { + return 0; + } + + ta_image_cache *image = find_image(data, length); + if (image->requested_frame == frame) { + return image; + } + + ta_image_cache decoded = {0}; + if (!decode_image(data, length, frame, &decoded)) { + return 0; + } + + ta_uint8 *old_pixels = image->pixels; + ta_image_cache *next = image->next; + *image = decoded; + image->next = next; + ta_free(old_pixels); + + return image; +} + +ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, float x, float y, float width, float height, ta_uint16 frame) { + if (data == 0 || length == 0 || x != x || y != y || width != width || height != height || x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f || width <= 0.0f || height <= 0.0f || width > 1.0f || height > 1.0f || ta_fb.buffers[1] == 0 || (ta_fb.bpp != 8 && ta_fb.bpp != 32)) { return ta_false; } - for (ta_uint32 image_y = 0; image_y < height; image_y++) { - ta_uint64 screen_y = (ta_uint64)y + image_y; + ta_uint32 pixel_x = (ta_uint32)((double)x * ta_fb.width); + ta_uint32 pixel_y = (ta_uint32)((double)y * ta_fb.height); + ta_uint32 pixel_width = (ta_uint32)((double)width * ta_fb.width); + ta_uint32 pixel_height = (ta_uint32)((double)height * ta_fb.height); + + if (pixel_width == 0 || pixel_height == 0) { + return ta_false; + } + + ta_image_cache *image = get_image(data, length, frame); + if (image == 0) { + return ta_false; + } + + for (ta_uint32 image_y = 0; image_y < pixel_height; image_y++) { + ta_uint64 screen_y = (ta_uint64)pixel_y + image_y; if (screen_y >= ta_fb.height) { continue; } - ta_uint32 source_y = (ta_uint32)((ta_uint64)image_y * cached_image.height / height); - for (ta_uint32 image_x = 0; image_x < width; image_x++) { - ta_uint64 screen_x = (ta_uint64)x + image_x; + ta_uint32 source_y = (ta_uint32)((ta_uint64)image_y * image->height / pixel_height); + for (ta_uint32 image_x = 0; image_x < pixel_width; image_x++) { + ta_uint64 screen_x = (ta_uint64)pixel_x + image_x; if (screen_x >= ta_fb.width) { continue; } - ta_uint32 source_x = (ta_uint32)((ta_uint64)image_x * cached_image.width / width); + ta_uint32 source_x = (ta_uint32)((ta_uint64)image_x * image->width / pixel_width); ta_uint8 index; - if ((cached_image.flags & TAIF_FLAG_8BPP) != 0) { - index = cached_image.pixels[source_y * cached_image.stride + source_x]; + if ((image->flags & TAIF_FLAG_8BPP) != 0) { + index = image->pixels[source_y * image->stride + source_x]; } else { - ta_uint8 packed = cached_image.pixels[source_y * cached_image.stride + source_x / 2]; + ta_uint8 packed = image->pixels[source_y * image->stride + source_x / 2]; index = (source_x & 1) == 0 ? packed >> 4 : packed & 0x0f; } - if ((cached_image.flags & TAIF_FLAG_TRANSPARENT) != 0 && index == 0) { + if ((image->flags & TAIF_FLAG_TRANSPARENT) != 0 && index == 0) { continue; } - ta_uint32 palette_index = index - ((cached_image.flags & TAIF_FLAG_TRANSPARENT) != 0 ? 1 : 0); - ta_uint32 red = cached_image.palette[palette_index][0]; - ta_uint32 green = cached_image.palette[palette_index][1]; - ta_uint32 blue = cached_image.palette[palette_index][2]; + ta_uint32 palette_index = index - ((image->flags & TAIF_FLAG_TRANSPARENT) != 0 ? 1 : 0); + ta_uint32 red = image->palette[palette_index][0]; + ta_uint32 green = image->palette[palette_index][1]; + ta_uint32 blue = image->palette[palette_index][2]; ta_size destination_index = (ta_size)screen_y * ta_fb.width + (ta_size)screen_x; if (ta_fb.bpp == 32) { @@ -304,6 +368,10 @@ ta_bool ta_image_draw(const ta_uint8 *data, ta_uint32 length, ta_uint32 x, ta_ui } void ta_image_release(void) { - ta_free(cached_image.pixels); - cached_image = (ta_image_cache){0}; + while (image_cache != 0) { + ta_image_cache *image = image_cache; + image_cache = image->next; + ta_free(image->pixels); + ta_free(image); + } } diff --git a/src/ta_main.c b/src/ta_main.c @@ -1,24 +1,20 @@ #include "ta_api.h" #include "ta_files.h" +#include "ta_screen.h" #include <stdlib.h> -static ta_button_lists menu; - -static void quit(void) { - exit(0); -} - void ta_main_init(void) { ta_fb_init(&ta_fb); - ta_button_lists_init(&menu); - ta_button_lists_add(&menu, "Play", 0); - ta_button_lists_add(&menu, "Quit", quit); + ta_screen_init(); } void ta_main_update(void) { ta_fb_clear(&ta_fb); - ta_image_draw(ta_file_bg, ta_file_bg_len, 0, 0, ta_fb.width, ta_fb.height, 0); - ta_button_lists_update(&menu); - ta_button_lists_draw(&menu); + ta_screen_update(); ta_fb_swap(&ta_fb); } + +void ta_main_destroy(void) { + ta_screen_destroy(); + exit(0); +} diff --git a/src/ta_screen.c b/src/ta_screen.c @@ -0,0 +1,42 @@ +#include "ta_screen.h" +#include "ta_types.h" +#include "ta_image.h" + +#include "screens/ta_screen_loading.h" +#include "screens/ta_screen_main_menu.h" + +ta_screens ta_screen_current; + +void ta_screen_init(void) { + ta_screen_current = TA_SCREEN_LOADING; + ta_screen_loading_init(); +} + +void ta_screen_update(void) { + switch (ta_screen_current) { + case TA_SCREEN_LOADING: { + ta_screen_loading_update(); + if (ta_screen_current == TA_SCEEEN_MAIN_MENU) { + ta_screen_loading_destroy(); + ta_screen_main_menu_init(); + } + break; + } + case TA_SCEEEN_MAIN_MENU: { + ta_screen_main_menu_update(); + break; + } + default: { + return; + } + } +} + +void ta_screen_destroy(void) { + if (ta_screen_current == TA_SCREEN_LOADING) { + ta_screen_loading_destroy(); + } else if (ta_screen_current == TA_SCEEEN_MAIN_MENU) { + ta_screen_main_menu_destroy(); + } + ta_image_release(); +}