From a9d20cb7f98d519aafed4e7238a460857e3bec93 Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Wed, 10 Jan 2024 14:42:11 +0100 Subject: [PATCH] Fix and integrate BT --- game/unit_actions.c | 190 -------------------------------------------- game/unit_actions.h | 62 --------------- game/unit_ai.c | 163 ------------------------------------- game/unit_ai.h | 51 ------------ 4 files changed, 466 deletions(-) delete mode 100644 game/unit_actions.c delete mode 100644 game/unit_actions.h delete mode 100644 game/unit_ai.c delete mode 100644 game/unit_ai.h diff --git a/game/unit_actions.c b/game/unit_actions.c deleted file mode 100644 index e19ee65..0000000 --- a/game/unit_actions.c +++ /dev/null @@ -1,190 +0,0 @@ -#include "unit_actions.h" -#include "game_state.h" -#include "components.h" -#include "systems/systems.h" - -#include - -float shortestArc(float a, float b) { - if (fabs(b - a) < M_PI) - return b - a; - if (b > a) - return b - a - M_PI * 2.0f; - return b - a + M_PI * 2.0f; -} - -void actionMoveTo(ecs_entity_t entity, Action *action, Game *game) { - if (action->finished) return; - const Vector2 target = action->as.moveTo.target; - if (!ecs_has(ECS, entity, Path)) { - entitySetPath(entity, target, game); - return; - } - const f32 dt = GetFrameTime(); - const Vector2 pos = *ecs_get(ECS, entity, Position); - if (ecs_has(ECS, entity, Orientation)) { - Orientation *orientation = ecs_get_mut(ECS, entity, Orientation); - f32 currentAngle = *orientation; - f32 targetAngle = Vector2Angle(pos, target); - f32 dif = shortestArc(currentAngle, targetAngle); - dif = Clamp(dif, -1, 1) * dt * 10; - *orientation += dif; - *orientation = fmodf(*orientation + 180.0f, 360.0f) - 180.0f; - } - f32 dst = Vector2Distance(pos, target); - if (dst < action->as.moveTo.proximityThreshold) { - action->finished = true; - ecs_remove(ECS, entity, Path); - } -} - -void actionCollectResource(ecs_entity_t entity, Action *action, Game *game) { - if (action->finished) return; - BZ_ASSERT(ecs_has(ECS, entity, Worker)); - Worker *worker = ecs_get_mut(ECS, entity, Worker); - - if (worker->carry >= worker->carryCapacity) { - action->finished = true; - } - - ecs_entity_t target = action->as.collectResource.entity; - bool targetAlive = ecs_is_alive(ECS, target); - action->finished |= !targetAlive; - - if (!action->finished && action->elapsed > worker->collectSpeed) { - i32 spareCapacity = worker->carryCapacity - worker->carry; - i32 collected = harvestEvent(target, (HarvestEvent) { - .amount = BZ_MIN(1, spareCapacity), - }); - worker->carry += collected; - action->elapsed = 0; - } - -} - -void actionDepositResource(ecs_entity_t entity, Action *action, Game *game) { - if (action->finished) return; - - BZ_ASSERT(ecs_has(ECS, entity, Worker)); - Worker *worker = ecs_get_mut(ECS, entity, Worker); - - if (worker->carry == 0) { - action->finished = true; - } - ecs_entity_t target = action->as.depositResource.entity; - bool targetAlive = ecs_is_alive(ECS, target); - action->finished |= !targetAlive; - - if (!action->finished && action->elapsed > worker->depositSpeed) { - depositEvent(target, (DepositEvent) { - .amount = worker->carry - }); - worker->carry = 0; - action->finished = true; - } -} - -void handleAction(ecs_entity_t entity, UnitAction *unitAction, Game *game) { - Action *action = unitAction->first; - if (action == NULL) return; - if (action->finished || action->failed) return; - switch (action->type) { - case ACTION_NONE: - break; - case ACTION_MOVE_TO: - actionMoveTo(entity, action, game); - break; - case ACTION_COLLECT_RESOURCE: - actionCollectResource(entity, action, game); - break; - case ACTION_DEPOSIT_RESOURCE: - actionDepositResource(entity, action, game); - break; - default: - BZ_ASSERT(0); - break; - } - -} - -void updateAction(UnitAction *unitAction, Game *game) { - Action *action = unitAction->first; - if (action == NULL) return; - if (action->failed) return; - action->elapsed += GetFrameTime(); - if (action->finished) { - unitAction->first = action->next; - if (unitAction->last == action) { - unitAction->last = NULL; - BZ_ASSERT(unitAction->first == unitAction->last); - } - BzObjectPool *pool = game->pools.actions; - bzObjectPoolRelease(pool, action); - } -} - -void clearActions(ecs_entity_t entity, Game *game) { - if (!ecs_has(ECS, entity, UnitAction)) return; - UnitAction *unitAction = ecs_get_mut(ECS, entity, UnitAction); - - BzObjectPool *pool = game->pools.actions; - - Action *pAction = unitAction->first; - while (pAction) { - bzObjectPoolRelease(pool, pAction); - pAction = pAction->next; - } - - unitAction->first = NULL; - unitAction->last = NULL; -} - -void addAction(ecs_entity_t entity, Game *game, const Action *action) { - BZ_ASSERT(action); - BZ_ASSERT(ecs_has(ECS, entity, UnitAction)); - - UnitAction *unitAction = ecs_get_mut(ECS, entity, UnitAction); - BzObjectPool *pool = game->pools.actions; - - Action *newAction = bzObjectPool(pool); - BZ_ASSERT(newAction); - *newAction = *action; - - newAction->next = NULL; - if (unitAction->last) { - Action *last = unitAction->last; - last->next = newAction; - unitAction->last = newAction; - } else { - unitAction->first = newAction; - unitAction->last = newAction; - } -} - -void prependAction(ecs_entity_t entity, Game *game, const Action *action) { - BZ_ASSERT(action); - BZ_ASSERT(ecs_has(ECS, entity, UnitAction)); - - UnitAction *unitAction = ecs_get_mut(ECS, entity, UnitAction); - BzObjectPool *pool = game->pools.actions; - - Action *newAction = bzObjectPool(pool); - BZ_ASSERT(newAction); - *newAction = *action; - - newAction->next = unitAction->first; - unitAction->first = newAction; -} - -const char *actionTypeToPrettyStr(ActionType type) { - switch (type) { - case ACTION_MOVE_TO: - return "MOVE TO"; - case ACTION_COLLECT_RESOURCE: - return "COLLECT RESOURCE"; - case ACTION_DEPOSIT_RESOURCE: - return "DEPOSIT RESOURCE"; - default: - return "NONE"; - } -} diff --git a/game/unit_actions.h b/game/unit_actions.h deleted file mode 100644 index a1b7dd9..0000000 --- a/game/unit_actions.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef PIXELDEFENSE_UNIT_ACTIONS_H -#define PIXELDEFENSE_UNIT_ACTIONS_H - -#include -#include - -typedef struct Game Game; - -typedef enum ActionType { - ACTION_NONE, - ACTION_MOVE_TO, - ACTION_COLLECT_RESOURCE, - ACTION_DEPOSIT_RESOURCE, - ACTION_COUNT, -} ActionType; - -typedef struct ActionMoveTo { - Vector2 target; - f32 proximityThreshold; -} ActionMoveTo; -typedef struct ActionCollectResource { - ecs_entity_t entity; -} ActionCollectResource; -typedef struct ActionDepositResource { - ecs_entity_t entity; -} ActionDepositResource; - -typedef struct Action Action; -typedef void (*ActionCb)(ecs_entity_t entity, Action *action, Game *game); - -typedef struct Action { - ActionType type; - union { - ActionMoveTo moveTo; - ActionCollectResource collectResource; - ActionDepositResource depositResource; - } as; - - f32 elapsed; - bool finished; - bool failed; - - ActionCb onBegin; - ActionCb onFinish; - - struct Action *next; -} Action; - -typedef struct UnitAction { - Action *first; - Action *last; -} UnitAction; - -void handleAction(ecs_entity_t entity, UnitAction *unitAction, Game *game); -void updateAction(UnitAction *unitAction, Game *game); -void clearActions(ecs_entity_t entity, Game *game); -void addAction(ecs_entity_t entity, Game *game, const Action *action); -void prependAction(ecs_entity_t entity, Game *game, const Action *action); - -const char *actionTypeToPrettyStr(ActionType type); - -#endif //PIXELDEFENSE_UNIT_ACTIONS_H diff --git a/game/unit_ai.c b/game/unit_ai.c deleted file mode 100644 index 248b602..0000000 --- a/game/unit_ai.c +++ /dev/null @@ -1,163 +0,0 @@ -#include "unit_ai.h" -#include "unit_actions.h" -#include "game_state.h" -#include "buildings.h" - -#include - -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); -} diff --git a/game/unit_ai.h b/game/unit_ai.h deleted file mode 100644 index 8c1bab0..0000000 --- a/game/unit_ai.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef PIXELDEFENSE_UNIT_AI_H -#define PIXELDEFENSE_UNIT_AI_H - -#include - -#include "components.h" -#include "unit_actions.h" - -typedef struct Game Game; - -typedef enum AIType { - AI_NONE, - AI_WORKER_BUILD, - AI_WORKER_HARVEST, - AI_COUNT, -} AIType; - - -typedef struct AIWorkerHarvest { - ResourceType resource; - ecs_entity_t target; - Position targetPosition; -} AIWorkerHarvest; - -typedef struct AIWorkerBuild { - ecs_entity_t buildTarget; -} AIWorkerBuild; - -typedef struct UnitAI UnitAI; - -typedef void (*UnitAIFunc)(ecs_entity_t entity, UnitAI *ai, Game *game); - -typedef struct UnitAI { - AIType type; - union { - AIWorkerHarvest workerHarvest; - AIWorkerBuild workerBuild; - } as; - - Action *action; - - // vtable - UnitAIFunc onSet; - UnitAIFunc onRemove; - UnitAIFunc onUpdate; -} UnitAI; - -void updateUnitAI(ecs_entity_t entity, UnitAI *unitAI, Game *game); -void setUnitAI(ecs_entity_t entity, Game *game, const UnitAI *unitAI); - -#endif //PIXELDEFENSE_UNIT_AI_H