#include #include "dbc.hpp" #include #include #include "constants.hpp" #include "config.hpp" #include #include "shiterator.hpp" #include #include namespace fs = std::filesystem; constexpr const int TILE_COUNT=10; constexpr const sf::Color DEFAULT_COLOR{255, 255, 255, 255}; constexpr const size_t DEFAULT_DIM=64; using namespace nlohmann; using namespace shiterator; using MapRow = BaseRow; using MapGrid = Base; using BoolRow = BaseRow; using BoolGrid = Base; struct MapConfig { MapGrid map = make(TILE_COUNT, TILE_COUNT); BoolGrid centered = make(TILE_COUNT, TILE_COUNT); std::unordered_map colors; std::unordered_map backgrounds; each_row_t it{map}; }; struct MapTileBuilder { unsigned int $font_size = 20; sf::Glyph $glyph; sf::Font $font{FONT_FILE_NAME}; std::shared_ptr $render = nullptr; sf::Vector2i $size; sf::Vector2i $image_size; sf::RenderTexture $temp_render; MapTileBuilder(size_t x, size_t y) : $size(x, y), $image_size($size.x * TILE_COUNT, $size.y * TILE_COUNT), $temp_render({(unsigned int)$size.x, (unsigned int)$size.y}) { $font.setSmooth(false); } void best_size(wchar_t for_char, bool centered) { float factor = centered ? 0.8f : 1.0f; sf::Vector2i adjusted_size = {int($size.x * factor), int($size.y * factor)}; $font_size = 20; // reset the size // fit the glyph in our box height auto temp = $font.getGlyph(for_char, $font_size, false); auto temp_size = $font_size; while(temp.textureRect.size.y <= adjusted_size.y && temp.textureRect.size.x <= adjusted_size.x) { $glyph = temp; $font_size = temp_size; temp_size++; temp = $font.getGlyph(for_char, temp_size, false); } } void save_image(std::string icon_path) { dbc::check($render != nullptr, "You have to call run() first."); fs::path out_path{icon_path}; if(fs::exists(out_path)) { fs::remove(out_path); } sf::Image out_img = $render->getTexture().copyToImage(); bool worked = out_img.saveToFile(out_path); dbc::check(worked, "Failed to write screenshot.png"); } void run(MapConfig& config) { sf::Vector2u crop{$size.x * (unsigned int)config.it.width, $size.y * (unsigned int)config.it.y}; $render = std::make_shared(crop); $render->clear({0,0,0,0}); $render->setSmooth(false); sf::Vector2f cell_pos{0.0f,0.0f}; sf::RectangleShape background({(float)$size.x, (float)$size.y}); for(each_row_t it{config.map}; it.next();) { // a 0 slot means we're done if(config.map[it.y][it.x] == 0) break; cell_pos.x = it.x * $size.x; cell_pos.y = it.y * $size.y; bool is_centered = config.centered[it.y][it.x]; wchar_t display_char = config.map[it.y][it.x]; std::wstring content{display_char}; auto bg = config.backgrounds.at(display_char); auto fg = config.colors.at(display_char); best_size(display_char, is_centered); sf::Text icon{$font, content, $font_size}; icon.setFillColor({255, 255, 255, 255}); $temp_render.draw(icon); $temp_render.clear({0,0,0,0}); auto& font_texture = $font.getTexture($font_size); sf::Sprite sprite{font_texture, $glyph.textureRect}; auto t_size = $glyph.textureRect.size; dbc::check($size.x - t_size.x >= 0, "font too big on x"); dbc::check($size.y - t_size.y >= 0, "font too big on y"); fmt::println("display: {}, bg: {},{},{},{}; fg: {},{},{},{}", (int)display_char, bg.r, bg.g, bg.b, bg.a, fg.r, fg.g, fg.b, fg.a); // draw the background first background.setFillColor(bg); if(is_centered) { sf::Vector2f center{ float(($size.x - t_size.x) / 2), float(($size.y - t_size.y) / 2)}; sprite.setScale({1.0f, 1.0f}); sprite.setPosition({cell_pos.x + center.x, cell_pos.y + center.y}); } else { sf::Vector2f scale{float($size.x) / float(t_size.x), float($size.y) / float(t_size.y)}; sprite.setScale(scale); sprite.setPosition(cell_pos); background.setPosition(cell_pos); } sprite.setColor(fg); $render->draw(background); $render->draw(sprite); $render->display(); } } void save_config(MapConfig& config, const std::string &path) { (void)path; json result = json::array(); for(each_row_t it{config.map}; it.next();) { if(config.map[it.y][it.x] == 0) break; json val; val["x"] = $size.x * it.x; val["y"] = $size.y * it.y; val["display"] = (int)config.map[it.y][it.x]; val["centered"] = config.centered[it.y][it.x]; result.push_back(val); } std::ofstream o(path, std::ios::out | std::ios::binary); o << std::setw(4) << result << std::endl; } }; void load_config(MapConfig& config, bool is_centered, std::string path, std::function finder) { Config tiles(path); for(auto [key, val] : tiles.json().items()) { config.it.next(); auto data = finder(val); wchar_t display = data["display"]; config.map[config.it.y][config.it.x] = display; config.centered[config.it.y][config.it.x] = is_centered; dbc::check(!config.colors.contains(display), fmt::format("duplicate color for display={} key={}", (int)display, (std::string)key)); if(data.contains("foreground")) { auto fg_color = data["foreground"]; sf::Color fg{fg_color[0], fg_color[1], fg_color[2]}; fmt::println("TILE {}, display: {} has foreground: {},{},{}", key, (int)display, fg.r, fg.g, fg.b); config.colors.insert_or_assign(display, fg); } else { fmt::println("TILE {}, {} has DEFAULT COLOR", key, (int)display); config.colors.insert_or_assign(display, DEFAULT_COLOR); } if(data.contains("background")) { auto bg_color = data["background"]; sf::Color bg{bg_color[0], bg_color[1], bg_color[2]}; fmt::println("TILE {} display: {} has background: {},{},{}", key, (int)display, bg.r, bg.g, bg.b); config.backgrounds.insert_or_assign(display, bg); } else { fmt::println("TILE {} display: {} has transparent background", key, (int)display); sf::Color bg{0, 0, 0, 0}; config.backgrounds.insert_or_assign(display, bg); } } } json& component_display(json& val) { auto& components = val["components"]; for(auto& comp : components) { if(comp["_type"] == "Tile") { return comp; } } dbc::log("BAD CHAR"); return val; } int main() { MapConfig config; load_config(config, false, "./assets/tiles.json", [](json& val) -> json& { return val; }); load_config(config, true, "./assets/items.json", component_display); load_config(config, true, "./assets/devices.json", component_display); load_config(config, true, "./assets/enemies.json", component_display); fmt::println("-----------------------------------------"); MapTileBuilder builder(DEFAULT_DIM, DEFAULT_DIM); builder.run(config); builder.save_image("./assets/map_tiles.png"); builder.save_config(config, "./assets/map_tiles.json"); return 0; }