Files
PixelDefense/game/systems/s_ui.c

362 lines
13 KiB
C

#include "systems.h"
#include "../game_state.h"
#include "../input.h"
#include "../map_init.h"
#include "../ui_widgets.h"
#include "../building_factory.h"
#include "../utils.h"
#include "../entity_factory.h"
void drawGameUI(Game *game, f32 dt) {
// UI
i32 width = GetScreenWidth();
i32 height = GetScreenHeight();
bzUIBegin(UI, width, height);
bzUISetParentLayout(UI, (BzUILayout) {
.type = BZ_UI_LAYOUT_FLEX_BOX,
.flags = BZ_UI_FLEX_DIR_COLUMN |
BZ_UI_FLEX_JUSTIFY_SPACE_BETWEEN |
BZ_UI_FLEX_ALIGN_CENTER
});
// top bar
BzUINode *topBar = bzUINodeMake(UI, bzUIGetUniqueKey(UI), &(BzUINodeDesc) {
.semanticSize[BZ_UI_AXIS_X] = {BZ_UI_SIZE_AS_PARENT},
.semanticSize[BZ_UI_AXIS_Y] = {BZ_UI_SIZE_CHILD_MAX},
});
bzUIPushParent(UI, topBar);
bzUISetParentLayout(UI, (BzUILayout) {
.type = BZ_UI_LAYOUT_FLEX_BOX,
.flags = BZ_UI_FLEX_DIR_ROW |
BZ_UI_FLEX_ALIGN_CENTER |
BZ_UI_FLEX_JUSTIFY_START
});
Color topBarBG = {0, 0, 0, 50};
bzUISetBackgroundStyle(UI, topBar, (BzUIBackgroundStyle) {
.normal = topBarBG,
.hover = topBarBG,
.active = topBarBG,
});
BzTileset *tileset = &game->tileset;
Rectangle woodRec = bzTilesetGetTileRegion(tileset, getEntityTile(ENTITY_WOOD));
Rectangle foodRec = bzTilesetGetTileRegion(tileset, getEntityTile(ENTITY_APPLE));
Rectangle goldRec = bzTilesetGetTileRegion(tileset, getEntityTile(ENTITY_GOLD));
Rectangle popRec = bzTilesetGetTileRegion(tileset, getEntityTile(ENTITY_POP));
PlayerResources resources = game->playerResources[game->player];
uiGameResCount(resources.wood, -1, woodRec, tileset->tiles);
uiGameResCount(resources.food, -1, foodRec, tileset->tiles);
uiGameResCount(resources.gold, -1, goldRec, tileset->tiles);
uiGameResCount(resources.pop, resources.popCapacity, popRec, tileset->tiles);
bzUIPopParent(UI);
InputState *input = ecs_singleton_get_mut(ECS, InputState);
const BuildingType buildingOrder[] = {
BUILDING_WHEAT_0,
BUILDING_HOUSE_05,
BUILDING_GRANARY,
BUILDING_WAREHOUSE,
BUILDING_MARKET,
BUILDING_MILL,
BUILDING_BARRACKS,
BUILDING_TOWER
};
const char *buildingNames[] = {
"Farm",
"House",
"Granary",
"Warehouse",
"Market",
"Mill",
"Barracks",
"Tower"
};
i32 numBuildings = sizeof(buildingOrder) / sizeof(*buildingOrder);
BzUINode *menu = bzUINodeMake(UI, bzUIGetUniqueKey(UI), &(BzUINodeDesc) {
.semanticSize[BZ_UI_AXIS_X] = {BZ_UI_SIZE_REL_PARENT, 0.98f},
.semanticSize[BZ_UI_AXIS_Y] = {BZ_UI_SIZE_PIXELS, 140.0f * uiGetScale()},
.margin[BZ_UI_AXIS_Y + 2] = 10.0f * uiGetScale(),
});
bzUISetBackgroundStyle(UI, menu, (BzUIBackgroundStyle) {
.normal = DARKBROWN,
.hover = DARKBROWN,
.active = DARKBROWN,
.roundness = 0.2f * uiGetScale()
});
bzUISetBorderStyle(UI, menu, (BzUIBorderStyle) {
.roundness = 0.2f * uiGetScale(),
.thickness = 6.0f * uiGetScale(),
.normal = BLACK,
.hover = BLACK,
.active = BLACK,
});
bzUIPushParent(UI, menu);
bzUISetParentLayout(UI, (BzUILayout) {
.type = BZ_UI_LAYOUT_FLEX_BOX,
.flags = BZ_UI_FLEX_DIR_ROW | BZ_UI_FLEX_ALIGN_CENTER | BZ_UI_FLEX_JUSTIFY_START
});
Texture2D tex = tileset->tiles;
switch (input->state) {
case INPUT_NONE:
case INPUT_BUILDING: {
for (i32 i = 0; i < numBuildings; i++) {
BuildingType buildingType = buildingOrder[i];
Rectangle rec = bzTilesetGetTileRegion(tileset, getBuildingTile(buildingType));
// Adjust for size
i32 sizeX = 1, sizeY = 1;
getBuildingSize(buildingType, &sizeX, &sizeY);
rec.width *= sizeX;
rec.height *= sizeY;
bool selected = input->building == buildingOrder[i];
PlayerResources *res = &game->playerResources[game->player];
bool canAfford = canAffordBuilding(buildingType, *res);
uiGameBuild(buildingNames[i], rec, tex, canAfford, &selected);
if (!canAfford)
selected = false;
if (selected) {
input->building = buildingOrder[i];
input->state = INPUT_BUILDING;
}
}
break;
}
case INPUT_SELECTED_UNITS: {
typedef struct UnitPair {
i32 count;
Rectangle rec;
} UnitPair;
UnitPair unitTypes[ENTITY_COUNT] = {0, };
ecs_iter_t it = ecs_query_iter(ECS, input->queries.selected);
while (ecs_query_next(&it)) {
for (i32 i = 0; i < it.count; i++) {
ecs_entity_t entity = it.entities[i];
if (!ecs_has(ECS, entity, Unit))
continue;
Unit unit = *ecs_get(ECS, entity, Unit);
if (unitTypes[unit.unitType].count == 0) {
unitTypes[unit.unitType].rec = ecs_get(ECS, entity, TextureRegion)->rec;
}
unitTypes[unit.unitType].count++;
}
}
i32 filterUnit = -1;
for (i32 i = 0; i < ENTITY_COUNT; i++) {
i32 count = unitTypes[i].count;
if (count == 0) continue;
const char *label = getEntityStr(i);
if (uiGameUnit(label, count, unitTypes[i].rec, tex)) {
filterUnit = i;
}
}
if (filterUnit != -1) {
ecs_defer_begin(ECS);
it = ecs_query_iter(ECS, input->queries.selected);
while (ecs_query_next(&it)) {
for (i32 i = 0; i < it.count; i++) {
ecs_entity_t entity = it.entities[i];
if (ecs_has(ECS, entity, Unit)) {
Unit unit = *ecs_get(ECS, entity, Unit);
if (unit.unitType == filterUnit)
continue;
}
ecs_remove(ECS, entity, Selected);
}
}
ecs_defer_end(ECS);
}
break;
}
case INPUT_SELECTED_OBJECT:
break;
case INPUT_SELECTED_BUILDING: {
ecs_iter_t it = ecs_query_iter(ECS, input->queries.selected);
ecs_entity_t buildingEntity = 0;
while (ecs_query_next(&it)) {
for (i32 i = 0; i < it.count; i++) {
buildingEntity = it.entities[i];
}
}
if (!buildingEntity || !ecs_has(ECS, buildingEntity, Building))
break;
Building building = *ecs_get(ECS, buildingEntity, Building);
Vector2 placePos = {
(building.pos.x + building.size.x * 0.5f) * 16.0f,
(building.pos.y + building.size.y + 0.5f) * 16.0f
};
placePos.x += GetRandomValue(-16, 16);
placePos.y += GetRandomValue(-4, 4);
PlayerResources *playerRes = &game->playerResources[game->player];
bool canRecruit = ecs_has(ECS, buildingEntity, BuildingRecruitInfo);
if (canRecruit) {
BuildingRecruitInfo *info = ecs_get_mut(ECS, buildingEntity, BuildingRecruitInfo);
for (i32 i = 0; i < info->numSlots; i++) {
BuildingRecruitSlot *slot = &info->slots[i];
const char *label = getEntityStr(slot->entityType);
Rectangle rec = getTextureRect(getEntityTile(slot->entityType));
bool selected = false;
bool canAfford = canAffordEntity(slot->entityType, *playerRes);
canAfford &= playerRes->pop < playerRes->popCapacity;
f32 progress = slot->elapsed / slot->recruitTime;
if (slot->numRecruiting <= 0) progress = -1.0f;
uiGameRecruit(label, rec, tex, slot->numRecruiting, progress,
canAfford, &selected);
if (selected) {
i32 res[RES_COUNT] = {0,};
getEntityCost(slot->entityType, res);
playerRes->pop++;
playerRes->food -= res[RES_FOOD];
playerRes->wood -= res[RES_WOOD];
playerRes->gold -= res[RES_GOLD];
slot->numRecruiting++;
}
}
}
break;
}
default:;
}
bzUIEnd(UI);
}
void drawPauseUI(Game *game, f32 dt) {
i32 width = GetScreenWidth();
i32 height = GetScreenHeight();
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
});
uiMainMenuLabel("Paused");
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 (uiMainMenuButton("Resume", true)) {
setScreen(game, SCREEN_GAME);
}
if (uiMainMenuButton("Exit", true)) {
setScreen(game, SCREEN_MAIN_MENU);
unloadMap(game);
loadMap(game, "assets/maps/main_menu_01.tmj");
}
bzUIPopParent(UI);
bzUIEnd(UI);
}
void drawMainMenuUI(Game *game, f32 dt) {
i32 width = GetScreenWidth();
i32 height = GetScreenHeight();
game->camera.zoom = 3 * uiGetScale();
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
});
uiMainMenuLabel("Pixel Defense");
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 (uiMainMenuButton("Play", true)) {
setScreen(game, SCREEN_GAME);
unloadMap(game);
loadMap(game, "assets/maps/tree_test.tmj");
//loadMap(game, "assets/maps/entity_test.tmj");
//loadMap(game, "assets/maps/map_01.tmj");
}
if (uiMainMenuButton("Settings", true)) {
setScreen(game, SCREEN_SETTINGS);
}
if (uiMainMenuButton("Exit", true)) {
bzGameExit();
}
bzUIPopParent(UI);
bzUIEnd(UI);
}
void drawSettingsUI(Game *game, f32 dt) {
i32 width = GetScreenWidth();
i32 height = GetScreenHeight();
game->camera.zoom = 3 * uiGetScale();
bzUIBegin(UI, width, height);
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
});
bzUIPushDiv(UI, (BzUISize) { BZ_UI_SIZE_REL_PARENT, 0.8f},
(BzUISize) { BZ_UI_SIZE_REL_PARENT, 0.8f});
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
});
static GameData gameData = {};
if (game->screenPrevFrame != SCREEN_SETTINGS)
gameData = game->gameData;
uiSettingsLabel("Video");
uiSettingsCheckbox("Fullscreen", &gameData.options.fullscreen);
uiSettingsCheckbox("Limit FPS", &gameData.options.limitFps);
//uiSettingsCheckbox("V-Sync", &opts.vsync);
uiSettingsLabel("Audio");
uiSettingsSlider("Master: ", &gameData.options.master);
uiSettingsSlider("Music: ", &gameData.options.music);
uiSettingsSlider("Sound: ", &gameData.options.sound);
bzUIPopParent(UI);
bzUIPushDiv(UI, (BzUISize) {BZ_UI_SIZE_REL_PARENT, 0.8f},
(BzUISize) {BZ_UI_SIZE_REL_PARENT, 0.2f});
bzUISetParentLayout(UI, (BzUILayout) {
.type = BZ_UI_LAYOUT_FLEX_BOX,
.flags = BZ_UI_FLEX_DIR_ROW | BZ_UI_FLEX_JUSTIFY_CENTER | BZ_UI_FLEX_ALIGN_CENTER
});
if (uiSettingsButton("Back")) {
setScreen(game, SCREEN_MAIN_MENU);
}
if (uiSettingsButton("Reset")) {
gameData = game->gameData;
}
if (uiSettingsButton("Apply")) {
serializeGameData(GAME_DATA_SAVE_PATH, &gameData);
game->gameData = gameData;
applyOptions(&game->gameData.options);
setScreen(game, SCREEN_MAIN_MENU);
}
bzUIEnd(UI);
}