Better formation placement
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,9 @@ BzBTStatus aiMoveTo(AIBlackboard *data, f32 dt) {
|
||||
return BZ_BT_SUCCESS;
|
||||
}
|
||||
if (!ecs_has(ECS, data->entity, Path)) {
|
||||
entitySetPath(data->entity, target, game);
|
||||
bool pathfindSuccessful = entitySetPath(data->entity, target, game);
|
||||
if (!pathfindSuccessful)
|
||||
return BZ_BT_FAIL;
|
||||
}
|
||||
if (ecs_has(ECS, data->entity, Orientation)) {
|
||||
Orientation *orientation = ecs_get_mut(ECS, data->entity, Orientation);
|
||||
|
||||
@@ -57,6 +57,8 @@ typedef struct InputState {
|
||||
Vec2i buildingSize;
|
||||
// SELECTED_UNITS
|
||||
Rectangle pickArea;
|
||||
i32 numUnits;
|
||||
Vector2 *unitPlacePos;
|
||||
|
||||
|
||||
// SELECTED_OBJECT
|
||||
|
||||
34
game/main.c
34
game/main.c
@@ -250,8 +250,10 @@ bool init(void *userData) {
|
||||
game->BTs.moveTo = root;
|
||||
|
||||
// Just a single action for now
|
||||
node = bzBTAction(nodePool, root, (BzBTActionFn) aiMoveTo);
|
||||
BzBTNode *seq = bzBTCompSequence(nodePool, root);
|
||||
node = bzBTAction(nodePool, seq, (BzBTActionFn) aiMoveTo);
|
||||
bzBTNodeSetName(node, "moveTo");
|
||||
bzBTDecorDelay(nodePool, seq, 6.0f);
|
||||
}
|
||||
// evade
|
||||
BzBTNode *evade = NULL;
|
||||
@@ -547,36 +549,6 @@ static void renderGame(Game *game, float dt) {
|
||||
}
|
||||
ecs_defer_end(ECS);
|
||||
|
||||
|
||||
#if 0
|
||||
Vector2 target = GetMousePosition();
|
||||
target = GetScreenToWorld2D(target, game->camera);
|
||||
static f32 elapsed = 0;
|
||||
static bool attack = false;
|
||||
static Vector2 lockedTarget;
|
||||
if (!attack && IsMouseButtonPressed(0)) {
|
||||
attack = true;
|
||||
lockedTarget = target;
|
||||
elapsed = 0;
|
||||
}
|
||||
elapsed += dt * 2;
|
||||
elapsed = Clamp(elapsed, 0, 1.0f);
|
||||
attack = false;
|
||||
if (worker && false) {
|
||||
Position *pos = ecs_get_mut(ECS, worker, Position);
|
||||
DrawCircle(pos->x, pos->y, 2.0f, BLUE);
|
||||
Vector2 attackVector = Vector2Subtract(lockedTarget, *pos);
|
||||
attackVector = Vector2Normalize(attackVector);
|
||||
attackVector = Vector2Scale(attackVector, 2.0f);
|
||||
DrawLine(pos->x, pos->y, pos->x + attackVector.x, pos->y + attackVector.y, RED);
|
||||
Rotation *rot = ecs_get_mut(ECS, worker, Rotation);
|
||||
f32 targetRot = Vector2Angle(*pos, lockedTarget);
|
||||
targetRot += 25 * DEG2RAD;
|
||||
*rot = targetRot * bzEase(BZ_EASE_IN_BACK, elapsed);
|
||||
bzLogInfo("%.2f", Vector2Angle(*pos, lockedTarget) * RAD2DEG);
|
||||
}
|
||||
#endif
|
||||
|
||||
ecs_progress(ECS, dt);
|
||||
ecs_enable(ECS, renderDebugPathSystem, game->debug.drawPath);
|
||||
ecs_enable(ECS, renderCollidersSystem, game->debug.drawEntityColliders);
|
||||
|
||||
@@ -183,7 +183,6 @@ void entityUpdate(ecs_iter_t *it) {
|
||||
|
||||
// Attack update
|
||||
if (canAttack && ecs_has(ECS, other, Health) && ecs_has(ECS, other, Owner)) {
|
||||
Health *otherHealth = ecs_get_mut(ECS, other, Health);
|
||||
Player otherPlayer = ecs_get(ECS, other, Owner)->player;
|
||||
|
||||
if (otherPlayer != owner[i].player) {
|
||||
@@ -219,7 +218,7 @@ void entityUpdate(ecs_iter_t *it) {
|
||||
velocity[i] = Vector2Subtract(velocity[i], dir);
|
||||
}
|
||||
|
||||
slowDown = BZ_MIN(slowDown, 0.65f);
|
||||
slowDown = BZ_MIN(slowDown, 0.2f);
|
||||
if (!stationary && slowDown > 0.0f) {
|
||||
velocity[i] = Vector2Scale(velocity[i], 1 - slowDown);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ void damageEvent(ecs_entity_t entity, DamageEvent event) {
|
||||
});
|
||||
// Remove, so it becomes inactive
|
||||
ecs_remove_id(ECS, entity, Selectable);
|
||||
ecs_remove_id(ECS, entity, Selected);
|
||||
ecs_remove(ECS, entity, Health);
|
||||
ecs_remove(ECS, entity, Unit);
|
||||
ecs_remove(ECS, entity, Building);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <rlImGui.h>
|
||||
#include <raymath.h>
|
||||
#include <rlgl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
ecs_entity_t queryEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t tag);
|
||||
|
||||
@@ -21,7 +22,7 @@ void addEntityToInspected(ecs_entity_t entity, Game *game);
|
||||
static void iterateSelectedUnits(ecs_query_t *query, void (*fn)(ecs_entity_t entity, Position *pos));
|
||||
static void iterRemovePaths(ecs_entity_t entity, Position *pos);
|
||||
|
||||
void placeUnits(i32 numUnits, f32 unitSpacing, Vector2 start, Vector2 end, BzTileMap *map, Vector2 **outPlaces);
|
||||
i32 placeUnits(i32 numUnits, f32 unitSpacing, Vector2 start, Vector2 end, BzTileMap *map, Vector2 *outPlaces);
|
||||
|
||||
void resetInputState(InputState *input) {
|
||||
input->cursor = CURSOR_NONE;
|
||||
@@ -73,6 +74,28 @@ void inputPrimaryAction(Game *game, InputState *input) {
|
||||
if (selectedCount == 0)
|
||||
resetInputState(input);
|
||||
}
|
||||
|
||||
typedef struct EntityPosPair {
|
||||
ecs_entity_t entity;
|
||||
Vector2 pos;
|
||||
} EntityPosPair;
|
||||
static int vec2CmpZero(const void *lhsData, const void *rhsData) {
|
||||
const Vector2 *lhs = lhsData;
|
||||
const Vector2 *rhs = rhsData;
|
||||
|
||||
f32 dstL = Vector2DistanceSqr(Vector2Zero(), *lhs);
|
||||
f32 dstR = Vector2DistanceSqr(Vector2Zero(), *rhs);
|
||||
|
||||
if (dstL < dstR) return -1;
|
||||
if (dstL > dstR) return 1;
|
||||
return 0;
|
||||
}
|
||||
static int entityPosPairCmpZero(const void *lhsData, const void *rhsData) {
|
||||
const EntityPosPair *lhs = lhsData;
|
||||
const EntityPosPair *rhs = rhsData;
|
||||
|
||||
return vec2CmpZero(&lhs->pos, &rhs->pos);
|
||||
}
|
||||
void inputUnitAction(Game *game, InputState *input) {
|
||||
ecs_query_t *query = input->queries.selected;
|
||||
BzTileMap *map = &game->map;
|
||||
@@ -155,35 +178,58 @@ void inputUnitAction(Game *game, InputState *input) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInputBtnJustUp(input, actionBtn)) {
|
||||
// Unit place position
|
||||
Vector2 *positions = bzStackAlloc(&game->stackAlloc, sizeof(*positions) * numUnits);
|
||||
Vector2 start = Vector2Zero();
|
||||
Vector2 end = Vector2Zero();
|
||||
if (isInputBtnDragged(input, actionBtn) || isInputBtnJustDragged(input, actionBtn)) {
|
||||
start = input->mouseDownWorld;
|
||||
end = input->mouseWorld;
|
||||
} else {
|
||||
start = end = input->mouseWorld;
|
||||
f32 displace = 8 * numUnits;
|
||||
displace = BZ_MIN(displace, 50.0f);
|
||||
start.x -= displace;
|
||||
end.x += displace + 2;
|
||||
}
|
||||
i32 placedUnits = placeUnits(numUnits, 6.0f, start, end, map, positions);
|
||||
input->numUnits = placedUnits;
|
||||
input->unitPlacePos = positions;
|
||||
|
||||
if (IsMouseButtonReleased(actionBtn)) {
|
||||
// Note: We mustn't use ecs ecs_remove_all since this will also
|
||||
// remove ongoing paths that are not part of this query.
|
||||
ecs_defer_begin(ECS);
|
||||
iterateSelectedUnits(query, iterRemovePaths);
|
||||
ecs_defer_end(ECS);
|
||||
|
||||
const Position target = input->mouseWorld;
|
||||
|
||||
EntityPosPair *entities = bzStackAlloc(&game->stackAlloc, sizeof(*entities) * input->numUnits);
|
||||
ecs_iter_t it = ecs_query_iter(ECS, query);
|
||||
ecs_defer_begin(ECS);
|
||||
i32 unitIdx = 0;
|
||||
while (ecs_iter_next(&it)) {
|
||||
for (i32 i = 0; i < it.count; i++) {
|
||||
if (unitIdx >= input->numUnits)
|
||||
break;
|
||||
const ecs_entity_t entity = it.entities[i];
|
||||
setAIBehaviour(entity, game->BTs.moveTo, &(AIBlackboard) {
|
||||
.moveToPos = target,
|
||||
.proximity = 6.0f,
|
||||
});
|
||||
/*
|
||||
clearActions(entity, game);
|
||||
addAction(entity, game, &(const Action) {
|
||||
.type = ACTION_MOVE_TO,
|
||||
.as.moveTo.target = target,
|
||||
.as.moveTo.proximityThreshold = 6.0f,
|
||||
});
|
||||
*/
|
||||
if (!ecs_has(ECS, entity, Position)) continue;
|
||||
entities[unitIdx++] = (EntityPosPair) {
|
||||
.entity = entity,
|
||||
.pos = *ecs_get(ECS, entity, Position)
|
||||
};
|
||||
}
|
||||
}
|
||||
ecs_defer_end(ECS);
|
||||
|
||||
qsort(entities, unitIdx, sizeof(*entities), entityPosPairCmpZero);
|
||||
qsort(input->unitPlacePos, unitIdx, sizeof(*input->unitPlacePos), vec2CmpZero);
|
||||
|
||||
for (i32 i = 0; i < unitIdx; i++) {
|
||||
ecs_entity_t entity = entities[i].entity;
|
||||
setAIBehaviour(entity, game->BTs.moveTo, &(AIBlackboard) {
|
||||
.moveToPos = positions[i],
|
||||
.proximity = 1.0f,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -194,6 +240,9 @@ void updatePlayerInput() {
|
||||
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||
InputState *input = ecs_singleton_get_mut(ECS, InputState);
|
||||
|
||||
input->numUnits = 0;
|
||||
input->unitPlacePos = NULL;
|
||||
|
||||
const f32 maxZoom = 4.5f;
|
||||
const f32 minZoom = 0.9f;
|
||||
if (input->canUseKeyboard) {
|
||||
@@ -370,6 +419,12 @@ void drawPlayerInputUIGround() {
|
||||
}
|
||||
}
|
||||
|
||||
if (input->unitPlacePos) {
|
||||
for (i32 i = 0; i < input->numUnits; i++) {
|
||||
DrawCircleV(input->unitPlacePos[i], 2.0f, RED);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void drawPlayerInputUI() {
|
||||
@@ -512,8 +567,14 @@ static bool canPlaceUnit(Vector2 pos, f32 space, BzTileMap *map) {
|
||||
|
||||
return true;
|
||||
}
|
||||
static bool unitWithinMap(Vector2 pos, BzTileMap *map) {
|
||||
f32 x = pos.x / map->tileWidth;
|
||||
f32 y = pos.y / map->tileHeight;
|
||||
|
||||
void placeUnits(i32 numUnits, f32 unitSpacing, Vector2 start, Vector2 end, BzTileMap *map, Vector2 **outPlaces) {
|
||||
return !(x < 0 || y < 0 || x >= map->width || y >= map->height);
|
||||
}
|
||||
i32 placeUnits(i32 numUnits, f32 unitSpacing, Vector2 start, Vector2 end, BzTileMap *map, Vector2 *outPlaces) {
|
||||
i32 outIdx = 0;
|
||||
f32 angle = Vector2Angle(start, end);
|
||||
|
||||
f32 lineLength = Vector2Distance(start, end);
|
||||
@@ -527,13 +588,15 @@ void placeUnits(i32 numUnits, f32 unitSpacing, Vector2 start, Vector2 end, BzTil
|
||||
pos.y += unitSpacing * 2.0f;
|
||||
}
|
||||
Vector2 unitPos = Vector2Add(start, Vector2Rotate(pos, angle));
|
||||
if (!canPlaceUnit(unitPos, 4.0f, map)) {
|
||||
bool withinMap = unitWithinMap(unitPos, map);
|
||||
if (canPlaceUnit(unitPos, 4.0f, map) && withinMap) {
|
||||
outPlaces[outIdx++] = unitPos;
|
||||
} else if (withinMap) {
|
||||
i--;
|
||||
} else {
|
||||
bzArrayPush(*outPlaces, unitPos);
|
||||
}
|
||||
pos.x += unitSpacing * 2.0f;
|
||||
}
|
||||
return outIdx;
|
||||
}
|
||||
|
||||
static void iterateSelectedUnits(ecs_query_t *query, void (*fn)(ecs_entity_t entity, Position *pos)) {
|
||||
|
||||
@@ -291,8 +291,8 @@ void drawMainMenuUI(Game *game, f32 dt) {
|
||||
if (uiMainMenuButton("Play", true)) {
|
||||
setScreen(game, SCREEN_GAME);
|
||||
unloadMap(game);
|
||||
loadMap(game, "assets/maps/tree_test.tmj");
|
||||
//loadMap(game, "assets/maps/entity_test.tmj");
|
||||
//loadMap(game, "assets/maps/tree_test.tmj");
|
||||
loadMap(game, "assets/maps/entity_test.tmj");
|
||||
//loadMap(game, "assets/maps/map_01.tmj");
|
||||
}
|
||||
if (uiMainMenuButton("Settings", true)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="32" height="32" tilewidth="16" tileheight="16" infinite="0" nextlayerid="10" nextobjectid="438">
|
||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="32" height="32" tilewidth="16" tileheight="16" infinite="0" nextlayerid="10" nextobjectid="575">
|
||||
<editorsettings>
|
||||
<export target="../assets/maps/tree_test.tmj" format="json"/>
|
||||
</editorsettings>
|
||||
|
||||
Reference in New Issue
Block a user