#pragma once #include "dbc.hpp" #include "lel.hpp" #include #include #include #include #include #include #include #include "sfml/components.hpp" namespace guecs { using std::shared_ptr, std::wstring, std::string; using Entity = unsigned long; using EntityMap = std::unordered_map; template struct ComponentStorage { std::vector data; std::queue free_indices; }; struct Clickable { /* This is actually called by UI::mouse and passed the entity ID of the * button pressed so you can interact with it in the event handler. */ std::function action; }; struct ActionData { std::any data; }; struct CellName { string name; }; class UI { public: Entity MAIN = 0; unsigned long entity_count = 1; std::unordered_map $components; std::unordered_map $component_storages; std::unordered_map $name_ents; shared_ptr $font = nullptr; lel::Parser $parser; string $grid = ""; UI(); void position(int x, int y, int width, int height); sf::Vector2f get_position(); sf::Vector2f get_size(); void layout(const string& grid); Entity init_entity(const string& name); Entity entity(const string& name); Entity entity(const string& name, int id); inline lel::CellMap& cells() { return $parser.cells; } void init(); void render(sf::RenderWindow& window); bool mouse(float x, float y, bool hover); void click_on(const string& name, bool required=false); 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; } 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 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 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) { 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 ent) { EntityMap &map = entity_map_for(); return map.contains(ent); } template void remove(Entity 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) { EntityMap &map = entity_map_for(); 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); set(ent, val); } template void do_if(Entity ent, std::function cb) { if(has(ent)) { cb(get(ent)); } } lel::Cell& cell_for(Entity ent) { return get(ent); } lel::Cell& cell_for(const string& name) { Entity ent = entity(name); 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); } } template 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); } void show_sprite(const string& region, const string& sprite_name); void show_text(const string& region, const wstring& content); void show_label(const string& region, const wstring& content); }; wstring to_wstring(const string& str); }