Files
PixelDefense/game/main.c

295 lines
9.6 KiB
C

#include <rlImGui.h>
#include "systems.h"
#include "utils/building_types.h"
#include "components.h"
#include "game_state.h"
#include "map_init.h"
#include "map_layers.h"
#include "buildings.h"
#include "pathfinding.h"
#include <time.h>
#include <raymath.h>
ECS_COMPONENT_DECLARE(Game);
ECS_COMPONENT_DECLARE(InputState);
ecs_world_t *ECS = NULL;
static ecs_entity_t renderCollidersSystem;
static ecs_entity_t renderDebugPathSystem;
bool init(void *userData);
void deinit(void *userData);
void update(float dt, void *userData);
void render(float dt, void *userData);
void imguiRender(float dt, void *userData);
bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) {
appDesc->width = 1280;
appDesc->height = 720;
appDesc->title = "PixelDefense";
appDesc->fps = 60;
appDesc->init = (BzAppInitFunc) init;
appDesc->deinit = (BzAppDeinitFunc) deinit;
appDesc->update = (BzAppUpdateFunc) update;
appDesc->render = (BzAppRenderFunc) render;
appDesc->imguiRender = (BzAppRenderFunc) imguiRender;
appDesc->userData = NULL;
return true;
}
bool init(void *userData) {
BZ_UNUSED(userData);
SetExitKey(0);
ECS = ecs_init();
initComponentIDs(ECS);
// For ecs explorer
//ecs_singleton_set(ECS, EcsRest, {0});
//ECS_IMPORT(ECS, FlecsMonitor);
ECS_COMPONENT_DEFINE(ECS, Game);
ecs_singleton_set(ECS, Game, {});
Game *game = ecs_singleton_get_mut(ECS, Game);
ECS_COMPONENT_DEFINE(ECS, InputState);
ecs_singleton_set(ECS, InputState, {});
InputState *input = ecs_singleton_get_mut(ECS, InputState);
input->LMB = MOUSE_LEFT_BUTTON;
input->RMB = MOUSE_RIGHT_BUTTON;
input->MMB = MOUSE_MIDDLE_BUTTON;
input->ESC = KEY_ESCAPE;
input->CLICK_LIMIT = 0.2f;
input->entities = bzArrayCreate(ecs_entity_t, 16);
input->unitPositions = bzArrayCreate(Position, 16);
// init pools
game->pools.pathData = bzObjectPoolCreate(&(BzObjectPoolDesc) {
.objectSize=sizeof(PathData),
.numObjects=512
});
int screenWidth = 1280;
int screenHeight = 720;
game->camera = (Camera2D){ 0 };
game->camera.target = (Vector2) {0, 0};
game->camera.offset = (Vector2) {screenWidth / 2.0f, screenHeight / 2.0f};
game->camera.rotation = 0.0f;
game->camera.zoom = 1.0f;
game->frameDuration = 0.16f;
game->terrainTileset = bzTilesetCreate( &(BzTilesetDesc) {
.path="assets/terrain.tsj",
.texturePath="assets/terrain.png"
});
game->buildingsTileset = bzTilesetCreate(&(BzTilesetDesc) {
.path="assets/buildings.tsj",
.texturePath="assets/buildings.png"
});
game->entitiesTileset = bzTilesetCreate(&(BzTilesetDesc) {
.path="assets/entities.tsj",
.texturePath="assets/entities.png"
});
game->map = bzTileMapCreate(&(BzTileMapDesc) {
.path="assets/maps/test.tmj",
.generateCollisionMap=true,
.tilesets[0]=game->terrainTileset,
.tilesets[1]=game->buildingsTileset,
.tilesets[2]=game->entitiesTileset,
.layers[LAYER_TERRAIN]=(BzTileLayerDesc) {"Terrain", .applyColliders=true},
.layers[LAYER_FOLIAGE]=(BzTileLayerDesc) {"Foliage"},
.layers[LAYER_TREES]=(BzTileLayerDesc) {"Trees"},
.layers[LAYER_TREES2]=(BzTileLayerDesc) {"TreesS"},
.layers[LAYER_BUILDINGS]=(BzTileLayerDesc) {"Buildings", .applyColliders=true},
.layers[LAYER_BUILDING_OWNER]=(BzTileLayerDesc) {"BuildingOwnership", BZ_TILE_LAYER_SKIP_RENDER},
.objectGroups[OBJECTS_GAME]=(BzTileObjectsDesc) {"Game"},
.objectGroups[OBJECTS_ENTITIES]=(BzTileObjectsDesc ) {"Entities"}
});
game->entityGrid = bzSpatialGridCreate(&(BzSpatialGridDesc) {
.maxWidth=game->map.width * game->map.tileWidth,
.maxHeight=game->map.height * game->map.tileHeight,
.cellWidth=game->map.tileWidth * 4,
.cellHeight=game->map.tileHeight * 4,
.userDataSize=sizeof(ecs_entity_t)
});
bzTileMapOverrideLayer(&game->map, LAYER_TREES, initTreesLayer);
bzTileMapOverrideLayer(&game->map, LAYER_TREES2, initTreesLayer);
bzTileMapOverrideLayer(&game->map, LAYER_BUILDING_OWNER, initBuildingsLayer);
bzTileMapOverrideObjectGroup(&game->map, OBJECTS_GAME, initGameObjectsLayer);
bzTileMapOverrideObjectGroup(&game->map, OBJECTS_ENTITIES, initEntityObjectsLayer);
ECS_OBSERVER(ECS, entitySpatialRemoved, EcsOnRemove, Position, SpatialGridID);
ECS_OBSERVER(ECS, pathRemoved, EcsOnRemove, Path);
ECS_SYSTEM(ECS, entityUpdateSpatialID, EcsOnUpdate, Position, Size, Velocity, SpatialGridID);
ECS_SYSTEM(ECS, entityUpdateKinematic, EcsOnUpdate, Position, Rotation, Velocity, AngularVelocity, SteeringOutput);
ECS_SYSTEM(ECS, entityFollowPath, EcsOnUpdate, Position, Rotation, Velocity, AngularVelocity, SteeringOutput, Path);
ECS_SYSTEM(ECS, renderDebugPath, EcsOnUpdate, Path);
ECS_SYSTEM(ECS, renderTerrain, EcsOnUpdate, Position, Size, Rotation, TextureRegion, TextureTerrain);
ECS_SYSTEM(ECS, renderBuildings, EcsOnUpdate, Position, Size, Rotation, TextureRegion, TextureBuildings);
ECS_SYSTEM(ECS, renderEntities, EcsOnUpdate, Position, Size, Rotation, TextureRegion, TextureEntities);
ECS_SYSTEM(ECS, renderColliders, EcsOnUpdate, Position, Size);
renderDebugPathSystem = renderDebugPath;
renderCollidersSystem = renderColliders;
game->debugDraw.mapColliders = true;
game->debugDraw.spatialGrid = true;
game->debugDraw.path = true;
return true;
}
void deinit(void *userData) {
BZ_UNUSED(userData);
Game *game = ecs_singleton_get_mut(ECS, Game);
InputState *input = ecs_singleton_get_mut(ECS, InputState);
bzTileMapDestroy(&game->map);
bzTilesetDestroy(&game->terrainTileset);
bzTilesetDestroy(&game->buildingsTileset);
bzTilesetDestroy(&game->entitiesTileset);
Game gameCopy = *game;
InputState inputCopy = *input;
ecs_fini(ECS);
ECS = NULL;
bzArrayDestroy(inputCopy.entities);
bzArrayDestroy(inputCopy.unitPositions);
bzObjectPoolDestroy(gameCopy.pools.pathData);
bzSpatialGridDestroy(gameCopy.entityGrid);
}
void update(float dt, void *userData) {
BZ_UNUSED(userData);
char titleBuf[32];
snprintf(titleBuf, sizeof(titleBuf), "FPS: %d | %.2f ms", GetFPS(), GetFrameTime() * 1000);
SetWindowTitle(titleBuf);
updatePlayerInput(NULL);
}
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);
bzTileMapDraw(&game->map);
drawPlayerInputUI(NULL);
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);
EndMode2D();
}
void imguiRender(float dt, void *userData) {
BZ_UNUSED(userData);
Game *game = ecs_singleton_get_mut(ECS, Game);
InputState *input = ecs_singleton_get_mut(ECS, InputState);
igSetNextWindowSize((ImVec2){300, 400}, ImGuiCond_FirstUseEver);
igBegin("Debug Menu", NULL, 0);
igText("Num paths from pool available: %llu", bzObjectPoolCalcNumFree(game->pools.pathData));
const char *inputState = "NONE";
switch (input->state) {
case INPUT_NONE:
inputState = "NONE";
break;
case INPUT_BUILDING:
inputState = "BUILDING";
break;
case INPUT_SELECTED_UNITS:
inputState = "SELECTED_UNITS";
break;
case INPUT_SELECTED_OBJECT:
inputState = "SELECTED_OBJECT";
break;
case INPUT_SELECTED_BUILDING:
inputState = "SELECTED_BUILDING";
break;
}
igText("Input state: %s", inputState);
if (igCollapsingHeader_TreeNodeFlags("Selection", 0)) {
switch (input->state) {
case INPUT_SELECTED_UNITS:
igText("Selected units:");
for (i32 i = 0; i < bzArraySize(input->entities); i++) {
igText("\t%llu", bzArrayGet(input->entities, i));
}
break;
case INPUT_SELECTED_OBJECT:
break;
case INPUT_SELECTED_BUILDING:
break;
default:
igText("NONE");
break;
}
}
if (igCollapsingHeader_TreeNodeFlags("Resources", 0)) {
igText("Wood: %lld", game->resources.wood);
igText("Iron: %lld", game->resources.iron);
igText("Food: %lld", game->resources.food);
igText("Gold: %lld", game->resources.gold);
igText("Population: %lld", game->resources.pop);
}
if (igCollapsingHeader_TreeNodeFlags("BuildMenu", 0)) {
for (int i = 0; i < BUILDINGS_COUNT; i++) {
if (igSelectable_Bool(getBuildingStr(i), input->building == i, 0, (ImVec2){0, 0}))
input->building = i;
}
if (input->building)
input->state = INPUT_BUILDING;
}
if (igCollapsingHeader_TreeNodeFlags("DebugDraw", 0)) {
igCheckbox("map colliders", &game->debugDraw.mapColliders);
igCheckbox("entity colliders", &game->debugDraw.entityColliders);
igCheckbox("spatial grid", &game->debugDraw.spatialGrid);
igCheckbox("path", &game->debugDraw.path);
}
if (igCollapsingHeader_TreeNodeFlags("Entities", 0)) {
igSliderFloat("Frame duration", &game->frameDuration, 0.0f, 1.0f, NULL, 0);
}
igEnd();
}