diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e69c4c..d80dec7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,8 +28,8 @@ add_executable(PixelDefense game/systems_entity.c game/systems_input.c game/systems_ui.c - game/ui.c - game/ui.h + game/ui_widgets.c + game/ui_widgets.h game/unit_actions.c game/unit_actions.h game/unit_ai.c diff --git a/game/game_state.h b/game/game_state.h index 7a1b710..313e4ee 100644 --- a/game/game_state.h +++ b/game/game_state.h @@ -4,7 +4,14 @@ #include #include +typedef enum GameScreen { + SCREEN_MAIN_MENU, + SCREEN_SETTINGS, + SCREEN_GAME +} GameScreen; + typedef struct Game { + GameScreen screen; Camera2D camera; BzTileset tileset; BzTileMap map; diff --git a/game/main.c b/game/main.c index 1c74735..d360b55 100644 --- a/game/main.c +++ b/game/main.c @@ -1,4 +1,5 @@ #include +#include #include "systems.h" #include "components.h" @@ -8,6 +9,7 @@ #include "map_init.h" #include "map_layers.h" #include "buildings.h" +#include "ui_widgets.h" #include "unit_ai.h" #include "unit_actions.h" @@ -19,6 +21,7 @@ ECS_COMPONENT_DECLARE(Game); ECS_COMPONENT_DECLARE(InputState); +BzUI *UI = NULL; ecs_world_t *ECS = NULL; static ecs_entity_t renderCollidersSystem; @@ -45,6 +48,9 @@ bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) { appDesc->imguiRender = (BzAppRenderFunc) imguiRender; appDesc->userData = NULL; + + //SetConfigFlags(FLAG_WINDOW_RESIZABLE); + return true; } @@ -82,6 +88,8 @@ bool init(void *userData) { BZ_UNUSED(userData); SetExitKey(0); + UI = bzUICreate(); + ECS = ecs_init(); initComponentIDs(ECS); @@ -93,6 +101,7 @@ bool init(void *userData) { ECS_COMPONENT_DEFINE(ECS, Game); ecs_singleton_set(ECS, Game, {}); Game *game = ecs_singleton_get_mut(ECS, Game); + game->screen = SCREEN_MAIN_MENU; ECS_COMPONENT_DEFINE(ECS, InputState); ecs_singleton_set(ECS, InputState, {}); @@ -225,6 +234,9 @@ void deinit(void *userData) { bzObjectPoolDestroy(gameCopy.pools.pathData); bzObjectPoolDestroy(gameCopy.pools.actions); bzSpatialGridDestroy(gameCopy.entityGrid); + + bzUIDestroy(UI); + UI = NULL; } @@ -243,32 +255,92 @@ void update(float dt, void *userData) { updateInputState(input, game->camera, dt); - updatePlayerInput(); + switch (game->screen) { + case SCREEN_MAIN_MENU: + break; + case SCREEN_GAME: + updatePlayerInput(); + break; + } +} + +static void renderMainMenu(Game *game, float dt) { + i32 width = GetScreenWidth(); + i32 height = GetScreenHeight(); + + ClearBackground(RAYWHITE); + + bzUIBegin(UI, width, height); + bzUISetParentLayout(UI, (BzUILayout) { + .type = BZ_UI_LAYOUT_FLEX_BOX, + .flags = BZ_UI_FLEX_DIR_COLUMN + }); + + uiPushDivParentPercentage(1.0f, 0.4f); + bzUISetParentLayout(UI, (BzUILayout) { + .type = BZ_UI_LAYOUT_FLEX_BOX, + .flags = BZ_UI_FLEX_DIR_COLUMN | BZ_UI_FLEX_JUSTIFY_CENTER | BZ_UI_FLEX_ALIGN_CENTER + }); + uiLargeLabel("PixelDefense"); + bzUIPopParent(UI); + + uiPushDivParentPercentage(1.0f, 0.6f); + bzUISetParentLayout(UI, (BzUILayout) { + .type = BZ_UI_LAYOUT_FLEX_BOX, + .flags = BZ_UI_FLEX_DIR_COLUMN | BZ_UI_FLEX_ALIGN_CENTER + }); + if (uiLargeTextButton("Play")) { + bzLogInfo("Play"); + } + if (uiLargeTextButton("Settings")) { + bzLogInfo("Settings"); + } + if (uiLargeTextButton("Exit")) { + bzLogInfo("Bye"); + bzGameExit(); + } + bzUIPopParent(UI); + bzUIEnd(UI); + +} + +static void renderSettings(Game *game, float dt) { } void render(float dt, void *userData) { BZ_UNUSED(userData); Game *game = ecs_singleton_get_mut(ECS, Game); - InputState *input = ecs_singleton_get_mut(ECS, InputState); - ClearBackground(RAYWHITE); - BeginMode2D(game->camera); + switch (game->screen) { + case SCREEN_MAIN_MENU: + renderMainMenu(game, dt); + break; + case SCREEN_SETTINGS: + renderSettings(game, dt); + break; + case SCREEN_GAME: + ClearBackground(RAYWHITE); + BeginMode2D(game->camera); - bzTileMapDraw(&game->map); + bzTileMapDraw(&game->map); - drawPlayerInputUIGround(); + drawPlayerInputUIGround(); - ecs_progress(ECS, dt); - ecs_enable(ECS, renderDebugPathSystem, game->debugDraw.path); - ecs_enable(ECS, renderCollidersSystem, game->debugDraw.entityColliders); - if (game->debugDraw.mapColliders) - bzTileMapDrawCollisions(&game->map); - if (game->debugDraw.spatialGrid) - bzSpatialGridDrawDebugGrid(game->entityGrid); + ecs_progress(ECS, dt); + ecs_enable(ECS, renderDebugPathSystem, game->debugDraw.path); + ecs_enable(ECS, renderCollidersSystem, game->debugDraw.entityColliders); + if (game->debugDraw.mapColliders) + bzTileMapDrawCollisions(&game->map); + if (game->debugDraw.spatialGrid) + bzSpatialGridDrawDebugGrid(game->entityGrid); + + drawPlayerInputUI(); + + EndMode2D(); + break; + } - drawPlayerInputUI(); - EndMode2D(); } void imguiRender(float dt, void *userData) { @@ -276,6 +348,8 @@ void imguiRender(float dt, void *userData) { Game *game = ecs_singleton_get_mut(ECS, Game); InputState *input = ecs_singleton_get_mut(ECS, InputState); + if (game->screen != SCREEN_GAME) return; + igSetNextWindowSize((ImVec2){300, 400}, ImGuiCond_FirstUseEver); igBegin("Debug Menu", NULL, 0); if (igSmallButton("Recruit worker [50 food]")) { diff --git a/game/ui_widgets.c b/game/ui_widgets.c index 045c499..c53b87d 100644 --- a/game/ui_widgets.c +++ b/game/ui_widgets.c @@ -1,152 +1,38 @@ -#include "ui.h" +#include "ui_widgets.h" -UIGrid uiGridFromRoot(i32 width, i32 height) { - return (UIGrid) {.bounds = {0, 0, width, height}}; -} -UIGrid uiGridFromGrid(const UIGrid grid, i32 row, i32 col) { - return uiGridFromGridSpan(grid, row, col, 1, 1); -} -UIGrid uiGridFromGridSpan(const UIGrid grid, i32 row, i32 col, i32 rowSpan, i32 colSpan) { - Rectangle bounds = uiGridCellSpan(grid, row, col, rowSpan, colSpan); - return (UIGrid) { - .rowPadding = grid.rowPadding, - .cellPadding = grid.cellPadding, - .bounds = bounds - }; +void uiPushDivParentPercentage(f32 xPercent, f32 yPercent) { + bzUIPushDiv(UI, (BzUISize) { + .kind = BZ_UI_SIZE_PARENT_PERCENT, + .value = xPercent, + }, (BzUISize) { + .kind = BZ_UI_SIZE_PARENT_PERCENT, + .value = yPercent + }); } -Rectangle uiGridCell(const UIGrid grid, i32 row, i32 col) { - return uiGridCellSpan(grid, row, col, 1, 1); -} -Rectangle uiGridCellSpan(const UIGrid grid, i32 row, i32 col, i32 rowSpan, i32 colSpan) { - BZ_ASSERT(grid.rows > 0 && grid.cols > 0); - BZ_ASSERT(rowSpan > 0 && colSpan > 0); - BZ_ASSERT(row >= 0 && col >= 0); - BZ_ASSERT(row + rowSpan <= grid.rows); - BZ_ASSERT(col + colSpan <= grid.cols); - - f32 cellWidth = (grid.bounds.width - (grid.cols - 1) * grid.cellPadding) / grid.cols; - f32 cellHeight = (grid.bounds.height - (grid.rows - 1) * grid.rowPadding) / grid.rows; - - Rectangle cellBounds = { - .width = cellWidth * colSpan, - .height = cellHeight * rowSpan, - .x = (cellWidth + grid.cellPadding) * col, - .y = (cellHeight + grid.rowPadding) * row, - }; - return cellBounds; -} - -void uiGridDrawCells(const UIGrid grid) { - BZ_ASSERT(grid.rows > 0 && grid.cols > 0); - DrawRectangleLines(grid.bounds.x, grid.bounds.y, grid.bounds.width, grid.bounds.height, RED); - - for (i32 rowIdx = 0; rowIdx < grid.rows; rowIdx++) { - for (i32 colIdx = 0; colIdx < grid.cols; colIdx++) { - Rectangle rec = uiGridCell(grid, rowIdx, colIdx); - DrawRectangleLines(rec.x, rec.y, rec.width, rec.height, BLUE); +void uiLargeLabel(const char *txt) { + bzUINodeMake(UI, bzUIKeyFromString(txt), + &(BzUINodeDesc) { + .flags = BZ_UI_DRAW_TEXT | BZ_UI_ALIGN_CENTER, + .semanticSize[BZ_UI_AXIS_X] = { + .kind = BZ_UI_SIZE_FIT, + }, + .semanticSize[BZ_UI_AXIS_Y] = { + .kind = BZ_UI_SIZE_FIT + }, + .string = txt, + .padding = {5, 5, 5, 5}, + .style = { + .font = GetFontDefault(), + .fontSize = 62, + .textColor = BLACK, + .textHoverColor = BLACK, + .textActiveColor = BLACK, } - } + }); } -static f32 getX(const Rectangle rec) { - return rec.x; -} -static f32 getY(const Rectangle rec) { - return rec.y; -} -static f32 getWidth(const Rectangle rec) { - return rec.width; -} -static f32 getHeight(const Rectangle rec) { - return rec.height; -} - -typedef f32 (*FlexBoxGetFunc)(const Rectangle rec); - -void uiFlexBoxCalc(const FlexBoxDesc *desc, i32 numElements, ...) { - if (numElements <= 0) return; - f32 totalWidth = 0; - f32 totalHeight = 0; - - UIFlexBoxFlag flags = desc->flags; - - // Calculate total size of elements in the main axis - va_list ap; - va_start(ap, numElements); - for (i32 i = 0; i < numElements; i++) { - const Rectangle *rec = va_arg(ap, Rectangle *); - totalWidth += rec->width; - totalHeight += rec->height; - } - va_end(ap); - - // Default: DIR_ROW - FlexBoxGetFunc axisX = getX, axisY = getY, axisWidth = getWidth, axisHeight = getHeight; - FlexBoxGetFunc crossX = getY, crossY = getX, crossWidth = getHeight, crossHeight = getWidth; - f32 mainAxisSize = totalWidth; - if (flags & UI_FLEX_DIR_COLUMN) { - axisX = getY; - axisY = getX; - axisWidth = getHeight; - axisHeight = getWidth; - crossX = getX; - crossY = getY; - crossWidth = getWidth; - crossHeight = getHeight; - mainAxisSize = totalHeight; - } - - - f32 spacingWidth = 0.0f; - f32 spacingHeight = 0.0f; - - f32 mainAxisTotalSpacing = getWidth(desc->window) - mainAxisSize; - f32 mainAxisPadding = desc->padding * (numElements - 1); - - f32 mainAxisSpacing = mainAxisTotalSpacing - mainAxisPadding; - if (mainAxisSpacing < 0) mainAxisSpacing = 0; - - // Default: JUSTIFY_START - f32 mainAxisOffset = 0; - if (flags & UI_FLEX_JUSTIFY_CENTER) { - mainAxisOffset = mainAxisSpacing * 0.5f; - } else if (flags & UI_FLEX_JUSTIFY_END) { - mainAxisOffset = mainAxisSpacing; - } - - bool rowDir = flags & UI_FLEX_DIR_ROW; - - f32 offsetX = desc->window.x; - f32 offsetY = desc->window.y; - if (rowDir) offsetX += mainAxisOffset; - else offsetY += mainAxisOffset; - - va_start(ap, numElements); - for (i32 i = 0; i < numElements; i++) { - f32 x = offsetX; - f32 y = offsetY; - - Rectangle *rec = va_arg(ap, Rectangle *); - f32 crossOffset = 0; - if (flags & UI_FLEX_ALIGN_CENTER) { - crossOffset = (getHeight(desc->window) - getHeight(*rec)) * 0.5f; - } else if (flags & UI_FLEX_ALIGN_END) { - crossOffset = (getHeight(desc->window) - getHeight(*rec)); - } - if (rowDir) { - offsetX += rec->width + spacingWidth + desc->padding; - y += crossOffset; - } else { - offsetY += rec->height + spacingHeight + desc->padding; - x += crossOffset; - } - - - rec->x = x; - rec->y = y; - - } - va_end(ap); +bool uiLargeTextButton(const char *txt) { + return bzUIButton(UI, txt, NULL); } diff --git a/game/ui_widgets.h b/game/ui_widgets.h index d417fa9..9fe2e73 100644 --- a/game/ui_widgets.h +++ b/game/ui_widgets.h @@ -1,49 +1,15 @@ -#ifndef PIXELDEFENSE_UI_H -#define PIXELDEFENSE_UI_H +#ifndef PIXELDEFENSE_UI_WIDGETS_H +#define PIXELDEFENSE_UI_WIDGETS_H #include #include -typedef struct UIGrid { - i32 rows; - i32 cols; - f32 rowPadding; - f32 cellPadding; - Rectangle bounds; -} UIGrid; +extern BzUI *UI; // defined in main.c -UIGrid uiGridFromRoot(i32 width, i32 height); -UIGrid uiGridFromGrid(const UIGrid grid, i32 row, i32 col); -UIGrid uiGridFromGridSpan(const UIGrid grid, i32 row, i32 col, i32 rowSpan, i32 colSpan); +void uiPushDivParentPercentage(f32 xPercent, f32 yPercent); -Rectangle uiGridCell(const UIGrid grid, i32 row, i32 col); -Rectangle uiGridCellSpan(const UIGrid grid, i32 row, i32 col, i32 rowSpan, i32 colSpan); +void uiLargeLabel(const char *txt); -void uiGridDrawCells(const UIGrid grid); +bool uiLargeTextButton(const char *txt); -typedef enum UIFlexBoxFlag { - UI_FLEX_NONE = 0, - UI_FLEX_DIR_ROW = 0b1, - UI_FLEX_DIR_COLUMN = 0b10, - UI_FLEX_ALIGN_START = 0b100, - UI_FLEX_ALIGN_CENTER = 0b1000, - UI_FLEX_ALIGN_END = 0b10000, - UI_FLEX_JUSTIFY_START = 0b100000, - UI_FLEX_JUSTIFY_CENTER = 0b1000000, - UI_FLEX_JUSTIFY_END = 0b10000000, -} UIFlexBoxFlag; - -typedef struct FlexBoxDesc { - Rectangle window; - UIFlexBoxFlag flags; - f32 padding; -} FlexBoxDesc; - -void uiFlexBoxCalc(const FlexBoxDesc *desc, i32 numElements, ...); - -static f32 uiScale() { - return GetScreenHeight() / 720.0f; -} - - -#endif //PIXELDEFENSE_UI_H +#endif //PIXELDEFENSE_UI_WIDGETS_H