diff --git a/build_dbg.bat b/build_dbg.bat new file mode 100644 index 0000000..a8dbebe --- /dev/null +++ b/build_dbg.bat @@ -0,0 +1,3 @@ +@echo off +call win\setup_cl_generic.bat amd64 +cmake --build build && build\eco2d diff --git a/code/foundation/src/dev/debug_ui_tools.c b/code/foundation/src/dev/debug_ui_tools.c index a631070..26a052a 100644 --- a/code/foundation/src/dev/debug_ui_tools.c +++ b/code/foundation/src/dev/debug_ui_tools.c @@ -3,6 +3,8 @@ #include "world/blocks.h" #include "models/items.h" +extern void tooltip_show(const char* name, float xpos, float ypos); + void ToolAssetInspector(void) { if (nk_begin(dev_ui, "Asset Inspector", nk_rect(400, 100, 240, 800), NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| NK_WINDOW_TITLE)) @@ -16,6 +18,11 @@ void ToolAssetInspector(void) { nk_labelf(dev_ui, NK_TEXT_LEFT, "kind: %s", asset_kind_name); nk_labelf(dev_ui, NK_TEXT_LEFT, "spawnable entity: %s", entity_spawn_provided(i) ? "true" : "false"); + if (nk_button_label(dev_ui, "show tooltip")) { + Vector2 mpos = GetMousePosition(); + tooltip_show(asset_names[i] , mpos.x + 5, mpos.y + 5); + } + // draw block block_id blk_id = blocks_find(i); if (blk_id != 0xF) { diff --git a/code/foundation/src/gui/inventory.c b/code/foundation/src/gui/inventory.c index 08ef819..7c9ede1 100644 --- a/code/foundation/src/gui/inventory.c +++ b/code/foundation/src/gui/inventory.c @@ -95,8 +95,8 @@ inv_draw_result inventory_draw_crafting_btn(float xpos, float ypos, const char * recipe rp = craft_get_recipe_data(craft_get_recipe_id_from_product(id)); int num_reagents = 0; for (int i = 0; rp.reagents[i].id; i++) num_reagents++; - if (nk_begin(game_ui , name, nk_rect(mpos.x+15, mpos.y+15, name_width+5, 30+30*(float)num_reagents), - NK_WINDOW_BORDER | NK_WINDOW_NO_INPUT | NK_WINDOW_NO_SCROLLBAR)) { + if (nk_begin(game_ui , name, nk_rect(mpos.x+15, mpos.y+15, name_width+5, 600), + NK_WINDOW_BORDER | NK_WINDOW_NO_INPUT | NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_DYNAMIC)) { if (nk_tree_push_id(game_ui, NK_TREE_NODE, "Reagents", NK_MAXIMIZED, id)) { for (asset_id i = 0; rp.reagents[i].id; i++) { nk_label(game_ui, asset_names[rp.reagents[i].id], NK_TEXT_LEFT); diff --git a/code/foundation/src/gui/tooltip.c b/code/foundation/src/gui/tooltip.c new file mode 100644 index 0000000..713546a --- /dev/null +++ b/code/foundation/src/gui/tooltip.c @@ -0,0 +1,170 @@ +// Tooltip system with multilevel modal support + +typedef struct _tooltip { + const char *name; + const char *content; + const char **links; +} tooltip; + +static tooltip *tooltips = 0; + +//~ registration + +void tooltip_register(tooltip desc) { + if (!tooltips) { + zpl_array_init(tooltips, zpl_heap()); + } + + desc.links = 0; + zpl_array_append(tooltips, desc); +} + +void tooltip_destroy_all(void) { + if (!tooltips) return; + + for (zpl_isize i = 0; i < zpl_array_count(tooltips); ++i) { + tooltip *tp = (tooltips + i); + + if (tp->links) { + zpl_array_free(tp->links); + } + } + + zpl_array_free(tooltips); +} + +void tooltip_build_links(void) { + for (zpl_isize i = 0; i < zpl_array_count(tooltips); ++i) { + tooltip *tp = (tooltips + i); + + for (zpl_isize j = 0; j < zpl_array_count(tooltips); ++j) { + tooltip *linked_tp = (tooltips + j); + if (tp == linked_tp) + continue; + + if (strstr(tp->content, linked_tp->name)) { + if (!tp->links) { + zpl_array_init(tp->links, zpl_heap()); + } + + zpl_array_append(tp->links, linked_tp->name); + } + } + } +} + +void tooltip_register_defaults(void) { + // test + tooltip_register( (tooltip) { .name = "ASSET_WOOD", .content = "Used as a building material or fuel for the ASSET_FURNACE." } ); + tooltip_register( (tooltip) { .name = "ASSET_FURNACE", .content = "Producer used to smelt ASSET_IRON_ORE into ASSET_IRON_INGOT." } ); + tooltip_register( (tooltip) { .name = "ASSET_IRON_ORE", .content = "Natural resource that can be smelted in ASSET_FURNACE." } ); + tooltip_register( (tooltip) { .name = "ASSET_IRON_INGOT", .content = "Used as a building material. It is smelted from ASSET_IRON_ORE" } ); +} + +//~ rendering + +#define TOOLTIP_MOUSE_DIST 400.0f + +typedef struct _tooltip_node { + float xpos, ypos; + tooltip *desc; + struct _tooltip_node *next; +} tooltip_node; + +static tooltip_node main_tooltip = { 0 }; +static bool tooltip__should_stay_open = false; + +tooltip *tooltip__find_desc(const char *name) { + for (zpl_isize i = 0; i < zpl_array_count(tooltips); ++i) { + tooltip *tp = (tooltips + i); + + if (!strcmp(tp->name, name)) + return tp; + } + + return 0; +} + +void tooltip_show(const char* name, float xpos, float ypos) { + if (!tooltips) return; + + tooltip *desc = tooltip__find_desc(name); + if (!name) return; + + main_tooltip = (tooltip_node) { + .xpos = xpos, + .ypos = ypos, + .desc = desc, + .next = 0 + }; +} + +void tooltip__clear_node(tooltip_node *node) { + if (node->next) { + tooltip__clear_node(node->next); + zpl_mfree(node->next); + } +} + +void tooltip_clear(void) { + tooltip__clear_node(&main_tooltip); + main_tooltip = (tooltip_node) {0}; +} + +void tooltip__draw_node(tooltip_node *node) { + if (!node) return; + + tooltip *desc = node->desc; + Vector2 mpos = GetMousePosition(); + + if (nk_begin_titled(game_ui, zpl_bprintf("%d%s", (int)node->xpos, desc->name), desc->name, nk_rect(node->xpos, node->ypos, 300, 1200), + NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_DYNAMIC | NK_WINDOW_TITLE | NK_WINDOW_MOVABLE)) { + nk_layout_row_dynamic(game_ui, 30, 1); + nk_label_wrap(game_ui, desc->content); + + if (desc->links) { + nk_label(game_ui, "See Also:", NK_TEXT_LEFT); + nk_layout_row_dynamic(game_ui, 10, 2); + + for (zpl_isize i = 0; i < zpl_array_count(desc->links); ++i) { + // todo styling + if (nk_button_label(game_ui, desc->links[i])) { + if (node->next) tooltip__clear_node(node->next); + if (!node->next) node->next = zpl_malloc(sizeof(tooltip_node)); + *node->next = (tooltip_node) { + .xpos = mpos.x+5, + .ypos = mpos.y+5, + .desc = tooltip__find_desc(desc->links[i]), + .next = 0 + }; + } + } + } + + // suggest closing tooltip + struct nk_vec2 wpos = nk_window_get_position(game_ui); + Vector2 tp_pos = (Vector2) { .x = wpos.x, .y = wpos.y }; + if (Vector2Distance(mpos, tp_pos) <= TOOLTIP_MOUSE_DIST) { + tooltip__should_stay_open = true; + } + + nk_end(game_ui); + + // draw nested tooltip + if (node->next) { + tooltip__draw_node(node->next); + } + } +} + +void tooltip_draw(void) { + if (!main_tooltip.desc) return; + + tooltip__draw_node(&main_tooltip); + + if (!tooltip__should_stay_open) { + tooltip_clear(); + } + + tooltip__should_stay_open = false; +} diff --git a/code/games/minimal/src/platform.c b/code/games/minimal/src/platform.c index be02823..d1222c0 100644 --- a/code/games/minimal/src/platform.c +++ b/code/games/minimal/src/platform.c @@ -21,6 +21,8 @@ ZPL_DIAGNOSTIC_POP #define ARCH_IMPL #include "platform/arch.h" +#include "gui/tooltip.c" + #include "renderer.c" void platform_init() { diff --git a/code/games/sandbox/src/platform.c b/code/games/sandbox/src/platform.c index 6c57966..5c58309 100644 --- a/code/games/sandbox/src/platform.c +++ b/code/games/sandbox/src/platform.c @@ -26,6 +26,7 @@ ZPL_DIAGNOSTIC_POP #include "renderer.c" // NOTE(zaklaus): add-ins +#include "gui/tooltip.c" #include "gui/build_mode.c" #include "gui/inventory.c" @@ -34,6 +35,20 @@ void platform_init() { renderer_init(); target_zoom = 2.70f; + + tooltip_register_defaults(); + + // room for game-specific tooltips + + tooltip_build_links(); + +#if 0 + // TEST + { + Vector2 mpos = GetMousePosition(); + tooltip_show("ASSET_FURNACE", mpos.x, mpos.y); + } +#endif } inline static @@ -51,6 +66,7 @@ void display_conn_status() { void platform_shutdown() { renderer_shutdown(); + tooltip_destroy_all(); CloseWindow(); } @@ -187,9 +203,12 @@ void platform_render() { // NOTE(zaklaus): add-ins buildmode_draw(); inventory_draw(); + + // goes last + tooltip_draw(); } display_conn_status(); - debug_draw(); + debug_draw(); game_draw_ui(); } EndDrawing(); diff --git a/eco2d.10x b/eco2d.10x index 5845514..4610827 100644 --- a/eco2d.10x +++ b/eco2d.10x @@ -15,7 +15,7 @@ - + build\eco2d.sln false Debug