diff --git a/CMakeLists.txt b/CMakeLists.txt index 626c53c..ab09fb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,9 +24,14 @@ add_executable(PixelDefense game/pathfinding.c game/pathfinding.h game/systems.h + game/systems_ai.c game/systems_entity.c game/systems_input.c game/systems_ui.c + game/unit_actions.c + game/unit_actions.h + game/unit_ai.c + game/unit_ai.h ) diff --git a/game/components.c b/game/components.c index 4228690..308b016 100644 --- a/game/components.c +++ b/game/components.c @@ -23,6 +23,7 @@ ECS_COMPONENT_DECLARE(TextureRegion); ECS_COMPONENT_DECLARE(Animation); +ECS_COMPONENT_DECLARE(UnitAction); ECS_TAG_DECLARE(Selectable); ECS_TAG_DECLARE(Selected); @@ -35,8 +36,6 @@ ECS_TAG_DECLARE(Attackable); ECS_COMPONENT_DECLARE(Storage); -ECS_COMPONENT_DECLARE(HarvestTask); - void initComponentIDs(ecs_world_t *ecs) { ECS_TAG_DEFINE(ecs, TextureTerrain); ECS_TAG_DEFINE(ecs, TextureBuildings); @@ -61,6 +60,7 @@ void initComponentIDs(ecs_world_t *ecs) { ECS_COMPONENT_DEFINE(ecs, Animation); + ECS_COMPONENT_DEFINE(ecs, UnitAction); ECS_TAG_DEFINE(ecs, Selectable); ECS_TAG_DEFINE(ecs, Selected); @@ -72,6 +72,4 @@ void initComponentIDs(ecs_world_t *ecs) { ECS_TAG_DEFINE(ecs, Attackable); ECS_COMPONENT_DEFINE(ecs, Storage); - - ECS_COMPONENT_DEFINE(ecs, HarvestTask); } diff --git a/game/components.h b/game/components.h index f7fbf68..27a74cb 100644 --- a/game/components.h +++ b/game/components.h @@ -5,6 +5,7 @@ #include #include "game_tileset.h" +#include "unit_actions.h" extern ECS_TAG_DECLARE(TextureTerrain); extern ECS_TAG_DECLARE(TextureBuildings); @@ -124,6 +125,12 @@ typedef struct EntityArms { * Gameplay components *********************************************************/ +extern ECS_COMPONENT_DECLARE(UnitAction); + +typedef struct ActionOverseer { + +} ActionOverseer; + extern ECS_TAG_DECLARE(Selectable); extern ECS_TAG_DECLARE(Selected); @@ -144,27 +151,13 @@ extern ECS_TAG_DECLARE(Workable); extern ECS_TAG_DECLARE(Attackable); typedef struct Storage { - int capacity[RES_COUNT]; - int amount[RES_COUNT]; - int reserved[RES_COUNT]; - int pending[RES_COUNT]; + int capacity[RES_COUNT]; + int amount[RES_COUNT]; + int reserved[RES_COUNT]; + int pending[RES_COUNT]; } Storage; extern ECS_COMPONENT_DECLARE(Storage); -typedef struct HarvestTask { - ecs_entity_t entity; -} HarvestTask; -extern ECS_COMPONENT_DECLARE(HarvestTask); - -typedef struct WorkerTask { - enum { - HARVEST, - BUILD, - } type; - ecs_entity_t target; - //struct WorkerTask *next; -} WorkerTask; - void initComponentIDs(ecs_world_t *ecs); diff --git a/game/game_state.h b/game/game_state.h index d631e41..7a1b710 100644 --- a/game/game_state.h +++ b/game/game_state.h @@ -20,6 +20,7 @@ typedef struct Game { BzStackAlloc stackAlloc; struct { BzObjectPool *pathData; + BzObjectPool *actions; } pools; struct { bool path; diff --git a/game/main.c b/game/main.c index 773d6b0..f7cb9a9 100644 --- a/game/main.c +++ b/game/main.c @@ -108,8 +108,12 @@ bool init(void *userData) { game->stackAlloc = bzStackAllocCreate(10 * 1000 * 1000); // 10 MB // init pools game->pools.pathData = bzObjectPoolCreate(&(BzObjectPoolDesc) { - .objectSize=sizeof(PathData), - .objectsPerPage=512 + .objectSize = sizeof(PathData), + .objectsPerPage = 512 + }); + game->pools.actions = bzObjectPoolCreate(&(BzObjectPoolDesc) { + .objectSize = sizeof(UnitAction), + .objectsPerPage = 1024, }); @@ -171,7 +175,8 @@ bool init(void *userData) { ECS_SYSTEM(ECS, entityMoveToTarget, EcsOnUpdate, Position, Rotation, Velocity, TargetPosition, Steering); ECS_SYSTEM(ECS, entityFollowPath, EcsOnUpdate, Path); - ECS_SYSTEM(ECS, entityHarvestTaskSystem, EcsOnUpdate, Position, Rotation, HarvestTask); + //ECS_SYSTEM(ECS, entityHarvestTaskSystem, EcsOnUpdate, Position, Rotation, HarvestTask); + ECS_SYSTEM(ECS, updateUnitActions, EcsOnUpdate, UnitAction); //ECS_SYSTEM(ECS, entityUpdateAnimationState, EcsOnUpdate, Velocity, AnimationType); ECS_SYSTEM(ECS, entityUpdateAnimation, EcsOnUpdate, Animation, TextureRegion); @@ -213,6 +218,7 @@ void deinit(void *userData) { bzStackAllocDestroy(&gameCopy.stackAlloc); bzObjectPoolDestroy(gameCopy.pools.pathData); + bzObjectPoolDestroy(gameCopy.pools.actions); bzSpatialGridDestroy(gameCopy.entityGrid); } @@ -271,7 +277,8 @@ void imguiRender(float dt, void *userData) { createWorker((Position) {1100, 400}, (Size) {10, 10}, game->entityGrid, &game->map.tilesets[2], 1322); } - igText("Num paths from pool available: %llu", bzObjectPoolGetNumFree(game->pools.pathData)); + igText("PathData pool available: %llu", bzObjectPoolGetNumFree(game->pools.pathData)); + igText("Action pool available: %llu", bzObjectPoolGetNumFree(game->pools.actions)); const char *inputState = "NONE"; switch (input->state) { case INPUT_NONE: diff --git a/game/map_init.c b/game/map_init.c index 0d1330f..26e42bc 100644 --- a/game/map_init.c +++ b/game/map_init.c @@ -148,6 +148,7 @@ ecs_entity_t createWorker(Position position, Size size, BzSpatialGrid *grid, BzT .curFrame = 0, .elapsed = 0.0f, }); + ecs_set(ECS, e, UnitAction, {NULL, NULL}); ecs_add_id(ECS, e, Selectable); ecs_add_id(ECS, e, Unit); ecs_add_id(ECS, e, Worker); diff --git a/game/systems.h b/game/systems.h index dd88113..f5e6b65 100644 --- a/game/systems.h +++ b/game/systems.h @@ -7,9 +7,18 @@ typedef struct Game Game; -void entityClearTasks(const ecs_entity_t entity); bool entitySetPath(const ecs_entity_t entity, const Vector2 target, Game *game); +/********************************** + * AI systems + **********************************/ + +/* + * 0: Game (singleton) + * 1: UnitAction + */ +void updateUnitActions(ecs_iter_t *it); + /********************************** * Entity Systems diff --git a/game/systems_ai.c b/game/systems_ai.c new file mode 100644 index 0000000..b592651 --- /dev/null +++ b/game/systems_ai.c @@ -0,0 +1,13 @@ +#include "systems.h" + +#include "game_state.h" + +void updateUnitActions(ecs_iter_t *it) { + Game *game = ecs_singleton_get_mut(ECS, Game); + UnitAction *action = ecs_field(it, UnitAction, 1); + + for (i32 i = 0; i < it->count; i++) { + ecs_entity_t entity = it->entities[i]; + handleAction(entity, &action[i], game); + } +} diff --git a/game/systems_entity.c b/game/systems_entity.c index c36398d..e3b6459 100644 --- a/game/systems_entity.c +++ b/game/systems_entity.c @@ -8,9 +8,6 @@ #include #include -void entityClearTasks(const ecs_entity_t entity) { - ecs_remove(ECS, entity, HarvestTask); -} bool entitySetPath(const ecs_entity_t entity, const Vector2 target, Game *game) { const Vector2 *pPath = ecs_get(ECS, entity, Position); BZ_ASSERT(pPath); @@ -217,6 +214,7 @@ static ecs_entity_t findNearestStorage(ResourceType type) { return closest; } +/* void entityHarvestTaskSystem(ecs_iter_t *it) { Game *game = ecs_singleton_get_mut(ECS, Game); @@ -270,6 +268,7 @@ void entityHarvestTaskSystem(ecs_iter_t *it) { } } + */ void entityUpdateAnimationState(ecs_iter_t *it) { Velocity *velocity = ecs_field(it, Velocity, 1); diff --git a/game/systems_input.c b/game/systems_input.c index c1f8558..d497282 100644 --- a/game/systems_input.c +++ b/game/systems_input.c @@ -83,7 +83,13 @@ void inputUnitAction(Game *game, InputState *input) { while (ecs_query_next(&it)) { for (i32 i = 0; i < it.count; i++) { const ecs_entity_t entity = it.entities[i]; - ecs_set(ECS, entity, HarvestTask, {taskEntity}); + const Position target = *ecs_get(ECS, taskEntity, Position); + addAction(entity, game, &(const Action) { + .type = ACTION_MOVE_TO, + .as.moveTo.target = target, + .as.moveTo.proximityThreshold = 10.0f, + }); + //ecs_set(ECS, entity, HarvestTask, {taskEntity}); goto while_break; } } diff --git a/game/unit_actions.c b/game/unit_actions.c new file mode 100644 index 0000000..15001a1 --- /dev/null +++ b/game/unit_actions.c @@ -0,0 +1,93 @@ +#include "unit_actions.h" +#include "game_state.h" +#include "components.h" +#include "systems.h" + +#include + +void actionMoveTo(ecs_entity_t entity, Action *action, Game *game) { + const Vector2 target = action->as.moveTo.target; + if (!ecs_has(ECS, entity, Path)) { + entitySetPath(entity, target, game); + return; + } + Vector2 pos = *ecs_get(ECS, entity, Position); + + f32 dst = Vector2Distance(pos, target); + if (dst < action->as.moveTo.proximityThreshold) { + action->finished = true; + } +} +void actionCollectResource(ecs_entity_t entity, Action *action, Game *game) { + +} +void actionDepositResource(ecs_entity_t entity, Action *action, Game *game) { + +} + +void handleAction(ecs_entity_t entity, UnitAction *unitAction, Game *game) { + Action *action = unitAction->first; + if (action == NULL) 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; + } + 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; + } +} diff --git a/game/unit_actions.h b/game/unit_actions.h new file mode 100644 index 0000000..f490303 --- /dev/null +++ b/game/unit_actions.h @@ -0,0 +1,51 @@ +#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 { + +} ActionCollectResource; +typedef struct ActionDepositResource { + +} ActionDepositResource; + +typedef struct Action { + ActionType type; + union { + ActionMoveTo moveTo; + ActionCollectResource collectResource; + ActionDepositResource depositResource; + } as; + + f32 elapsed; + bool finished; + + struct Action *next; +} Action; + +typedef struct UnitAction { + Action *first; + Action *last; +} UnitAction; + +void handleAction(ecs_entity_t entity, UnitAction *unitAction, Game *game); +void clearActions(ecs_entity_t entity, Game *game); +void addAction(ecs_entity_t entity, Game *game, const Action *action); + +#endif //PIXELDEFENSE_UNIT_ACTIONS_H diff --git a/game/unit_ai.c b/game/unit_ai.c new file mode 100644 index 0000000..a0bf66a --- /dev/null +++ b/game/unit_ai.c @@ -0,0 +1 @@ +#include "unit_ai.h" diff --git a/game/unit_ai.h b/game/unit_ai.h new file mode 100644 index 0000000..1366d42 --- /dev/null +++ b/game/unit_ai.h @@ -0,0 +1,5 @@ +#ifndef PIXELDEFENSE_UNIT_AI_H +#define PIXELDEFENSE_UNIT_AI_H + + +#endif //PIXELDEFENSE_UNIT_AI_H