#include "twn_util.h" #include "twn_engine_context_c.h" #include #include #define STB_DS_IMPLEMENTATION #define STBDS_ASSERT SDL_assert #include #define STB_RECT_PACK_IMPLEMENTATION #define STBRP_ASSERT SDL_assert #include #define STB_TRUETYPE_IMPLEMENTATION #include #include #include #include #include void cry_impl(const char *file, const int line, const char *title, const char *text) { SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "TEARS AT %s:%d: %s ... %s", file, line, title, text); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, text, NULL); } static void log_impl(const char *restrict format, va_list args, SDL_LogPriority priority) { SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, priority, format, args); } void log_info(const char *restrict format, ...) { va_list args; va_start(args, format); log_impl(format, args, SDL_LOG_PRIORITY_INFO); va_end(args); } void log_critical(const char *restrict format, ...) { va_list args; va_start(args, format); log_impl(format, args, SDL_LOG_PRIORITY_CRITICAL); va_end(args); } void log_warn(const char *restrict format, ...) { va_list args; va_start(args, format); log_impl(format, args, SDL_LOG_PRIORITY_WARN); va_end(args); } noreturn static void alloc_failure_death(void) { log_critical("Allocation failure. Aborting NOW."); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "MEMORY ALLOCATION FAILURE.", "FAILED TO ALLOCATE MEMORY. " "YOU MIGHT BE UNLUCKY. " "THE GAME WILL EXIT NOW.", NULL); die_abruptly(); } noreturn void die_abruptly(void) { /* a zombie window will linger if we don't at least try to quit SDL */ SDL_Quit(); abort(); } void *cmalloc(size_t size) { void *ptr = malloc(size); if (ptr == NULL) alloc_failure_death(); return ptr; } void *crealloc(void *ptr, size_t size) { void *out = realloc(ptr, size); if (out == NULL) alloc_failure_death(); return out; } void *ccalloc(size_t num, size_t size) { void *ptr = calloc(num, size); if (ptr == NULL) alloc_failure_death(); return ptr; } double clamp(double d, double min, double max) { const double t = d < min ? min : d; return t > max ? max : t; } float clampf(float f, float min, float max) { const float t = f < min ? min : f; return t > max ? max : t; } int clampi(int i, int min, int max) { const int t = i < min ? min : i; return t > max ? max : t; } int64_t file_to_bytes(const char *path, unsigned char **buf_out) { SDL_RWops *handle = PHYSFSRWOPS_openRead(path); if (handle == NULL) { return -1; } int64_t data_size = SDL_RWseek(handle, 0, RW_SEEK_END); SDL_RWseek(handle, 0, RW_SEEK_SET); /* reset offset into data */ *buf_out = cmalloc(data_size); SDL_RWread(handle, *buf_out, sizeof **buf_out, data_size / sizeof **buf_out); SDL_RWclose(handle); /* we got all we needed from the stream */ return data_size; } char *file_to_str(const char *path) { SDL_RWops *handle = PHYSFSRWOPS_openRead(path); if (handle == NULL) { return NULL; } int64_t data_size = SDL_RWseek(handle, 0, RW_SEEK_END); SDL_RWseek(handle, 0, RW_SEEK_SET); /* reset offset into data */ char *str_out = cmalloc(data_size + 1); /* data plus final null */ size_t len = data_size / sizeof *str_out; SDL_RWread(handle, str_out, sizeof *str_out, len); SDL_RWclose(handle); /* we got all we needed from the stream */ str_out[len] = '\0'; return str_out; } bool strends(const char *str, const char *suffix) { size_t str_length = strlen(str); size_t suffix_length = strlen(suffix); if (suffix_length > str_length) return false; return memcmp((str + str_length) - suffix_length, suffix, suffix_length) == 0; } bool overlap_rect(const Recti *a, const Recti *b, Recti *result) { SDL_Rect a_sdl = { a->x, a->y, a->w, a->h }; SDL_Rect b_sdl = { b->x, b->y, b->w, b->h }; SDL_Rect result_sdl = { 0 }; bool intersection = SDL_IntersectRect(&a_sdl, &b_sdl, &result_sdl); if (result != NULL) *result = (Recti){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h }; return intersection; } bool overlap_frect(const Rect *a, const Rect *b, Rect *result) { SDL_FRect a_sdl = { a->x, a->y, a->w, a->h }; SDL_FRect b_sdl = { b->x, b->y, b->w, b->h }; SDL_FRect result_sdl = { 0 }; bool intersection = SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl); if (result != NULL) *result = (Rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h }; return intersection; } bool intersect_rect(const Recti *a, const Recti *b) { SDL_Rect a_sdl = { a->x, a->y, a->w, a->h }; SDL_Rect b_sdl = { b->x, b->y, b->w, b->h }; return SDL_HasIntersection(&a_sdl, &b_sdl); } bool intersect_frect(const Rect *a, const Rect *b) { SDL_FRect a_sdl = { a->x, a->y, a->w, a->h }; SDL_FRect b_sdl = { b->x, b->y, b->w, b->h }; return SDL_HasIntersectionF(&a_sdl, &b_sdl); } Rect to_frect(Recti rect) { return (Rect) { .h = (float)rect.h, .w = (float)rect.w, .x = (float)rect.x, .y = (float)rect.y, }; } Vec2 frect_center(Rect rect) { return (Vec2){ .x = rect.x + rect.w / 2, .y = rect.y + rect.h / 2, }; } void tick_timer(int *value) { *value = MAX(*value - 1, 0); } void tick_ftimer(float *value) { *value = MAX(*value - ((float)ctx.game.delta_time / (float)ctx.clocks_per_second), 0.0f); } bool repeat_ftimer(float *value, float at) { *value -= (float)ctx.game.delta_time / (float)ctx.clocks_per_second; if (*value < 0.0f) { *value += at; return true; } return false; }