From 783db8ba908da5e8330a8dc6630ff9407c30e709 Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Wed, 22 Nov 2023 09:56:20 +0100 Subject: [PATCH] Add input logic for building --- game/buildings.c | 26 +++--- game/game_state.h | 38 +++++--- game/main.c | 219 ++++++++++++++++++++++++++++++++++++--------- game/pathfinding.h | 3 + 4 files changed, 214 insertions(+), 72 deletions(-) diff --git a/game/buildings.c b/game/buildings.c index 215a27d..6fc21b8 100644 --- a/game/buildings.c +++ b/game/buildings.c @@ -18,28 +18,22 @@ bool canPlaceBuilding(BzTileMap *map, BuildingType type, BzTile tileX, BzTile ti sizeX * map->tileWidth, sizeY * map->tileHeight}; // Need to check neighbour tiles - tileX -= 1; - tileY -= 1; - sizeX += 2; - sizeY += 2; + //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; - } - f32 posX = x * map->tileWidth; - f32 posY = y * map->tileHeight; - if (bzTileMapHasCollision(map, posX, posY)) { + BzTile tile = bzTileLayerGetTile(buildLayer, x, y); + tile = bzTilesetGetTile(tileset, tile); + tile = getTileBuilding(tile); + if (tile == BUILDINGS_ROAD) + return false; + if (bzTileMapHasCollision(map, x, y)) { return false; } } diff --git a/game/game_state.h b/game/game_state.h index 3a7158f..a76c724 100644 --- a/game/game_state.h +++ b/game/game_state.h @@ -4,12 +4,34 @@ #include #include -typedef enum InputState { +typedef enum InputType { INPUT_NONE, - INPUT_PLACING, - INPUT_DRAGGING, + INPUT_BUILDING, INPUT_SELECTED_UNITS, INPUT_SELECTED_OBJECT, + INPUT_SELECTED_BUILDING, +} InputType; + +typedef struct InputState { + InputType state; + // Input mapping + MouseButton LMB; + MouseButton RMB; + MouseButton MMB; + KeyboardKey ESC; + f32 DRAG_LIMIT; + // Common + Vector2 mouseDown; + Vector2 mouseDownWorld; + f32 mouseDownElapsed; + // INPUT_BUILDING + int building; + bool buildingCanPlace; + TilePosition buildingPos; + TileSize buildingSize; + // SELECTED_UNITS + // SELECTED_OBJECTS + // SELECTED_BUILDING } InputState; typedef struct Game { @@ -21,15 +43,7 @@ typedef struct Game { BzSpatialGrid *entityGrid; f32 frameDuration; ecs_entity_t entity; - struct { - InputState state; - int building; - bool buildingCanPlace; - TilePosition buildingPos; - TileSize buildingSize; - Vector2 mouseDown; - f32 mouseDownElapsed; - } input; + InputState input; struct { i64 wood; i64 iron; diff --git a/game/main.c b/game/main.c index 801903a..2167b69 100644 --- a/game/main.c +++ b/game/main.c @@ -11,6 +11,7 @@ #include "pathfinding.h" #include +#include ECS_COMPONENT_DECLARE(Game); @@ -45,7 +46,7 @@ bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) { bool init(void *userData) { BZ_UNUSED(userData); - SetExitKey(KEY_ESCAPE); + SetExitKey(0); ECS = ecs_init(); @@ -59,6 +60,13 @@ bool init(void *userData) { ecs_singleton_set(ECS, Game, {}); Game *game = ecs_singleton_get_mut(ECS, Game); + game->input.LMB = MOUSE_LEFT_BUTTON; + game->input.RMB = MOUSE_RIGHT_BUTTON; + game->input.MMB = MOUSE_MIDDLE_BUTTON; + + game->input.ESC = KEY_ESCAPE; + + // init pools game->pools.pathData = bzObjectPoolCreate(&(BzObjectPoolDesc) { .objectSize=sizeof(PathData), @@ -141,6 +149,7 @@ bool init(void *userData) { renderCollidersSystem = renderColliders; game->debugDraw.mapColliders = true; + game->debugDraw.spatialGrid = true; return true; } @@ -187,50 +196,104 @@ void update(float dt, void *userData) { game->camera.zoom += ((float) GetMouseWheelMove() * 0.05f); BzTileMap *map = &game->map; - Vector2 worldPos = GetScreenToWorld2D(GetMousePosition(), game->camera); - int tileX = (int) worldPos.x / map->tileWidth; - int tileY = (int) worldPos.y / map->tileHeight; - - if (IsKeyPressed(KEY_ESCAPE) || IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) { - game->input.state = INPUT_NONE; - } - if (game->input.state == INPUT_NONE) { - game->input.building = 0; + InputState *input = &game->input; + if (IsMouseButtonPressed(input->LMB)) { + game->input.mouseDown = GetMousePosition(); + game->input.mouseDownWorld = GetScreenToWorld2D(game->input.mouseDown, game->camera); + game->input.mouseDownElapsed = 0; + } else if (IsMouseButtonDown(input->LMB)) { + game->input.mouseDownElapsed += dt; } - switch (game->input.state) { + switch (input->state) { case INPUT_NONE: - break; - case INPUT_PLACING: - BZ_ASSERT(game->input.building); - BzTile sizeX = 0, sizeY = 0; - getBuildingSize(game->input.building, &sizeX, &sizeY); - bool canPlace = canPlaceBuilding(&game->map, game->input.building, tileX, tileY); - if (canPlace && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - placeBuilding(&game->map, game->input.building, tileX, tileY); + if (IsMouseButtonUp(input->LMB)) { + } - game->input.buildingCanPlace = canPlace; - game->input.buildingPos = (TilePosition) {tileX, tileY}; - game->input.buildingSize = (TileSize) {sizeX, sizeY}; break; - case INPUT_DRAGGING: + case INPUT_BUILDING: + if (IsKeyPressed(input->ESC) || + IsMouseButtonPressed(input->RMB) || + input->building == 0) { + input->state = INPUT_NONE; + input->building = 0; + break; + } + Vector2 worldPos = GetScreenToWorld2D(GetMousePosition(), game->camera); + int tileX = (int) worldPos.x / map->tileWidth; + int tileY = (int) worldPos.y / map->tileHeight; + BZ_ASSERT(input->building); + BzTile sizeX = 0, sizeY = 0; + getBuildingSize(input->building, &sizeX, &sizeY); + bool canPlace = canPlaceBuilding(&game->map, input->building, tileX, tileY); + if (canPlace && IsMouseButtonDown(input->LMB)) { + placeBuilding(&game->map, input->building, tileX, tileY); + } + input->buildingCanPlace = canPlace; + input->buildingPos = (TilePosition) {tileX, tileY}; + input->buildingSize = (TileSize) {sizeX, sizeY}; break; case INPUT_SELECTED_UNITS: break; case INPUT_SELECTED_OBJECT: break; + case INPUT_SELECTED_BUILDING: + break; } - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - game->input.mouseDown = GetMousePosition(); - game->input.mouseDownElapsed = 0; - bzLogInfo("Pressed"); - } else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - game->input.mouseDownElapsed += dt; - bzLogInfo("Down: %.2f", game->input.mouseDownElapsed); - } else if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - bzLogInfo("Released"); + +} + +static bool isUnitObstructed(f32 x, f32 y, BzTileMap *map) { + return bzTileMapHasCollision(map, x / map->tileWidth, y / map->tileHeight); +} + +static bool canPlaceUnit(Vector2 pos, f32 space, BzTileMap *map) { + for (i32 y = -1; y <= 1; y++) { + for (i32 x = -1; x <= 1; x++) { + if (isUnitObstructed(pos.x + x * space, pos.y + y * space, map)) + return false; + } } + + return true; +} + +static void placeUnits(i32 numUnits, f32 unitSpacing, Vector2 start, Vector2 end, BzTileMap *map, Vector2 **outPlaces) { + BZ_UNUSED(outPlaces); + + f32 angle = Vector2Angle(start, end); + + Vector2 size = {Vector2Distance(start, end), 20.0f}; + Rectangle rec = {start.x, start.y, size.x, size.y}; + Color color = RED; + color.a = 80; + + Vector2 pos = Vector2Zero(); + pos.x = unitSpacing; + + for (i32 i = 0; i < numUnits; i++) { + if (pos.x + unitSpacing * 2.0f > size.x) { + pos.x = unitSpacing; + pos.y += unitSpacing * 2.0f; + } + Vector2 unitPos = Vector2Add(start, Vector2Rotate(pos, angle)); + Color color = ORANGE; + if (!canPlaceUnit(unitPos, 4.0f, map)) { + color = RED; + color.a = 80; + i--; + } else { + bzArrayPush(*outPlaces, unitPos); + } + DrawCircle(unitPos.x, unitPos.y, 2.0f, color); + pos.x += unitSpacing * 2.0f; + } + + static char buf[128]; + snprintf(buf, sizeof(buf), "Num units: %d", bzArraySize(*outPlaces)); + DrawText(buf, 800, 200, 32, BLUE); + } void render(float dt, void *userData) { @@ -242,24 +305,72 @@ void render(float dt, void *userData) { bzTileMapDraw(&game->map); - if (game->input.building) { - Color placeColor = game->input.buildingCanPlace ? - (Color) {0, 255, 0, 200} : - (Color) {255, 0, 0, 200}; + InputState *input = &game->input; + switch (input->state) { + case INPUT_NONE: + break; + case INPUT_BUILDING: { + Color placeColor = game->input.buildingCanPlace ? + (Color) {0, 255, 0, 200} : + (Color) {255, 0, 0, 200}; - BzTile width = game->map.tileWidth; - BzTile height = game->map.tileHeight; - DrawRectangleLines(game->input.buildingPos.x * width, - game->input.buildingPos.y * height, - game->input.buildingSize.sizeX * width, - game->input.buildingSize.sizeY * height, placeColor); + BzTile width = game->map.tileWidth; + BzTile height = game->map.tileHeight; + DrawRectangleLines(game->input.buildingPos.x * width, + game->input.buildingPos.y * height, + game->input.buildingSize.sizeX * width, + game->input.buildingSize.sizeY * height, placeColor); + break; + } + case INPUT_SELECTED_UNITS: + break; + case INPUT_SELECTED_OBJECT: + break; + case INPUT_SELECTED_BUILDING: + break; } - +#if 0 Vector2 worldPos = GetScreenToWorld2D(GetMousePosition(), game->camera); int tileX = (int) worldPos.x / 16; int tileY = (int) worldPos.y / 16; + i32 numUnits = 240; + static Vector2 *units = NULL; + if (!units) units = bzArrayCreate(Vector2, 3); + if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && game->input.mouseDownElapsed > 0.1) { + Vector2 start = game->input.mouseDownWorld; + Vector2 end = worldPos; + //bzArrayClear(units); + placeUnits(numUnits, 3.5f, start, end, &game->map, &units); + if (start.x > end.x) { + f32 tmp = start.x; + start.x = end.x; + end.x = tmp; + } + if (start.y > end.y) { + f32 tmp = start.y; + start.y = end.y; + end.y = tmp; + } + Vector2 size = Vector2Subtract(end, start); + + DrawRectangleLines(start.x, start.y, size.x, size.y, RED); + + BzSpatialGridIter it = bzSpatialGridIter(game->entityGrid, start.x, start.y, size.x, size.y); + i32 count = 0; + while (bzSpatialGridQueryNext(&it)) { + ecs_entity_t *e = it.data; + count++; + } + bzLogInfo("%d", count); + + //bzArrayClear(units); + //placeUnits(numUnits, 3.5f, start, end, &game->map, &units); + + + } + static PathNode *heap = NULL; if (!heap) @@ -287,6 +398,7 @@ void render(float dt, void *userData) { timeSpent *= 1000; bzLogInfo("A* took: %.3fms", timeSpent); } +#endif ecs_progress(ECS, dt); ecs_enable(ECS, renderDebugPathSystem, game->debugDraw.path); @@ -305,6 +417,25 @@ void imguiRender(float dt, void *userData) { igSetNextWindowSize((ImVec2){300, 400}, ImGuiCond_FirstUseEver); igBegin("Debug Menu", NULL, 0); + const char *inputState = "NONE"; + switch (game->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)) { } @@ -321,7 +452,7 @@ void imguiRender(float dt, void *userData) { game->input.building = i; } if (game->input.building) - game->input.state = INPUT_PLACING; + game->input.state = INPUT_BUILDING; } if (igCollapsingHeader_TreeNodeFlags("DebugDraw", 0)) { diff --git a/game/pathfinding.h b/game/pathfinding.h index 7ee0b56..ca7c0f7 100644 --- a/game/pathfinding.h +++ b/game/pathfinding.h @@ -24,4 +24,7 @@ typedef struct PathfindingDesc { bool findPath(const PathfindingDesc *desc); +// TODO: Flowfield +void calculateFlowField(); + #endif //PIXELDEFENSE_PATHFINDING_H