#include "StarPlantDatabase.hpp" #include "StarPlant.hpp" #include "StarJsonExtra.hpp" #include "StarAssets.hpp" #include "StarRoot.hpp" namespace Star { TreeVariant::TreeVariant() : stemHueShift(), foliageHueShift(), ceiling(), ephemeral() {} TreeVariant::TreeVariant(Json const& variant) { stemName = variant.getString("stemName"); foliageName = variant.getString("foliageName"); stemDirectory = variant.getString("stemDirectory"); stemSettings = variant.get("stemSettings"); stemHueShift = variant.getFloat("stemHueShift"); foliageDirectory = variant.getString("foliageDirectory"); foliageSettings = variant.get("foliageSettings"); foliageHueShift = variant.getFloat("foliageHueShift"); descriptions = variant.get("descriptions"); ceiling = variant.getBool("ceiling"); ephemeral = variant.getBool("ephemeral"); stemDropConfig = variant.get("stemDropConfig"); foliageDropConfig = variant.get("foliageDropConfig"); tileDamageParameters = TileDamageParameters(variant.get("tileDamageParameters")); } Json TreeVariant::toJson() const { return JsonObject{ {"stemName", stemName}, {"foliageName", foliageName}, {"stemDirectory", stemDirectory}, {"stemSettings", stemSettings}, {"stemHueShift", stemHueShift}, {"foliageDirectory", foliageDirectory}, {"foliageSettings", foliageSettings}, {"foliageHueShift", foliageHueShift}, {"descriptions", descriptions}, {"ceiling", ceiling}, {"ephemeral", ephemeral}, {"stemDropConfig", stemDropConfig}, {"foliageDropConfig", foliageDropConfig}, {"tileDamageParameters", tileDamageParameters.toJson()}, }; } GrassVariant::GrassVariant() : hueShift(), ceiling(), ephemeral() {} GrassVariant::GrassVariant(Json const& variant) { name = variant.getString("name"); directory = variant.getString("directory"); images = jsonToStringList(variant.get("images")); hueShift = variant.getFloat("hueShift"); descriptions = variant.get("descriptions"); ceiling = variant.getBool("ceiling"); ephemeral = variant.getBool("ephemeral"); tileDamageParameters = TileDamageParameters(variant.get("tileDamageParameters")); } Json GrassVariant::toJson() const { return JsonObject{{"name", name}, {"directory", directory}, {"images", jsonFromStringList(images)}, {"hueShift", hueShift}, {"descriptions", descriptions}, {"ceiling", ceiling}, {"ephemeral", ephemeral}, {"tileDamageParameters", tileDamageParameters.toJson()}}; } BushVariant::BushVariant() : baseHueShift(), modHueShift(), ceiling(), ephemeral() {} BushVariant::BushVariant(Json const& variant) { bushName = variant.getString("bushName"); modName = variant.getString("modName"); directory = variant.getString("directory"); shapes = variant.getArray("shapes").transformed([](Json const& v) { return BushShape{v.getString(0), jsonToStringList(v.get(1))}; }); baseHueShift = variant.getFloat("baseHueShift"); modHueShift = variant.getFloat("modHueShift"); descriptions = variant.get("descriptions"); ceiling = variant.getBool("ceiling"); ephemeral = variant.getBool("ephemeral"); tileDamageParameters = TileDamageParameters(variant.get("tileDamageParameters")); } Json BushVariant::toJson() const { return JsonObject{{"bushName", bushName}, {"modName", modName}, {"directory", directory}, {"shapes", shapes.transformed([](BushShape const& shape) -> Json { return JsonArray{shape.image, jsonFromStringList(shape.mods)}; })}, {"baseHueShift", baseHueShift}, {"modHueShift", modHueShift}, {"descriptions", descriptions}, {"ceiling", ceiling}, {"ephemeral", ephemeral}, {"tileDamageParameters", tileDamageParameters.toJson()}}; } PlantDatabase::PlantDatabase() { auto assets = Root::singleton().assets(); auto& stems = assets->scanExtension("modularstem"); auto& foliages = assets->scanExtension("modularfoliage"); auto& grasses = assets->scanExtension("grass"); auto& bushes = assets->scanExtension("bush"); assets->queueJsons(stems); assets->queueJsons(foliages); assets->queueJsons(grasses); assets->queueJsons(bushes); try { for (auto& file : stems) { auto config = assets->json(file); m_treeStemConfigs.insert(config.getString("name"), Config{AssetPath::directory(file), config.toObject()}); } for (auto& file : foliages) { auto config = assets->json(file); m_treeFoliageConfigs.insert(config.getString("name"), Config{AssetPath::directory(file), config.toObject()}); } for (auto& file : grasses) { auto config = assets->json(file); m_grassConfigs.insert(config.getString("name"), Config{AssetPath::directory(file), config.toObject()}); } for (auto& file : bushes) { auto config = assets->json(file); m_bushConfigs.insert(config.getString("name"), Config{AssetPath::directory(file), config.toObject()}); } } catch (StarException const& e) { throw PlantDatabaseException("Error loading plant database", e); } } StringList PlantDatabase::treeStemNames(bool ceiling) const { StringList names; for (auto const& pair : m_treeStemConfigs) { if (pair.second.settings.getBool("ceiling", false) == ceiling) names.append(pair.first); } return names; } StringList PlantDatabase::treeFoliageNames() const { return m_treeFoliageConfigs.keys(); } String PlantDatabase::treeStemShape(String const& stemName) const { return m_treeStemConfigs.get(stemName).settings.get("shape").toString(); } String PlantDatabase::treeFoliageShape(String const& foliageName) const { return m_treeFoliageConfigs.get(foliageName).settings.get("shape").toString(); } Maybe PlantDatabase::treeStemDirectory(String const& stemName) const { if (auto stem = m_treeStemConfigs.maybe(stemName)) return stem->directory; return {}; } Maybe PlantDatabase::treeFoliageDirectory(String const& foliageName) const { if (auto foliage = m_treeFoliageConfigs.maybe(foliageName)) return foliage->directory; return {}; } TreeVariant PlantDatabase::buildTreeVariant( String const& stemName, float stemHueShift, String const& foliageName, float foliageHueShift) const { if (!m_treeStemConfigs.contains(stemName) || !m_treeFoliageConfigs.contains(foliageName)) throw PlantDatabaseException::format("stemName '{}' or foliageName '{}' not found in plant database", stemName, foliageName); TreeVariant treeVariant; treeVariant.stemName = stemName; treeVariant.foliageName = foliageName; auto stemConfig = m_treeStemConfigs.get(stemName); treeVariant.stemDirectory = stemConfig.directory; treeVariant.stemSettings = stemConfig.settings; treeVariant.stemHueShift = stemHueShift; auto foliageConfig = m_treeFoliageConfigs.get(foliageName); treeVariant.foliageDirectory = foliageConfig.directory; treeVariant.foliageSettings = foliageConfig.settings; treeVariant.foliageHueShift = foliageHueShift; treeVariant.ceiling = stemConfig.settings.getBool("ceiling", false); treeVariant.stemDropConfig = stemConfig.settings.get("dropConfig", JsonObject()); treeVariant.foliageDropConfig = foliageConfig.settings.get("dropConfig", JsonObject()); JsonObject descriptions; for (auto const& entry : stemConfig.settings.iterateObject()) { if (entry.first.endsWith("Description")) descriptions[entry.first] = entry.second; } descriptions["description"] = stemConfig.settings.getString("description", stemName + " with " + foliageName); treeVariant.descriptions = descriptions; treeVariant.ephemeral = stemConfig.settings.getBool("allowsBlockPlacement", false); treeVariant.tileDamageParameters = TileDamageParameters( stemConfig.settings.get("damageTable", "/plants/treeDamage.config"), stemConfig.settings.getFloat("health", 1.0f)); return treeVariant; } TreeVariant PlantDatabase::buildTreeVariant(String const& stemName, float stemHueShift) const { if (!m_treeStemConfigs.contains(stemName)) throw PlantDatabaseException(strf("stemName '{}' not found in plant database", stemName)); TreeVariant treeVariant; auto stemConfig = m_treeStemConfigs.get(stemName); treeVariant.stemDirectory = stemConfig.directory; treeVariant.stemSettings = stemConfig.settings; treeVariant.stemHueShift = stemHueShift; treeVariant.ceiling = stemConfig.settings.getBool("ceiling", false); treeVariant.stemDropConfig = stemConfig.settings.get("dropConfig", JsonObject()); treeVariant.foliageSettings = JsonObject(); treeVariant.foliageDropConfig = JsonObject(); JsonObject descriptions; for (auto const& entry : stemConfig.settings.iterateObject()) { if (entry.first.endsWith("Description")) descriptions[entry.first] = entry.second; } descriptions["description"] = stemConfig.settings.getString("description", stemName); treeVariant.descriptions = descriptions; treeVariant.ephemeral = stemConfig.settings.getBool("ephemeral", false); treeVariant.tileDamageParameters = TileDamageParameters( stemConfig.settings.get("damageTable", "/plants/treeDamage.config"), stemConfig.settings.getFloat("health", 1.0f)); return treeVariant; } StringList PlantDatabase::grassNames(bool ceiling) const { StringList names; for (auto const& pair : m_grassConfigs) { if (pair.second.settings.getBool("ceiling", false) == ceiling) names.append(pair.first); } return names; } GrassVariant PlantDatabase::buildGrassVariant(String const& name, float hueShift) const { if (!m_grassConfigs.contains(name)) throw PlantDatabaseException(strf("grass '{}' not found in plant database", name)); GrassVariant grassVariant; auto config = m_grassConfigs.get(name); grassVariant.name = name; grassVariant.directory = config.directory; grassVariant.images = jsonToStringList(config.settings.get("images")); grassVariant.hueShift = hueShift; grassVariant.ceiling = config.settings.getBool("ceiling", false); JsonObject descriptions; for (auto const& entry : config.settings.iterateObject()) { if (entry.first.endsWith("Description")) descriptions[entry.first] = entry.second; } descriptions["description"] = config.settings.getString("description", name); grassVariant.descriptions = descriptions; grassVariant.ephemeral = config.settings.getBool("ephemeral", true); grassVariant.tileDamageParameters = TileDamageParameters( config.settings.get("damageTable", "/plants/grassDamage.config"), config.settings.getFloat("health", 1.0f)); return grassVariant; } StringList PlantDatabase::bushNames(bool ceiling) const { StringList names; for (auto const& pair : m_bushConfigs) { if (pair.second.settings.getBool("ceiling") == ceiling) names.append(pair.first); } return names; } StringList PlantDatabase::bushMods(String const& bushName) const { return m_bushConfigs.get(bushName).settings.opt("mods").apply(jsonToStringList).value(); } BushVariant PlantDatabase::buildBushVariant(String const& bushName, float baseHueShift, String const& modName, float modHueShift) const { if (!m_bushConfigs.contains(bushName)) throw PlantDatabaseException(strf("bush '{}' not found in plant database", bushName)); BushVariant bushVariant; auto config = m_bushConfigs.get(bushName); bushVariant.bushName = bushName; bushVariant.modName = modName; bushVariant.directory = config.directory; auto shapes = config.settings.get("shapes").toArray(); for (auto shapeVar : shapes) { auto shapeMap = shapeVar.toObject(); auto base = shapeMap.get("base").toString(); StringList mods; if (!modName.empty()) mods = jsonToStringList(shapeMap.get("mods").get(modName, JsonArray())); bushVariant.shapes.push_back({base, mods}); } bushVariant.baseHueShift = baseHueShift; bushVariant.modHueShift = modHueShift; bushVariant.ceiling = config.settings.getBool("ceiling", false); JsonObject descriptions; for (auto const& entry : config.settings.iterateObject()) { if (entry.first.endsWith("Description")) descriptions[entry.first] = entry.second; } descriptions["description"] = config.settings.getString("description", bushName + " with " + modName); bushVariant.descriptions = descriptions; bushVariant.ephemeral = config.settings.getBool("ephemeral", true); bushVariant.tileDamageParameters = TileDamageParameters( config.settings.get("damageTable", "/plants/bushDamage.config"), config.settings.getFloat("health", 1.0f)); return bushVariant; } PlantPtr PlantDatabase::createPlant(TreeVariant const& treeVariant, uint64_t seed) const { try { return make_shared(treeVariant, seed); } catch (std::exception const& e) { throw PlantDatabaseException(strf("Error constructing plant from tree variant stem: {} foliage: {}", treeVariant.stemName, treeVariant.foliageName), e); } } PlantPtr PlantDatabase::createPlant(GrassVariant const& grassVariant, uint64_t seed) const { try { return make_shared(grassVariant, seed); } catch (std::exception const& e) { throw PlantDatabaseException(strf("Error constructing plant from grass variant name: {}", grassVariant.name), e); } } PlantPtr PlantDatabase::createPlant(BushVariant const& bushVariant, uint64_t seed) const { try { return make_shared(bushVariant, seed); } catch (std::exception const& e) { throw PlantDatabaseException( strf("Error constructing plant from bush variant name: {} mod: {}", bushVariant.bushName, bushVariant.modName), e); } } }