diff --git a/engine/breeze/ai/behaviour_tree.c b/engine/breeze/ai/behaviour_tree.c index b303920..732b869 100644 --- a/engine/breeze/ai/behaviour_tree.c +++ b/engine/breeze/ai/behaviour_tree.c @@ -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); diff --git a/engine/breeze/ai/behaviour_tree.h b/engine/breeze/ai/behaviour_tree.h index dec6ccb..51e0667 100644 --- a/engine/breeze/ai/behaviour_tree.h +++ b/engine/breeze/ai/behaviour_tree.h @@ -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. * diff --git a/game/ai_actions.c b/game/ai_actions.c index aa84de3..49673ef 100644 --- a/game/ai_actions.c +++ b/game/ai_actions.c @@ -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)) { diff --git a/game/ai_actions.h b/game/ai_actions.h index d00a01e..a5944b6 100644 --- a/game/ai_actions.h +++ b/game/ai_actions.h @@ -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); diff --git a/game/components.c b/game/components.c index 9390720..d1373e2 100644 --- a/game/components.c +++ b/game/components.c @@ -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); diff --git a/game/components.h b/game/components.h index 2e7fa97..a68cc82 100644 --- a/game/components.h +++ b/game/components.h @@ -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 diff --git a/game/entity_factory.c b/game/entity_factory.c index d33c594..529f51c 100644 --- a/game/entity_factory.c +++ b/game/entity_factory.c @@ -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; } diff --git a/game/main.c b/game/main.c index f0ce1d5..6dcf625 100644 --- a/game/main.c +++ b/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); { diff --git a/game/systems/s_entity.c b/game/systems/s_entity.c index b2c4cce..e8393f5 100644 --- a/game/systems/s_entity.c +++ b/game/systems/s_entity.c @@ -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); diff --git a/game/systems/s_input.c b/game/systems/s_input.c index 6e17fdf..597efce 100644 --- a/game/systems/s_input.c +++ b/game/systems/s_input.c @@ -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; diff --git a/game/systems/systems.c b/game/systems/systems.c index 8218264..f4dec02 100644 --- a/game/systems/systems.c +++ b/game/systems/systems.c @@ -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); diff --git a/game/systems/systems.h b/game/systems/systems.h index 64defa6..f5c7028 100644 --- a/game/systems/systems.h +++ b/game/systems/systems.h @@ -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 diff --git a/game/ui_widgets.c b/game/ui_widgets.c index 8dad017..535f356 100644 --- a/game/ui_widgets.c +++ b/game/ui_widgets.c @@ -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, diff --git a/game/ui_widgets.h b/game/ui_widgets.h index e4288f7..e1fd5c7 100644 --- a/game/ui_widgets.h +++ b/game/ui_widgets.h @@ -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);