Add input logic for building

This commit is contained in:
2023-11-22 09:56:20 +01:00
parent 433b012715
commit 783db8ba90
4 changed files with 214 additions and 72 deletions

View File

@@ -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;
}
}

View File

@@ -4,12 +4,34 @@
#include <breeze.h>
#include <flecs.h>
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;

View File

@@ -11,6 +11,7 @@
#include "pathfinding.h"
#include <time.h>
#include <raymath.h>
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)) {

View File

@@ -24,4 +24,7 @@ typedef struct PathfindingDesc {
bool findPath(const PathfindingDesc *desc);
// TODO: Flowfield
void calculateFlowField();
#endif //PIXELDEFENSE_PATHFINDING_H