diff --git a/game/building_factory.c b/game/building_factory.c index 21915e5..9849903 100644 --- a/game/building_factory.c +++ b/game/building_factory.c @@ -176,9 +176,9 @@ ecs_entity_t placeBuilding(Game *game, BuildingType type, .range = 46.0f, .minDamage = 40.0f, .maxDamage = 80.0f, - .damageFalloff = 8.0f, - .damageRadius = 28.0f, - .projectileSpeed = 10.0f, + .projectileSpeed = 100.0f, + .projectileRadius = 4.0f, + .projectileDamageCount = 3, .fireCooldown = 1.2f, .fireElapsed = 0.0f, }); diff --git a/game/components.c b/game/components.c index bdf7881..d45803d 100644 --- a/game/components.c +++ b/game/components.c @@ -43,6 +43,7 @@ ECS_COMPONENT_DECLARE(Worker); ECS_COMPONENT_DECLARE(Building); ECS_COMPONENT_DECLARE(Unit); ECS_COMPONENT_DECLARE(Tower); +ECS_COMPONENT_DECLARE(Projectile); ECS_COMPONENT_DECLARE(BuildingRecruitInfo); ECS_COMPONENT_DECLARE(Storage); ECS_COMPONENT_DECLARE(Harvestable); @@ -96,6 +97,7 @@ void initComponentIDs(ecs_world_t *ecs) { ECS_COMPONENT_DEFINE(ecs, Building); ECS_COMPONENT_DEFINE(ecs, Unit); ECS_COMPONENT_DEFINE(ecs, Tower); + ECS_COMPONENT_DEFINE(ecs, Projectile); ECS_COMPONENT_DEFINE(ecs, BuildingRecruitInfo); ECS_COMPONENT_DEFINE(ecs, Storage); ECS_COMPONENT_DEFINE(ecs, Harvestable); diff --git a/game/components.h b/game/components.h index 31a55c3..e63128a 100644 --- a/game/components.h +++ b/game/components.h @@ -280,14 +280,23 @@ typedef struct Tower { f32 range; f32 minDamage; f32 maxDamage; - f32 damageFalloff; - f32 damageRadius; f32 projectileSpeed; + f32 projectileRadius; + i32 projectileDamageCount; f32 fireCooldown; f32 fireElapsed; } Tower; extern ECS_COMPONENT_DECLARE(Tower); +typedef struct Projectile { + Vector2 target; + f32 radius; + f32 damage; + ecs_entity_t lastDamaged; + i32 damageCount; +} Projectile; +extern ECS_COMPONENT_DECLARE(Projectile); + #define BUILDING_MAX_RECRUIT_SLOTS 4 typedef struct BuildingRecruitSlot { EntityType entityType; diff --git a/game/systems/s_entity.c b/game/systems/s_entity.c index cad6fa8..0d3f11f 100644 --- a/game/systems/s_entity.c +++ b/game/systems/s_entity.c @@ -337,8 +337,7 @@ void updateTower(ecs_iter_t *it) { Rectangle bounds = { center.x - range, center.y - range, - range * 2, - range * 2, + range * 2.0f, range * 2.0f }; BzSpatialGridIter spatialIt = bzSpatialGridIter(game->entityGrid, bounds.x, bounds.y, @@ -361,7 +360,7 @@ void updateTower(ecs_iter_t *it) { continue; Vector2 otherCenter = { otherBounds.x + otherBounds.width * 0.5f, - otherBounds.y - otherBounds.height * 0.5f + otherBounds.y + otherBounds.height * 0.5f }; f32 dst = Vector2Distance(center, otherCenter); if (dst > range) @@ -375,14 +374,96 @@ void updateTower(ecs_iter_t *it) { if (target == 0) continue; + Vector2 dir = Vector2Subtract(targetPos, center); + dir = Vector2Normalize(dir); + dir = Vector2Scale(dir, tower[i].projectileSpeed); + + ecs_entity_t proj = entityCreateEmpty(); + ecs_set(ECS, proj, Position, { center.x, center.y }); + ecs_set(ECS, proj, Velocity, { dir.x, dir.y }); + ecs_set(ECS, proj, Projectile, { + .damage = randFloatRange(tower[i].minDamage, tower[i].maxDamage), + .target = targetPos, + .radius = tower[i].projectileRadius, + .damageCount = tower[i].projectileDamageCount + }); + ecs_set(ECS, proj, Owner, { owner[i].player }); tower[i].fireElapsed = 0.0f; - - bzLogInfo("FIRE!"); } } +void updateProjectile(ecs_iter_t *it) { + Game *game = ecs_singleton_get_mut(ECS, Game); + Owner *owner = ecs_field(it, Owner, 1); + Projectile *proj = ecs_field(it, Projectile, 2); + Position *pos = ecs_field(it, Position, 3); + Velocity *vel = ecs_field(it, Velocity, 4); + + f32 dt = GetFrameTime(); + + for (i32 i = 0; i < it->count; i++) { + + + const f32 radius = proj[i].radius; + const f32 hRadius = radius * 0.5f; + + const Rectangle bounds = { + pos[i].x - hRadius, + pos[i].y + hRadius, + radius, radius + }; + + BzSpatialGridIter spatialIt = bzSpatialGridIter(game->entityGrid, + bounds.x, bounds.y, + bounds.width, bounds.height); + + while (bzSpatialGridQueryNext(&spatialIt)) { + ecs_entity_t other = *(ecs_entity_t *) spatialIt.data; + if (proj[i].lastDamaged == other) + continue; + if (!ecs_has(ECS, other, Owner)) + continue; + Owner otherOwner = *ecs_get(ECS, other, Owner); + if (otherOwner.player == owner[i].player) + continue; + if (!ecs_has(ECS, other, Health)) + continue; + + Vector2 otherPos; + Rectangle otherHB; + if (!entityGetHitBox(other, &otherPos, &otherHB)) + continue; + + if (!CheckCollisionRecs(bounds, otherHB)) + continue; + + damageEvent(other, (DamageEvent) { + .amount = proj[i].damage, + .hitbox = otherHB + }); + proj[i].lastDamaged = other; + proj[i].damageCount--; + break; + } + + if (proj[i].damageCount <= 0) { + ecs_delete(ECS, it->entities[i]); + continue; + } + + DrawRectangleV((Vector2) {bounds.x, bounds.y}, (Vector2) {bounds.width, bounds.height}, + RED); + + pos[i] = Vector2Add(pos[i], Vector2Scale(vel[i], dt)); + if (pos[i].x < 0.0f || pos[i].y < 0.0f || + pos[i].x >= game->map.width * game->map.tileWidth || + pos[i].y >= game->map.height * game->map.tileHeight) { + ecs_delete(ECS, it->entities[i]); + } + } +} void renderHealthBar(ecs_iter_t *it) { Position *pos = ecs_field(it, Position, 1); diff --git a/game/systems/systems.c b/game/systems/systems.c index afbbc00..4751d8b 100644 --- a/game/systems/systems.c +++ b/game/systems/systems.c @@ -44,17 +44,22 @@ ecs_entity_t renderDebugPathSystem; ECS_DTOR(SpatialGridID, gridID, { Game *game = ecs_singleton_get_mut(ECS, Game); - bzSpatialGridRemove(game->entityGrid, *gridID); + if (*gridID != -1) { + bzSpatialGridRemove(game->entityGrid, *gridID); + *gridID = -1; + } }) ECS_MOVE(SpatialGridID, dst, src, { *dst = *src; - *src = 0; + *src = -1; }) void spatialIDRemoved(ecs_iter_t *it) { Game *game = ecs_singleton_get_mut(ECS, Game); SpatialGridID *spatialID = ecs_field(it, SpatialGridID, 1); for (int i = 0; i < it->count; i++) { + if (spatialID[i] == -1) continue; bzSpatialGridRemove(game->entityGrid, spatialID[i]); + spatialID[i] = -1; } } ECS_DTOR(Path, path, { @@ -184,6 +189,7 @@ void setupSystems() { ECS_SYSTEM(ECS, updateAISystem, EcsOnUpdate, BzBTState); ECS_SYSTEM(ECS, updateTower, EcsOnUpdate, Owner, Tower, Position, Size); + ECS_SYSTEM(ECS, updateProjectile, EcsOnUpdate, Owner, Projectile, Position, Velocity); ECS_SYSTEM(ECS, updateAnimationState, EcsOnUpdate, Animation, TextureRegion); ECS_SYSTEM(ECS, updateAnimation, EcsOnUpdate, Animation, TextureRegion); diff --git a/game/systems/systems.h b/game/systems/systems.h index 69cd26b..c49326a 100644 --- a/game/systems/systems.h +++ b/game/systems/systems.h @@ -166,6 +166,15 @@ void updateBuildingRecruitment(ecs_iter_t *it); */ void updateTower(ecs_iter_t *it); +/* + * 0: Game (singleton for querying entities) + * 1: Owner + * 2 Projectile + * 3: Position + * 4: Velocity + */ +void updateProjectile(ecs_iter_t *it); + /* * 1: Position * 2: HitBox