diff --git a/code/game/src/debug_ui.c b/code/game/src/debug_ui.c index ac382f2..868bd0c 100644 --- a/code/game/src/debug_ui.c +++ b/code/game/src/debug_ui.c @@ -1,387 +1,410 @@ -#include "debug_ui.h" -#include "debug_draw.h" -#include "raylib.h" -#include "vehicle.h" -#include "camera.h" -#include "world/world.h" -#include "game.h" -#include "sfd.h" - -#include "modules/components.h" - -typedef enum { - DITEM_RAW, - DITEM_GAP, - DITEM_TEXT, - DITEM_BUTTON, - DITEM_SLIDER, - DITEM_LIST, - DITEM_COND, - DITEM_END, - - DITEM_FORCE_UINT8 = UINT8_MAX -} debug_kind; - -typedef struct { - float x, y; -} debug_draw_result; - -#define DBG_FONT_SIZE 22 -#define DBG_FONT_SPACING DBG_FONT_SIZE * 1.2f -#define DBG_START_XPOS 15 -#define DBG_START_YPOS 200 -#define DBG_LIST_XPOS_OFFSET 10 -#define DBG_SHADOW_OFFSET_XPOS 1 -#define DBG_SHADOW_OFFSET_YPOS 1 -#define DBG_CTRL_HANDLE_DIM 10 -#define DBG_GAP_HEIGHT DBG_FONT_SPACING * 0.5f - -static uint8_t is_shadow_rendered; -static uint8_t is_debug_open = 1; -static uint8_t is_handle_ctrl_held; -static float debug_xpos = DBG_START_XPOS; -static float debug_ypos = DBG_START_YPOS; - -typedef struct debug_item { - debug_kind kind; - char const *name; - float name_width; - uint8_t skip; - - union { - union { - char const *text; - uint64_t val; - }; - - struct { - struct debug_item *items; - uint8_t is_collapsed; - uint8_t is_sp_only; - } list; - - struct { - float val, min, max; - void (*on_change)(float); - } slider; - - void (*on_click)(void); - - uint8_t (*on_success)(void); - }; - - debug_draw_result (*proc)(struct debug_item*, float, float); -} debug_item; - -static void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color); -static int UIMeasureText(const char *text, int fontSize); - -#include "debug_replay.c" - -#include "debug_ui_actions.c" -#include "debug_ui_widgets.c" - -static debug_item items[] = { - { - .kind = DITEM_LIST, - .name = "general", - .list = { - .items = (debug_item[]) { - { .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime }, - { .kind = DITEM_TEXT, .name = "pos", .proc = DrawCameraPos }, - { .kind = DITEM_TEXT, .name = "zoom", .proc = DrawZoom }, - { .kind = DITEM_SLIDER, .name = "slider", .slider = { .min = 0.0f, .max = 1.0f, .val = 0.5f } }, - { .kind = DITEM_END }, - } - } - }, - { - .kind = DITEM_LIST, - .name = "debug actions", - .list = { - .items = (debug_item[]) { - { .kind = DITEM_BUTTON, .name = "spawn car", .on_click = ActSpawnCar }, - { .kind = DITEM_BUTTON, .name = "place ice rink", .on_click = ActPlaceIceRink }, - { .kind = DITEM_BUTTON, .name = "erase world changes", .on_click = ActEraseWorldChanges }, - { .kind = DITEM_BUTTON, .name = "spawn circling driver", .on_click = ActSpawnCirclingDriver }, - { .kind = DITEM_BUTTON, .name = "spawn icemaker item", .on_click = ActSpawnIcemaker }, - { - .kind = DITEM_LIST, - .name = "demo npcs", - .list = { - .items = (debug_item[]) { - { .kind = DITEM_TEXT, .name = "npcs", .proc = DrawDemoNPCCount }, - { .kind = DITEM_BUTTON, .name = "spawn 1000 npcs", .on_click = ActSpawnDemoNPCs }, - { .kind = DITEM_BUTTON, .name = "remove all demo npcs", .on_click = ActDespawnDemoNPCs }, - { .kind = DITEM_END }, - }, - .is_collapsed = true - } - }, - { .kind = DITEM_END }, - }, - .is_sp_only = true, - } - }, - { - .kind = DITEM_LIST, - .name = "replay system", - .list = { - .items = (debug_item[]) { - { .kind = DITEM_TEXT, .name = "macro", .proc = DrawReplayFileName }, - { .kind = DITEM_TEXT, .name = "samples", .proc = DrawReplaySamples }, - - { .kind = DITEM_COND, .on_success = CondReplayDataPresentAndNotPlaying }, - { .kind = DITEM_BUTTON, .name = "replay", .on_click = ActReplayRun }, - - { .kind = DITEM_COND, .on_success = CondReplayStatusOff }, - { .kind = DITEM_BUTTON, .name = "record", .on_click = ActReplayBegin }, - - { .kind = DITEM_COND, .on_success = CondReplayStatusOn }, - { .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd }, - - { .kind = DITEM_COND, .on_success = CondReplayIsPlaying }, - { .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd }, - - { .kind = DITEM_COND, .on_success = CondReplayIsNotPlayingOrRecordsNotClear }, - { .kind = DITEM_BUTTON, .name = "clear", .on_click = ActReplayClear }, - - { .kind = DITEM_GAP }, - - { .kind = DITEM_COND, .on_success = CondReplayIsNotPlaying, .skip = 4 }, - { .kind = DITEM_BUTTON, .name = "new", .on_click = ActReplayNew }, - { .kind = DITEM_BUTTON, .name = "load", .on_click = ActReplayLoad }, - { .kind = DITEM_BUTTON, .name = "save", .on_click = ActReplaySave }, - { .kind = DITEM_BUTTON, .name = "save as...", .on_click = ActReplaySaveAs }, - - { .kind = DITEM_END }, - }, - .is_sp_only = true, - } - }, - { - .kind = DITEM_LIST, - .name = "profilers", - .list = { - .items = (debug_item[]) { - { .kind = DITEM_RAW, .val = PROF_MAIN_LOOP, .proc = DrawProfilerDelta }, - { .kind = DITEM_TEXT, .name = "unmeasured time", .proc = DrawUnmeasuredTime }, - { .kind = DITEM_RAW, .val = PROF_WORLD_WRITE, .proc = DrawProfilerDelta }, - { .kind = DITEM_RAW, .val = PROF_RENDER, .proc = DrawProfilerDelta }, - { .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta }, - { .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta }, - { .kind = DITEM_RAW, .val = PROF_ENTITY_REMOVAL, .proc = DrawProfilerDelta }, - { .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta }, - { .kind = DITEM_END }, - }, - .is_collapsed = 1 - } - }, - { - .kind = DITEM_BUTTON, - .name = "exit game", - .on_click = ActExitGame, - }, - {.kind = DITEM_END}, -}; - -debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool is_shadow) { - is_shadow_rendered = is_shadow; - for (debug_item *it = list; it->kind != DITEM_END; it += 1) { - switch (it->kind) { - case DITEM_GAP: { - ypos += DBG_GAP_HEIGHT; - }break; - case DITEM_COND: { - ZPL_ASSERT(it->on_success); - - if (!it->on_success()) { - it += it->skip ? it->skip : 1; - } - }break; - case DITEM_LIST: { - // NOTE(zaklaus): calculate and cache name width for future use - if (it->name_width == 0) { - it->name_width = UIMeasureText(it->name, DBG_FONT_SIZE); - } - Color color = RAYWHITE; - if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color)) { - it->list.is_collapsed = !it->list.is_collapsed; - } - - UIDrawText(it->name, xpos, ypos, DBG_FONT_SIZE, color); - ypos += DBG_FONT_SPACING; - if (it->list.is_collapsed) break; - if (it->list.is_sp_only && game_get_kind() != GAMEKIND_SINGLE) break; - debug_draw_result res = debug_draw_list(it->list.items, xpos+DBG_LIST_XPOS_OFFSET, ypos, is_shadow); - ypos = res.y; - }break; - - case DITEM_TEXT: { - char const *text = TextFormat("%s: ", it->name); - if (it->name_width == 0) { - it->name_width = UIMeasureText(text, DBG_FONT_SIZE); - } - UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE); - ZPL_ASSERT(it->proc); - - debug_draw_result res = it->proc(it, xpos + it->name_width, ypos); - ypos = res.y; - }break; - - case DITEM_RAW: { - ZPL_ASSERT(it->proc); - - debug_draw_result res = it->proc(it, xpos, ypos); - ypos = res.y; - }break; - - case DITEM_BUTTON: { - char const *text = TextFormat("> %s", it->name); - if (it->name_width == 0) { - it->name_width = UIMeasureText(text, DBG_FONT_SIZE); - } - Color color = RAYWHITE; - if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color) && it->on_click) { - it->on_click(); - } - - if (!it->on_click) { - color = GRAY; - } - - debug_draw_result res = DrawColoredText(xpos, ypos, text, color); - ypos = res.y; - }break; - - case DITEM_SLIDER: { - ZPL_ASSERT(it->slider.min != it->slider.max); - char const *text = TextFormat("%s: ", it->name); - if (it->name_width == 0) { - it->name_width = UIMeasureText(text, DBG_FONT_SIZE); - } - UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE); - xpos += it->name_width; - - DrawRectangleLines(xpos, ypos, 100.0f, DBG_FONT_SIZE, RAYWHITE); - - float stick_x = xpos + ((it->slider.val / it->slider.max) * 100.0f) - 5.0f; - DrawRectangle(stick_x, ypos, 10.0f, DBG_FONT_SIZE, RED); - - xpos += 100.0f + 5.0f; - DrawFloat(xpos, ypos, it->slider.val); - ypos += DBG_FONT_SPACING; - }break; - - default: { - - }break; - } - } - - return (debug_draw_result){xpos, ypos}; -} - -void debug_draw(void) { - // NOTE(zaklaus): Flush old debug samples - debug_draw_flush(); - - float xpos = debug_xpos; - float ypos = debug_ypos; - - // NOTE(zaklaus): move debug ui - { - debug_area_status area = check_mouse_area(xpos, ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM); - Color color = BLUE; - if (area == DAREA_HOVER) color = YELLOW; - if (area == DAREA_HELD) { - color = RED; - is_handle_ctrl_held = 1; - } - - if (is_handle_ctrl_held) { - debug_xpos = xpos = GetMouseX() - DBG_CTRL_HANDLE_DIM/2; - debug_ypos = ypos = GetMouseY() - DBG_CTRL_HANDLE_DIM/2; - - if (area == DAREA_PRESS) { - is_handle_ctrl_held = 0; - } - } - - DrawRectangle(xpos, ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM, color); - } - - // NOTE(zaklaus): toggle debug ui - { - Color color = BLUE; - debug_area_status area = check_mouse_area(xpos, 15+ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM); - if (area == DAREA_HOVER) color = YELLOW; - if (area == DAREA_HELD) { - color = RED; - } - if (area == DAREA_PRESS) { - is_debug_open = !is_debug_open; - } - DrawPoly((Vector2){xpos+DBG_CTRL_HANDLE_DIM/2, ypos+15+DBG_CTRL_HANDLE_DIM/2}, 3, 6.0f,is_debug_open ? 0.0f : 180.0f, color); - } - - if (is_debug_open) { - xpos += 15; - debug_draw_list(items, xpos+DBG_SHADOW_OFFSET_XPOS, ypos+DBG_SHADOW_OFFSET_YPOS, 1); // NOTE(zaklaus): draw shadow - debug_draw_list(items, xpos, ypos, 0); - } -} - -debug_area_status check_mouse_area(float xpos, float ypos, float w, float h) { - if (is_shadow_rendered) return DAREA_OUTSIDE; - bool is_inside = CheckCollisionPointRec(GetMousePosition(), (Rectangle){xpos, ypos, w, h}); - - if (is_inside) { - return IsMouseButtonReleased(MOUSE_LEFT_BUTTON) ? DAREA_PRESS : IsMouseButtonDown(MOUSE_LEFT_BUTTON) ? DAREA_HELD : DAREA_HOVER; - } - return DAREA_OUTSIDE; -} - -bool is_btn_pressed(float xpos, float ypos, float w, float h, Color *color) { - ZPL_ASSERT(color); - *color = RAYWHITE; - debug_area_status area = check_mouse_area(xpos, ypos, w, h); - if (area == DAREA_PRESS) { - *color = RED; - return true; - } else if (area == DAREA_HOVER) { - *color = YELLOW; - } else if (area == DAREA_HELD) { - *color = RED; - } - - return false; -} - -static inline -void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color) { - // Check if default font has been loaded - if (GetFontDefault().texture.id != 0) { - Vector2 position = { (float)posX , (float)posY }; - - int defaultFontSize = 10; // Default Font chars height in pixel - int new_spacing = fontSize/defaultFontSize; - - DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , is_shadow_rendered ? BLACK : color); - } -} - -static inline -int UIMeasureText(const char *text, int fontSize) { - Vector2 vec = { 0.0f, 0.0f }; - - // Check if default font has been loaded - if (GetFontDefault().texture.id != 0) { - int defaultFontSize = 10; // Default Font chars height in pixel - int new_spacing = fontSize/defaultFontSize; - - vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing); - } - - return (int)vec.x; -} +#include "debug_ui.h" +#include "debug_draw.h" +#include "raylib.h" +#include "vehicle.h" +#include "camera.h" +#include "world/world.h" +#include "game.h" +#include "sfd.h" + +#include "modules/components.h" + +typedef enum { + DITEM_RAW, + DITEM_GAP, + DITEM_TEXT, + DITEM_BUTTON, + DITEM_SLIDER, + DITEM_LIST, + DITEM_COND, + DITEM_END, + + DITEM_FORCE_UINT8 = UINT8_MAX +} debug_kind; + +typedef struct { + float x, y; +} debug_draw_result; + +#define DBG_FONT_SIZE 22 +#define DBG_FONT_SPACING DBG_FONT_SIZE * 1.2f +#define DBG_START_XPOS 15 +#define DBG_START_YPOS 200 +#define DBG_LIST_XPOS_OFFSET 10 +#define DBG_SHADOW_OFFSET_XPOS 1 +#define DBG_SHADOW_OFFSET_YPOS 1 +#define DBG_CTRL_HANDLE_DIM 10 +#define DBG_GAP_HEIGHT DBG_FONT_SPACING * 0.5f + +static uint8_t is_shadow_rendered; +static uint8_t is_debug_open = 1; +static uint8_t is_handle_ctrl_held; +static float debug_xpos = DBG_START_XPOS; +static float debug_ypos = DBG_START_YPOS; + +typedef struct debug_item { + debug_kind kind; + char const *name; + float name_width; + uint8_t skip; + + union { + union { + char const *text; + uint64_t val; + }; + + struct { + struct debug_item *items; + uint8_t is_collapsed; + uint8_t is_sp_only; + } list; + + struct { + float val, min, max; + void (*on_change)(float); + } slider; + + void (*on_click)(void); + + uint8_t (*on_success)(void); + }; + + debug_draw_result (*proc)(struct debug_item*, float, float); +} debug_item; + +static void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color); +static int UIMeasureText(const char *text, int fontSize); + +#include "debug_replay.c" + +#include "debug_ui_actions.c" +#include "debug_ui_widgets.c" + +static debug_item items[] = { + { + .kind = DITEM_LIST, + .name = "general", + .list = { + .items = (debug_item[]) { + { .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime }, + { .kind = DITEM_TEXT, .name = "pos", .proc = DrawCameraPos }, + { .kind = DITEM_TEXT, .name = "zoom", .proc = DrawZoom }, + { .kind = DITEM_SLIDER, .name = "slider", .slider = { .min = 0.0f, .max = 1.0f, .val = 0.5f } }, + { .kind = DITEM_END }, + } + } + }, + { + .kind = DITEM_LIST, + .name = "world simulation", + .list = { + .items = (debug_item[]) { + { .kind = DITEM_COND, .on_success = CondIsWorldRunning }, + { .kind = DITEM_BUTTON, .name = "pause", .on_click = ActWorldToggleSim }, + + { .kind = DITEM_COND, .on_success = CondIsWorldPaused, .skip = 6 }, + { .kind = DITEM_BUTTON, .name = "resume", .on_click = ActWorldToggleSim }, + + { .kind = DITEM_GAP }, + + { .kind = DITEM_TEXT, .name = "step size", .proc = DrawWorldStepSize }, + { .kind = DITEM_BUTTON, .name = "single-step", .on_click = ActWorldStep }, + { .kind = DITEM_BUTTON, .name = "increment step size", .on_click = ActWorldIncrementSimStepSize }, + { .kind = DITEM_BUTTON, .name = "decrement step size", .on_click = ActWorldDecrementSimStepSize }, + + { .kind = DITEM_END }, + }, + .is_sp_only = true, + } + }, + { + .kind = DITEM_LIST, + .name = "debug actions", + .list = { + .items = (debug_item[]) { + { .kind = DITEM_BUTTON, .name = "spawn car", .on_click = ActSpawnCar }, + { .kind = DITEM_BUTTON, .name = "place ice rink", .on_click = ActPlaceIceRink }, + { .kind = DITEM_BUTTON, .name = "erase world changes", .on_click = ActEraseWorldChanges }, + { .kind = DITEM_BUTTON, .name = "spawn circling driver", .on_click = ActSpawnCirclingDriver }, + { .kind = DITEM_BUTTON, .name = "spawn icemaker item", .on_click = ActSpawnIcemaker }, + { + .kind = DITEM_LIST, + .name = "demo npcs", + .list = { + .items = (debug_item[]) { + { .kind = DITEM_TEXT, .name = "npcs", .proc = DrawDemoNPCCount }, + { .kind = DITEM_BUTTON, .name = "spawn 1000 npcs", .on_click = ActSpawnDemoNPCs }, + { .kind = DITEM_BUTTON, .name = "remove all demo npcs", .on_click = ActDespawnDemoNPCs }, + { .kind = DITEM_END }, + }, + .is_collapsed = true + } + }, + { .kind = DITEM_END }, + }, + .is_sp_only = true, + } + }, + { + .kind = DITEM_LIST, + .name = "replay system", + .list = { + .items = (debug_item[]) { + { .kind = DITEM_TEXT, .name = "macro", .proc = DrawReplayFileName }, + { .kind = DITEM_TEXT, .name = "samples", .proc = DrawReplaySamples }, + + { .kind = DITEM_COND, .on_success = CondReplayDataPresentAndNotPlaying }, + { .kind = DITEM_BUTTON, .name = "replay", .on_click = ActReplayRun }, + + { .kind = DITEM_COND, .on_success = CondReplayStatusOff }, + { .kind = DITEM_BUTTON, .name = "record", .on_click = ActReplayBegin }, + + { .kind = DITEM_COND, .on_success = CondReplayStatusOn }, + { .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd }, + + { .kind = DITEM_COND, .on_success = CondReplayIsPlaying }, + { .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd }, + + { .kind = DITEM_COND, .on_success = CondReplayIsNotPlayingOrRecordsNotClear }, + { .kind = DITEM_BUTTON, .name = "clear", .on_click = ActReplayClear }, + + { .kind = DITEM_GAP }, + + { .kind = DITEM_COND, .on_success = CondReplayIsNotPlaying, .skip = 4 }, + { .kind = DITEM_BUTTON, .name = "new", .on_click = ActReplayNew }, + { .kind = DITEM_BUTTON, .name = "load", .on_click = ActReplayLoad }, + { .kind = DITEM_BUTTON, .name = "save", .on_click = ActReplaySave }, + { .kind = DITEM_BUTTON, .name = "save as...", .on_click = ActReplaySaveAs }, + + { .kind = DITEM_END }, + }, + .is_sp_only = true, + } + }, + { + .kind = DITEM_LIST, + .name = "profilers", + .list = { + .items = (debug_item[]) { + { .kind = DITEM_RAW, .val = PROF_MAIN_LOOP, .proc = DrawProfilerDelta }, + { .kind = DITEM_TEXT, .name = "unmeasured time", .proc = DrawUnmeasuredTime }, + { .kind = DITEM_RAW, .val = PROF_WORLD_WRITE, .proc = DrawProfilerDelta }, + { .kind = DITEM_RAW, .val = PROF_RENDER, .proc = DrawProfilerDelta }, + { .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta }, + { .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta }, + { .kind = DITEM_RAW, .val = PROF_ENTITY_REMOVAL, .proc = DrawProfilerDelta }, + { .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta }, + { .kind = DITEM_END }, + }, + .is_collapsed = 1 + } + }, + { + .kind = DITEM_BUTTON, + .name = "exit game", + .on_click = ActExitGame, + }, + {.kind = DITEM_END}, +}; + +debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool is_shadow) { + is_shadow_rendered = is_shadow; + for (debug_item *it = list; it->kind != DITEM_END; it += 1) { + switch (it->kind) { + case DITEM_GAP: { + ypos += DBG_GAP_HEIGHT; + }break; + case DITEM_COND: { + ZPL_ASSERT(it->on_success); + + if (!it->on_success()) { + it += it->skip ? it->skip : 1; + } + }break; + case DITEM_LIST: { + // NOTE(zaklaus): calculate and cache name width for future use + if (it->name_width == 0) { + it->name_width = UIMeasureText(it->name, DBG_FONT_SIZE); + } + Color color = RAYWHITE; + if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color)) { + it->list.is_collapsed = !it->list.is_collapsed; + } + + UIDrawText(it->name, xpos, ypos, DBG_FONT_SIZE, color); + ypos += DBG_FONT_SPACING; + if (it->list.is_collapsed) break; + if (it->list.is_sp_only && game_get_kind() != GAMEKIND_SINGLE) break; + debug_draw_result res = debug_draw_list(it->list.items, xpos+DBG_LIST_XPOS_OFFSET, ypos, is_shadow); + ypos = res.y; + }break; + + case DITEM_TEXT: { + char const *text = TextFormat("%s: ", it->name); + if (it->name_width == 0) { + it->name_width = UIMeasureText(text, DBG_FONT_SIZE); + } + UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE); + ZPL_ASSERT(it->proc); + + debug_draw_result res = it->proc(it, xpos + it->name_width, ypos); + ypos = res.y; + }break; + + case DITEM_RAW: { + ZPL_ASSERT(it->proc); + + debug_draw_result res = it->proc(it, xpos, ypos); + ypos = res.y; + }break; + + case DITEM_BUTTON: { + char const *text = TextFormat("> %s", it->name); + if (it->name_width == 0) { + it->name_width = UIMeasureText(text, DBG_FONT_SIZE); + } + Color color = RAYWHITE; + if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color) && it->on_click) { + it->on_click(); + } + + if (!it->on_click) { + color = GRAY; + } + + debug_draw_result res = DrawColoredText(xpos, ypos, text, color); + ypos = res.y; + }break; + + case DITEM_SLIDER: { + ZPL_ASSERT(it->slider.min != it->slider.max); + char const *text = TextFormat("%s: ", it->name); + if (it->name_width == 0) { + it->name_width = UIMeasureText(text, DBG_FONT_SIZE); + } + UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE); + xpos += it->name_width; + + DrawRectangleLines(xpos, ypos, 100.0f, DBG_FONT_SIZE, RAYWHITE); + + float stick_x = xpos + ((it->slider.val / it->slider.max) * 100.0f) - 5.0f; + DrawRectangle(stick_x, ypos, 10.0f, DBG_FONT_SIZE, RED); + + xpos += 100.0f + 5.0f; + DrawFloat(xpos, ypos, it->slider.val); + ypos += DBG_FONT_SPACING; + }break; + + default: { + + }break; + } + } + + return (debug_draw_result){xpos, ypos}; +} + +void debug_draw(void) { + // NOTE(zaklaus): Flush old debug samples + debug_draw_flush(); + + float xpos = debug_xpos; + float ypos = debug_ypos; + + // NOTE(zaklaus): move debug ui + { + debug_area_status area = check_mouse_area(xpos, ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM); + Color color = BLUE; + if (area == DAREA_HOVER) color = YELLOW; + if (area == DAREA_HELD) { + color = RED; + is_handle_ctrl_held = 1; + } + + if (is_handle_ctrl_held) { + debug_xpos = xpos = GetMouseX() - DBG_CTRL_HANDLE_DIM/2; + debug_ypos = ypos = GetMouseY() - DBG_CTRL_HANDLE_DIM/2; + + if (area == DAREA_PRESS) { + is_handle_ctrl_held = 0; + } + } + + DrawRectangle(xpos, ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM, color); + } + + // NOTE(zaklaus): toggle debug ui + { + Color color = BLUE; + debug_area_status area = check_mouse_area(xpos, 15+ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM); + if (area == DAREA_HOVER) color = YELLOW; + if (area == DAREA_HELD) { + color = RED; + } + if (area == DAREA_PRESS) { + is_debug_open = !is_debug_open; + } + DrawPoly((Vector2){xpos+DBG_CTRL_HANDLE_DIM/2, ypos+15+DBG_CTRL_HANDLE_DIM/2}, 3, 6.0f,is_debug_open ? 0.0f : 180.0f, color); + } + + if (is_debug_open) { + xpos += 15; + debug_draw_list(items, xpos+DBG_SHADOW_OFFSET_XPOS, ypos+DBG_SHADOW_OFFSET_YPOS, 1); // NOTE(zaklaus): draw shadow + debug_draw_list(items, xpos, ypos, 0); + } +} + +debug_area_status check_mouse_area(float xpos, float ypos, float w, float h) { + if (is_shadow_rendered) return DAREA_OUTSIDE; + bool is_inside = CheckCollisionPointRec(GetMousePosition(), (Rectangle){xpos, ypos, w, h}); + + if (is_inside) { + return IsMouseButtonReleased(MOUSE_LEFT_BUTTON) ? DAREA_PRESS : IsMouseButtonDown(MOUSE_LEFT_BUTTON) ? DAREA_HELD : DAREA_HOVER; + } + return DAREA_OUTSIDE; +} + +bool is_btn_pressed(float xpos, float ypos, float w, float h, Color *color) { + ZPL_ASSERT(color); + *color = RAYWHITE; + debug_area_status area = check_mouse_area(xpos, ypos, w, h); + if (area == DAREA_PRESS) { + *color = RED; + return true; + } else if (area == DAREA_HOVER) { + *color = YELLOW; + } else if (area == DAREA_HELD) { + *color = RED; + } + + return false; +} + +static inline +void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color) { + // Check if default font has been loaded + if (GetFontDefault().texture.id != 0) { + Vector2 position = { (float)posX , (float)posY }; + + int defaultFontSize = 10; // Default Font chars height in pixel + int new_spacing = fontSize/defaultFontSize; + + DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , is_shadow_rendered ? BLACK : color); + } +} + +static inline +int UIMeasureText(const char *text, int fontSize) { + Vector2 vec = { 0.0f, 0.0f }; + + // Check if default font has been loaded + if (GetFontDefault().texture.id != 0) { + int defaultFontSize = 10; // Default Font chars height in pixel + int new_spacing = fontSize/defaultFontSize; + + vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing); + } + + return (int)vec.x; +} diff --git a/code/game/src/debug_ui_actions.c b/code/game/src/debug_ui_actions.c index 2d68a54..65d32bc 100644 --- a/code/game/src/debug_ui_actions.c +++ b/code/game/src/debug_ui_actions.c @@ -1,235 +1,274 @@ -#include "debug_ui.h" -#include "world/blocks.h" -#include "items.h" - -void -ActExitGame(void) { - game_request_close(); -} - -void -ActSpawnCar(void) { - ecs_entity_t e = vehicle_spawn(); - ecs_entity_t plr = camera_get().ent_id; - - Position const* origin = ecs_get(world_ecs(), plr, Position); - Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL); - *dest = *origin; - - debug_replay_special_action(RPKIND_SPAWN_CAR); -} - -void -ActSpawnIcemaker(void) { - ecs_entity_t e = item_spawn(IKIND_DEMO_ICEMAKER, 32); - ecs_entity_t plr = camera_get().ent_id; - - Position const* origin = ecs_get(world_ecs(), plr, Position); - Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL); - *dest = *origin; - - debug_replay_special_action(RPKIND_SPAWN_ICEMAKER_ITEM); -} - -void -ActSpawnCirclingDriver(void) { - ecs_entity_t plr = camera_get().ent_id; - ecs_entity_t ve = vehicle_spawn(); - ecs_entity_t e = entity_spawn(EKIND_DEMO_NPC); - - Position const *origin = ecs_get(world_ecs(), plr, Position); - Position *veh_dest = ecs_get_mut(world_ecs(), ve, Position, NULL); - Position *dest = ecs_get_mut(world_ecs(), e, Position, NULL); - *veh_dest = *origin; - *dest = *origin; - - Input *input = ecs_get_mut(world_ecs(), e, Input, NULL); - zpl_zero_item(input); - input->x = input->y = 1.0f; - - Vehicle *veh = ecs_get_mut(world_ecs(), ve, Vehicle, NULL); - veh->seats[0] = e; - - ecs_set(world_ecs(), e, IsInVehicle, { .veh = ve }); - - debug_replay_special_action(RPKIND_SPAWN_CIRCLING_DRIVER); -} - -void -ActPlaceIceRink(void) { - ecs_entity_t plr = camera_get().ent_id; - uint8_t watr_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER); - Position const *p = ecs_get(world_ecs(), plr, Position); - float const bs = WORLD_BLOCK_SIZE; - - for (int y = 0; y < 100; y++) { - for (int x = 0; x < 100; x++) { - world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f); - world_chunk_replace_block(l.chunk_id, l.id, watr_id); - } - } - - debug_replay_special_action(RPKIND_PLACE_ICE_RINK); -} - -void -ActEraseWorldChanges(void) { - ecs_entity_t plr = camera_get().ent_id; - Position const *p = ecs_get(world_ecs(), plr, Position); - float const bs = WORLD_BLOCK_SIZE; - - for (int y = 0; y < 100; y++) { - for (int x = 0; x < 100; x++) { - world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f); - world_chunk_place_block(l.chunk_id, l.id, 0); - } - } - - debug_replay_special_action(RPKIND_PLACE_ERASE_CHANGES); -} - -// NOTE(zaklaus): Replay system - -uint8_t -CondReplayStatusOn(void) { - return is_recording && !is_playing; -} - -uint8_t -CondReplayStatusOff(void) { - return !is_recording && !is_playing; -} - -uint8_t -CondReplayDataPresentAndNotPlaying(void) { - return records != NULL && !is_recording && !is_playing; -} - -uint8_t -CondReplayIsPlaying(void) { - return records != NULL && !is_recording && is_playing; -} - -uint8_t -CondReplayIsNotPlaying(void) { - return !is_recording && !is_playing; -} - -uint8_t -CondReplayIsNotPlayingOrRecordsNotClear(void) { - return records != NULL && !is_recording && !is_playing; -} - -void -ActReplayBegin(void) { - debug_replay_start(); -} - -void -ActReplayEnd(void) { - debug_replay_stop(); -} - -void -ActReplayRun(void) { - debug_replay_run(); -} - -void -ActReplayClear(void) { - debug_replay_clear(); -} - - -void -ActReplayNew(void) { - debug_replay_clear(); - zpl_zero_size(replay_filename, sizeof(replay_filename)); -} - -void -ActReplaySaveAs(void) { - if (!records) return; - char const *workdir = GetWorkingDirectory(); - - sfd_Options sfd = { - .title = "Save Macro", - .path = "art", - .filter_name = "eco2d Macro", - .filter = "*.dem", - }; - - char const *path = sfd_save_dialog(&sfd); - ChangeDirectory(workdir); - - if (path) { - zpl_strcpy(replay_filename, zpl_bprintf("%s.dem", path)); - debug_replay_store(); - } - -} - -void -ActReplaySave(void) { - if (!replay_filename[0]) { - ActReplaySaveAs(); - } - else debug_replay_store(); -} - -void -ActReplayLoad(void) { - char const *workdir = GetWorkingDirectory(); - - sfd_Options sfd = { - .title = "Load Macro", - .path = "art", - .filter_name = "eco2d Macro", - .filter = "*.dem", - }; - - char const *path = sfd_open_dialog(&sfd); - ChangeDirectory(workdir); - - if (path) { - zpl_zero_size(replay_filename, sizeof(replay_filename)); - zpl_strcpy(replay_filename, path); - debug_replay_clear(); - debug_replay_load(); - } -} - -// NOTE(zaklaus): Demo NPCs -static ecs_entity_t *demo_npcs = NULL; - -void -ActSpawnDemoNPCs(void) { - if (!demo_npcs) zpl_array_init(demo_npcs, zpl_heap()); - if (zpl_array_count(demo_npcs) >= 10000) return; - - for (uint32_t i = 0; i < 1000; i++) { - uint64_t e = entity_spawn(EKIND_DEMO_NPC); - ecs_add(world_ecs(), e, EcsDemoNPC); - Position *pos = ecs_get_mut(world_ecs(), e, Position, NULL); - pos->x=rand() % world_dim(); - pos->y=rand() % world_dim(); - - Velocity *v = ecs_get_mut(world_ecs(), e, Velocity, NULL); - v->x = (rand()%3-1) * 10; - v->y = (rand()%3-1) * 10; - - zpl_array_append(demo_npcs, e); - } -} - -void -ActDespawnDemoNPCs(void) { - if (!demo_npcs) return; - - for (uint32_t i = 0; i < zpl_array_count(demo_npcs); i++) { - entity_despawn(demo_npcs[i]); - } - - zpl_array_free(demo_npcs); - demo_npcs = 0; -} \ No newline at end of file +#include "debug_ui.h" +#include "world/blocks.h" +#include "items.h" + +void +ActExitGame(void) { + game_request_close(); +} + +void +ActSpawnCar(void) { + ecs_entity_t e = vehicle_spawn(); + ecs_entity_t plr = camera_get().ent_id; + + Position const* origin = ecs_get(world_ecs(), plr, Position); + Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL); + *dest = *origin; + + debug_replay_special_action(RPKIND_SPAWN_CAR); +} + +void +ActSpawnIcemaker(void) { + ecs_entity_t e = item_spawn(IKIND_DEMO_ICEMAKER, 32); + ecs_entity_t plr = camera_get().ent_id; + + Position const* origin = ecs_get(world_ecs(), plr, Position); + Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL); + *dest = *origin; + + debug_replay_special_action(RPKIND_SPAWN_ICEMAKER_ITEM); +} + +void +ActSpawnCirclingDriver(void) { + ecs_entity_t plr = camera_get().ent_id; + ecs_entity_t ve = vehicle_spawn(); + ecs_entity_t e = entity_spawn(EKIND_DEMO_NPC); + + Position const *origin = ecs_get(world_ecs(), plr, Position); + Position *veh_dest = ecs_get_mut(world_ecs(), ve, Position, NULL); + Position *dest = ecs_get_mut(world_ecs(), e, Position, NULL); + *veh_dest = *origin; + *dest = *origin; + + Input *input = ecs_get_mut(world_ecs(), e, Input, NULL); + zpl_zero_item(input); + input->x = input->y = 1.0f; + + Vehicle *veh = ecs_get_mut(world_ecs(), ve, Vehicle, NULL); + veh->seats[0] = e; + + ecs_set(world_ecs(), e, IsInVehicle, { .veh = ve }); + + debug_replay_special_action(RPKIND_SPAWN_CIRCLING_DRIVER); +} + +void +ActPlaceIceRink(void) { + ecs_entity_t plr = camera_get().ent_id; + uint8_t watr_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER); + Position const *p = ecs_get(world_ecs(), plr, Position); + float const bs = WORLD_BLOCK_SIZE; + + for (int y = 0; y < 100; y++) { + for (int x = 0; x < 100; x++) { + world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f); + world_chunk_replace_block(l.chunk_id, l.id, watr_id); + } + } + + debug_replay_special_action(RPKIND_PLACE_ICE_RINK); +} + +void +ActEraseWorldChanges(void) { + ecs_entity_t plr = camera_get().ent_id; + Position const *p = ecs_get(world_ecs(), plr, Position); + float const bs = WORLD_BLOCK_SIZE; + + for (int y = 0; y < 100; y++) { + for (int x = 0; x < 100; x++) { + world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f); + world_chunk_place_block(l.chunk_id, l.id, 0); + } + } + + debug_replay_special_action(RPKIND_PLACE_ERASE_CHANGES); +} + +// NOTE(zaklaus): Replay system + +uint8_t +CondReplayStatusOn(void) { + return is_recording && !is_playing; +} + +uint8_t +CondReplayStatusOff(void) { + return !is_recording && !is_playing; +} + +uint8_t +CondReplayDataPresentAndNotPlaying(void) { + return records != NULL && !is_recording && !is_playing; +} + +uint8_t +CondReplayIsPlaying(void) { + return records != NULL && !is_recording && is_playing; +} + +uint8_t +CondReplayIsNotPlaying(void) { + return !is_recording && !is_playing; +} + +uint8_t +CondReplayIsNotPlayingOrRecordsNotClear(void) { + return records != NULL && !is_recording && !is_playing; +} + +void +ActReplayBegin(void) { + debug_replay_start(); +} + +void +ActReplayEnd(void) { + debug_replay_stop(); +} + +void +ActReplayRun(void) { + debug_replay_run(); +} + +void +ActReplayClear(void) { + debug_replay_clear(); +} + + +void +ActReplayNew(void) { + debug_replay_clear(); + zpl_zero_size(replay_filename, sizeof(replay_filename)); +} + +void +ActReplaySaveAs(void) { + if (!records) return; + char const *workdir = GetWorkingDirectory(); + + sfd_Options sfd = { + .title = "Save Macro", + .path = "art", + .filter_name = "eco2d Macro", + .filter = "*.dem", + }; + + char const *path = sfd_save_dialog(&sfd); + ChangeDirectory(workdir); + + if (path) { + zpl_strcpy(replay_filename, zpl_bprintf("%s.dem", path)); + debug_replay_store(); + } + +} + +void +ActReplaySave(void) { + if (!replay_filename[0]) { + ActReplaySaveAs(); + } + else debug_replay_store(); +} + +void +ActReplayLoad(void) { + char const *workdir = GetWorkingDirectory(); + + sfd_Options sfd = { + .title = "Load Macro", + .path = "art", + .filter_name = "eco2d Macro", + .filter = "*.dem", + }; + + char const *path = sfd_open_dialog(&sfd); + ChangeDirectory(workdir); + + if (path) { + zpl_zero_size(replay_filename, sizeof(replay_filename)); + zpl_strcpy(replay_filename, path); + debug_replay_clear(); + debug_replay_load(); + } +} + +// NOTE(zaklaus): Demo NPCs +static ecs_entity_t *demo_npcs = NULL; + +void +ActSpawnDemoNPCs(void) { + if (!demo_npcs) zpl_array_init(demo_npcs, zpl_heap()); + if (zpl_array_count(demo_npcs) >= 10000) return; + + for (uint32_t i = 0; i < 1000; i++) { + uint64_t e = entity_spawn(EKIND_DEMO_NPC); + ecs_add(world_ecs(), e, EcsDemoNPC); + Position *pos = ecs_get_mut(world_ecs(), e, Position, NULL); + pos->x=rand() % world_dim(); + pos->y=rand() % world_dim(); + + Velocity *v = ecs_get_mut(world_ecs(), e, Velocity, NULL); + v->x = (rand()%3-1) * 10; + v->y = (rand()%3-1) * 10; + + zpl_array_append(demo_npcs, e); + } +} + +void +ActDespawnDemoNPCs(void) { + if (!demo_npcs) return; + + for (uint32_t i = 0; i < zpl_array_count(demo_npcs); i++) { + entity_despawn(demo_npcs[i]); + } + + zpl_array_free(demo_npcs); + demo_npcs = 0; +} + +// NOTE(zaklaus): world simulation controls +#define WORLDSIM_STEPPING 0.01f +static float sim_step_size = 0.1f; + +void +ActWorldToggleSim(void) { + if (world_is_paused()) { + world_resume(); + } else { + world_pause(); + } +} + +void +ActWorldIncrementSimStepSize(void) { + sim_step_size += WORLDSIM_STEPPING; +} + +void +ActWorldDecrementSimStepSize(void) { + if (sim_step_size > WORLDSIM_STEPPING) + sim_step_size -= WORLDSIM_STEPPING; +} + +void +ActWorldStep(void) { + world_step(sim_step_size); +} + +uint8_t +CondIsWorldPaused(void) { + return world_is_paused(); +} + +uint8_t +CondIsWorldRunning(void) { + return !world_is_paused(); +} diff --git a/code/game/src/debug_ui_widgets.c b/code/game/src/debug_ui_widgets.c index 7328399..c59a922 100644 --- a/code/game/src/debug_ui_widgets.c +++ b/code/game/src/debug_ui_widgets.c @@ -1,93 +1,101 @@ -#include "debug_ui.h" -#include "raylib.h" -#include "platform.h" -#include "profiler.h" - -//~ NOTE(zaklaus): helpers - -static inline debug_draw_result -DrawFloat(float xpos, float ypos, float val) { - char const *text = TextFormat("%.02f\n", val); - UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE); - return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING}; -} - -static inline debug_draw_result -DrawColoredText(float xpos, float ypos, char const *text, Color color) { - ZPL_ASSERT(text); - UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, color); - return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING}; -} - -static inline debug_draw_result -DrawFormattedText(float xpos, float ypos, char const *text) { - return DrawColoredText(xpos, ypos, text, RAYWHITE); -} - -//~ NOTE(zaklaus): widgets - -static inline debug_draw_result -DrawCameraPos(debug_item *it, float xpos, float ypos) { - (void)it; - camera cam = camera_get(); - return DrawFormattedText(xpos, ypos, TextFormat("%d %d", (int)(cam.x/WORLD_BLOCK_SIZE), (int)(cam.y/WORLD_BLOCK_SIZE))); -} - -static inline debug_draw_result -DrawUnmeasuredTime(debug_item *it, float xpos, float ypos) { - (void)it; - float total_time = profiler_delta(PROF_TOTAL_TIME); - float acc_time = profiler_delta(PROF_MAIN_LOOP); - - return DrawFormattedText(xpos, ypos, TextFormat("%.02f ms", (total_time-acc_time) * 1000.0f)); -} - -static inline debug_draw_result -DrawDeltaTime(debug_item *it, float xpos, float ypos) { - (void)it; - float dt = GetFrameTime(); - return DrawFormattedText(xpos, ypos, TextFormat("%.02f (%.02f fps)", dt * 1000.0f, 1.0f/dt)); -} - -static inline debug_draw_result -DrawZoom(debug_item *it, float xpos, float ypos) { - (void)it; - - return DrawFloat(xpos, ypos, platform_zoom_get()); -} - -static inline debug_draw_result -DrawLiteral(debug_item *it, float xpos, float ypos) { - ZPL_ASSERT(it->text); - return DrawFormattedText(xpos, ypos, it->text); -} - -static inline debug_draw_result -DrawProfilerDelta(debug_item *it, float xpos, float ypos) { - float dt = profiler_delta(it->val); - return DrawFormattedText(xpos, ypos, TextFormat("%s: %.02f ms", profiler_name(it->val), dt * 1000.0f)); -} - -static inline debug_draw_result -DrawReplaySamples(debug_item *it, float xpos, float ypos) { - (void)it; - size_t cnt = 0; - if (records) { - cnt = zpl_array_count(records); - } - return DrawFormattedText(xpos, ypos, TextFormat("%d of %d", record_pos, cnt)); -} - -static inline debug_draw_result -DrawReplayFileName(debug_item *it, float xpos, float ypos) { - (void)it; - return DrawFormattedText(xpos, ypos, TextFormat("%s", replay_filename[0] ? replay_filename : "")); -} - -// NOTE(zaklaus): demo npcs - -static inline debug_draw_result -DrawDemoNPCCount(debug_item *it, float xpos, float ypos) { - (void)it; - return DrawFormattedText(xpos, ypos, TextFormat("%d", demo_npcs ? zpl_array_count(demo_npcs) : 0)); -} +#include "debug_ui.h" +#include "raylib.h" +#include "platform.h" +#include "profiler.h" + +//~ NOTE(zaklaus): helpers + +static inline debug_draw_result +DrawFloat(float xpos, float ypos, float val) { + char const *text = TextFormat("%.02f\n", val); + UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE); + return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING}; +} + +static inline debug_draw_result +DrawColoredText(float xpos, float ypos, char const *text, Color color) { + ZPL_ASSERT(text); + UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, color); + return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING}; +} + +static inline debug_draw_result +DrawFormattedText(float xpos, float ypos, char const *text) { + return DrawColoredText(xpos, ypos, text, RAYWHITE); +} + +//~ NOTE(zaklaus): widgets + +static inline debug_draw_result +DrawCameraPos(debug_item *it, float xpos, float ypos) { + (void)it; + camera cam = camera_get(); + return DrawFormattedText(xpos, ypos, TextFormat("%d %d", (int)(cam.x/WORLD_BLOCK_SIZE), (int)(cam.y/WORLD_BLOCK_SIZE))); +} + +static inline debug_draw_result +DrawUnmeasuredTime(debug_item *it, float xpos, float ypos) { + (void)it; + float total_time = profiler_delta(PROF_TOTAL_TIME); + float acc_time = profiler_delta(PROF_MAIN_LOOP); + + return DrawFormattedText(xpos, ypos, TextFormat("%.02f ms", (total_time-acc_time) * 1000.0f)); +} + +static inline debug_draw_result +DrawDeltaTime(debug_item *it, float xpos, float ypos) { + (void)it; + float dt = GetFrameTime(); + return DrawFormattedText(xpos, ypos, TextFormat("%.02f (%.02f fps)", dt * 1000.0f, 1.0f/dt)); +} + +static inline debug_draw_result +DrawZoom(debug_item *it, float xpos, float ypos) { + (void)it; + + return DrawFloat(xpos, ypos, platform_zoom_get()); +} + +static inline debug_draw_result +DrawLiteral(debug_item *it, float xpos, float ypos) { + ZPL_ASSERT(it->text); + return DrawFormattedText(xpos, ypos, it->text); +} + +static inline debug_draw_result +DrawProfilerDelta(debug_item *it, float xpos, float ypos) { + float dt = profiler_delta(it->val); + return DrawFormattedText(xpos, ypos, TextFormat("%s: %.02f ms", profiler_name(it->val), dt * 1000.0f)); +} + +static inline debug_draw_result +DrawReplaySamples(debug_item *it, float xpos, float ypos) { + (void)it; + size_t cnt = 0; + if (records) { + cnt = zpl_array_count(records); + } + return DrawFormattedText(xpos, ypos, TextFormat("%d of %d", record_pos, cnt)); +} + +static inline debug_draw_result +DrawReplayFileName(debug_item *it, float xpos, float ypos) { + (void)it; + return DrawFormattedText(xpos, ypos, TextFormat("%s", replay_filename[0] ? replay_filename : "")); +} + +// NOTE(zaklaus): demo npcs + +static inline debug_draw_result +DrawDemoNPCCount(debug_item *it, float xpos, float ypos) { + (void)it; + return DrawFormattedText(xpos, ypos, TextFormat("%d", demo_npcs ? zpl_array_count(demo_npcs) : 0)); +} + + +// NOTE(zaklaus): world simulation +static inline debug_draw_result +DrawWorldStepSize(debug_item *it, float xpos, float ypos) { + (void)it; + return DrawFormattedText(xpos, ypos, TextFormat("%d ms", (int16_t)(sim_step_size*1000.f))); +} diff --git a/code/game/src/world/world.c b/code/game/src/world/world.c index f828e84..2898460 100644 --- a/code/game/src/world/world.c +++ b/code/game/src/world/world.c @@ -147,6 +147,7 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) { return 0; } + world.is_paused = false; world.seed = seed; world.chunk_size = chunk_size; world.chunk_amount = chunk_amount; @@ -349,6 +350,26 @@ librg_world *world_tracker() { return world.tracker; } +void world_pause(void) { + ecs_set_time_scale(world.ecs, 0.0f); + world.is_paused = true; +} + +void world_resume(void) { + ecs_set_time_scale(world.ecs, 1.0f); + world.is_paused = false; +} + +bool world_is_paused(void) { + return world.is_paused; +} + +void world_step(float step_size) { + world_resume(); + ecs_progress(world.ecs, step_size); + world_pause(); +} + uint16_t world_chunk_size(void) { return world.chunk_size; } diff --git a/code/game/src/world/world.h b/code/game/src/world/world.h index 77e096e..466dcb6 100644 --- a/code/game/src/world/world.h +++ b/code/game/src/world/world.h @@ -30,6 +30,7 @@ typedef WORLD_PKT_READER(world_pkt_reader_proc); typedef WORLD_PKT_WRITER(world_pkt_writer_proc); typedef struct { + bool is_paused; uint8_t *data; uint32_t seed; uint32_t size; @@ -63,6 +64,12 @@ ecs_world_t *world_ecs(void); void world_set_stage(ecs_world_t *ecs); librg_world *world_tracker(void); +// NOTE(zaklaus): World simulation time control +void world_pause(void); +void world_resume(void); +bool world_is_paused(void); +void world_step(float step_size); + uint16_t world_chunk_size(void); uint16_t world_chunk_amount(void); uint16_t world_dim(void);