Consume pop capacity + evade AI boiler plate
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
22
game/main.c
22
game/main.c
@@ -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);
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user