Consume pop capacity + evade AI boiler plate

This commit is contained in:
2024-01-23 21:43:58 +01:00
parent 07d1852fb9
commit 0423a962df
14 changed files with 125 additions and 28 deletions

View File

@@ -123,6 +123,17 @@ void bzBTDestroyRoot(BzObjectPool *nodePool, BzBTNode *node) {
bzObjectPoolRelease(nodePool, node);
}
void bzBTSubTree(BzBTNode *tree, BzBTNode *parent) {
// Subtree must not have siblings
BZ_ASSERT(tree->prev == NULL && tree->next == NULL);
if (parent && parent->last) {
parent->last->next = tree;
parent->last = tree;
} else if (parent) {
parent->first = tree;
parent->last = tree;
}
}
BzBTNode *bzBTCompSelector(BzObjectPool *nodePool, BzBTNode *parent) {
return bzBTNodeMake(nodePool, parent, BZ_BT_COMP_SELECTOR);

View File

@@ -86,6 +86,8 @@ BzBTNode *bzBTMakeRoot(BzObjectPool *nodePool);
*/
void bzBTDestroyRoot(BzObjectPool *nodePool, BzBTNode *node);
void bzBTSubTree(BzBTNode *tree, BzBTNode *parent);
/**
* @brief Execute all children in turn until one fails.
*

View File

@@ -42,6 +42,14 @@ BzBTStatus aiResetElapsed(AIBlackboard *data, f32 dt) {
data->elapsed = 0.0f;
return BZ_BT_SUCCESS;
}
BzBTStatus aiIsEnemyNearby(AIBlackboard *data, f32 dt) {
return BZ_BT_FAIL;
}
BzBTStatus aiEvadeTarget(AIBlackboard *data, f32 dt) {
return BZ_BT_SUCCESS;
}
BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt) {
ecs_entity_t harvestTarget = data->as.worker.harvestTarget;
if (ecs_is_alive(ECS, harvestTarget)) {

View File

@@ -28,6 +28,9 @@ typedef struct AIBlackboard {
BzBTStatus aiMoveTo(AIBlackboard *data, f32 dt);
BzBTStatus aiResetElapsed(AIBlackboard *data, f32 dt);
BzBTStatus aiIsEnemyNearby(AIBlackboard *data, f32 dt);
BzBTStatus aiEvadeTarget(AIBlackboard *data, f32 dt);
// Worker
BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt);

View File

@@ -32,6 +32,7 @@ ECS_TAG_DECLARE(Selectable);
ECS_TAG_DECLARE(Selected);
ECS_COMPONENT_DECLARE(AddPopCapacity);
ECS_COMPONENT_DECLARE(ConsumePopCapacity);
ECS_COMPONENT_DECLARE(Worker);
ECS_COMPONENT_DECLARE(Building);
ECS_COMPONENT_DECLARE(Unit);
@@ -72,6 +73,7 @@ void initComponentIDs(ecs_world_t *ecs) {
ECS_TAG_DEFINE(ecs, Selected);
ECS_COMPONENT_DEFINE(ecs, AddPopCapacity);
ECS_COMPONENT_DEFINE(ecs, ConsumePopCapacity);
ECS_COMPONENT_DEFINE(ecs, Worker);
ECS_COMPONENT_DEFINE(ecs, Building);
ECS_COMPONENT_DEFINE(ecs, Unit);

View File

@@ -170,6 +170,10 @@ typedef struct AddPopCapacity {
i32 amount;
} AddPopCapacity;
extern ECS_COMPONENT_DECLARE(AddPopCapacity);
typedef struct ConsumePopCapacity {
i32 amount;
} ConsumePopCapacity;
extern ECS_COMPONENT_DECLARE(ConsumePopCapacity);
// Worker can:
// - Harvest

View File

@@ -16,7 +16,6 @@ ecs_entity_t entityCreateBaseUnit(const Position position, Player player,
getTextureRect(getEntityTile(type))
};
const Size size = {10.0f * region.rec.width / 16.0f, 10.0f * region.rec.height / 16.0f};
bzLogInfo("%.2f %.2f", size.x, size.y);
ecs_entity_t e = entityCreateEmpty();
ecs_set_ptr(ECS, e, Position, &position);
ecs_set_ptr(ECS, e, Size, &size);
@@ -55,6 +54,7 @@ ecs_entity_t entityCreateBaseUnit(const Position position, Player player,
.deceleration = 0.1f,
.unitType = type
});
ecs_set(ECS, e, ConsumePopCapacity, {1});
return e;
}

View File

@@ -167,6 +167,23 @@ bool init(void *userData) {
// Just a single action for now
node = bzBTAction(nodePool, root, (BzBTActionFn) aiMoveTo);
bzBTNodeSetName(node, "moveTo");
}
// evade
BzBTNode *evade = NULL;
{
BzBTNode *node = NULL;
evade = bzBTMakeRoot(nodePool);
BzBTNode *evadeSeq = bzBTCompSequence(nodePool, evade);
{
node = bzBTAction(nodePool, evadeSeq, (BzBTActionFn) aiIsEnemyNearby);
bzBTNodeSetName(node, "enemyNearby");
node = bzBTAction(nodePool, evadeSeq, (BzBTActionFn) aiEvadeTarget);
bzBTNodeSetName(node, "evadeTarget");
}
}
// worker harvest
{
@@ -175,9 +192,10 @@ bool init(void *userData) {
root = bzBTMakeRoot(nodePool);
game->BTs.workerHarvest = root;
//node = bzBTDecorUntilFail(nodePool, root);
BzBTNode *pSel = bzBTCompPSelector(nodePool, root);
bzBTSubTree(evade, pSel);
BzBTNode *collectSeq = bzBTCompSequence(nodePool, root);
BzBTNode *collectSeq = bzBTCompSequence(nodePool, pSel);
BzBTNode *untilFail = bzBTDecorUntilFail(nodePool, collectSeq);
{

View File

@@ -77,6 +77,26 @@ void buildingRemovePopCapacity(ecs_iter_t *it) {
res->popCapacity -= addPop[i].amount;
}
}
void entityConsumePopCapacity(ecs_iter_t *it) {
Game *game = ecs_singleton_get_mut(ECS, Game);
ConsumePopCapacity *pop = ecs_field(it, ConsumePopCapacity, 1);
Owner *owner = ecs_field(it, Owner, 2);
for (i32 i = 0; i < it->count; i++) {
PlayerResources *res = &game->playerResources[owner[i].player];
res->pop += pop[i].amount;
}
}
void entityUnConsumePopCapacity(ecs_iter_t *it) {
Game *game = ecs_singleton_get_mut(ECS, Game);
ConsumePopCapacity *pop = ecs_field(it, ConsumePopCapacity, 1);
Owner *owner = ecs_field(it, Owner, 2);
for (i32 i = 0; i < it->count; i++) {
PlayerResources *res = &game->playerResources[owner[i].player];
res->pop -= pop[i].amount;
}
}
void entityUpdateSpatialID(ecs_iter_t *it) {
Game *game = ecs_singleton_get_mut(ECS, Game);

View File

@@ -11,8 +11,8 @@
ecs_entity_t queryEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t tag);
bool selectEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t tag);
void selectUnits(BzSpatialGrid *entityGrid, Rectangle area);
bool selectEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t tag, Player player);
void selectUnits(BzSpatialGrid *entityGrid, Rectangle area, Player player);
void addEntityToInspected(ecs_entity_t entity, Game *game);
@@ -43,7 +43,7 @@ void inputPrimaryAction(Game *game, InputState *input) {
end.y = tmp;
}
input->pickArea = (Rectangle) {start.x, start.y, end.x - start.x, end.y - start.y};
selectUnits(game->entityGrid, input->pickArea);
selectUnits(game->entityGrid, input->pickArea, game->player);
}
i32 selectedCount = ecs_query_entity_count(input->queries.selected);
if (isInputBtnJustDragged(input, primaryBtn) && selectedCount > 0) {
@@ -55,11 +55,11 @@ void inputPrimaryAction(Game *game, InputState *input) {
if (entity) addEntityToInspected(entity, game);
} else if (isInputBtnJustUp(input, primaryBtn)) {
InputType type = input->state;
if (selectEntity(game->entityGrid, input->mouseDownWorld, ecs_id(Unit)))
if (selectEntity(game->entityGrid, input->mouseDownWorld, ecs_id(Unit), game->player))
type = INPUT_SELECTED_UNITS;
else if (selectEntity(game->entityGrid, input->mouseDownWorld, ecs_id(Harvestable)))
else if (selectEntity(game->entityGrid, input->mouseDownWorld, ecs_id(Harvestable), PLAYER_COUNT))
type = INPUT_SELECTED_OBJECT;
else if (selectEntity(game->entityGrid, input->mouseDownWorld, ecs_id(Building)))
else if (selectEntity(game->entityGrid, input->mouseDownWorld, ecs_id(Building), game->player))
type = INPUT_SELECTED_BUILDING;
selectedCount = ecs_query_entity_count(input->queries.selected);
if (selectedCount > 0) {
@@ -232,6 +232,10 @@ void updatePlayerInput() {
const MouseButton primaryBtn = input->mapping.primaryBtn;
const MouseButton secondaryBtn = input->mapping.secondaryBtn;
i32 count = ecs_query_entity_count(input->queries.selected);
if (count > 0)
input->state = INPUT_SELECTED_UNITS;
switch (input->state) {
case INPUT_NONE: {
inputPrimaryAction(game, input);
@@ -304,9 +308,7 @@ void drawPlayerInputUIGround() {
Size *size = ecs_field(&it, Size, 2);
for (i32 i = 0; i < it.count; i++) {
ecs_entity_t entity = it.entities[i];
f32 radius = size[i].x;
if (size[i].y > radius)
radius = size[i].y;
f32 radius = BZ_MIN(size[i].x, size[i].y);
radius *= 0.5f;
const f32 lineThickness = 1.0f;
if (ecs_has(ECS, entity, Building)) {
@@ -379,21 +381,29 @@ ecs_entity_t queryEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t
return closest;
}
bool selectEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t tag) {
bool selectEntity(BzSpatialGrid *entityGrid, Vector2 point, ecs_entity_t tag, Player player) {
ecs_remove_all(ECS, Selected);
const ecs_entity_t entity = queryEntity(entityGrid, point, tag);
if (entity) {
ecs_add(ECS, entity, Selected);
return true;
if (!entity) return false;
if (player != PLAYER_COUNT) {
if (!ecs_has(ECS, entity, Owner)) return false;
Owner owner = *ecs_get(ECS, entity, Owner);
if (player != owner.player) return false;
}
return false;
ecs_add(ECS, entity, Selected);
return true;
}
void selectUnits(BzSpatialGrid *entityGrid, Rectangle area) {
void selectUnits(BzSpatialGrid *entityGrid, Rectangle area, Player player) {
ecs_remove_all(ECS, Selected);
BzSpatialGridIter it = bzSpatialGridIter(entityGrid, area.x, area.y, area.width, area.height);
while (bzSpatialGridQueryNext(&it)) {
ecs_entity_t entity = *(ecs_entity_t *) it.data;
if (player != PLAYER_COUNT) {
if (!ecs_has(ECS, entity, Owner)) continue;
Owner owner = *ecs_get(ECS, entity, Owner);
if (owner.player != player) continue;
}
if (!ecs_has_id(ECS, entity, ecs_id(Unit))) continue;
Rectangle bounds;
if (!getEntityBounds(entity, NULL, NULL, &bounds)) continue;

View File

@@ -112,6 +112,9 @@ void setupSystems() {
ECS_OBSERVER(ECS, buildingAddPopCapacity, EcsOnSet, AddPopCapacity, Owner);
ECS_OBSERVER(ECS, buildingRemovePopCapacity, EcsOnRemove, AddPopCapacity, Owner);
ECS_OBSERVER(ECS, entityConsumePopCapacity, EcsOnSet, ConsumePopCapacity, Owner);
ECS_OBSERVER(ECS, entityUnConsumePopCapacity, EcsOnRemove, ConsumePopCapacity, Owner);
ECS_SYSTEM(ECS, entityUpdateSpatialID, EcsOnUpdate, Position, Size, Velocity, SpatialGridID);
ECS_SYSTEM(ECS, entityUpdateKinematic, EcsOnUpdate, Position, Velocity, Steering, Unit);

View File

@@ -75,6 +75,19 @@ void buildingAddPopCapacity(ecs_iter_t *it);
*/
void buildingRemovePopCapacity(ecs_iter_t *it);
/* Observer (for consuming pop capacity)
* 0: Game (singleton)
* 1: ConsumePopCapacity
* 2: Owner
*/
void entityConsumePopCapacity(ecs_iter_t *it);
/* Observer (for unconsuming pop capacity)
* 0: Game (singleton)
* 1: ConsumePopCapacity
* 2: Owner
*/
void entityUnConsumePopCapacity(ecs_iter_t *it);
/*
* 0: Game (singleton) for entity map
* 1: Position

View File

@@ -29,7 +29,7 @@ BzUINode *uiPushDivParentPercentage(f32 xPercent, f32 yPercent) {
});
}
void uiBaseLabel(const char *txt, Font font, f32 scl) {
void uiBaseLabel(const char *txt, Font font, f32 scl, Color color) {
BzUINode *node = bzUINodeMake(UI, bzUIKeyFromString(txt), &(BzUINodeDesc) {
.flags = BZ_UI_DRAW_TEXT | BZ_UI_DRAW_TEXT_SHADOW | BZ_UI_ALIGN_CENTER,
.semanticSize[BZ_UI_AXIS_X] = {
@@ -45,9 +45,9 @@ void uiBaseLabel(const char *txt, Font font, f32 scl) {
.font = font,
.fontSpacing = 2 * uiGetScale() * scl,
.fontSize = 62 * uiGetScale() * scl,
.normal = WHITE,
.hover = WHITE,
.active = WHITE,
.normal = color,
.hover = color,
.active = color,
});
bzUISetTextShadowStyle(UI, node, (BzUITextShadowStyle) {
.offset[BZ_UI_AXIS_X] = 2 * uiGetScale() * scl,
@@ -158,7 +158,7 @@ void uiBaseSlider(const char *txt, Font font, f32 scl, f32 *value, f32 min, f32
BZ_UI_FLEX_DIR_ROW | BZ_UI_FLEX_ALIGN_CENTER | BZ_UI_FLEX_JUSTIFY_SPACE_BETWEEN
});
uiBaseLabel(txt, font, 0.6 * scl);
uiBaseLabel(txt, font, 0.6 * scl, WHITE);
bzUIPushDiv(UI, (BzUISize) {BZ_UI_SIZE_CHILD_SUM}, (BzUISize) { BZ_UI_SIZE_CHILD_MAX});
bzUISetParentLayout(UI, (BzUILayout) {
@@ -173,7 +173,7 @@ void uiBaseSlider(const char *txt, Font font, f32 scl, f32 *value, f32 min, f32
*value = BZ_MAX(*value, min);
snprintf(buf, sizeof(buf), "%2.0f##%s", *value, txt);
uiBaseLabel(buf, font, 0.6 * scl);
uiBaseLabel(buf, font, 0.6 * scl, WHITE);
snprintf(buf, sizeof(buf), "+##%s", txt);
if (uiBaseTextButton(buf, font, 0.6 * scl))
@@ -185,7 +185,7 @@ void uiBaseSlider(const char *txt, Font font, f32 scl, f32 *value, f32 min, f32
}
void uiMainMenuLabel(const char *txt) {
uiBaseLabel(txt, getFont(), 1.8f);
uiBaseLabel(txt, getFont(), 1.8f, WHITE);
}
bool uiMainMenuButton(const char *txt) {
@@ -193,7 +193,7 @@ bool uiMainMenuButton(const char *txt) {
}
void uiSettingsLabel(const char *txt) {
uiBaseLabel(txt, getFont(), 0.8f);
uiBaseLabel(txt, getFont(), 0.8f, WHITE);
}
bool uiSettingsButton(const char *txt) {
return uiBaseTextButton(txt, getFont(), 0.7f);
@@ -219,7 +219,10 @@ void uiGameResCount(i32 amount, i32 capacity, Rectangle icon, Texture2D texture)
else
snprintf(buf, sizeof(buf), "%d/%d##%d", amount, capacity, id);
const f32 scl = 0.4f;
uiBaseLabel(buf, getFont(), scl);
Color color = WHITE;
if (capacity != -1 && amount >= capacity)
color = RED;
uiBaseLabel(buf, getFont(), scl, color);
BzUINode *iconNode = bzUINodeMake(UI, id, &(BzUINodeDesc) {
.flags = BZ_UI_DRAW_SPRITE | BZ_UI_ALIGN_CENTER,

View File

@@ -11,7 +11,7 @@ f32 uiGetScale();
BzUINode *uiPushDivParentPercentage(f32 xPercent, f32 yPercent);
// Template stuff
void uiBaseLabel(const char *txt, Font font, f32 scl);
void uiBaseLabel(const char *txt, Font font, f32 scl, Color color);
bool uiBaseTextButton(const char *txt, Font font, f32 scl);
void uiBaseCheckbox(const char *txt, Font font, f32 scl, bool *check);
void uiBaseSlider(const char *txt, Font font, f32 scl, f32 *value, f32 min, f32 max);