Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/StarBiomeDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/StarBiomeDatabase.cpp')
-rw-r--r--source/game/StarBiomeDatabase.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/source/game/StarBiomeDatabase.cpp b/source/game/StarBiomeDatabase.cpp
new file mode 100644
index 0000000..d797a56
--- /dev/null
+++ b/source/game/StarBiomeDatabase.cpp
@@ -0,0 +1,233 @@
+#include "StarBiomeDatabase.hpp"
+#include "StarRoot.hpp"
+#include "StarJsonExtra.hpp"
+#include "StarStoredFunctions.hpp"
+#include "StarParallax.hpp"
+#include "StarAmbient.hpp"
+#include "StarMaterialDatabase.hpp"
+#include "StarAssets.hpp"
+#include "StarSpawnTypeDatabase.hpp"
+
+namespace Star {
+
+BiomeDatabase::BiomeDatabase() {
+ auto assets = Root::singleton().assets();
+
+ // 'type' here is the extension of the file, and determines the selector type
+ auto scanFiles = [=](String const& type, ConfigMap& map) {
+ auto files = assets->scanExtension(type);
+ assets->queueJsons(files);
+ for (auto path : files) {
+ auto parameters = assets->json(path);
+ if (parameters.isNull())
+ continue;
+
+ auto name = parameters.getString("name");
+ if (map.contains(name))
+ throw BiomeException(strf("Duplicate %s generator name '%s'", type, name));
+ map[name] = {path, name, parameters};
+ }
+ };
+
+ scanFiles("biome", m_biomes);
+ scanFiles("weather", m_weathers);
+}
+
+StringList BiomeDatabase::biomeNames() const {
+ return m_biomes.keys();
+}
+
+float BiomeDatabase::biomeHueShift(String const& biomeName, uint64_t seed) const {
+ auto const& config = m_biomes.get(biomeName);
+ return pickHueShiftFromJson(config.parameters.get("hueShiftOptions", {}), seed, "BiomeHueShift");
+}
+
+WeatherPool BiomeDatabase::biomeWeathers(String const& biomeName, uint64_t seed, float threatLevel) const {
+ WeatherPool weatherPool;
+ if (auto weatherList = binnedChoiceFromJson(m_biomes.get(biomeName).parameters.get("weather", JsonArray{}), threatLevel).optArray()) {
+ auto weatherPoolPath = staticRandomFrom(*weatherList, seed, "WeatherPool");
+
+ auto assets = Root::singleton().assets();
+ auto weatherPoolConfig = assets->fetchJson(weatherPoolPath);
+ weatherPool = jsonToWeightedPool<String>(weatherPoolConfig);
+ }
+
+ return weatherPool;
+}
+
+bool BiomeDatabase::biomeIsAirless(String const& biomeName) const {
+ auto const& config = m_biomes.get(biomeName);
+ return config.parameters.getBool("airless", false);
+}
+
+SkyColoring BiomeDatabase::biomeSkyColoring(String const& biomeName, uint64_t seed) const {
+ SkyColoring skyColoring;
+
+ auto const& config = m_biomes.get(biomeName);
+ if (auto skyOptions = config.parameters.optArray("skyOptions")) {
+ auto option = staticRandomFrom(*skyOptions, seed, "BiomeSkyOption");
+
+ skyColoring.mainColor = jsonToColor(option.get("mainColor"));
+
+ skyColoring.morningColors.first = jsonToColor(option.query("morningColors[0]"));
+ skyColoring.morningColors.second = jsonToColor(option.query("morningColors[1]"));
+
+ skyColoring.dayColors.first = jsonToColor(option.query("dayColors[0]"));
+ skyColoring.dayColors.second = jsonToColor(option.query("dayColors[1]"));
+
+ skyColoring.eveningColors.first = jsonToColor(option.query("eveningColors[0]"));
+ skyColoring.eveningColors.second = jsonToColor(option.query("eveningColors[1]"));
+
+ skyColoring.nightColors.first = jsonToColor(option.query("nightColors[0]"));
+ skyColoring.nightColors.second = jsonToColor(option.query("nightColors[1]"));
+
+ skyColoring.morningLightColor = jsonToColor(option.get("morningLightColor"));
+ skyColoring.dayLightColor = jsonToColor(option.get("dayLightColor"));
+ skyColoring.eveningLightColor = jsonToColor(option.get("eveningLightColor"));
+ skyColoring.nightLightColor = jsonToColor(option.get("nightLightColor"));
+ }
+
+ return skyColoring;
+}
+
+String BiomeDatabase::biomeFriendlyName(String const& biomeName) const {
+ auto const& config = m_biomes.get(biomeName);
+ return config.parameters.getString("friendlyName");
+}
+
+StringList BiomeDatabase::biomeStatusEffects(String const& biomeName) const {
+ auto const& config = m_biomes.get(biomeName);
+ return config.parameters.opt("statusEffects").apply(jsonToStringList).value();
+}
+
+StringList BiomeDatabase::biomeOres(String const& biomeName, float threatLevel) const {
+ StringList res;
+
+ auto const& config = m_biomes.get(biomeName);
+ auto oreDistribution = config.parameters.get("ores", {});
+ if (!oreDistribution.isNull()) {
+ auto& root = Root::singleton();
+ auto functionDatabase = root.functionDatabase();
+
+ auto oresList = functionDatabase->configFunction(oreDistribution)->get(threatLevel);
+ for (Json v : oresList.iterateArray()) {
+ if (v.getFloat(1) > 0)
+ res.append(v.getString(0));
+ }
+ }
+
+ return res;
+}
+
+StringList BiomeDatabase::weatherNames() const {
+ return m_weathers.keys();
+}
+
+WeatherType BiomeDatabase::weatherType(String const& name) const {
+ if (!m_weathers.contains(name))
+ throw BiomeException(strf("No such weather type '%s'", name));
+
+ auto config = m_weathers.get(name);
+
+ try {
+ return WeatherType(config.parameters, config.path);
+ } catch (MapException const& e) {
+ throw BiomeException(strf("Required key not found in weather config %s", config.path), e);
+ }
+}
+
+BiomePtr BiomeDatabase::createBiome(String const& biomeName, uint64_t seed, float verticalMidPoint, float threatLevel) const {
+ if (!m_biomes.contains(biomeName))
+ throw BiomeException(strf("No such biome '%s'", biomeName));
+
+ auto& root = Root::singleton();
+ auto materialDatabase = root.materialDatabase();
+
+ try {
+ RandomSource random(seed);
+ auto config = m_biomes.get(biomeName);
+
+ auto biome = make_shared<Biome>();
+ float mainHueShift = biomeHueShift(biomeName, seed);
+
+ biome->baseName = biomeName;
+ biome->description = config.parameters.getString("description", "");
+
+ if (config.parameters.contains("mainBlock"))
+ biome->mainBlock = materialDatabase->materialId(config.parameters.getString("mainBlock"));
+
+ for (Json v : config.parameters.getArray("subBlocks", {}))
+ biome->subBlocks.append(materialDatabase->materialId(v.toString()));
+
+ biome->ores = readOres(config.parameters.get("ores", {}), threatLevel);
+
+ biome->surfacePlaceables = readBiomePlaceables(config.parameters.getObject("surfacePlaceables", {}), random.randu64(), mainHueShift);
+ biome->undergroundPlaceables = readBiomePlaceables(config.parameters.getObject("undergroundPlaceables", {}), random.randu64(), mainHueShift);
+
+ biome->hueShift = mainHueShift;
+ biome->materialHueShift = materialHueFromDegrees(biome->hueShift);
+
+ if (config.parameters.contains("parallax")) {
+ auto parallaxFile = AssetPath::relativeTo(config.path, config.parameters.getString("parallax"));
+ biome->parallax = make_shared<Parallax>(parallaxFile, seed, verticalMidPoint, mainHueShift, biome->surfacePlaceables.firstTreeType());
+ }
+
+ if (config.parameters.contains("musicTrack"))
+ biome->musicTrack = make_shared<AmbientNoisesDescription>(config.parameters.getObject("musicTrack"), config.path);
+
+ if (config.parameters.contains("ambientNoises"))
+ biome->ambientNoises = make_shared<AmbientNoisesDescription>(config.parameters.getObject("ambientNoises"), config.path);
+
+ if (config.parameters.contains("spawnProfile"))
+ biome->spawnProfile = constructSpawnProfile(config.parameters.getObject("spawnProfile"), seed);
+
+ return biome;
+ } catch (std::exception const& cause) {
+ throw BiomeException(strf("Failed to parse biome: '%s'", biomeName), cause);
+ }
+}
+
+float BiomeDatabase::pickHueShiftFromJson(Json source, uint64_t seed, String const& key) {
+ if (source.isNull())
+ return 0;
+ auto options = jsonToFloatList(source);
+ if (options.size() == 0)
+ return 0;
+ auto t = staticRandomU32(seed, key);
+ return options.at(t % options.size());
+}
+
+BiomePlaceables BiomeDatabase::readBiomePlaceables(Json const& config, uint64_t seed, float biomeHueShift) const {
+ auto& root = Root::singleton();
+ RandomSource rand(seed);
+ BiomePlaceables placeables;
+ if (config.contains("grassMod") && !config.getArray("grassMod").empty())
+ placeables.grassMod = root.materialDatabase()->modId(rand.randFrom(config.getArray("grassMod")).toString());
+ placeables.grassModDensity = config.getFloat("grassModDensity", 0);
+ if (config.contains("ceilingGrassMod") && !config.getArray("ceilingGrassMod").empty())
+ placeables.ceilingGrassMod = root.materialDatabase()->modId(rand.randFrom(config.getArray("ceilingGrassMod")).toString());
+ placeables.ceilingGrassModDensity = config.getFloat("ceilingGrassModDensity", 0);
+
+ for (auto const& itemConfig : config.getArray("items", {}))
+ placeables.itemDistributions.append(BiomeItemDistribution(itemConfig, rand.randu64(), biomeHueShift));
+
+ return placeables;
+}
+
+List<pair<ModId, float>> BiomeDatabase::readOres(Json const& oreDistribution, float threatLevel) const {
+ List<pair<ModId, float>> ores;
+ if (!oreDistribution.isNull()) {
+ auto& root = Root::singleton();
+ auto functionDatabase = root.functionDatabase();
+ auto materialDatabase = root.materialDatabase();
+
+ auto oresList = functionDatabase->configFunction(oreDistribution)->get(threatLevel);
+ for (Json v : oresList.iterateArray()) {
+ if (v.getFloat(1) > 0)
+ ores.append({materialDatabase->modId(v.getString(0)), v.getFloat(1)});
+ }
+ }
+ return ores;
+}
+
+}