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:
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();
+}