osb/source/game/StarPlantDatabase.cpp
2024-03-15 21:28:11 +11:00

368 lines
13 KiB
C++

#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<String> PlantDatabase::treeStemDirectory(String const& stemName) const {
if (auto stem = m_treeStemConfigs.maybe(stemName))
return stem->directory;
return {};
}
Maybe<String> 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<Plant>(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<Plant>(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<Plant>(bushVariant, seed);
} catch (std::exception const& e) {
throw PlantDatabaseException(
strf("Error constructing plant from bush variant name: {} mod: {}", bushVariant.bushName, bushVariant.modName),
e);
}
}
}