Limit how many workers can harvest the same resource
This commit is contained in:
@@ -53,10 +53,14 @@ BzBTStatus aiEvadeTarget(AIBlackboard *data, f32 dt) {
|
|||||||
BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt) {
|
BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt) {
|
||||||
ecs_entity_t harvestTarget = data->as.worker.harvestTarget;
|
ecs_entity_t harvestTarget = data->as.worker.harvestTarget;
|
||||||
if (ecs_is_alive(ECS, harvestTarget)) {
|
if (ecs_is_alive(ECS, harvestTarget)) {
|
||||||
BZ_ASSERT(ecs_has_id(ECS, harvestTarget, Harvestable));
|
BZ_ASSERT(ecs_has_id(ECS, harvestTarget, ecs_id(Harvestable)));
|
||||||
// Target still alive, no need to find next harvestable
|
Harvestable harvestable = *ecs_get(ECS, harvestTarget, Harvestable);
|
||||||
data->moveToPos = data->as.worker.harvestPos;
|
|
||||||
return BZ_BT_SUCCESS;
|
if (harvestable.harvestCount < harvestable.harvestLimit) {
|
||||||
|
// Target still alive, no need to find next harvestable
|
||||||
|
data->moveToPos = data->as.worker.harvestPos;
|
||||||
|
return BZ_BT_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BZ_ASSERT(ecs_has(ECS, data->entity, Worker));
|
BZ_ASSERT(ecs_has(ECS, data->entity, Worker));
|
||||||
@@ -82,10 +86,13 @@ BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt) {
|
|||||||
while (bzSpatialGridQueryNext(&it)) {
|
while (bzSpatialGridQueryNext(&it)) {
|
||||||
ecs_entity_t entity = *(ecs_entity_t *) it.data;
|
ecs_entity_t entity = *(ecs_entity_t *) it.data;
|
||||||
if (!ecs_is_alive(ECS, entity)) continue;
|
if (!ecs_is_alive(ECS, entity)) continue;
|
||||||
if (!ecs_has_id(ECS, entity, Harvestable) ||
|
if (!ecs_has_id(ECS, entity, ecs_id(Harvestable)) ||
|
||||||
!ecs_has(ECS, entity, Resource) ||
|
!ecs_has(ECS, entity, Resource) ||
|
||||||
!ecs_has(ECS, entity, Position))
|
!ecs_has(ECS, entity, Position))
|
||||||
continue;
|
continue;
|
||||||
|
Harvestable harvestable = *ecs_get(ECS, entity, Harvestable);
|
||||||
|
if (harvestable.harvestCount >= harvestable.harvestLimit)
|
||||||
|
continue;
|
||||||
Resource resource = *ecs_get(ECS, entity, Resource);
|
Resource resource = *ecs_get(ECS, entity, Resource);
|
||||||
Position resPos = *ecs_get(ECS, entity, Position);
|
Position resPos = *ecs_get(ECS, entity, Position);
|
||||||
if (resource.type != harvestType) continue;
|
if (resource.type != harvestType) continue;
|
||||||
@@ -151,7 +158,11 @@ BzBTStatus aiHarvestRes(AIBlackboard *data, f32 dt) {
|
|||||||
if (!ecs_is_alive(ECS, harvestTarget))
|
if (!ecs_is_alive(ECS, harvestTarget))
|
||||||
return BZ_BT_FAIL;
|
return BZ_BT_FAIL;
|
||||||
|
|
||||||
BZ_ASSERT(ecs_has_id(ECS, harvestTarget, Harvestable));
|
BZ_ASSERT(ecs_has_id(ECS, harvestTarget, ecs_id(Harvestable)));
|
||||||
|
Harvestable *harvestable = ecs_get_mut(ECS, harvestTarget, Harvestable);
|
||||||
|
if (harvestable->harvestCount >= harvestable->harvestLimit)
|
||||||
|
return BZ_BT_FAIL;
|
||||||
|
harvestable->harvestCount++;
|
||||||
if (data->elapsed < worker->collectSpeed) {
|
if (data->elapsed < worker->collectSpeed) {
|
||||||
data->elapsed += dt;
|
data->elapsed += dt;
|
||||||
return BZ_BT_RUNNING;
|
return BZ_BT_RUNNING;
|
||||||
|
|||||||
@@ -95,10 +95,21 @@ ecs_entity_t placeBuilding(Game *game, BuildingType type,
|
|||||||
ecs_set(ECS, building, AddPopCapacity, {10});
|
ecs_set(ECS, building, AddPopCapacity, {10});
|
||||||
ecs_add_id(ECS, building, Storage);
|
ecs_add_id(ECS, building, Storage);
|
||||||
break;
|
break;
|
||||||
|
case BUILDING_HOUSE_01:
|
||||||
|
case BUILDING_HOUSE_02:
|
||||||
|
case BUILDING_HOUSE_03:
|
||||||
|
case BUILDING_HOUSE_04:
|
||||||
|
case BUILDING_HOUSE_05:
|
||||||
|
case BUILDING_HOUSE_06:
|
||||||
|
ecs_set(ECS, building, AddPopCapacity, {5});
|
||||||
|
ecs_add_id(ECS, building, Storage);
|
||||||
|
break;
|
||||||
case BUILDING_WHEAT_0:
|
case BUILDING_WHEAT_0:
|
||||||
case BUILDING_WHEAT_1:
|
case BUILDING_WHEAT_1:
|
||||||
hasCollision = false;
|
hasCollision = false;
|
||||||
ecs_add_id(ECS, building, Harvestable);
|
ecs_set(ECS, building, Harvestable, {
|
||||||
|
.harvestLimit = 1
|
||||||
|
});
|
||||||
ecs_set(ECS, building, Resource, {RES_FOOD, INT32_MAX});
|
ecs_set(ECS, building, Resource, {RES_FOOD, INT32_MAX});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -178,7 +189,7 @@ Vector2 getPositionNearBuilding(ecs_entity_t building, Vector2 fromPos) {
|
|||||||
Vector2 center = entityGetCenter(pos, hitbox);
|
Vector2 center = entityGetCenter(pos, hitbox);
|
||||||
|
|
||||||
Vector2 size = {hitbox.width, hitbox.height};
|
Vector2 size = {hitbox.width, hitbox.height};
|
||||||
size = Vector2SubtractValue(size, 10.0f);
|
size = Vector2SubtractValue(size, 5.0f);
|
||||||
|
|
||||||
Vector2 dir = Vector2Normalize(Vector2Subtract(fromPos, center));
|
Vector2 dir = Vector2Normalize(Vector2Subtract(fromPos, center));
|
||||||
dir = Vector2Multiply(dir, size);
|
dir = Vector2Multiply(dir, size);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ ECS_COMPONENT_DECLARE(Worker);
|
|||||||
ECS_COMPONENT_DECLARE(Building);
|
ECS_COMPONENT_DECLARE(Building);
|
||||||
ECS_COMPONENT_DECLARE(Unit);
|
ECS_COMPONENT_DECLARE(Unit);
|
||||||
ECS_TAG_DECLARE(Storage);
|
ECS_TAG_DECLARE(Storage);
|
||||||
ECS_TAG_DECLARE(Harvestable);
|
ECS_COMPONENT_DECLARE(Harvestable);
|
||||||
ECS_TAG_DECLARE(Buildable);
|
ECS_TAG_DECLARE(Buildable);
|
||||||
ECS_TAG_DECLARE(Workable);
|
ECS_TAG_DECLARE(Workable);
|
||||||
ECS_TAG_DECLARE(Attackable);
|
ECS_TAG_DECLARE(Attackable);
|
||||||
@@ -90,7 +90,7 @@ void initComponentIDs(ecs_world_t *ecs) {
|
|||||||
ECS_COMPONENT_DEFINE(ecs, Building);
|
ECS_COMPONENT_DEFINE(ecs, Building);
|
||||||
ECS_COMPONENT_DEFINE(ecs, Unit);
|
ECS_COMPONENT_DEFINE(ecs, Unit);
|
||||||
ECS_TAG_DEFINE(ecs, Storage);
|
ECS_TAG_DEFINE(ecs, Storage);
|
||||||
ECS_TAG_DEFINE(ecs, Harvestable);
|
ECS_COMPONENT_DEFINE(ecs, Harvestable);
|
||||||
ECS_TAG_DEFINE(ecs, Buildable);
|
ECS_TAG_DEFINE(ecs, Buildable);
|
||||||
ECS_TAG_DEFINE(ecs, Workable);
|
ECS_TAG_DEFINE(ecs, Workable);
|
||||||
ECS_TAG_DEFINE(ecs, Attackable);
|
ECS_TAG_DEFINE(ecs, Attackable);
|
||||||
|
|||||||
@@ -270,7 +270,11 @@ typedef struct Building {
|
|||||||
} Building;
|
} Building;
|
||||||
extern ECS_TAG_DECLARE(Storage);
|
extern ECS_TAG_DECLARE(Storage);
|
||||||
extern ECS_COMPONENT_DECLARE(Building);
|
extern ECS_COMPONENT_DECLARE(Building);
|
||||||
extern ECS_TAG_DECLARE(Harvestable);
|
typedef struct Harvestable {
|
||||||
|
i32 harvestLimit;
|
||||||
|
i32 harvestCount;
|
||||||
|
} Harvestable;
|
||||||
|
extern ECS_COMPONENT_DECLARE(Harvestable);
|
||||||
extern ECS_TAG_DECLARE(Buildable);
|
extern ECS_TAG_DECLARE(Buildable);
|
||||||
extern ECS_TAG_DECLARE(Attackable);
|
extern ECS_TAG_DECLARE(Attackable);
|
||||||
|
|
||||||
|
|||||||
@@ -487,7 +487,6 @@ static void renderGame(Game *game, float dt) {
|
|||||||
Rectangle dst = {p[i].x, p[i].y - s[i].y,
|
Rectangle dst = {p[i].x, p[i].y - s[i].y,
|
||||||
s[i].x, s[i].y};
|
s[i].x, s[i].y};
|
||||||
|
|
||||||
DrawCircleV(p[i], 1.0f, BLUE);
|
|
||||||
Vector2 origin = {dst.width * 0.5f, dst.height};
|
Vector2 origin = {dst.width * 0.5f, dst.height};
|
||||||
dst.x += origin.x;
|
dst.x += origin.x;
|
||||||
dst.y += origin.y;
|
dst.y += origin.y;
|
||||||
@@ -666,7 +665,6 @@ void igInspectWindow(ecs_entity_t entity, bool *open) {
|
|||||||
igTagCheckbox("Selectable", ECS, entity, Selectable);
|
igTagCheckbox("Selectable", ECS, entity, Selectable);
|
||||||
igTagCheckbox("Selected", ECS, entity, Selected);
|
igTagCheckbox("Selected", ECS, entity, Selected);
|
||||||
igTagCheckbox("Storage", ECS, entity, Storage);
|
igTagCheckbox("Storage", ECS, entity, Storage);
|
||||||
igTagCheckbox("Harvestable", ECS, entity, Harvestable);
|
|
||||||
igTagCheckbox("Attackable", ECS, entity, Attackable);
|
igTagCheckbox("Attackable", ECS, entity, Attackable);
|
||||||
}
|
}
|
||||||
if (ecs_has(ECS, entity, BzBTState) &&
|
if (ecs_has(ECS, entity, BzBTState) &&
|
||||||
|
|||||||
@@ -144,7 +144,9 @@ bool initRocksLayer(BzTileMap *map, BzTileLayer *layer) {
|
|||||||
ecs_set(ECS, e, TextureRegion, {tileset->tiles, getTextureRect(tileID)});
|
ecs_set(ECS, e, TextureRegion, {tileset->tiles, getTextureRect(tileID)});
|
||||||
ecs_set(ECS, e, Resource, {RES_GOLD, 80});
|
ecs_set(ECS, e, Resource, {RES_GOLD, 80});
|
||||||
ecs_add_id(ECS, e, Selectable);
|
ecs_add_id(ECS, e, Selectable);
|
||||||
ecs_add_id(ECS, e, Harvestable);
|
ecs_set(ECS, e, Harvestable, {
|
||||||
|
.harvestLimit = 4,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -185,7 +187,9 @@ bool initTreesLayer(BzTileMap *map, BzTileLayer *layer) {
|
|||||||
ecs_set(ECS, e, TextureRegion, {tileset->tiles, getTextureRect(tileID)});
|
ecs_set(ECS, e, TextureRegion, {tileset->tiles, getTextureRect(tileID)});
|
||||||
ecs_set(ECS, e, Resource, {RES_WOOD, 20});
|
ecs_set(ECS, e, Resource, {RES_WOOD, 20});
|
||||||
ecs_add_id(ECS, e, Selectable);
|
ecs_add_id(ECS, e, Selectable);
|
||||||
ecs_add_id(ECS, e, Harvestable);
|
ecs_set(ECS, e, Harvestable, {
|
||||||
|
.harvestLimit = 4
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,13 @@
|
|||||||
#include "../game_state.h"
|
#include "../game_state.h"
|
||||||
#include "../ai_actions.h"
|
#include "../ai_actions.h"
|
||||||
|
|
||||||
|
void resetHarvestCount(ecs_iter_t *it) {
|
||||||
|
Harvestable *harvestable = ecs_field(it, Harvestable, 1);
|
||||||
|
|
||||||
|
for (i32 i = 0; i < it->count; i++) {
|
||||||
|
harvestable[i].harvestCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void updateAISystem(ecs_iter_t *it) {
|
void updateAISystem(ecs_iter_t *it) {
|
||||||
Game *game = ecs_singleton_get_mut(ECS, Game);
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "../sounds.h"
|
#include "../sounds.h"
|
||||||
|
|
||||||
i32 harvestEvent(ecs_entity_t entity, HarvestEvent event) {
|
i32 harvestEvent(ecs_entity_t entity, HarvestEvent event) {
|
||||||
BZ_ASSERT(ecs_has_id(ECS, entity, Harvestable));
|
BZ_ASSERT(ecs_has_id(ECS, entity, ecs_id(Harvestable)));
|
||||||
BZ_ASSERT(ecs_has(ECS, entity, Resource));
|
BZ_ASSERT(ecs_has(ECS, entity, Resource));
|
||||||
|
|
||||||
ecs_set(ECS, entity, Easing, {
|
ecs_set(ECS, entity, Easing, {
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ void inputUnitAction(Game *game, InputState *input) {
|
|||||||
input->cursor = CURSOR_NONE;
|
input->cursor = CURSOR_NONE;
|
||||||
ecs_entity_t taskEntity;
|
ecs_entity_t taskEntity;
|
||||||
bool isWorker = selectedAnyHasID(query, ecs_id(Worker));
|
bool isWorker = selectedAnyHasID(query, ecs_id(Worker));
|
||||||
if (isWorker && (taskEntity = queryEntity(game->entityGrid, input->mouseWorld, Harvestable))) {
|
if (isWorker && (taskEntity = queryEntity(game->entityGrid, input->mouseWorld, ecs_id(Harvestable)))) {
|
||||||
Resource resource = *ecs_get(ECS, taskEntity, Resource);
|
Resource resource = *ecs_get(ECS, taskEntity, Resource);
|
||||||
switch (resource.type) {
|
switch (resource.type) {
|
||||||
case RES_WOOD:
|
case RES_WOOD:
|
||||||
@@ -99,12 +99,39 @@ void inputUnitAction(Game *game, InputState *input) {
|
|||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
if (isInputBtnJustUp(input, actionBtn)) {
|
if (isInputBtnJustUp(input, actionBtn)) {
|
||||||
|
const f32 hRadius = 10.0f;
|
||||||
|
const Vector2 mPos = input->mouseWorld;
|
||||||
|
BzSpatialGridIter gridIt = bzSpatialGridIter(game->entityGrid,
|
||||||
|
mPos.x - hRadius, mPos.y - hRadius,
|
||||||
|
mPos.x + hRadius, mPos.y + hRadius);
|
||||||
ecs_defer_begin(ECS);
|
ecs_defer_begin(ECS);
|
||||||
ecs_iter_t it = ecs_query_iter(ECS, query);
|
ecs_iter_t it = ecs_query_iter(ECS, query);
|
||||||
while (ecs_query_next(&it)) {
|
while (ecs_query_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];
|
||||||
Position target = *ecs_get(ECS, taskEntity, Position);
|
|
||||||
|
bool hasNext = false;
|
||||||
|
ecs_entity_t harvestEntity = 0;
|
||||||
|
do {
|
||||||
|
hasNext = bzSpatialGridQueryNext(&gridIt);
|
||||||
|
if (!hasNext) break;
|
||||||
|
harvestEntity = *(ecs_entity_t *) gridIt.data;
|
||||||
|
if (!ecs_has(ECS, harvestEntity, Resource))
|
||||||
|
continue;
|
||||||
|
const Resource *res = ecs_get(ECS, harvestEntity, Resource);
|
||||||
|
if (res->type != resource.type)
|
||||||
|
continue;
|
||||||
|
if (!ecs_has(ECS, harvestEntity, Harvestable))
|
||||||
|
continue;
|
||||||
|
Harvestable *harvestable = ecs_get_mut(ECS, harvestEntity, Harvestable);
|
||||||
|
if (harvestable->harvestCount >= harvestable->harvestLimit)
|
||||||
|
continue;
|
||||||
|
harvestable->harvestCount++;
|
||||||
|
break;
|
||||||
|
} while (hasNext);
|
||||||
|
|
||||||
|
if (!hasNext) break;
|
||||||
|
Position target = *ecs_get(ECS, harvestEntity, Position);
|
||||||
|
|
||||||
f32 proximity = 6.0f;
|
f32 proximity = 6.0f;
|
||||||
if (resource.type == RES_FOOD)
|
if (resource.type == RES_FOOD)
|
||||||
@@ -116,7 +143,7 @@ void inputUnitAction(Game *game, InputState *input) {
|
|||||||
setAIBehaviour(entity, game->BTs.workerHarvest, &(AIBlackboard) {
|
setAIBehaviour(entity, game->BTs.workerHarvest, &(AIBlackboard) {
|
||||||
.as.worker = {
|
.as.worker = {
|
||||||
.harvestType = resource.type,
|
.harvestType = resource.type,
|
||||||
.harvestTarget = taskEntity,
|
.harvestTarget = harvestEntity,
|
||||||
.harvestPos = target,
|
.harvestPos = target,
|
||||||
},
|
},
|
||||||
.proximity = proximity,
|
.proximity = proximity,
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ void setupSystems() {
|
|||||||
ECS_SYSTEM(ECS, entityFollowPath, EcsOnUpdate, Path);
|
ECS_SYSTEM(ECS, entityFollowPath, EcsOnUpdate, Path);
|
||||||
ECS_SYSTEM(ECS, entityUpdateArms, EcsOnUpdate, Position, Velocity, Rotation, Orientation, Arms);
|
ECS_SYSTEM(ECS, entityUpdateArms, EcsOnUpdate, Position, Velocity, Rotation, Orientation, Arms);
|
||||||
|
|
||||||
|
ECS_SYSTEM(ECS, resetHarvestCount, EcsOnUpdate, Harvestable);
|
||||||
ECS_SYSTEM(ECS, updateAISystem, EcsOnUpdate, BzBTState);
|
ECS_SYSTEM(ECS, updateAISystem, EcsOnUpdate, BzBTState);
|
||||||
|
|
||||||
ECS_SYSTEM(ECS, updateAnimationState, EcsOnUpdate, Animation, TextureRegion);
|
ECS_SYSTEM(ECS, updateAnimationState, EcsOnUpdate, Animation, TextureRegion);
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ bool entitySetPath(const ecs_entity_t entity, const Vector2 target, Game *game);
|
|||||||
* AI systems
|
* AI systems
|
||||||
**********************************/
|
**********************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 1. Harvestable
|
||||||
|
*/
|
||||||
|
void resetHarvestCount(ecs_iter_t *it);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 0: Game (singleton)
|
* 0: Game (singleton)
|
||||||
* 1: BzBTState
|
* 1: BzBTState
|
||||||
|
|||||||
Reference in New Issue
Block a user