#include "entity_factory.h" #include "ai_actions.h" #include "systems/systems.h" ecs_entity_t entityCreateEmpty() { ecs_entity_t e = ecs_new_id(ECS); ecs_add_id(ECS, e, GameEntity); return e; } ecs_entity_t entityCreateBaseUnit(const Position position, f32 size, Player player, EntityType type, AnimType startAnim, Unit *unit, Game *game) { BzTileset *tileset = &game->tileset; BzTileID tileID = getEntityTile(type); TextureRegion region = { tileset->tiles, getTextureRect(tileID) }; HitBox hitbox = getEntityHitBoxRec(tileID); ecs_entity_t e = entityCreateEmpty(); ecs_set(ECS, e, Position, { position.x - size * 0.5f, position.y + size * 0.5f, }); f32 scl = size / region.rec.width; ecs_set(ECS, e, Size, {region.rec.width * scl, region.rec.height * scl}); hitbox.x *= scl; hitbox.y *= scl; hitbox.width *= scl; hitbox.height *= scl; ecs_set_ptr(ECS, e, HitBox, &hitbox); BzSpatialGridID spatialID = bzSpatialGridInsert(game->entityGrid, &e, position.x + hitbox.x, position.y + hitbox.y, hitbox.width, hitbox.height); ecs_set(ECS, e, SpatialGridID, { spatialID }); ecs_set(ECS, e, Rotation, { 0.0f }); ecs_set(ECS, e, Orientation, {0.0f}); ecs_set(ECS, e, Velocity, {}); ecs_set(ECS, e, Steering, {}); ecs_set(ECS, e, Owner, {player}); ecs_set_ptr(ECS, e, TextureRegion, ®ion); if (startAnim != ANIM_NONE) { ecs_set(ECS, e, Animation, { .entityType = type, .animType = startAnim, .sequence = entityGetAnimationSequence(type, startAnim), .tileset = tileset, .curFrame = 0, .elapsed = 0.0f, }); } ecs_set(ECS, e, BzBTState, { .root = NULL, .nodeStatePool = game->pools.btNodeState }); ecs_set(ECS, e, AIBlackboard, {.entity = e}); ecs_add_id(ECS, e, Selectable); Unit unitComp = { .attackElapsed = 0.0f, .attackCooldown = 1.0f, .minDamage = 1.0f, .maxDamage = 2.0f, .acceleration = 80.0f, .maxSpeed = 15.0f, .deceleration = 0.1f, .unitType = type }; if (unit) { *unit = unitComp; } else { ecs_set_ptr(ECS, e, Unit, &unitComp); } ecs_set(ECS, e, ConsumePopCapacity, {1}); return e; } ecs_entity_t entityCreateSoldier(const Position position, Player player, Game *game) { Unit unit = {0}; ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_SOLDIER, ANIM_IDLE, &unit, game); ecs_set(ECS, e, Health, { .startHP = 40.0f, .hp = 40.0f, .lastChanged = -1000.0f }); unit.minDamage = 5.0f; unit.maxDamage = 10.0f; unit.attackCooldown = 1.0f; ecs_set_ptr(ECS, e, Unit, &unit); setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { .moveToPos = position, }); return e; } ecs_entity_t entityCreateWarrior(const Position position, Player player, Game *game) { Unit unit = {0}; ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_WARRIOR, ANIM_IDLE, &unit, game); ecs_set(ECS, e, Health, { .startHP = 80.0f, .hp = 80.0f, .lastChanged = -1000.0f }); unit.minDamage = 8.0f; unit.maxDamage = 22.0f; unit.attackCooldown = 1.8f; ecs_set_ptr(ECS, e, Unit, &unit); setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { .moveToPos = position, }); return e; } ecs_entity_t entityCreateWorker(const Position position, Player player, Game *game) { ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_WORKER, ANIM_IDLE, NULL, game); ecs_set(ECS, e, Worker, { .collectSpeed = 0.8f, .depositSpeed = 0.2f, .carryCapacity = 5, }); ecs_set(ECS, e, Health, { .startHP = 20.0f, .hp = 20.0f, .lastChanged = -1000.0f }); setAIBehaviour(e, game->BTs.unit, &(AIBlackboard) { .moveToPos = position, }); return e; } ecs_entity_t entityCreateSwarmGoblin(const Position position, Player player, Game *game) { Unit unit = {0}; ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_GOBLIN, ANIM_IDLE, &unit, game); ecs_set(ECS, e, Swarm, { .currWaypoint = 0, }); ecs_set(ECS, e, Health, { .startHP = 40.0f, .hp = 40.0f, .lastChanged = -1000.0f }); unit.minDamage = 5.0f; unit.maxDamage = 10.0f; unit.attackCooldown = 1.0f; unit.maxSpeed = 20.0f; ecs_set_ptr(ECS, e, Unit, &unit); return e; } ecs_entity_t entityCreateSwarmOrc(const Position position, Player player, Game *game) { Unit unit = {0}; ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_ORC, ANIM_IDLE, &unit, game); ecs_set(ECS, e, Swarm, { .currWaypoint = 0, }); ecs_set(ECS, e, Health, { .startHP = 80.0f, .hp = 80.0f, .lastChanged = -1000.0f }); unit.minDamage = 8.0f; unit.maxDamage = 22.0f; unit.attackCooldown = 1.8f; unit.maxSpeed = 12.0f; ecs_set_ptr(ECS, e, Unit, &unit); return e; } ecs_entity_t entityCreate(EntityType type, Position position, Player player, Game *game) { switch (type) { case ENTITY_WORKER: return entityCreateWorker(position, player, game); case ENTITY_SOLDIER: return entityCreateSoldier(position, player, game); case ENTITY_WARRIOR: return entityCreateWarrior(position, player, game); case ENTITY_GOBLIN: return entityCreateSwarmGoblin(position, player, game); case ENTITY_ORC: return entityCreateSwarmOrc(position, player, game); default: return 0; } } void getEntityCost(EntityType type, i32 cost[RES_COUNT]) { for (i32 i = 0; i < RES_COUNT; i++) { cost[i] = 0; } switch (type) { case ENTITY_WORKER: cost[RES_FOOD] = 20; break; case ENTITY_SOLDIER: cost[RES_FOOD] = 10; cost[RES_GOLD] = 20; break; case ENTITY_WARRIOR: cost[RES_FOOD] = 20; cost[RES_GOLD] = 50; break; default:; } } bool canAffordEntity(EntityType type, PlayerResources res) { i32 needed[RES_COUNT] = {0, }; getEntityCost(type, needed); if (needed[RES_WOOD] > res.wood) return false; if (needed[RES_FOOD] > res.food) return false; if (needed[RES_GOLD] > res.gold) return false; return true; }