From 1780a758b397ebb1ae17948d955b75a7a557eb0f Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 4 May 2025 11:56:30 -0400 Subject: [PATCH] Initial GUECS refactor is done, it's now on its own with no other dependencies, but now I need to trim it down to do only what it needs. --- boss_fight_ui.cpp | 2 +- combat_ui.cpp | 2 +- guecs.hpp | 200 ++++++++++++++++++++++++++++++---------------- status_ui.cpp | 2 +- tests/guecs.cpp | 9 +-- 5 files changed, 140 insertions(+), 75 deletions(-) diff --git a/boss_fight_ui.cpp b/boss_fight_ui.cpp index 486ecd2..95a4e0f 100644 --- a/boss_fight_ui.cpp +++ b/boss_fight_ui.cpp @@ -54,7 +54,7 @@ namespace gui { $boss_background = textures::get(boss.background); $boss_background.sprite->setPosition({BOSS_VIEW_X, BOSS_VIEW_Y}); - $status.world().set_the({$status.$parser}); + $status.set_the({$status.$parser}); if(boss.stage) { $boss_has_stage = true; diff --git a/combat_ui.cpp b/combat_ui.cpp index e46d50a..a95140f 100644 --- a/combat_ui.cpp +++ b/combat_ui.cpp @@ -36,7 +36,7 @@ namespace gui { } void CombatUI::init() { - $gui.world().set_the({$gui.$parser, ColorValue::DARK_MID}); + $gui.set_the({$gui.$parser, ColorValue::DARK_MID}); auto& the_belt = $level.world->get_the(); for(int slot = 0; slot < the_belt.max_slots; slot++) { diff --git a/guecs.hpp b/guecs.hpp index 838d4fd..bdfac41 100644 --- a/guecs.hpp +++ b/guecs.hpp @@ -16,7 +16,7 @@ namespace guecs { using std::shared_ptr, std::make_shared, std::wstring, std::string; - using Entity = DinkyECS::Entity; + using Entity = unsigned long; using EntityMap = std::unordered_map; @@ -41,7 +41,7 @@ namespace guecs { struct Label : public Textual { template - Label(Args... args) : Textual(args...) + Label(Args... args) : Textual(args...) { centered = true; } @@ -140,7 +140,6 @@ namespace guecs { class UI { public: - DinkyECS::World $world; unsigned long entity_count = 0; std::unordered_map $components; std::unordered_map $facts; @@ -165,10 +164,6 @@ namespace guecs { return $parser.cells; } - inline DinkyECS::World& world() { - return $world; - } - void init(); void render(sf::RenderWindow& window); bool mouse(float x, float y, bool hover); @@ -176,111 +171,183 @@ namespace guecs { void click_on(Entity slot_id); void debug_layout(sf::RenderWindow& window); + Entity entity() { return ++entity_count; } + template + size_t make_component() { + auto &storage = component_storage_for(); + size_t index; + + if(!storage.free_indices.empty()) { + index = storage.free_indices.front(); + storage.free_indices.pop(); + } else { + storage.data.emplace_back(); + index = storage.data.size() - 1; + } + return index; + } - Entity entity() { return $world.entity(); } + template + ComponentStorage &component_storage_for() { + auto type_index = std::type_index(typeid(Comp)); + $component_storages.try_emplace(type_index, ComponentStorage{}); + return std::any_cast &>( + $component_storages.at(type_index)); + } + template + EntityMap &entity_map_for() { + return $components[std::type_index(typeid(Comp))]; + } - template - bool has_the() { - return $world.has_the(); - } - template - void set_the(Comp val) { - return $world.set_the(val); - } + template + bool has_the() { + auto comp_id = std::type_index(typeid(Comp)); + return $facts.contains(comp_id); + } - template - Comp &get_the() { - return $world.get_the(); - } + template + void set_the(Comp val) { + $facts.insert_or_assign(std::type_index(typeid(Comp)), val); + } template - void set(Entity ent, Comp val) { - $world.set(ent, val); - } + 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 " + "been set yet: {}", + typeid(Comp).name())); + + // use .at to get std::out_of_range if fact not set + std::any &res = $facts.at(comp_id); + return std::any_cast(res); + } + + template + void set(Entity ent, Comp val) { + EntityMap &map = entity_map_for(); + + if(has(ent)) { + get(ent) = val; + return; + } + + map.insert_or_assign(ent, make_component()); + get(ent) = val; + } template - Comp& get(Entity entity) { - return $world.get(entity); + Comp& get(Entity ent) { + EntityMap &map = entity_map_for(); + auto &storage = component_storage_for(); + auto index = map.at(ent); + return storage.data[index]; } template Comp* get_if(Entity entity) { - return $world.get_if(entity); + EntityMap &map = entity_map_for(); + auto &storage = component_storage_for(); + if(map.contains(entity)) { + auto index = map.at(entity); + return &storage.data[index]; + } else { + return nullptr; + } } template - bool has(Entity entity) { - return $world.has(entity); + bool has(Entity ent) { + EntityMap &map = entity_map_for(); + return map.contains(ent); } template void remove(Entity ent) { - $world.remove(ent); + EntityMap &map = entity_map_for(); + + if(map.contains(ent)) { + size_t index = map.at(ent); + component_storage_for().free_indices.push(index); + } + + map.erase(ent); } - template - void query(std::function cb) { - $world.query(cb); - } + template + void query(std::function cb) { + EntityMap &map = entity_map_for(); - template - void query(std::function cb) { - $world.query(cb); - } + for(auto &[entity, index] : map) { + cb(entity, get(entity)); + } + } + + template + 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)) { + cb(entity, get(entity), get(entity)); + } + } + } template - void set_init(Entity ent, Comp val) { - dbc::check(has(ent),"WRONG! slot is missing its cell?!"); - auto& cell = get(ent); - val.init(cell); - $world.set(ent, val); - } + void set_init(Entity ent, Comp val) { + dbc::check(has(ent),"WRONG! slot is missing its cell?!"); + auto& cell = get(ent); + val.init(cell); + set(ent, val); + } template - void do_if(Entity ent, std::function cb) { - if($world.has(ent)) { - cb($world.get(ent)); + void do_if(Entity ent, std::function cb) { + if(has(ent)) { + cb(get(ent)); + } } - } lel::Cell& cell_for(Entity ent) { - return $world.get(ent); + return get(ent); } lel::Cell& cell_for(const string& name) { Entity ent = entity(name); - return $world.get(ent); + return get(ent); } // BUG: close could just be remove with overload template - void close(string region) { - auto ent = entity(region); - if(has(ent)) { - remove(ent); + void close(string region) { + auto ent = entity(region); + if(has(ent)) { + remove(ent); + } } - } template - void render_helper(sf::RenderWindow& window, Entity ent, bool is_shape, T& target) { - sf::Shader *shader_ptr = nullptr; - - if(auto shader = $world.get_if(ent)) { - if(shader->$active && !is_shape) { - auto ptr = shader->checkout_ptr(); - ptr->setUniform("is_shape", is_shape); - // NOTE: this is needed because SFML doesn't handle shared_ptr - shader_ptr = ptr.get(); + void render_helper(sf::RenderWindow& window, Entity ent, bool is_shape, T& target) { + sf::Shader *shader_ptr = nullptr; + + if(auto shader = get_if(ent)) { + if(shader->$active && !is_shape) { + auto ptr = shader->checkout_ptr(); + ptr->setUniform("is_shape", is_shape); + // NOTE: this is needed because SFML doesn't handle shared_ptr + shader_ptr = ptr.get(); + } } - } - window.draw(*target, shader_ptr); - } + window.draw(*target, shader_ptr); + } void show_sprite(const string& region, const string& sprite_name); void show_text(const string& region, const wstring& content); @@ -289,5 +356,4 @@ namespace guecs { Clickable make_action(DinkyECS::World& target, Events::GUI event); Clickable make_action(DinkyECS::World& target, Events::GUI event, std::any data); - } diff --git a/status_ui.cpp b/status_ui.cpp index f2420cb..85c1b38 100644 --- a/status_ui.cpp +++ b/status_ui.cpp @@ -31,7 +31,7 @@ namespace gui { } void StatusUI::init() { - $gui.world().set_the({$gui.$parser}); + $gui.set_the({$gui.$parser}); for(auto& [name, cell] : $gui.cells()) { if(name == "log_view") { diff --git a/tests/guecs.cpp b/tests/guecs.cpp index 5eec88d..3b41065 100644 --- a/tests/guecs.cpp +++ b/tests/guecs.cpp @@ -15,12 +15,11 @@ TEST_CASE("prototype one gui", "[ecs-gui]") { gui.layout("[test1|test2|test3][test4|_|test5]"); for(auto& [name, cell] : gui.cells()) { - auto& world = gui.world(); auto button = gui.entity(name); - world.set(button, cell); - world.set(button, {}); - world.set(button, {}); - world.set(button, {L"whatever"}); + gui.set(button, cell); + gui.set(button, {}); + gui.set(button, {}); + gui.set(button, {L"whatever"}); } gui.init();