Add input logic for building
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
219
game/main.c
219
game/main.c
@@ -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)) {
|
||||
|
||||
@@ -24,4 +24,7 @@ typedef struct PathfindingDesc {
|
||||
|
||||
bool findPath(const PathfindingDesc *desc);
|
||||
|
||||
// TODO: Flowfield
|
||||
void calculateFlowField();
|
||||
|
||||
#endif //PIXELDEFENSE_PATHFINDING_H
|
||||
|
||||
Reference in New Issue
Block a user