Implement harvest worker AI
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
#include "map_layers.h"
|
#include "map_layers.h"
|
||||||
|
|
||||||
|
#include <raymath.h>
|
||||||
|
|
||||||
bool canPlaceBuilding(BzTileMap *map, BuildingType type, BzTile tileX, BzTile tileY) {
|
bool canPlaceBuilding(BzTileMap *map, BuildingType type, BzTile tileX, BzTile tileY) {
|
||||||
i32 sizeX, sizeY;
|
i32 sizeX, sizeY;
|
||||||
getBuildingSize(type, &sizeX, &sizeY);
|
getBuildingSize(type, &sizeX, &sizeY);
|
||||||
@@ -73,3 +75,20 @@ ecs_entity_t placeBuilding(BzTileMap *map, BuildingType type, BzTile tileX, BzTi
|
|||||||
return e;
|
return e;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector2 getPositionNearBuilding(ecs_entity_t building, Vector2 fromPos) {
|
||||||
|
BZ_ASSERT(ecs_is_alive(ECS, building));
|
||||||
|
BZ_ASSERT(ecs_has(ECS, building, Position));
|
||||||
|
BZ_ASSERT(ecs_has(ECS, building, Size));
|
||||||
|
|
||||||
|
Vector2 pos = *ecs_get(ECS, building, Position);
|
||||||
|
Vector2 size = *ecs_get(ECS, building, Size);
|
||||||
|
|
||||||
|
size = Vector2SubtractValue(size, 10.0f);
|
||||||
|
|
||||||
|
Vector2 dir = Vector2Normalize(Vector2Subtract(fromPos, pos));
|
||||||
|
dir = Vector2Multiply(dir, size);
|
||||||
|
|
||||||
|
pos = Vector2Add(pos, dir);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,4 +9,6 @@
|
|||||||
bool canPlaceBuilding(BzTileMap *map, BuildingType type, i32 tileX, i32 tileY);
|
bool canPlaceBuilding(BzTileMap *map, BuildingType type, i32 tileX, i32 tileY);
|
||||||
ecs_entity_t placeBuilding(BzTileMap *map, BuildingType type, i32 tileX, i32 tileY);
|
ecs_entity_t placeBuilding(BzTileMap *map, BuildingType type, i32 tileX, i32 tileY);
|
||||||
|
|
||||||
|
Vector2 getPositionNearBuilding(ecs_entity_t building, Vector2 fromPos);
|
||||||
|
|
||||||
#endif //PIXELDEFENSE_BUILDINGS_H
|
#endif //PIXELDEFENSE_BUILDINGS_H
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "components.h"
|
#include "components.h"
|
||||||
|
|
||||||
|
#include "unit_ai.h"
|
||||||
|
#include "unit_actions.h"
|
||||||
|
|
||||||
ECS_TAG_DECLARE(TextureTerrain);
|
ECS_TAG_DECLARE(TextureTerrain);
|
||||||
ECS_TAG_DECLARE(TextureBuildings);
|
ECS_TAG_DECLARE(TextureBuildings);
|
||||||
ECS_TAG_DECLARE(TextureEntities);
|
ECS_TAG_DECLARE(TextureEntities);
|
||||||
@@ -23,7 +26,9 @@ ECS_COMPONENT_DECLARE(TextureRegion);
|
|||||||
|
|
||||||
ECS_COMPONENT_DECLARE(Animation);
|
ECS_COMPONENT_DECLARE(Animation);
|
||||||
|
|
||||||
|
ECS_COMPONENT_DECLARE(UnitAI);
|
||||||
ECS_COMPONENT_DECLARE(UnitAction);
|
ECS_COMPONENT_DECLARE(UnitAction);
|
||||||
|
|
||||||
ECS_TAG_DECLARE(Selectable);
|
ECS_TAG_DECLARE(Selectable);
|
||||||
ECS_TAG_DECLARE(Selected);
|
ECS_TAG_DECLARE(Selected);
|
||||||
|
|
||||||
@@ -60,7 +65,9 @@ void initComponentIDs(ecs_world_t *ecs) {
|
|||||||
|
|
||||||
ECS_COMPONENT_DEFINE(ecs, Animation);
|
ECS_COMPONENT_DEFINE(ecs, Animation);
|
||||||
|
|
||||||
|
ECS_COMPONENT_DEFINE(ecs, UnitAI);
|
||||||
ECS_COMPONENT_DEFINE(ecs, UnitAction);
|
ECS_COMPONENT_DEFINE(ecs, UnitAction);
|
||||||
|
|
||||||
ECS_TAG_DEFINE(ecs, Selectable);
|
ECS_TAG_DEFINE(ecs, Selectable);
|
||||||
ECS_TAG_DEFINE(ecs, Selected);
|
ECS_TAG_DEFINE(ecs, Selected);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <flecs.h>
|
#include <flecs.h>
|
||||||
|
|
||||||
#include "game_tileset.h"
|
#include "game_tileset.h"
|
||||||
#include "unit_actions.h"
|
|
||||||
|
|
||||||
extern ECS_TAG_DECLARE(TextureTerrain);
|
extern ECS_TAG_DECLARE(TextureTerrain);
|
||||||
extern ECS_TAG_DECLARE(TextureBuildings);
|
extern ECS_TAG_DECLARE(TextureBuildings);
|
||||||
@@ -126,10 +125,7 @@ typedef struct EntityArms {
|
|||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
||||||
extern ECS_COMPONENT_DECLARE(UnitAction);
|
extern ECS_COMPONENT_DECLARE(UnitAction);
|
||||||
|
extern ECS_COMPONENT_DECLARE(UnitAI);
|
||||||
typedef struct ActionOverseer {
|
|
||||||
|
|
||||||
} ActionOverseer;
|
|
||||||
|
|
||||||
extern ECS_TAG_DECLARE(Selectable);
|
extern ECS_TAG_DECLARE(Selectable);
|
||||||
extern ECS_TAG_DECLARE(Selected);
|
extern ECS_TAG_DECLARE(Selected);
|
||||||
|
|||||||
20
game/main.c
20
game/main.c
@@ -8,6 +8,8 @@
|
|||||||
#include "map_init.h"
|
#include "map_init.h"
|
||||||
#include "map_layers.h"
|
#include "map_layers.h"
|
||||||
#include "buildings.h"
|
#include "buildings.h"
|
||||||
|
#include "unit_ai.h"
|
||||||
|
#include "unit_actions.h"
|
||||||
|
|
||||||
#include "pathfinding.h"
|
#include "pathfinding.h"
|
||||||
|
|
||||||
@@ -112,7 +114,7 @@ bool init(void *userData) {
|
|||||||
.objectsPerPage = 512
|
.objectsPerPage = 512
|
||||||
});
|
});
|
||||||
game->pools.actions = bzObjectPoolCreate(&(BzObjectPoolDesc) {
|
game->pools.actions = bzObjectPoolCreate(&(BzObjectPoolDesc) {
|
||||||
.objectSize = sizeof(UnitAction),
|
.objectSize = sizeof(Action),
|
||||||
.objectsPerPage = 1024,
|
.objectsPerPage = 1024,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -176,7 +178,10 @@ bool init(void *userData) {
|
|||||||
ECS_SYSTEM(ECS, entityFollowPath, EcsOnUpdate, Path);
|
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, handleUnitActionsSystem, EcsOnUpdate, UnitAction);
|
||||||
|
ECS_SYSTEM(ECS, updateUnitAISystem, EcsOnUpdate, UnitAI, UnitAction);
|
||||||
|
// Needs to be called after AI update, since it removes finished actions
|
||||||
|
ECS_SYSTEM(ECS, updateUnitActionsSystem, EcsOnUpdate, UnitAction);
|
||||||
|
|
||||||
//ECS_SYSTEM(ECS, entityUpdateAnimationState, EcsOnUpdate, Velocity, AnimationType);
|
//ECS_SYSTEM(ECS, entityUpdateAnimationState, EcsOnUpdate, Velocity, AnimationType);
|
||||||
ECS_SYSTEM(ECS, entityUpdateAnimation, EcsOnUpdate, Animation, TextureRegion);
|
ECS_SYSTEM(ECS, entityUpdateAnimation, EcsOnUpdate, Animation, TextureRegion);
|
||||||
@@ -306,7 +311,16 @@ void imguiRender(float dt, void *userData) {
|
|||||||
while (ecs_iter_next(&it)) {
|
while (ecs_iter_next(&it)) {
|
||||||
for (i32 i = 0; i < it.count; i++) {
|
for (i32 i = 0; i < it.count; i++) {
|
||||||
ecs_entity_t entity = it.entities[i];
|
ecs_entity_t entity = it.entities[i];
|
||||||
igText("\tEntity %llu", entity);
|
igText("Entity %llu", entity);
|
||||||
|
igText("Actions:");
|
||||||
|
const Action *pAction = NULL;
|
||||||
|
if (ecs_has(ECS, entity, UnitAction)) {
|
||||||
|
pAction = ecs_get(ECS, entity, UnitAction)->first;
|
||||||
|
}
|
||||||
|
while (pAction) {
|
||||||
|
igText("\t%d: %s", pAction->type, actionTypeToPrettyStr(pAction->type));
|
||||||
|
pAction = pAction->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
#include "map_layers.h"
|
#include "map_layers.h"
|
||||||
|
|
||||||
|
#include "unit_ai.h"
|
||||||
|
#include "unit_actions.h"
|
||||||
|
|
||||||
bool initGameObjectsLayer(BzTileMap *map, BzTileObjectGroup *objectGroup) {
|
bool initGameObjectsLayer(BzTileMap *map, BzTileObjectGroup *objectGroup) {
|
||||||
Game *game = ecs_singleton_get_mut(ECS, Game);
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
for (i32 i = 0; i < objectGroup->objectCount; i++) {
|
for (i32 i = 0; i < objectGroup->objectCount; i++) {
|
||||||
@@ -67,15 +70,19 @@ bool initBuildingsLayer(BzTileMap *map, BzTileLayer *layer) {
|
|||||||
buildingTile = getTileBuilding(buildingTile);
|
buildingTile = getTileBuilding(buildingTile);
|
||||||
if (buildingTile == BUILDING_NONE || ownerTile == 0) continue;
|
if (buildingTile == BUILDING_NONE || ownerTile == 0) continue;
|
||||||
// We have a building
|
// We have a building
|
||||||
TileSize size = {};
|
TileSize tileSize = {};
|
||||||
getBuildingSize(buildingTile, &size.sizeX, &size.sizeY);
|
getBuildingSize(buildingTile, &tileSize.sizeX, &tileSize.sizeY);
|
||||||
bzTileLayerSetTile(ownershipLayer, 0, x, y, size.sizeX, size.sizeY);
|
bzTileLayerSetTile(ownershipLayer, 0, x, y, tileSize.sizeX, tileSize.sizeY);
|
||||||
|
|
||||||
const i32 tileWidth = map->tileWidth;
|
const i32 tileWidth = map->tileWidth;
|
||||||
const i32 tileHeight = map->tileHeight;
|
const i32 tileHeight = map->tileHeight;
|
||||||
ecs_entity_t e = ecs_new_id(ECS);
|
ecs_entity_t e = ecs_new_id(ECS);
|
||||||
ecs_set(ECS, e, Position, {.x = x * tileWidth, .y = y * tileHeight});
|
Size size = {.x = tileSize.sizeX * tileWidth, .y = tileSize.sizeY * tileHeight };
|
||||||
ecs_set(ECS, e, TileSize, {.sizeX = size.sizeX * tileWidth, .sizeY = size.sizeY * tileHeight});
|
ecs_set_ptr(ECS, e, Size, &size);
|
||||||
|
ecs_set(ECS, e, Position, {
|
||||||
|
.x = x * tileWidth + size.x * 0.5f,
|
||||||
|
.y = y * tileHeight + size.y * 0.5f
|
||||||
|
});
|
||||||
ownerTile = bzTilesetGetTileID(buildingTileset, ownerTile);
|
ownerTile = bzTilesetGetTileID(buildingTileset, ownerTile);
|
||||||
ownerTile = getTileBuilding(ownerTile);
|
ownerTile = getTileBuilding(ownerTile);
|
||||||
ecs_set(ECS, e, Owner, {.playerID=ownerTile});
|
ecs_set(ECS, e, Owner, {.playerID=ownerTile});
|
||||||
@@ -83,6 +90,9 @@ bool initBuildingsLayer(BzTileMap *map, BzTileLayer *layer) {
|
|||||||
//if (buildingTile == BUILDINGS_WAREHOUSE) {
|
//if (buildingTile == BUILDINGS_WAREHOUSE) {
|
||||||
// ecs_set(ECS, e, Storage, {});
|
// ecs_set(ECS, e, Storage, {});
|
||||||
//}
|
//}
|
||||||
|
if (buildingTile == BUILDING_KEEP) {
|
||||||
|
ecs_set(ECS, e, Storage, {.capacity[RES_WOOD] = 1000});
|
||||||
|
}
|
||||||
|
|
||||||
//bzTileMapUpdateCollider(&GAME.map, x, y);
|
//bzTileMapUpdateCollider(&GAME.map, x, y);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
typedef struct Game Game;
|
typedef struct Game Game;
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
* Utils
|
||||||
|
**********************************/
|
||||||
|
|
||||||
bool entitySetPath(const ecs_entity_t entity, const Vector2 target, Game *game);
|
bool entitySetPath(const ecs_entity_t entity, const Vector2 target, Game *game);
|
||||||
|
|
||||||
/**********************************
|
/**********************************
|
||||||
@@ -17,7 +21,20 @@ bool entitySetPath(const ecs_entity_t entity, const Vector2 target, Game *game);
|
|||||||
* 0: Game (singleton)
|
* 0: Game (singleton)
|
||||||
* 1: UnitAction
|
* 1: UnitAction
|
||||||
*/
|
*/
|
||||||
void updateUnitActions(ecs_iter_t *it);
|
void handleUnitActionsSystem(ecs_iter_t *it);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0: Game (singleton)
|
||||||
|
* 1: UnitAI
|
||||||
|
* 2: UnitAction
|
||||||
|
*/
|
||||||
|
void updateUnitAISystem(ecs_iter_t *it);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0: Game (singleton)
|
||||||
|
* 1: UnitAction
|
||||||
|
*/
|
||||||
|
void updateUnitActionsSystem(ecs_iter_t *it);
|
||||||
|
|
||||||
|
|
||||||
/**********************************
|
/**********************************
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
|
|
||||||
void updateUnitActions(ecs_iter_t *it) {
|
#include "unit_ai.h"
|
||||||
|
#include "unit_actions.h"
|
||||||
|
|
||||||
|
void handleUnitActionsSystem(ecs_iter_t *it) {
|
||||||
Game *game = ecs_singleton_get_mut(ECS, Game);
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
UnitAction *action = ecs_field(it, UnitAction, 1);
|
UnitAction *action = ecs_field(it, UnitAction, 1);
|
||||||
|
|
||||||
@@ -11,3 +14,31 @@ void updateUnitActions(ecs_iter_t *it) {
|
|||||||
handleAction(entity, &action[i], game);
|
handleAction(entity, &action[i], game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateUnitAISystem(ecs_iter_t *it) {
|
||||||
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
|
UnitAI *unitAI = ecs_field(it, UnitAI, 1);
|
||||||
|
UnitAction *action = ecs_field(it, UnitAction, 2);
|
||||||
|
|
||||||
|
for (i32 i = 0; i < it->count; i++) {
|
||||||
|
ecs_entity_t entity = it->entities[i];
|
||||||
|
|
||||||
|
Action *firstAction = action[i].first;
|
||||||
|
if (firstAction)
|
||||||
|
unitAI[i].action = firstAction;
|
||||||
|
else
|
||||||
|
unitAI[i].action = NULL;
|
||||||
|
|
||||||
|
updateUnitAI(entity, &unitAI[i], game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateUnitActionsSystem(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];
|
||||||
|
updateAction(&action[i], game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -194,25 +194,7 @@ void entityFollowPath(ecs_iter_t *it) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ecs_entity_t findNearestStorage(ResourceType type) {
|
|
||||||
ecs_filter_t *storageFilter = ecs_filter(ECS, {
|
|
||||||
.terms = {{ecs_id(Storage)} }
|
|
||||||
});
|
|
||||||
ecs_iter_t it = ecs_filter_iter(ECS, storageFilter);
|
|
||||||
|
|
||||||
ecs_entity_t closest = 0;
|
|
||||||
while (ecs_filter_next(&it)) {
|
|
||||||
Storage *storage = ecs_field(&it, Storage, 1);
|
|
||||||
|
|
||||||
for (i32 i = 0; i < it.count; i++) {
|
|
||||||
if (true || storage[i].capacity[type]) {
|
|
||||||
closest = it.entities[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ecs_filter_fini(storageFilter);
|
|
||||||
return closest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void entityHarvestTaskSystem(ecs_iter_t *it) {
|
void entityHarvestTaskSystem(ecs_iter_t *it) {
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "buildings.h"
|
#include "buildings.h"
|
||||||
#include "pathfinding.h"
|
#include "pathfinding.h"
|
||||||
|
#include "unit_ai.h"
|
||||||
|
#include "unit_actions.h"
|
||||||
|
|
||||||
#include <rlImGui.h>
|
#include <rlImGui.h>
|
||||||
#include <raymath.h>
|
#include <raymath.h>
|
||||||
#include <rlgl.h>
|
#include <rlgl.h>
|
||||||
@@ -84,11 +87,17 @@ void inputUnitAction(Game *game, InputState *input) {
|
|||||||
for (i32 i = 0; i < it.count; i++) {
|
for (i32 i = 0; i < it.count; i++) {
|
||||||
const ecs_entity_t entity = it.entities[i];
|
const ecs_entity_t entity = it.entities[i];
|
||||||
const Position target = *ecs_get(ECS, taskEntity, Position);
|
const Position target = *ecs_get(ECS, taskEntity, Position);
|
||||||
addAction(entity, game, &(const Action) {
|
setUnitAI(entity, game, &(const UnitAI) {
|
||||||
.type = ACTION_MOVE_TO,
|
.type = AI_WORKER_HARVEST,
|
||||||
.as.moveTo.target = target,
|
.as.workerHarvest.resource = RES_WOOD,
|
||||||
.as.moveTo.proximityThreshold = 10.0f,
|
.as.workerHarvest.target = taskEntity,
|
||||||
|
.as.workerHarvest.targetPosition = target
|
||||||
});
|
});
|
||||||
|
//addAction(entity, game, &(const Action) {
|
||||||
|
// .type = ACTION_MOVE_TO,
|
||||||
|
// .as.moveTo.target = target,
|
||||||
|
// .as.moveTo.proximityThreshold = 10.0f,
|
||||||
|
//});
|
||||||
//ecs_set(ECS, entity, HarvestTask, {taskEntity});
|
//ecs_set(ECS, entity, HarvestTask, {taskEntity});
|
||||||
goto while_break;
|
goto while_break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <raymath.h>
|
#include <raymath.h>
|
||||||
|
|
||||||
void actionMoveTo(ecs_entity_t entity, Action *action, Game *game) {
|
void actionMoveTo(ecs_entity_t entity, Action *action, Game *game) {
|
||||||
|
if (action->finished) return;
|
||||||
const Vector2 target = action->as.moveTo.target;
|
const Vector2 target = action->as.moveTo.target;
|
||||||
if (!ecs_has(ECS, entity, Path)) {
|
if (!ecs_has(ECS, entity, Path)) {
|
||||||
entitySetPath(entity, target, game);
|
entitySetPath(entity, target, game);
|
||||||
@@ -16,18 +17,27 @@ void actionMoveTo(ecs_entity_t entity, Action *action, Game *game) {
|
|||||||
f32 dst = Vector2Distance(pos, target);
|
f32 dst = Vector2Distance(pos, target);
|
||||||
if (dst < action->as.moveTo.proximityThreshold) {
|
if (dst < action->as.moveTo.proximityThreshold) {
|
||||||
action->finished = true;
|
action->finished = true;
|
||||||
|
ecs_remove(ECS, entity, Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void actionCollectResource(ecs_entity_t entity, Action *action, Game *game) {
|
void actionCollectResource(ecs_entity_t entity, Action *action, Game *game) {
|
||||||
|
if (action->finished) return;
|
||||||
|
if (action->elapsed > 2.0f) {
|
||||||
|
action->finished = true;
|
||||||
|
ecs_entity_t target = action->as.collectResource.entity;
|
||||||
|
ecs_delete(ECS, target);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
void actionDepositResource(ecs_entity_t entity, Action *action, Game *game) {
|
void actionDepositResource(ecs_entity_t entity, Action *action, Game *game) {
|
||||||
|
if (action->elapsed > 0.2f)
|
||||||
|
action->finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAction(ecs_entity_t entity, UnitAction *unitAction, Game *game) {
|
void handleAction(ecs_entity_t entity, UnitAction *unitAction, Game *game) {
|
||||||
Action *action = unitAction->first;
|
Action *action = unitAction->first;
|
||||||
if (action == NULL) return;
|
if (action == NULL) return;
|
||||||
|
if (action->finished || action->failed) return;
|
||||||
switch (action->type) {
|
switch (action->type) {
|
||||||
case ACTION_NONE:
|
case ACTION_NONE:
|
||||||
break;
|
break;
|
||||||
@@ -44,6 +54,12 @@ void handleAction(ecs_entity_t entity, UnitAction *unitAction, Game *game) {
|
|||||||
BZ_ASSERT(0);
|
BZ_ASSERT(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
void updateAction(UnitAction *unitAction, Game *game) {
|
||||||
|
Action *action = unitAction->first;
|
||||||
|
if (action == NULL) return;
|
||||||
|
if (action->failed) return;
|
||||||
action->elapsed += GetFrameTime();
|
action->elapsed += GetFrameTime();
|
||||||
if (action->finished) {
|
if (action->finished) {
|
||||||
unitAction->first = action->next;
|
unitAction->first = action->next;
|
||||||
@@ -91,3 +107,16 @@ void addAction(ecs_entity_t entity, Game *game, const Action *action) {
|
|||||||
unitAction->last = newAction;
|
unitAction->last = 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ typedef struct ActionMoveTo {
|
|||||||
f32 proximityThreshold;
|
f32 proximityThreshold;
|
||||||
} ActionMoveTo;
|
} ActionMoveTo;
|
||||||
typedef struct ActionCollectResource {
|
typedef struct ActionCollectResource {
|
||||||
|
ecs_entity_t entity;
|
||||||
} ActionCollectResource;
|
} ActionCollectResource;
|
||||||
typedef struct ActionDepositResource {
|
typedef struct ActionDepositResource {
|
||||||
|
ecs_entity_t entity;
|
||||||
} ActionDepositResource;
|
} ActionDepositResource;
|
||||||
|
|
||||||
typedef struct Action {
|
typedef struct Action {
|
||||||
@@ -35,6 +35,7 @@ typedef struct Action {
|
|||||||
|
|
||||||
f32 elapsed;
|
f32 elapsed;
|
||||||
bool finished;
|
bool finished;
|
||||||
|
bool failed;
|
||||||
|
|
||||||
struct Action *next;
|
struct Action *next;
|
||||||
} Action;
|
} Action;
|
||||||
@@ -45,7 +46,10 @@ typedef struct UnitAction {
|
|||||||
} UnitAction;
|
} UnitAction;
|
||||||
|
|
||||||
void handleAction(ecs_entity_t entity, UnitAction *unitAction, Game *game);
|
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 clearActions(ecs_entity_t entity, Game *game);
|
||||||
void addAction(ecs_entity_t entity, Game *game, const Action *action);
|
void addAction(ecs_entity_t entity, Game *game, const Action *action);
|
||||||
|
|
||||||
|
const char *actionTypeToPrettyStr(ActionType type);
|
||||||
|
|
||||||
#endif //PIXELDEFENSE_UNIT_ACTIONS_H
|
#endif //PIXELDEFENSE_UNIT_ACTIONS_H
|
||||||
|
|||||||
132
game/unit_ai.c
132
game/unit_ai.c
@@ -1 +1,133 @@
|
|||||||
#include "unit_ai.h"
|
#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(Storage)}, {ecs_id(Position)} }
|
||||||
|
});
|
||||||
|
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)) {
|
||||||
|
Storage *storage = ecs_field(&it, Storage, 1);
|
||||||
|
Position *storagePos = ecs_field(&it, Position, 2);
|
||||||
|
|
||||||
|
for (i32 i = 0; i < it.count; i++) {
|
||||||
|
f32 dst = Vector2Distance(pos, storagePos[i]);
|
||||||
|
if (storage[i].capacity[type] && 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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
void aiWorkerHarvestUpdate(ecs_entity_t entity, UnitAI *unitAI, Game *game) {
|
||||||
|
const Action *action = unitAI->action;
|
||||||
|
if (action) return;
|
||||||
|
|
||||||
|
AIWorkerHarvest worker = unitAI->as.workerHarvest;
|
||||||
|
|
||||||
|
Position workerPos = *ecs_get(ECS, entity, Position);
|
||||||
|
|
||||||
|
// Finished all tasks, repeat
|
||||||
|
ecs_entity_t target = worker.target;
|
||||||
|
Position targetPos = worker.targetPosition;
|
||||||
|
if (!ecs_is_alive(ECS, target)) {
|
||||||
|
// Find new resource
|
||||||
|
target = findNearestResource(game, targetPos, worker.resource, 20.0f);
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
worker.target = target;
|
||||||
|
worker.targetPosition = *ecs_get(ECS, target, Position);
|
||||||
|
}
|
||||||
|
// Find the closest warehouse
|
||||||
|
Position warehousePos = Vector2Zero();
|
||||||
|
ecs_entity_t warehouse = findNearestStorage(workerPos, worker.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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,51 @@
|
|||||||
#ifndef PIXELDEFENSE_UNIT_AI_H
|
#ifndef PIXELDEFENSE_UNIT_AI_H
|
||||||
#define PIXELDEFENSE_UNIT_AI_H
|
#define PIXELDEFENSE_UNIT_AI_H
|
||||||
|
|
||||||
|
#include <flecs.h>
|
||||||
|
|
||||||
|
#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
|
#endif //PIXELDEFENSE_UNIT_AI_H
|
||||||
|
|||||||
Reference in New Issue
Block a user