From 80a0f2ba75b649974cbe3dc8d9b74a6da8ee0233 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sat, 22 Feb 2025 01:36:31 -0500 Subject: [PATCH] Basic simple animations where the enemies just move forward. --- assets/devices.json | 4 +- assets/enemies.json | 13 ++++-- components.cpp | 2 + components.hpp | 19 ++++++++ constants.hpp | 2 +- dinkyecs.hpp | 104 +++++++++++++++++--------------------------- guecs.hpp | 6 +-- raycaster.cpp | 16 ++++--- systems.cpp | 6 +++ 9 files changed, 92 insertions(+), 80 deletions(-) diff --git a/assets/devices.json b/assets/devices.json index 285edba..ad5eab3 100644 --- a/assets/devices.json +++ b/assets/devices.json @@ -34,8 +34,8 @@ {"_type": "Sprite", "name": "rope_vines_up"} ] }, - "TRIPEWIRE_TRAP": { - "id": "TRIPEWIRE_TRAP", + "TRIPWIRE_TRAP": { + "id": "TRIPWIRE_TRAP", "name": "Tripwire Trap", "description": "Watch where you're going.", "inventory_count": 0, diff --git a/assets/enemies.json b/assets/enemies.json index f3ca362..d5275fe 100644 --- a/assets/enemies.json +++ b/assets/enemies.json @@ -10,7 +10,7 @@ {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "LightSource", "strength": 60, "radius": 1.8}, {"_type": "EnemyConfig", "hearing_distance": 5}, - {"_type": "Sprite", "name": "peasant_girl"} + {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10} ] }, "KNIGHT": { @@ -22,6 +22,7 @@ {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 5}, + {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10}, {"_type": "Sprite", "name": "armored_knight"} ] }, @@ -31,11 +32,12 @@ "foreground": [156, 172, 197], "background": [30, 20, 75] }, - {"_type": "LightSource", "strength": 80, "radius": 2.8}, + {"_type": "LightSource", "strength": 60, "radius": 1.8}, {"_type": "Combat", "hp": 40, "max_hp": 40, "damage": 10, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": true}, {"_type": "EnemyConfig", "hearing_distance": 5}, - {"_type": "Sprite", "name": "axe_ranger"} + {"_type": "Sprite", "name": "axe_ranger"}, + {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10} ] }, "EVIL_EYE": { @@ -47,7 +49,8 @@ {"_type": "Combat", "hp": 50, "max_hp": 50, "damage": 50, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 5}, - {"_type": "Sprite", "name": "evil_eye"} + {"_type": "Sprite", "name": "evil_eye"}, + {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10} ] }, "RAT_GIANT": { @@ -59,6 +62,7 @@ {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 10}, + {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10}, {"_type": "Sprite", "name": "rat_with_sword"} ] }, @@ -71,6 +75,7 @@ {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 10}, + {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10}, {"_type": "Sprite", "name": "hairy_spider"} ] } diff --git a/components.cpp b/components.cpp index e41a78b..ef64806 100644 --- a/components.cpp +++ b/components.cpp @@ -12,6 +12,7 @@ namespace components { ENROLL_COMPONENT(LightSource, strength, radius); ENROLL_COMPONENT(Device, config, events); ENROLL_COMPONENT(Sprite, name); + ENROLL_COMPONENT(Animation, scale, simple, frames); void configure_entity(const ComponentMap& component_map, DinkyECS::World& world, DinkyECS::Entity ent, json& data) { for (auto &i : data) { @@ -33,5 +34,6 @@ namespace components { components::enroll(component_map); components::enroll(component_map); components::enroll(component_map); + components::enroll(component_map); } } diff --git a/components.hpp b/components.hpp index ffd349d..e68667d 100644 --- a/components.hpp +++ b/components.hpp @@ -4,6 +4,8 @@ #include "config.hpp" #include "dinky_components.hpp" #include "point.hpp" +#include +#include namespace components { struct Player { @@ -83,6 +85,23 @@ namespace components { string name; }; + struct Animation { + float scale = 0.0f; + bool simple = true; + int frames = 10; + int current = 0; + + void step(sf::Vector2f& scale_out, sf::IntRect& rect_out) { + if(current < frames) { + scale_out.x += scale; + scale_out.y += scale; + current++; + } + + (void) rect_out; + } + }; + void configure(ComponentMap& component_map); // these need to be here if you're using components::convert outside of components.cpp diff --git a/constants.hpp b/constants.hpp index 52cb9b7..3068134 100644 --- a/constants.hpp +++ b/constants.hpp @@ -11,7 +11,7 @@ constexpr const int SCREEN_WIDTH=1280; constexpr const int SCREEN_HEIGHT=720; constexpr const int RAY_VIEW_X=(SCREEN_WIDTH - RAY_VIEW_WIDTH); constexpr const int RAY_VIEW_Y=0; -constexpr const bool VSYNC=false; +constexpr const bool VSYNC=true; constexpr const int FRAME_LIMIT=60; constexpr const int NUM_SPRITES=1; constexpr const int MAX_LOG_MESSAGES=17; diff --git a/dinkyecs.hpp b/dinkyecs.hpp index 17dee17..a282823 100644 --- a/dinkyecs.hpp +++ b/dinkyecs.hpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace DinkyECS { @@ -16,14 +17,12 @@ namespace DinkyECS using EntityMap = std::unordered_map; template - struct ComponentStorage - { + struct ComponentStorage { std::vector data; std::queue free_indices; }; - struct Event - { + struct Event { int event = 0; Entity entity = 0; std::any data; @@ -31,8 +30,7 @@ namespace DinkyECS typedef std::queue EventQueue; - struct World - { + struct World { unsigned long entity_count = 0; std::unordered_map $components; std::unordered_map $facts; @@ -42,44 +40,35 @@ namespace DinkyECS Entity entity() { return ++entity_count; } - void clone_into(DinkyECS::World &to_world) - { + void clone_into(DinkyECS::World &to_world) { to_world.$constants = $constants; to_world.$facts = $facts; to_world.entity_count = entity_count; to_world.$component_storages = $component_storages; - for (auto eid : $constants) - { - for (const auto &[tid, eid_map] : $components) - { + for(auto eid : $constants) { + for(const auto &[tid, eid_map] : $components) { auto &their_map = to_world.$components[tid]; - if (eid_map.contains(eid)) - { + if(eid_map.contains(eid)) { their_map.insert_or_assign(eid, eid_map.at(eid)); } } } } - void make_constant(DinkyECS::Entity entity) - { + void make_constant(DinkyECS::Entity entity) { $constants.push_back(entity); } template - size_t make_component() - { + size_t make_component() { auto &storage = component_storage_for(); size_t index; - if (!storage.free_indices.empty()) - { + if(!storage.free_indices.empty()) { index = storage.free_indices.front(); storage.free_indices.pop(); - } - else - { + } else { storage.data.emplace_back(); index = storage.data.size() - 1; } @@ -88,8 +77,7 @@ namespace DinkyECS } template - ComponentStorage &component_storage_for() - { + ComponentStorage &component_storage_for() { auto type_index = std::type_index(typeid(Comp)); $component_storages.try_emplace(type_index, ComponentStorage{}); return std::any_cast &>( @@ -97,24 +85,20 @@ namespace DinkyECS } template - EntityMap &entity_map_for() - { + EntityMap &entity_map_for() { return $components[std::type_index(typeid(Comp))]; } template - EventQueue &queue_map_for() - { + EventQueue &queue_map_for() { return $events[std::type_index(typeid(Comp))]; } template - void remove(Entity ent) - { + void remove(Entity ent) { EntityMap &map = entity_map_for(); - if (map.contains(ent)) - { + if(map.contains(ent)) { size_t index = map.at(ent); component_storage_for().free_indices.push(index); } @@ -123,14 +107,12 @@ namespace DinkyECS } template - void set_the(Comp val) - { + void set_the(Comp val) { $facts.insert_or_assign(std::type_index(typeid(Comp)), val); } template - Comp &get_the() - { + Comp &get_the() { auto comp_id = std::type_index(typeid(Comp)); dbc::check($facts.contains(comp_id), fmt::format("!!!! ATTEMPT to access world fact that hasn't " @@ -143,19 +125,16 @@ namespace DinkyECS } template - bool has_the() - { + bool has_the() { auto comp_id = std::type_index(typeid(Comp)); return $facts.contains(comp_id); } template - void set(Entity ent, Comp val) - { + void set(Entity ent, Comp val) { EntityMap &map = entity_map_for(); - if (has(ent)) - { + if(has(ent)) { get(ent) = val; return; } @@ -165,8 +144,7 @@ namespace DinkyECS } template - Comp &get(Entity ent) - { + Comp &get(Entity ent) { EntityMap &map = entity_map_for(); auto &storage = component_storage_for(); auto index = map.at(ent); @@ -174,48 +152,40 @@ namespace DinkyECS } template - bool has(Entity ent) - { + bool has(Entity ent) { EntityMap &map = entity_map_for(); return map.contains(ent); } template - void query(std::function cb) - { + void query(std::function cb) { EntityMap &map = entity_map_for(); - for (auto &[entity, index] : map) - { + for(auto &[entity, index] : map) { cb(entity, get(entity)); } } template - void query(std::function cb) - { + void query(std::function cb) { EntityMap &map_a = entity_map_for(); EntityMap &map_b = entity_map_for(); - for (auto &[entity, index_a] : map_a) - { - if (map_b.contains(entity)) - { + for(auto &[entity, index_a] : map_a) { + if(map_b.contains(entity)) { cb(entity, get(entity), get(entity)); } } } template - void send(Comp event, Entity entity, std::any data) - { + void send(Comp event, Entity entity, std::any data) { EventQueue &queue = queue_map_for(); queue.push({event, entity, data}); } template - Event recv() - { + Event recv() { EventQueue &queue = queue_map_for(); Event evt = queue.front(); queue.pop(); @@ -223,10 +193,18 @@ namespace DinkyECS } template - bool has_event() - { + bool has_event() { EventQueue &queue = queue_map_for(); return !queue.empty(); } + + template + std::optional get_if(DinkyECS::Entity entity) { + if(has(entity)) { + return std::make_optional(get(entity)); + } else { + return std::nullopt; + } + } }; } // namespace DinkyECS diff --git a/guecs.hpp b/guecs.hpp index f1dc26d..9332527 100644 --- a/guecs.hpp +++ b/guecs.hpp @@ -165,11 +165,7 @@ namespace guecs { template std::optional get_if(DinkyECS::Entity entity) { - if($world.has(entity)) { - return std::make_optional($world.get(entity)); - } else { - return std::nullopt; - } + return $world.get_if(entity); } template diff --git a/raycaster.cpp b/raycaster.cpp index 09ffaaa..fa5a2f9 100644 --- a/raycaster.cpp +++ b/raycaster.cpp @@ -80,6 +80,7 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) { // after sorting the sprites, do the projection for(auto& rec : sprite_order) { if(!$sprites.contains(rec.second)) continue; + // BUG: eventually this needs to go away too auto& sf_sprite = $sprites.at(rec.second).sprite; auto sprite_pos = $level.world->get(rec.second); @@ -145,12 +146,17 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) { int d = y * texture_height - $height * half_height + sprite_height * half_height; int tex_y = ((d * texture_height) / sprite_height) / texture_height; - sf_sprite->setScale({sprite_scale_w, sprite_scale_h}); - sf_sprite->setTextureRect(sf::IntRect({ - {tex_x, tex_y}, - {tex_render_width, texture_height}})); - sf_sprite->setPosition({x, y}); + sf::Vector2f scale{sprite_scale_w, sprite_scale_h}; + sf::IntRect in_texture{ {tex_x, tex_y}, {tex_render_width, texture_height}}; + if($level.world->has(rec.second)) { + auto& animation = $level.world->get(rec.second); + animation.step(scale, in_texture); + } + + sf_sprite->setScale(scale); + sf_sprite->setTextureRect(in_texture); + sf_sprite->setPosition({x, y}); $brightness.setUniform("offsetFactor", sf::Glsl::Vec2{0.0f, 0.0f}); // the SpatialMap.distance_sorted only calculates the diff --git a/systems.cpp b/systems.cpp index 17d8a8d..1ee23e5 100644 --- a/systems.cpp +++ b/systems.cpp @@ -131,6 +131,7 @@ void System::death(GameLevel &level, components::ComponentMap& components) { world.remove(ent); world.remove(ent); world.remove(ent); + world.remove(ent); auto entity_data = config.items["GRAVE_STONE"]; components::configure_entity(components, world, ent, entity_data["components"]); @@ -161,6 +162,11 @@ void System::combat(GameLevel &level) { enemy_combat.attack(player_combat) }; + if(world.has(entity)) { + auto& animation = world.get(entity); + animation.current = 0; + } + world.send(Events::GUI::COMBAT, entity, result); } }