164 lines
5.2 KiB
C
164 lines
5.2 KiB
C
#include "unit_ai.h"
|
|
#include "unit_actions.h"
|
|
#include "game_state.h"
|
|
#include "buildings.h"
|
|
|
|
#include <raymath.h>
|
|
|
|
static ecs_entity_t findNearestStorage(Position pos, ResourceType type, Position *outPos) {
|
|
ecs_filter_t *storageFilter = ecs_filter(ECS, {
|
|
.terms = {{ecs_id(Position)}, {ecs_id(Storage)}},
|
|
});
|
|
ecs_iter_t it = ecs_filter_iter(ECS, storageFilter);
|
|
|
|
ecs_entity_t closest = 0;
|
|
f32 closestDst = INFINITY;
|
|
Position closestPos = Vector2Zero();
|
|
while (ecs_filter_next(&it)) {
|
|
Position *storagePos = ecs_field(&it, Position, 1);
|
|
|
|
for (i32 i = 0; i < it.count; i++) {
|
|
f32 dst = Vector2Distance(pos, storagePos[i]);
|
|
if (dst < closestDst) {
|
|
closest = it.entities[i];
|
|
closestDst = dst;
|
|
closestPos = storagePos[i];
|
|
}
|
|
}
|
|
}
|
|
ecs_filter_fini(storageFilter);
|
|
if (outPos) *outPos = closestPos;
|
|
return closest;
|
|
}
|
|
static ecs_entity_t findNearestResource(Game *game, Position pos, ResourceType type, f32 range) {
|
|
ecs_entity_t closest = 0;
|
|
f32 closestDst = 10000.0f;
|
|
|
|
f32 hRange = range * 0.5f;
|
|
Rectangle area = {pos.x - hRange, pos.y + hRange, hRange, hRange};
|
|
|
|
BzSpatialGridIter it = bzSpatialGridIter(game->entityGrid, area.x, area.y, area.width, area.height);
|
|
|
|
while (bzSpatialGridQueryNext(&it)) {
|
|
ecs_entity_t entity = *(ecs_entity_t *) it.data;
|
|
if (!ecs_is_alive(ECS, entity)) continue;
|
|
if (!ecs_has(ECS, entity, Resource) || !ecs_has(ECS, entity, Position))
|
|
continue;
|
|
const Resource *resource = ecs_get(ECS, entity, Resource);
|
|
const Position *resPos = ecs_get(ECS, entity, Position);
|
|
if (resource->type != type) continue;
|
|
|
|
f32 dst = Vector2Distance(pos, *resPos);
|
|
if (dst < closestDst) {
|
|
closest = entity;
|
|
closestDst = dst;
|
|
}
|
|
}
|
|
|
|
return closest;
|
|
}
|
|
|
|
void aiWorkerBuild(ecs_entity_t entity, UnitAI *unitAI, Game *game) {
|
|
|
|
}
|
|
/**********************************
|
|
* Worker AI
|
|
**********************************/
|
|
|
|
void actionCollectResourceOnFinish(ecs_entity_t entity, Action *action, Game *game) {
|
|
Worker *worker = ecs_get_mut(ECS, entity, Worker);
|
|
// Full, nothing to be done
|
|
if (worker->carry >= worker->carryCapacity)
|
|
return;
|
|
|
|
// Not full yet, find a new resource
|
|
Position pos = *ecs_get(ECS, entity, Position);
|
|
ecs_entity_t target = findNearestResource(game, pos, worker->carryRes, 20.0f);
|
|
if (target) {
|
|
|
|
}
|
|
}
|
|
|
|
void aiWorkerHarvestUpdate(ecs_entity_t entity, UnitAI *unitAI, Game *game) {
|
|
const Action *action = unitAI->action;
|
|
|
|
BZ_ASSERT(ecs_has(ECS, entity, Worker));
|
|
Worker *worker = ecs_get_mut(ECS, entity, Worker);
|
|
|
|
if (action && action->type == ACTION_COLLECT_RESOURCE && action->finished) {
|
|
// Full, nothing to be done
|
|
if (worker->carry >= worker->carryCapacity)
|
|
return;
|
|
|
|
// TODO: Not full yet, find a new resource
|
|
|
|
}
|
|
|
|
if (action) return;
|
|
|
|
AIWorkerHarvest workerAI = unitAI->as.workerHarvest;
|
|
|
|
Position workerPos = *ecs_get(ECS, entity, Position);
|
|
|
|
// Finished all tasks, repeat
|
|
ecs_entity_t target = workerAI.target;
|
|
Position targetPos = workerAI.targetPosition;
|
|
if (!ecs_is_alive(ECS, target)) {
|
|
// Find new resource
|
|
target = findNearestResource(game, targetPos, workerAI.resource, 20.0f);
|
|
if (!target) return;
|
|
|
|
workerAI.target = target;
|
|
workerAI.targetPosition = *ecs_get(ECS, target, Position);
|
|
}
|
|
// Find the closest warehouse
|
|
Position warehousePos = Vector2Zero();
|
|
ecs_entity_t warehouse = findNearestStorage(workerPos, workerAI.resource,
|
|
&warehousePos);
|
|
if (!warehouse) {
|
|
return;
|
|
}
|
|
warehousePos = getPositionNearBuilding(warehouse, targetPos);
|
|
|
|
const f32 proximityThreshold = 8.0f;
|
|
|
|
addAction(entity, game, &(const Action) {
|
|
.type = ACTION_MOVE_TO,
|
|
.as.moveTo.target = targetPos,
|
|
.as.moveTo.proximityThreshold = proximityThreshold,
|
|
});
|
|
addAction(entity, game, &(const Action) {
|
|
.type = ACTION_COLLECT_RESOURCE,
|
|
.as.collectResource.entity = target,
|
|
});
|
|
addAction(entity, game, &(const Action) {
|
|
.type = ACTION_MOVE_TO,
|
|
.as.moveTo.target = warehousePos,
|
|
.as.moveTo.proximityThreshold = proximityThreshold,
|
|
});
|
|
addAction(entity, game, &(const Action) {
|
|
.type = ACTION_DEPOSIT_RESOURCE,
|
|
.as.depositResource.entity = warehouse
|
|
});
|
|
|
|
}
|
|
|
|
void updateUnitAI(ecs_entity_t entity, UnitAI *unitAI, Game *game) {
|
|
//if (unitAI->onUpdate) unitAI->onUpdate(entity, unitAI, game);
|
|
aiWorkerHarvestUpdate(entity, unitAI, game);
|
|
}
|
|
void setUnitAI(ecs_entity_t entity, Game *game, const UnitAI *unitAI) {
|
|
BZ_ASSERT(unitAI);
|
|
if (ecs_has(ECS, entity, UnitAI)) {
|
|
UnitAI *entityAI = ecs_get_mut(ECS, entity, UnitAI);
|
|
if (entityAI->onRemove) entityAI->onRemove(entity, entityAI, game);
|
|
}
|
|
clearActions(entity, game);
|
|
|
|
UnitAI ai = *unitAI;
|
|
if (unitAI->onSet) unitAI->onSet(entity, &ai, game);
|
|
ecs_set_ptr(ECS, entity, UnitAI, &ai);
|
|
if (unitAI->action != NULL)
|
|
addAction(entity, game, unitAI->action);
|
|
}
|