From bc7da3c7a33ecabdbe35bf01094954b3ced55611 Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Fri, 9 Feb 2024 15:46:53 +0100 Subject: [PATCH] Partial tower implementation --- game/building_factory.c | 38 +++++++++++++++++++++++ game/components.c | 6 ++++ game/components.h | 24 +++++++++++++- game/game_state.h | 2 +- game/main.c | 10 +++++- game/systems/s_entity.c | 69 +++++++++++++++++++++++++++++++++++++++++ game/systems/systems.c | 16 ++++++++++ game/systems/systems.h | 9 ++++++ 8 files changed, 171 insertions(+), 3 deletions(-) diff --git a/game/building_factory.c b/game/building_factory.c index 69386f6..21915e5 100644 --- a/game/building_factory.c +++ b/game/building_factory.c @@ -146,6 +146,44 @@ ecs_entity_t placeBuilding(Game *game, BuildingType type, } }); break; + case BUILDING_TOWER: { + ecs_entity_t mage = entityCreateEmpty(); + const Vector2 mageSize = { 8.0f, 16.0f }; + Vector2 magePos = { + pos.x + (size.x - mageSize.x) * 0.5f, + pos.y - size.y * 0.5f + }; + ecs_set_ptr(ECS, mage, Position, &magePos); + ecs_set_ptr(ECS, mage, Size, &mageSize); + ecs_set(ECS, mage, Rotation, { 0 }); + ecs_set(ECS, mage, TextureRegion, { + tileset->tiles, + getTextureRect(getEntityTile(ENTITY_MAGE)) + }); + ecs_set(ECS, mage, Animation, { + .entityType = ENTITY_MAGE, + .animType = ANIM_IDLE, + + .sequence = entityGetAnimationSequence(ENTITY_MAGE, ANIM_IDLE), + .tileset = tileset, + .curFrame = 0, + .elapsed = 0.0f + }); + ecs_set(ECS, mage, DrawLayer, { 1 }); + + ecs_set(ECS, building, AttachedEntity, {mage}); + ecs_set(ECS, building, Tower, { + .range = 46.0f, + .minDamage = 40.0f, + .maxDamage = 80.0f, + .damageFalloff = 8.0f, + .damageRadius = 28.0f, + .projectileSpeed = 10.0f, + .fireCooldown = 1.2f, + .fireElapsed = 0.0f, + }); + break; + } case BUILDING_HOUSE_01: case BUILDING_HOUSE_02: case BUILDING_HOUSE_03: diff --git a/game/components.c b/game/components.c index 00f67fc..bdf7881 100644 --- a/game/components.c +++ b/game/components.c @@ -42,6 +42,7 @@ ECS_COMPONENT_DECLARE(Health); ECS_COMPONENT_DECLARE(Worker); ECS_COMPONENT_DECLARE(Building); ECS_COMPONENT_DECLARE(Unit); +ECS_COMPONENT_DECLARE(Tower); ECS_COMPONENT_DECLARE(BuildingRecruitInfo); ECS_COMPONENT_DECLARE(Storage); ECS_COMPONENT_DECLARE(Harvestable); @@ -49,6 +50,8 @@ ECS_TAG_DECLARE(Buildable); ECS_TAG_DECLARE(Workable); ECS_TAG_DECLARE(Attackable); +ECS_COMPONENT_DECLARE(DrawLayer); +ECS_COMPONENT_DECLARE(AttachedEntity); ECS_COMPONENT_DECLARE(DelayDelete); void initComponentIDs(ecs_world_t *ecs) { @@ -92,6 +95,7 @@ void initComponentIDs(ecs_world_t *ecs) { ECS_COMPONENT_DEFINE(ecs, Worker); ECS_COMPONENT_DEFINE(ecs, Building); ECS_COMPONENT_DEFINE(ecs, Unit); + ECS_COMPONENT_DEFINE(ecs, Tower); ECS_COMPONENT_DEFINE(ecs, BuildingRecruitInfo); ECS_COMPONENT_DEFINE(ecs, Storage); ECS_COMPONENT_DEFINE(ecs, Harvestable); @@ -99,6 +103,8 @@ void initComponentIDs(ecs_world_t *ecs) { ECS_TAG_DEFINE(ecs, Workable); ECS_TAG_DEFINE(ecs, Attackable); + ECS_COMPONENT_DEFINE(ecs, DrawLayer); + ECS_COMPONENT_DEFINE(ecs, AttachedEntity); ECS_COMPONENT_DEFINE(ecs, DelayDelete); } diff --git a/game/components.h b/game/components.h index 4438b4a..ecb0455 100644 --- a/game/components.h +++ b/game/components.h @@ -270,6 +270,19 @@ typedef struct Unit { EntityType unitType; } Unit; extern ECS_COMPONENT_DECLARE(Unit); + +typedef struct Tower { + f32 range; + f32 minDamage; + f32 maxDamage; + f32 damageFalloff; + f32 damageRadius; + f32 projectileSpeed; + f32 fireCooldown; + f32 fireElapsed; +} Tower; +extern ECS_COMPONENT_DECLARE(Tower); + #define BUILDING_MAX_RECRUIT_SLOTS 4 typedef struct BuildingRecruitSlot { EntityType entityType; @@ -301,8 +314,17 @@ extern ECS_TAG_DECLARE(Buildable); extern ECS_TAG_DECLARE(Attackable); /********************************************************** - * DelayDelete components + * Misc components *********************************************************/ + +typedef u8 DrawLayer; +extern ECS_COMPONENT_DECLARE(DrawLayer); + +typedef struct AttachedEntity { + ecs_entity_t entity; +} AttachedEntity; +extern ECS_COMPONENT_DECLARE(AttachedEntity); + typedef struct DelayDelete { f32 time; f32 elapsed; diff --git a/game/game_state.h b/game/game_state.h index a66458e..0fdb156 100644 --- a/game/game_state.h +++ b/game/game_state.h @@ -19,7 +19,7 @@ typedef struct DrawData { Rectangle dst; Vector2 origin; f32 rotation; - u8 layer; + u8 layer; bool canHaveAlpha; } DrawData; diff --git a/game/main.c b/game/main.c index f061306..762e20d 100644 --- a/game/main.c +++ b/game/main.c @@ -88,10 +88,13 @@ bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) { return true; } -int cmpDrawData(const void *a, const void *b) { +static int cmpDrawData(const void *a, const void *b) { const DrawData *lhs = (DrawData *) a; const DrawData *rhs = (DrawData *) b; + if (lhs->layer != rhs->layer) + return lhs->layer - rhs->layer; + f32 dif = (rhs->dst.y) - (lhs->dst.y); int cmpVal = 0; if (dif < 0) cmpVal = 1; @@ -499,11 +502,16 @@ static void renderGame(Game *game, float dt) { src.height -= 0.02f; if (t[i].flipX) src.width *= -1.0f; if (t[i].flipY) src.height *= -1.0f; + u8 drawLayer = 0; + if (ecs_has(ECS, it.entities[i], DrawLayer)) { + drawLayer = *ecs_get(ECS, it.entities[i], DrawLayer); + } DrawData draw = (DrawData) { .src = src, .dst = dst, .origin = origin, .rotation = r[i], + .layer = drawLayer, .canHaveAlpha = true, }; if (ecs_has_id(ECS, it.entities[i], ecs_id(Unit))) { diff --git a/game/systems/s_entity.c b/game/systems/s_entity.c index 6f95cb8..ac5484d 100644 --- a/game/systems/s_entity.c +++ b/game/systems/s_entity.c @@ -310,6 +310,75 @@ void updateBuildingRecruitment(ecs_iter_t *it) { } } +void updateTower(ecs_iter_t *it) { + Game *game = ecs_singleton_get_mut(ECS, Game); + Owner *owner = ecs_field(it, Owner, 1); + Tower *tower = ecs_field(it, Tower, 2); + Position *position = ecs_field(it, Position, 3); + Size *size = ecs_field(it, Size, 4); + + f32 dt = GetFrameTime(); + + for (i32 i = 0; i < it->count; i++) { + tower[i].fireElapsed += dt; + if (tower[i].fireElapsed < tower[i].fireCooldown) + continue; + + Vector2 center = { + position[i].x + size[i].x * 0.5f, + position[i].y - size[i].y * 0.5f, + }; + f32 range = tower[i].range; + Rectangle bounds = { + center.x - range, + center.y - range, + range * 2, + range * 2, + }; + BzSpatialGridIter spatialIt = bzSpatialGridIter(game->entityGrid, + bounds.x, bounds.y, + bounds.width, bounds.height); + + ecs_entity_t target = 0; + Vector2 targetPos = Vector2Zero(); + f32 targetDst = INFINITY; + while (bzSpatialGridQueryNext(&spatialIt)) { + ecs_entity_t other = *(ecs_entity_t *) spatialIt.data; + if (!ecs_has(ECS, other, Owner)) + continue; + Owner otherOwner = *ecs_get(ECS, other, Owner); + if (owner[i].player == otherOwner.player) + continue; + if (other == it->entities[i]) continue; + Position otherPos; + Rectangle otherBounds; + if (!entityGetHitBox(other, &otherPos, &otherBounds)) + continue; + Vector2 otherCenter = { + otherBounds.x + otherBounds.width * 0.5f, + otherBounds.y - otherBounds.height * 0.5f + }; + f32 dst = Vector2Distance(center, otherCenter); + if (dst > range) + continue; + if (targetDst == INFINITY || dst < targetDst) { + target = other; + targetPos = otherCenter; + targetDst = dst; + } + } + + if (target == 0) continue; + + + + tower[i].fireElapsed = 0.0f; + + bzLogInfo("FIRE!"); + } + +} + void renderHealthBar(ecs_iter_t *it) { Position *pos = ecs_field(it, Position, 1); HitBox *hitbox = ecs_field(it, HitBox, 2); diff --git a/game/systems/systems.c b/game/systems/systems.c index d0dea8e..7da3de9 100644 --- a/game/systems/systems.c +++ b/game/systems/systems.c @@ -90,6 +90,16 @@ ECS_MOVE(Building, dst, src, { *src = (Building) {.type = 0}; }) +ECS_DTOR(AttachedEntity, attacked, { + if (ecs_is_alive(ECS, attacked->entity)) + ecs_delete(ECS, attacked->entity); + attacked->entity = 0; +}) +ECS_MOVE(AttachedEntity, dst, src, { + *dst = *src; + src->entity = 0; +}) + void delayDeleteUpdate(ecs_iter_t *it) { DelayDelete *delay = ecs_field(it, DelayDelete, 1); @@ -117,6 +127,10 @@ void setupSystems() { .dtor = ecs_dtor(Building), .move_dtor = ecs_move(Building) }); + ecs_set_hooks(ECS, AttachedEntity, { + .dtor = ecs_dtor(AttachedEntity), + .move_dtor = ecs_move(AttachedEntity) + }); ECS_OBSERVER(ECS, entityPathRemove, EcsOnRemove, Path); @@ -140,6 +154,8 @@ void setupSystems() { ECS_SYSTEM(ECS, resetHarvestCount, EcsOnUpdate, Harvestable); ECS_SYSTEM(ECS, updateAISystem, EcsOnUpdate, BzBTState); + ECS_SYSTEM(ECS, updateTower, EcsOnUpdate, Owner, Tower, Position, Size); + ECS_SYSTEM(ECS, updateAnimationState, EcsOnUpdate, Animation, TextureRegion); ECS_SYSTEM(ECS, updateAnimation, EcsOnUpdate, Animation, TextureRegion); ECS_SYSTEM(ECS, updateEasingSystem, EcsOnUpdate, Easing, Position, HitBox, Rotation); diff --git a/game/systems/systems.h b/game/systems/systems.h index 2bbb2e9..648c652 100644 --- a/game/systems/systems.h +++ b/game/systems/systems.h @@ -157,6 +157,15 @@ void entityFollowPath(ecs_iter_t *it); */ void updateBuildingRecruitment(ecs_iter_t *it); +/* + * 0: Game (singleton for querying entities) + * 1: Owner + * 2: Tower + * 3: Position + * 4: Size + */ +void updateTower(ecs_iter_t *it); + /* * 1: Position * 2: HitBox