diff --git a/engine/breeze/map/map.c b/engine/breeze/map/map.c index 145c2d6..a017440 100644 --- a/engine/breeze/map/map.c +++ b/engine/breeze/map/map.c @@ -285,6 +285,12 @@ void bzTileMapDestroy(BzTileMap *map) { *map = BZ_TILEMAP_INVALID; } + +void bzTileMapPosToTile(BzTileMap *map, Vector2 pos, BzTile *x, BzTile *y) { + if (x) *x = (BzTile) (pos.x / (f32) map->tileWidth); + if (y) *y = (BzTile) (pos.y / (f32) map->tileHeight); +} + void bzTileMapOverrideLayer(BzTileMap *map, i32 slotID, BzTileLayerFunc func) { BZ_ASSERT(slotID >= 0 && slotID < map->layerCount); BzTileLayer *layer = map->layers + slotID; diff --git a/engine/breeze/map/map.h b/engine/breeze/map/map.h index 7892984..c870674 100644 --- a/engine/breeze/map/map.h +++ b/engine/breeze/map/map.h @@ -116,6 +116,8 @@ BzTileset *bzTileObjectGroupGetTileset(BzTileMap *map, BzTileObjectGroup *object BzTileMap bzTileMapCreate(const BzTileMapDesc *desc); void bzTileMapDestroy(BzTileMap *map); +void bzTileMapPosToTile(BzTileMap *map, Vector2 pos, BzTile *x, BzTile *y); + void bzTileMapOverrideLayer(BzTileMap *map, i32 slotID, BzTileLayerFunc func); void bzTileMapOverrideObjectGroup(BzTileMap *map, i32 slotID, BzTileObjectsFunc func); diff --git a/game/main.c b/game/main.c index 5fb0b7c..f0cce67 100644 --- a/game/main.c +++ b/game/main.c @@ -192,58 +192,6 @@ void update(float dt, void *userData) { updatePlayerInput(NULL); } -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) { BZ_UNUSED(userData); Game *game = ecs_singleton_get_mut(ECS, Game); diff --git a/game/pathfinding.c b/game/pathfinding.c index dfb90f3..cbe439b 100644 --- a/game/pathfinding.c +++ b/game/pathfinding.c @@ -14,11 +14,28 @@ static i32 dst(TilePosition a, TilePosition b) { return 14 * dstX + 10 * (dstY - dstX); } +static PathData *pushPathWaypoint(PathData *pathData, Position waypoint, BzObjectPool *pathPool) { + if (pathData->numWaypoints + 1 > PATH_DATA_SIZE) { + PathData *newPathData = bzObjectPool(pathPool); + BZ_ASSERT(newPathData); + newPathData->numWaypoints = 0; + newPathData->next = pathData; + pathData = newPathData; + } + pathData->waypoints[pathData->numWaypoints++] = waypoint; + return pathData; +} + bool findPath(const PathfindingDesc *desc) { BZ_ASSERT(desc->map); BzTileMap *map = desc->map; - BZ_ASSERT(desc->start.x >= 0 && desc->start.x < map->width); - BZ_ASSERT(desc->start.y >= 0 && desc->start.y < map->height); + + TilePosition start = {0}, target = {0}; + bzTileMapPosToTile(map, desc->start, &start.x, &start.y); + bzTileMapPosToTile(map, desc->target, &target.x, &target.y); + BZ_ASSERT(start.x >= 0 && start.x < map->width); + BZ_ASSERT(start.y >= 0 && start.y < map->height); + typedef struct Visited { bool visited : 1; i8 x : 3; @@ -30,21 +47,22 @@ bool findPath(const PathfindingDesc *desc) { if (!openSet) openSet = bzHeapCreate(PathNode, map->width * map->height); else bzHeapClear(openSet); - i32 toTargetCost = dst(desc->start, desc->target); + + i32 toTargetCost = dst(start, target); bzHeapPush(openSet, (PathNode) { .weight=toTargetCost, .gCost=0, .hCost=toTargetCost, .visited=false, - .pos=desc->start + .pos=start }); bool foundPath = false; while (!bzHeapIsEmpty(openSet)) { PathNode node = bzHeapPop(openSet); - if (node.pos.x == desc->target.x && - node.pos.y == desc->target.y) { + if (node.pos.x == target.x && + node.pos.y == target.y) { // Found path foundPath = true; break; @@ -69,7 +87,7 @@ bool findPath(const PathfindingDesc *desc) { TilePosition curPos = {x, y}; i32 gCost = node.gCost + dst(node.pos, curPos); //if (gCost >= node.gCost) continue; - toTargetCost = dst(curPos, desc->target); + toTargetCost = dst(curPos, target); curClosed->x = (i8) (curPos.x - node.pos.x); curClosed->y = (i8) (curPos.y - node.pos.y); @@ -87,31 +105,25 @@ bool findPath(const PathfindingDesc *desc) { i32 pathLen = 0; if (foundPath && desc->outPath) { - TilePosition pos = desc->target; + TilePosition pos = target; Path *out = desc->outPath; out->curWaypoint = 0; BZ_ASSERT(desc->pool); PathData *pathData = bzObjectPool(desc->pool); pathData->numWaypoints = 0; pathData->next = NULL; - i32 numWaypoints = 0; + + pathData = pushPathWaypoint(pathData, desc->target, desc->pool); + // Write path - // TODO: Write end pos - while (pos.x != desc->start.x || pos.y != desc->start.y) { + while (pos.x != start.x || pos.y != start.y) { Position waypoint = { pos.x * map->tileWidth + map->tileWidth * 0.5f, pos.y * map->tileHeight + map->tileHeight * 0.5f }; - if (pathData->numWaypoints + 1 > PATH_DATA_SIZE) { - PathData *newPathData = bzObjectPool(desc->pool); - newPathData->numWaypoints = 0; - newPathData->next = pathData; - pathData = newPathData; - numWaypoints = 0; - } - pathData->waypoints[numWaypoints++] = waypoint; - pathData->numWaypoints = numWaypoints; + if (pathLen != 0) + pathData = pushPathWaypoint(pathData, waypoint, desc->pool); Visited visit = closedSet[pos.y * map->width + pos.x]; BZ_ASSERT(visit.x != 0 || visit.y != 0); diff --git a/game/pathfinding.h b/game/pathfinding.h index ca7c0f7..6a5e5d9 100644 --- a/game/pathfinding.h +++ b/game/pathfinding.h @@ -14,8 +14,8 @@ typedef struct PathNode { } PathNode; typedef struct PathfindingDesc { - TilePosition start; - TilePosition target; + Position start; + Position target; BzObjectPool *pool; BzTileMap *map; PathNode *openSet; // heap diff --git a/game/systems_input.c b/game/systems_input.c index fe0f0f6..8d1ff2c 100644 --- a/game/systems_input.c +++ b/game/systems_input.c @@ -8,6 +8,15 @@ void pickEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t **outEntities); void pickEntities(BzSpatialGrid *entityGrid, Rectangle area, ecs_entity_t **outEntities); +static void placeUnits(i32 numUnits, f32 unitSpacing, Vector2 start, Vector2 end, BzTileMap *map, Vector2 **outPlaces); + +static bool wasInputDragged(InputState *input) { + return IsMouseButtonReleased(input->LMB) && input->mouseDownElapsed > input->CLICK_LIMIT; +} +static bool wasInputClicked(InputState *input) { + return IsMouseButtonReleased(input->LMB); +} + void updatePlayerInput(ecs_iter_t *it) { f32 dt = GetFrameTime(); ImGuiIO *io = igGetIO(); @@ -37,11 +46,11 @@ void updatePlayerInput(ecs_iter_t *it) { } Vector2 worldPos = GetScreenToWorld2D(GetMousePosition(), game->camera); - int tileX = (int) worldPos.x / map->tileWidth; - int tileY = (int) worldPos.y / map->tileHeight; + BzTile tileX = 0, tileY = 0; + bzTileMapPosToTile(map, worldPos, &tileX, &tileY); switch (input->state) { case INPUT_NONE: - if (IsMouseButtonReleased(input->LMB) && input->mouseDownElapsed > input->CLICK_LIMIT) { + if (wasInputDragged(input)) { // Dragging Vector2 start = input->mouseDownWorld; @@ -64,7 +73,7 @@ void updatePlayerInput(ecs_iter_t *it) { break; } - } else if (IsMouseButtonReleased(input->LMB)) { + } else if (wasInputClicked(input)) { // Click // 1. Entity @@ -103,7 +112,37 @@ void updatePlayerInput(ecs_iter_t *it) { input->state = INPUT_NONE; break; } - if (IsMouseButtonPressed(input->LMB)) { + if (bzArraySize(input->entities) > 1 && wasInputDragged(input)) { + // TODO: For click it should just move them + i32 numUnits = bzArraySize(input->entities); + f32 unitSpacing = 3.5f; + Vector2 *unitPositions = bzArrayCreate(Position, numUnits); + placeUnits(numUnits, unitSpacing, input->mouseDownWorld, worldPos, + map, &unitPositions); + + BZ_ASSERT(bzArraySize(unitPositions) == numUnits); + + bzArrayFor(unitPositions, i) { + Position target = bzArrayGet(unitPositions, i); + ecs_entity_t entity = bzArrayGet(input->entities, i); + + const Position *start = ecs_get(ECS, entity, Position); + Path path = {NULL, 0}; + findPath(&(PathfindingDesc) { + .start=*start, + .target=target, + .map=map, + .outPath=&path, + .pool=game->pools.pathData + }); + if (!path.paths) continue; + ecs_set_ptr(ECS, entity, Path, &path); + input->state = INPUT_NONE; + } + + bzArrayDestroy(unitPositions); + + } else if (wasInputClicked(input)) { bzArrayFor(input->entities, i) { ecs_entity_t entity = bzArrayGet(input->entities, i); ecs_remove(ECS, entity, Path); @@ -111,12 +150,9 @@ void updatePlayerInput(ecs_iter_t *it) { const Position *start = ecs_get(ECS, entity, Position); Path path = {NULL, 0}; findPath(&(PathfindingDesc) { - .start=(TilePosition) { - start->x / game->map.tileWidth, - start->y / game->map.tileHeight, - }, - .target=(TilePosition) {tileX, tileY}, - .map=&game->map, + .start=*start, + .target=worldPos, + .map=map, .outPath=&path, .pool=game->pools.pathData }); @@ -200,3 +236,43 @@ void pickEntities(BzSpatialGrid *entityGrid, Rectangle area, ecs_entity_t **outE bzArrayPush(*outEntities, entity); } } + + + +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; +} + +void placeUnits(i32 numUnits, f32 unitSpacing, Vector2 start, Vector2 end, BzTileMap *map, Vector2 **outPlaces) { + f32 angle = Vector2Angle(start, end); + + f32 lineLength = Vector2Distance(start, end); + + Vector2 pos = Vector2Zero(); + pos.x = unitSpacing; + + for (i32 i = 0; i < numUnits; i++) { + if (pos.x + unitSpacing * 2.0f > lineLength) { + pos.x = unitSpacing; + pos.y += unitSpacing * 2.0f; + } + Vector2 unitPos = Vector2Add(start, Vector2Rotate(pos, angle)); + if (!canPlaceUnit(unitPos, 4.0f, map)) { + i--; + } else { + bzArrayPush(*outPlaces, unitPos); + } + pos.x += unitSpacing * 2.0f; + } +}