#include "entity_factory.h" #include "ai_actions.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, 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_ptr(ECS, e, Position, &position); 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); ecs_set(ECS, e, Unit, { .attackElapsed = 0.0f, .attackCooldown = 1.0f, .minDamage = 1.0f, .maxDamage = 2.0f, .acceleration = 80.0f, .maxSpeed = 15.0f, .deceleration = 0.1f, .unitType = type }); ecs_set(ECS, e, ConsumePopCapacity, {1}); return e; } ecs_entity_t entityCreateSoldier(const Position position, Player player, Game *game) { ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_SOLDIER, ANIM_IDLE, game); ecs_set(ECS, e, Health, { .startHP = 40.0f, .hp = 40.0f, .lastChanged = -1.0f }); Unit *unit = ecs_get_mut(ECS, e, Unit); unit->minDamage = 5.0f; unit->maxDamage = 10.0f; unit->attackCooldown = 1.0f; return e; } ecs_entity_t entityCreateWarrior(const Position position, Player player, Game *game) { ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_WARRIOR, ANIM_IDLE, game); ecs_set(ECS, e, Health, { .startHP = 80.0f, .hp = 80.0f, .lastChanged = -1.0f }); Unit *unit = ecs_get_mut(ECS, e, Unit); unit->minDamage = 8.0f; unit->maxDamage = 22.0f; unit->attackCooldown = 1.8f; return e; } ecs_entity_t entityCreateWorker(const Position position, Player player, Game *game) { BzTileset *tileset = &game->tileset; ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_WORKER, ANIM_IDLE, 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 = -1.0f }); return e; } ecs_entity_t entityCreateSwarmGoblin(const Position position, Player player, Game *game) { ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_GOBLIN, ANIM_IDLE, game); ecs_set(ECS, e, Swarm, { .currWaypoint = 0, }); ecs_set(ECS, e, Health, { .startHP = 40.0f, .hp = 40.0f, .lastChanged = -1.0f }); Unit *unit = ecs_get_mut(ECS, e, Unit); unit->minDamage = 5.0f; unit->maxDamage = 10.0f; unit->attackCooldown = 1.0f; unit->maxSpeed = 20.0f; return e; } ecs_entity_t entityCreateSwarmOrc(const Position position, Player player, Game *game) { ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_ORC, ANIM_IDLE, game); ecs_set(ECS, e, Swarm, { .currWaypoint = 0, }); ecs_set(ECS, e, Health, { .startHP = 80.0f, .hp = 80.0f, .lastChanged = -1.0f }); Unit *unit = ecs_get_mut(ECS, e, Unit); unit->minDamage = 8.0f; unit->maxDamage = 22.0f; unit->attackCooldown = 1.8f; unit->maxSpeed = 12.0f; 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] = 50; break; case ENTITY_SOLDIER: cost[RES_FOOD] = 50; cost[RES_GOLD] = 50; break; case ENTITY_WARRIOR: cost[RES_FOOD] = 200; cost[RES_GOLD] = 100; 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; }