#include #include #define BZ_ENTRYPOINT #include #include "utils/buildings.h" #include "components.h" typedef enum Layers { LAYER_TERRAIN = 0, LAYER_FOLIAGE, LAYER_TREES, LAYER_TREES2, LAYER_BUILDINGS, LAYER_BUILDING_OWNER, } Layers; typedef enum ObjectGroup { OBJECTS_GAME = 0, OBJECTS_ENTITIES, } ObjectGroup; typedef struct Game { Camera2D camera; BzTileset terrainTileset; BzTileset buildingsTileset; BzTileMap map; } Game; static Game GAME = {}; bool handleGameObjects(BzTileObjectGroup *objectLayer, BzTileObject *objects, i32 objectCount) { for (i32 i = 0; i < objectLayer->objectCount; i++) { BzTileObject object = objectLayer->objects[i]; if (bzStringDefaultHash("camera") == object.id) { printf("Got camera\n"); GAME.camera.target.x = object.shape.x; GAME.camera.target.y = object.shape.y; } } return true; } ecs_entity_t *entityMap = NULL; bool prepareBuildings(BzTileLayer *layer, BzTile *data, i32 dataCount) { ECS_COMPONENT(ECS, TilePosition); ECS_COMPONENT(ECS, TileSize); entityMap = bzCalloc(sizeof(*entityMap), layer->width * layer->height); BzTileMap *map = &GAME.map; BzTileLayer *ownershipLayer = layer; BzTileLayer *buildingLayer = bzTileMapGetLayer(map, LAYER_BUILDINGS); BzTileset *buildingTileset = bzTileLayerGetTileset(map, buildingLayer); BzTile *buildingData = buildingLayer->data; for (i32 y = 0; y < layer->height; y++) { for (i32 x = 0; x < layer->width; x++) { BzTile ownerTile = data[y * layer->width + x]; BzTile buildingRawTile = buildingData[y * layer->width + x]; BzTile buildingTile = bzTilesetGetTile(buildingTileset, buildingRawTile); buildingTile = getTileBuilding(buildingTile); if (buildingTile == BUILDINGS_NONE || ownerTile == 0) continue; // We have a building TileSize size = {}; getBuildingSize(buildingTile, &size.w, &size.h); bzTileLayerSetTile(ownershipLayer, 0, x, y, size.w, size.h); bzLogInfo("Got size: %2d %2d", size.w, size.h); ecs_entity_t e = ecs_new_id(ECS); ecs_set(ECS, e, TilePosition, {.x=x, .y=y}); ecs_set(ECS, e, TileSize, {.w=size.w, .h=size.h}); for (i32 yIdx = y; yIdx < y + size.h; yIdx++) { for (i32 xIdx = x; xIdx < x + size.w; xIdx++) { entityMap[yIdx * layer->width + xIdx] = e; } } //bzTileMapUpdateCollider(&GAME.map, x, y); } } return true; } bool canBuildOn(BzTileMap *map, i32 tileX, i32 tileY, i32 sizeX, i32 sizeY) { // Ensure that it is within the map if (tileX < 0 || tileX + sizeX > map->width || tileY < 0 || tileY + sizeY > map->height) return false; Rectangle buildArea = {tileX * map->tileWidth, tileY * map->tileHeight, sizeX * map->tileWidth, sizeY * map->tileHeight}; // Need to check neighbour tiles tileX -= 1; tileY -= 1; sizeX += 2; sizeY += 2; BzTileLayer *buildLayer = bzTileMapGetLayer(map, LAYER_BUILDINGS); BzTileset *tileset = bzTileLayerGetTileset(map, buildLayer); for (i32 y = tileY; y < tileY + sizeY; y++) { for (i32 x = tileX; x < tileX + sizeX; x++) { if (x != tileX && x != tileX + sizeX - 1 && y != tileY && y != tileY + sizeY - 1) { // Without padding BzTile tile = bzTileLayerGetTile(buildLayer, x, y); tile = bzTilesetGetTile(tileset, tile); tile = getTileBuilding(tile); if (tile == BUILDINGS_ROAD) return false; } BzTileCollider collider = bzTileMapGetCollider(map, x, y); f32 posX = x * map->tileWidth; f32 posY = y * map->tileHeight; for (int i = 0; i < BZ_MAP_COLLIDER_DEPTH; i++) { BzTileShape shape = collider.shapes[i]; shape.x += posX; shape.y += posY; switch (shape.type) { case BZ_TILE_SHAPE_NONE: case BZ_TILE_SHAPE_POINT: break; case BZ_TILE_SHAPE_RECT: { Rectangle shapeRec = {shape.x, shape.y, shape.sizeX, shape.sizeY}; if (CheckCollisionRecs(buildArea, shapeRec)) return false; break; } case BZ_TILE_SHAPE_ELLIPSE: { Vector2 pos = {shape.x, shape.y}; f32 radius = (shape.sizeX + shape.sizeY) * 0.5f; if (CheckCollisionCircleRec(pos, radius, buildArea)) return false; break; } } } } } return true; } bool init(Game *game) { 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->terrainTileset = bzTilesetCreate( &(BzTilesetDesc) { .path="assets/terrain.tsj", .texturePath="assets/terrain.png" }); game->buildingsTileset = bzTilesetCreate(&(BzTilesetDesc) { .path="assets/buildings.tsj", .texturePath="assets/buildings.png" }); game->map = bzTileMapCreate(&(BzTileMapDesc) { .path="assets/maps/test.tmj", .tilesets[0]=game->terrainTileset, .tilesets[1]=game->buildingsTileset, .layers[LAYER_TERRAIN]=(BzTileLayerDesc) {"Terrain"}, .layers[LAYER_FOLIAGE]=(BzTileLayerDesc) {"Foliage"}, .layers[LAYER_TREES]=(BzTileLayerDesc) {"Trees"}, .layers[LAYER_TREES2]=(BzTileLayerDesc) {"TreesS"}, .layers[LAYER_BUILDINGS]=(BzTileLayerDesc) {"Buildings"}, .layers[LAYER_BUILDING_OWNER]=(BzTileLayerDesc) {"BuildingOwnership", BZ_TILE_LAYER_SKIP_RENDER}, .objectGroups[OBJECTS_GAME]=(BzTileObjectsDesc) {"Game"}, .objectGroups[OBJECTS_ENTITIES]=(BzTileObjectsDesc ) {"Entities"} }); bzTileMapOverrideLayer(&game->map, LAYER_BUILDING_OWNER, prepareBuildings); bzTileMapOverrideObjectGroup(&game->map, OBJECTS_GAME, handleGameObjects); bzTileMapOverrideObjectGroup(&game->map, OBJECTS_ENTITIES, BZ_TILE_OBJECTS_CLEAR); return true; } void deinit(Game *game) { bzTilesetDestroy(&game->terrainTileset); bzTilesetDestroy(&game->buildingsTileset); bzTileMapDestroy(&game->map); } int sizeX = 1; int sizeY = 1; void render(float dt, Game *game) { Camera2D *camera = &game->camera; if (!nk_item_is_any_active(NK)) { if (IsKeyDown(KEY_W)) camera->target.y -= 20; if (IsKeyDown(KEY_S)) camera->target.y += 20; if (IsKeyDown(KEY_A)) camera->target.x -= 20; if (IsKeyDown(KEY_D)) camera->target.x += 20; if (IsKeyDown(KEY_Q)) camera->rotation--; if (IsKeyDown(KEY_E)) camera->rotation++; camera->zoom += ((float) GetMouseWheelMove() * 0.05f); } BeginMode2D(*camera); ClearBackground(RAYWHITE); bzTileMapDraw(&game->map); bzTileMapDrawColliders(&game->map); Vector2 worldPos = GetScreenToWorld2D(GetMousePosition(), game->camera); int tileX = (int) worldPos.x / 16; int tileY = (int) worldPos.y / 16; bool canPlace = canBuildOn(&game->map, tileX, tileY, sizeX, sizeY); Color placeColor = canPlace ? (Color) {0, 255, 0, 200} : (Color) {255, 0, 0, 200}; DrawRectangleLines(tileX * 16, tileY * 16, sizeX * 16, sizeY * 16, placeColor); EndMode2D(); if (nk_begin(NK, "DebugMenu", nk_rect(50, 50, 220, 220), NK_WINDOW_BORDER |NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE | NK_WINDOW_TITLE | NK_WINDOW_MINIMIZABLE)) { // fixed widget pixel width nk_layout_row_static(NK, 30, 80, 1); nk_labelf(NK, NK_TEXT_LEFT, "tileX: %d", tileX); nk_labelf(NK, NK_TEXT_LEFT, "tileY: %d", tileY); static char buf[256] = {}; nk_edit_string_zero_terminated(NK, NK_EDIT_FIELD, buf, sizeof(buf) - 1, nk_filter_default); nk_labelf(NK, NK_TEXT_LEFT, "x: %d", sizeX); nk_labelf(NK, NK_TEXT_LEFT, "y: %d", sizeY); nk_slider_int(NK, 0, &sizeX, 10, 1); nk_slider_int(NK, 0, &sizeY, 10, 1); } nk_end(NK); } 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->render = (BzAppRenderFunc) render; appDesc->userData = &GAME; appDesc->useNuklear = true; appDesc->useFlecs = true; return true; }