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

summaryrefslogtreecommitdiff
path: root/source/game/StarWorldParameters.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/StarWorldParameters.cpp')
-rw-r--r--source/game/StarWorldParameters.cpp721
1 files changed, 721 insertions, 0 deletions
diff --git a/source/game/StarWorldParameters.cpp b/source/game/StarWorldParameters.cpp
new file mode 100644
index 0000000..83270a9
--- /dev/null
+++ b/source/game/StarWorldParameters.cpp
@@ -0,0 +1,721 @@
+#include "StarWorldParameters.hpp"
+#include "StarJsonExtra.hpp"
+#include "StarDataStreamExtra.hpp"
+#include "StarRoot.hpp"
+#include "StarAssets.hpp"
+#include "StarBiomeDatabase.hpp"
+#include "StarLiquidsDatabase.hpp"
+
+namespace Star {
+
+EnumMap<WorldParametersType> const WorldParametersTypeNames{
+ {WorldParametersType::TerrestrialWorldParameters, "TerrestrialWorldParameters"},
+ {WorldParametersType::AsteroidsWorldParameters, "AsteroidsWorldParameters"},
+ {WorldParametersType::FloatingDungeonWorldParameters, "FloatingDungeonWorldParameters"}};
+
+EnumMap<BeamUpRule> const BeamUpRuleNames{
+ {BeamUpRule::Nowhere, "Nowhere"},
+ {BeamUpRule::Surface, "Surface"},
+ {BeamUpRule::Anywhere, "Anywhere"},
+ {BeamUpRule::AnywhereWithWarning, "AnywhereWithWarning"}};
+
+EnumMap<WorldEdgeForceRegionType> const WorldEdgeForceRegionTypeNames{
+ {WorldEdgeForceRegionType::None, "None"},
+ {WorldEdgeForceRegionType::Top, "Top"},
+ {WorldEdgeForceRegionType::Bottom, "Bottom"},
+ {WorldEdgeForceRegionType::TopAndBottom, "TopAndBottom"}};
+
+VisitableWorldParameters::VisitableWorldParameters() {
+ threatLevel = 0;
+ gravity = 0;
+ airless = false;
+ disableDeathDrops = false;
+ terraformed = false;
+ worldEdgeForceRegions = WorldEdgeForceRegionType::None;
+}
+
+VisitableWorldParameters::VisitableWorldParameters(VisitableWorldParameters const& visitableWorldParameters) {
+ *this = visitableWorldParameters;
+}
+
+VisitableWorldParameters::VisitableWorldParameters(Json const& store) {
+ typeName = store.getString("typeName", "");
+ threatLevel = store.getFloat("threatLevel");
+ worldSize = jsonToVec2U(store.get("worldSize"));
+ gravity = store.getFloat("gravity", 1.0f);
+ airless = store.getBool("airless", false);
+ weatherPool = jsonToWeightedPool<String>(store.getArray("weatherPool", JsonArray()));
+ environmentStatusEffects = store.opt("environmentStatusEffects").apply(jsonToStringList).value();
+ overrideTech = store.opt("overrideTech").apply(jsonToStringList);
+ globalDirectives = store.opt("globalDirectives").apply(jsonToStringList);
+ beamUpRule = BeamUpRuleNames.getLeft(store.getString("beamUpRule", "Surface"));
+ disableDeathDrops = store.getBool("disableDeathDrops", false);
+ terraformed = store.getBool("terraformed", false);
+ worldEdgeForceRegions = WorldEdgeForceRegionTypeNames.getLeft(store.getString("worldEdgeForceRegions", "None"));
+}
+
+VisitableWorldParameters::~VisitableWorldParameters() {}
+
+Json VisitableWorldParameters::store() const {
+ return JsonObject{{"typeName", typeName},
+ {"threatLevel", threatLevel},
+ {"worldSize", jsonFromVec2U(worldSize)},
+ {"gravity", gravity},
+ {"airless", airless},
+ {"weatherPool", jsonFromWeightedPool<String>(weatherPool)},
+ {"environmentStatusEffects", jsonFromStringList(environmentStatusEffects)},
+ {"overrideTech", jsonFromMaybe(overrideTech.apply(&jsonFromStringList))},
+ {"globalDirectives", jsonFromMaybe(globalDirectives.apply(&jsonFromStringList))},
+ {"beamUpRule", BeamUpRuleNames.getRight(beamUpRule)},
+ {"disableDeathDrops", disableDeathDrops},
+ {"terraformed", terraformed},
+ {"worldEdgeForceRegions", WorldEdgeForceRegionTypeNames.getRight(worldEdgeForceRegions)}};
+}
+
+void VisitableWorldParameters::read(DataStream& ds) {
+ ds >> typeName;
+ ds >> threatLevel;
+ ds >> worldSize;
+ ds >> gravity;
+ ds >> airless;
+ weatherPool = WeatherPool(ds.read<WeatherPool::ItemsList>());
+ ds >> environmentStatusEffects;
+ ds >> overrideTech;
+ ds >> globalDirectives;
+ ds >> beamUpRule;
+ ds >> disableDeathDrops;
+ ds >> terraformed;
+ ds >> worldEdgeForceRegions;
+}
+
+void VisitableWorldParameters::write(DataStream& ds) const {
+ ds << typeName;
+ ds << threatLevel;
+ ds << worldSize;
+ ds << gravity;
+ ds << airless;
+ ds.writeContainer<WeatherPool::ItemsList>(weatherPool.items());
+ ds << environmentStatusEffects;
+ ds << overrideTech;
+ ds << globalDirectives;
+ ds << beamUpRule;
+ ds << disableDeathDrops;
+ ds << terraformed;
+ ds << worldEdgeForceRegions;
+}
+
+TerrestrialWorldParameters::TerrestrialWorldParameters() {
+ blendSize = 0;
+ dayLength = 0;
+}
+
+TerrestrialWorldParameters::TerrestrialWorldParameters(TerrestrialWorldParameters const& terrestrialWorldParameters) {
+ *this = terrestrialWorldParameters;
+}
+
+TerrestrialWorldParameters::TerrestrialWorldParameters(Json const& store) : VisitableWorldParameters(store) {
+ auto loadTerrestrialRegion = [](Json const& config) {
+ return TerrestrialRegion{config.getString("biome"),
+ config.getString("blockSelector"),
+ config.getString("fgCaveSelector"),
+ config.getString("bgCaveSelector"),
+ config.getString("fgOreSelector"),
+ config.getString("bgOreSelector"),
+ config.getString("subBlockSelector"),
+ (LiquidId)config.getUInt("caveLiquid"),
+ config.getFloat("caveLiquidSeedDensity"),
+ (LiquidId)config.getUInt("oceanLiquid"),
+ (int)config.getInt("oceanLiquidLevel"),
+ (bool)config.getBool("encloseLiquids"),
+ (bool)config.getBool("fillMicrodungeons")};
+ };
+
+ auto loadTerrestrialLayer = [loadTerrestrialRegion](Json const& config) {
+ return TerrestrialLayer{(int)config.getInt("layerMinHeight"),
+ (int)config.getInt("layerBaseHeight"),
+ jsonToStringList(config.get("dungeons")),
+ (int)config.getInt("dungeonXVariance"),
+ loadTerrestrialRegion(config.get("primaryRegion")),
+ loadTerrestrialRegion(config.get("primarySubRegion")),
+ config.getArray("secondaryRegions").transformed(loadTerrestrialRegion),
+ config.getArray("secondarySubRegions").transformed(loadTerrestrialRegion),
+ jsonToVec2F(config.get("secondaryRegionSizeRange")),
+ jsonToVec2F(config.get("subRegionSizeRange"))};
+ };
+
+ primaryBiome = store.getString("primaryBiome");
+ primarySurfaceLiquid = store.getUInt("surfaceLiquid");
+ sizeName = store.getString("sizeName");
+ hueShift = store.getFloat("hueShift");
+ skyColoring = SkyColoring(store.get("skyColoring"));
+ dayLength = store.getFloat("dayLength");
+ blockNoiseConfig = store.get("blockNoise");
+ blendNoiseConfig = store.get("blendNoise");
+ blendSize = store.getFloat("blendSize");
+
+ spaceLayer = loadTerrestrialLayer(store.get("spaceLayer"));
+ atmosphereLayer = loadTerrestrialLayer(store.get("atmosphereLayer"));
+ surfaceLayer = loadTerrestrialLayer(store.get("surfaceLayer"));
+ subsurfaceLayer = loadTerrestrialLayer(store.get("subsurfaceLayer"));
+ undergroundLayers = store.getArray("undergroundLayers").transformed(loadTerrestrialLayer);
+ coreLayer = loadTerrestrialLayer(store.get("coreLayer"));
+}
+
+WorldParametersType TerrestrialWorldParameters::type() const {
+ return WorldParametersType::TerrestrialWorldParameters;
+}
+
+Json TerrestrialWorldParameters::store() const {
+ auto storeTerrestrialRegion = [](TerrestrialRegion const& region) -> Json {
+ return JsonObject{{"biome", region.biome},
+ {"blockSelector", region.blockSelector},
+ {"fgCaveSelector", region.fgCaveSelector},
+ {"bgCaveSelector", region.bgCaveSelector},
+ {"fgOreSelector", region.fgOreSelector},
+ {"bgOreSelector", region.bgOreSelector},
+ {"subBlockSelector", region.subBlockSelector},
+ {"caveLiquid", region.caveLiquid},
+ {"caveLiquidSeedDensity", region.caveLiquidSeedDensity},
+ {"oceanLiquid", region.oceanLiquid},
+ {"oceanLiquidLevel", region.oceanLiquidLevel},
+ {"encloseLiquids", region.encloseLiquids},
+ {"fillMicrodungeons", region.fillMicrodungeons}};
+ };
+ auto storeTerrestrialLayer = [storeTerrestrialRegion](TerrestrialLayer const& layer) -> Json {
+ return JsonObject{{"layerMinHeight", layer.layerMinHeight},
+ {"layerBaseHeight", layer.layerBaseHeight},
+ {"dungeons", jsonFromStringList(layer.dungeons)},
+ {"dungeonXVariance", layer.dungeonXVariance},
+ {"primaryRegion", storeTerrestrialRegion(layer.primaryRegion)},
+ {"primarySubRegion", storeTerrestrialRegion(layer.primarySubRegion)},
+ {"secondaryRegions", layer.secondaryRegions.transformed(storeTerrestrialRegion)},
+ {"secondarySubRegions", layer.secondarySubRegions.transformed(storeTerrestrialRegion)},
+ {"secondaryRegionSizeRange", jsonFromVec2F(layer.secondaryRegionSizeRange)},
+ {"subRegionSizeRange", jsonFromVec2F(layer.subRegionSizeRange)}};
+ };
+
+ return VisitableWorldParameters::store().setAll(JsonObject{{"primaryBiome", primaryBiome},
+ {"sizeName", sizeName},
+ {"hueShift", hueShift},
+ {"surfaceLiquid", primarySurfaceLiquid},
+ {"skyColoring", skyColoring.toJson()},
+ {"dayLength", dayLength},
+ {"blockNoise", blockNoiseConfig},
+ {"blendNoise", blendNoiseConfig},
+ {"blendSize", blendSize},
+ {"spaceLayer", storeTerrestrialLayer(spaceLayer)},
+ {"atmosphereLayer", storeTerrestrialLayer(atmosphereLayer)},
+ {"surfaceLayer", storeTerrestrialLayer(surfaceLayer)},
+ {"subsurfaceLayer", storeTerrestrialLayer(subsurfaceLayer)},
+ {"undergroundLayers", undergroundLayers.transformed(storeTerrestrialLayer)},
+ {"coreLayer", storeTerrestrialLayer(coreLayer)}});
+}
+
+DataStream& operator>>(DataStream& ds, TerrestrialWorldParameters::TerrestrialRegion& region) {
+ ds >> region.biome;
+ ds >> region.blockSelector;
+ ds >> region.fgCaveSelector;
+ ds >> region.bgCaveSelector;
+ ds >> region.fgOreSelector;
+ ds >> region.bgOreSelector;
+ ds >> region.subBlockSelector;
+ ds >> region.caveLiquid;
+ ds >> region.caveLiquidSeedDensity;
+ ds >> region.oceanLiquid;
+ ds >> region.oceanLiquidLevel;
+ ds >> region.encloseLiquids;
+ ds >> region.fillMicrodungeons;
+ return ds;
+}
+
+void TerrestrialWorldParameters::read(DataStream& ds) {
+ auto readTerrestrialLayer = [](DataStream& ds, TerrestrialLayer& layer) {
+ ds >> layer.layerMinHeight;
+ ds >> layer.layerBaseHeight;
+ ds >> layer.dungeons;
+ ds >> layer.dungeonXVariance;
+ ds >> layer.primaryRegion;
+ ds >> layer.primarySubRegion;
+ ds >> layer.secondaryRegions;
+ ds >> layer.secondarySubRegions;
+ ds >> layer.secondaryRegionSizeRange;
+ ds >> layer.subRegionSizeRange;
+ };
+
+ VisitableWorldParameters::read(ds);
+ ds >> primaryBiome;
+ ds >> primarySurfaceLiquid;
+ ds >> sizeName;
+ ds >> hueShift;
+ ds >> skyColoring;
+ ds >> dayLength;
+ ds >> blendSize;
+ ds >> blockNoiseConfig;
+ ds >> blendNoiseConfig;
+ readTerrestrialLayer(ds, spaceLayer);
+ readTerrestrialLayer(ds, atmosphereLayer);
+ readTerrestrialLayer(ds, surfaceLayer);
+ readTerrestrialLayer(ds, subsurfaceLayer);
+ ds.readContainer(undergroundLayers, readTerrestrialLayer);
+ readTerrestrialLayer(ds, coreLayer);
+}
+
+DataStream& operator<<(DataStream& ds, TerrestrialWorldParameters::TerrestrialRegion const& region) {
+ ds << region.biome;
+ ds << region.blockSelector;
+ ds << region.fgCaveSelector;
+ ds << region.bgCaveSelector;
+ ds << region.fgOreSelector;
+ ds << region.bgOreSelector;
+ ds << region.subBlockSelector;
+ ds << region.caveLiquid;
+ ds << region.caveLiquidSeedDensity;
+ ds << region.oceanLiquid;
+ ds << region.oceanLiquidLevel;
+ ds << region.encloseLiquids;
+ ds << region.fillMicrodungeons;
+ return ds;
+}
+
+void TerrestrialWorldParameters::write(DataStream& ds) const {
+ auto writeTerrestrialLayer = [](DataStream& ds, TerrestrialLayer const& layer) {
+ ds << layer.layerMinHeight;
+ ds << layer.layerBaseHeight;
+ ds << layer.dungeons;
+ ds << layer.dungeonXVariance;
+ ds << layer.primaryRegion;
+ ds << layer.primarySubRegion;
+ ds << layer.secondaryRegions;
+ ds << layer.secondarySubRegions;
+ ds << layer.secondaryRegionSizeRange;
+ ds << layer.subRegionSizeRange;
+ };
+
+ VisitableWorldParameters::write(ds);
+ ds << primaryBiome;
+ ds << primarySurfaceLiquid;
+ ds << sizeName;
+ ds << hueShift;
+ ds << skyColoring;
+ ds << dayLength;
+ ds << blendSize;
+ ds << blockNoiseConfig;
+ ds << blendNoiseConfig;
+ writeTerrestrialLayer(ds, spaceLayer);
+ writeTerrestrialLayer(ds, atmosphereLayer);
+ writeTerrestrialLayer(ds, surfaceLayer);
+ writeTerrestrialLayer(ds, subsurfaceLayer);
+ ds.writeContainer(undergroundLayers, writeTerrestrialLayer);
+ writeTerrestrialLayer(ds, coreLayer);
+}
+
+AsteroidsWorldParameters::AsteroidsWorldParameters() {
+ airless = true;
+ asteroidTopLevel = 0;
+ asteroidBottomLevel = 0;
+ blendSize = 0;
+}
+
+AsteroidsWorldParameters::AsteroidsWorldParameters(Json const& store) : VisitableWorldParameters(store) {
+ asteroidTopLevel = store.getInt("asteroidTopLevel");
+ asteroidBottomLevel = store.getInt("asteroidBottomLevel");
+ blendSize = store.getFloat("blendSize");
+ asteroidBiome = store.getString("asteroidBiome");
+ ambientLightLevel = jsonToColor(store.get("ambientLightLevel"));
+}
+
+WorldParametersType AsteroidsWorldParameters::type() const {
+ return WorldParametersType::AsteroidsWorldParameters;
+}
+
+Json AsteroidsWorldParameters::store() const {
+ return VisitableWorldParameters::store().setAll(JsonObject{{"asteroidTopLevel", asteroidTopLevel},
+ {"asteroidBottomLevel", asteroidBottomLevel},
+ {"blendSize", blendSize},
+ {"asteroidBiome", asteroidBiome},
+ {"ambientLightLevel", jsonFromColor(ambientLightLevel)}});
+}
+
+void AsteroidsWorldParameters::read(DataStream& ds) {
+ VisitableWorldParameters::read(ds);
+ ds >> asteroidTopLevel;
+ ds >> asteroidBottomLevel;
+ ds >> blendSize;
+ ds >> asteroidBiome;
+ ds >> ambientLightLevel;
+}
+
+void AsteroidsWorldParameters::write(DataStream& ds) const {
+ VisitableWorldParameters::write(ds);
+ ds << asteroidTopLevel;
+ ds << asteroidBottomLevel;
+ ds << blendSize;
+ ds << asteroidBiome;
+ ds << ambientLightLevel;
+}
+
+FloatingDungeonWorldParameters::FloatingDungeonWorldParameters() {}
+
+FloatingDungeonWorldParameters::FloatingDungeonWorldParameters(Json const& store) : VisitableWorldParameters(store) {
+ dungeonBaseHeight = store.getInt("dungeonBaseHeight");
+ dungeonSurfaceHeight = store.getInt("dungeonSurfaceHeight");
+ dungeonUndergroundLevel = store.getInt("dungeonUndergroundLevel");
+ primaryDungeon = store.getString("primaryDungeon");
+ biome = store.optString("biome");
+ ambientLightLevel = jsonToColor(store.get("ambientLightLevel"));
+ dayMusicTrack = store.optString("dayMusicTrack");
+ nightMusicTrack = store.optString("nightMusicTrack");
+ dayAmbientNoises = store.optString("dayAmbientNoises");
+ nightAmbientNoises = store.optString("nightAmbientNoises");
+}
+
+WorldParametersType FloatingDungeonWorldParameters::type() const {
+ return WorldParametersType::FloatingDungeonWorldParameters;
+}
+
+Json FloatingDungeonWorldParameters::store() const {
+ return VisitableWorldParameters::store().setAll(JsonObject{{"dungeonBaseHeight", dungeonBaseHeight},
+ {"dungeonSurfaceHeight", dungeonSurfaceHeight},
+ {"dungeonUndergroundLevel", dungeonUndergroundLevel},
+ {"primaryDungeon", primaryDungeon},
+ {"biome", jsonFromMaybe(biome)},
+ {"ambientLightLevel", jsonFromColor(ambientLightLevel)},
+ {"dayMusicTrack", jsonFromMaybe(dayMusicTrack)},
+ {"nightMusicTrack", jsonFromMaybe(nightMusicTrack)},
+ {"dayAmbientNoises", jsonFromMaybe(dayAmbientNoises)},
+ {"nightAmbientNoises", jsonFromMaybe(nightAmbientNoises)}});
+}
+
+void FloatingDungeonWorldParameters::read(DataStream& ds) {
+ VisitableWorldParameters::read(ds);
+ ds >> dungeonBaseHeight;
+ ds >> dungeonSurfaceHeight;
+ ds >> dungeonUndergroundLevel;
+ ds >> primaryDungeon;
+ ds >> biome;
+ ds >> ambientLightLevel;
+ ds >> dayMusicTrack;
+ ds >> nightMusicTrack;
+ ds >> dayAmbientNoises;
+ ds >> nightAmbientNoises;
+}
+
+void FloatingDungeonWorldParameters::write(DataStream& ds) const {
+ VisitableWorldParameters::write(ds);
+ ds << dungeonBaseHeight;
+ ds << dungeonSurfaceHeight;
+ ds << dungeonUndergroundLevel;
+ ds << primaryDungeon;
+ ds << biome;
+ ds << ambientLightLevel;
+ ds << dayMusicTrack;
+ ds << nightMusicTrack;
+ ds << dayAmbientNoises;
+ ds << nightAmbientNoises;
+}
+
+Json diskStoreVisitableWorldParameters(VisitableWorldParametersConstPtr const& parameters) {
+ if (!parameters)
+ return Json();
+
+ return parameters->store().setAll({{"type", WorldParametersTypeNames.getRight(parameters->type())}});
+}
+
+VisitableWorldParametersPtr diskLoadVisitableWorldParameters(Json const& store) {
+ if (store.isNull())
+ return {};
+
+ auto type = WorldParametersTypeNames.getLeft(store.getString("type"));
+ if (type == WorldParametersType::TerrestrialWorldParameters)
+ return make_shared<TerrestrialWorldParameters>(store);
+ else if (type == WorldParametersType::AsteroidsWorldParameters)
+ return make_shared<AsteroidsWorldParameters>(store);
+ else if (type == WorldParametersType::FloatingDungeonWorldParameters)
+ return make_shared<FloatingDungeonWorldParameters>(store);
+ throw StarException("No such WorldParametersType");
+}
+
+ByteArray netStoreVisitableWorldParameters(VisitableWorldParametersConstPtr const& parameters) {
+ if (!parameters)
+ return ByteArray();
+
+ DataStreamBuffer ds;
+ ds.write(parameters->type());
+ parameters->write(ds);
+ return ds.takeData();
+}
+
+VisitableWorldParametersPtr netLoadVisitableWorldParameters(ByteArray data) {
+ if (data.empty())
+ return {};
+
+ DataStreamBuffer ds(move(data));
+ auto type = ds.read<WorldParametersType>();
+
+ VisitableWorldParametersPtr parameters;
+ if (type == WorldParametersType::TerrestrialWorldParameters)
+ parameters = make_shared<TerrestrialWorldParameters>();
+ else if (type == WorldParametersType::AsteroidsWorldParameters)
+ parameters = make_shared<AsteroidsWorldParameters>();
+ else if (type == WorldParametersType::FloatingDungeonWorldParameters)
+ parameters = make_shared<FloatingDungeonWorldParameters>();
+ else
+ throw StarException("No such WorldParametersType");
+
+ parameters->read(ds);
+
+ return parameters;
+}
+
+TerrestrialWorldParametersPtr generateTerrestrialWorldParameters(String const& typeName, String const& sizeName, uint64_t seed) {
+ auto& root = Root::singleton();
+ auto assets = root.assets();
+ auto liquidsDatabase = root.liquidsDatabase();
+ auto biomeDatabase = root.biomeDatabase();
+
+ auto terrestrialConfig = assets->json("/terrestrial_worlds.config");
+
+ auto regionDefaults = terrestrialConfig.get("regionDefaults");
+ auto regionTypes = terrestrialConfig.get("regionTypes");
+
+ auto baseConfig = terrestrialConfig.get("planetDefaults");
+ auto sizeConfig = terrestrialConfig.get("planetSizes").get(sizeName);
+ auto typeConfig = terrestrialConfig.get("planetTypes").get(typeName);
+ auto config = jsonMerge(baseConfig, sizeConfig, typeConfig);
+
+ auto gravityRange = jsonToVec2F(config.get("gravityRange"));
+ auto dayLengthRange = jsonToVec2F(config.get("dayLengthRange"));
+ auto threatLevelRange = jsonToVec2F(config.query("threatRange"));
+
+ float threatLevel = staticRandomDouble(seed, "ThreatLevel") * (threatLevelRange[1] - threatLevelRange[0]) + threatLevelRange[0];
+ auto surfaceBiomeSeed = staticRandomU64(seed, "SurfaceBiomeSeed");
+
+ auto readRegion = [liquidsDatabase, threatLevel, seed](Json const& regionConfig, String const& layerName, int layerBaseHeight) {
+ TerrestrialWorldParameters::TerrestrialRegion region;
+ auto biomeChoices = jsonToStringList(binnedChoiceFromJson(regionConfig.get("biome"), threatLevel));
+ region.biome = staticRandomValueFrom(biomeChoices, seed, layerName.utf8Ptr());
+
+ region.blockSelector = staticRandomFrom(regionConfig.getArray("blockSelector"), seed, "blockSelector", layerName.utf8Ptr()).toString();
+ region.fgCaveSelector = staticRandomFrom(regionConfig.getArray("fgCaveSelector"), seed, "fgCaveSelector", layerName.utf8Ptr()).toString();
+ region.bgCaveSelector = staticRandomFrom(regionConfig.getArray("bgCaveSelector"), seed, "bgCaveSelector", layerName.utf8Ptr()).toString();
+ region.fgOreSelector = staticRandomFrom(regionConfig.getArray("fgOreSelector"), seed, "fgOreSelector", layerName.utf8Ptr()).toString();
+ region.bgOreSelector = staticRandomFrom(regionConfig.getArray("bgOreSelector"), seed, "bgOreSelector", layerName.utf8Ptr()).toString();
+ region.subBlockSelector = staticRandomFrom(regionConfig.getArray("subBlockSelector"), seed, "subBlockSelector", layerName.utf8Ptr()).toString();
+
+ if (auto caveLiquid = staticRandomValueFrom(regionConfig.getArray("caveLiquid", {}), seed, "caveLiquid").optString()) {
+ auto caveLiquidSeedDensityRange = jsonToVec2F(regionConfig.get("caveLiquidSeedDensityRange"));
+ region.caveLiquid = liquidsDatabase->liquidId(*caveLiquid);
+ region.caveLiquidSeedDensity = staticRandomFloatRange(caveLiquidSeedDensityRange[0],
+ caveLiquidSeedDensityRange[1],
+ seed,
+ "caveLiquidSeedDensity",
+ layerName.utf8Ptr());
+ } else {
+ region.caveLiquid = EmptyLiquidId;
+ region.caveLiquidSeedDensity = 0.0f;
+ }
+
+ if (auto oceanLiquid = staticRandomValueFrom(regionConfig.getArray("oceanLiquid", {}), seed, "oceanLiquid", layerName.utf8Ptr()).optString()) {
+ region.oceanLiquid = liquidsDatabase->liquidId(*oceanLiquid);
+ region.oceanLiquidLevel = regionConfig.getInt("oceanLevelOffset", 0) + layerBaseHeight;
+ } else {
+ region.oceanLiquid = EmptyLiquidId;
+ region.oceanLiquidLevel = 0;
+ }
+ region.encloseLiquids = regionConfig.getBool("encloseLiquids", false);
+ region.fillMicrodungeons = regionConfig.getBool("fillMicrodungeons", false);
+
+ return region;
+ };
+
+ auto readLayer = [readRegion, regionDefaults, regionTypes, seed, config](String const& layerName) -> Maybe<TerrestrialWorldParameters::TerrestrialLayer> {
+ if (!config.get("layers").contains(layerName))
+ return {};
+
+ auto layerConfig = jsonMerge(config.get("layerDefaults"), config.get("layers").get(layerName));
+
+ if (!layerConfig || !layerConfig.getBool("enabled"))
+ return {};
+
+ TerrestrialWorldParameters::TerrestrialLayer layer;
+
+ layer.layerMinHeight = layerConfig.getFloat("layerLevel");
+ layer.layerBaseHeight = layerConfig.getFloat("baseHeight");
+
+ auto primaryRegionList = layerConfig.getArray("primaryRegion");
+ auto primaryRegionConfigName = staticRandomFrom(primaryRegionList, seed, layerName.utf8Ptr(), "PrimaryRegionSelection").toString();
+ Json primaryRegionConfig = jsonMerge(regionDefaults, regionTypes.get(primaryRegionConfigName));
+ layer.primaryRegion = readRegion(primaryRegionConfig, layerName, layer.layerBaseHeight);
+
+ auto subRegionList = primaryRegionConfig.getArray("subRegion");
+ Json subRegionConfig;
+ if (subRegionList.size() > 0) {
+ String subRegionName = staticRandomFrom(subRegionList, seed, layerName, primaryRegionConfigName).toString();
+ subRegionConfig = jsonMerge(regionDefaults, regionTypes.get(subRegionName));
+ } else {
+ subRegionConfig = primaryRegionConfig;
+ }
+ layer.primarySubRegion = readRegion(subRegionConfig, layerName, layer.layerBaseHeight);
+
+ Vec2U secondaryRegionCountRange = jsonToVec2U(layerConfig.get("secondaryRegionCount"));
+ int secondaryRegionCount = staticRandomI32Range(secondaryRegionCountRange[0], secondaryRegionCountRange[1], seed, layerName, "SecondaryRegionCount");
+ auto secondaryRegionList = layerConfig.getArray("secondaryRegions");
+ if (secondaryRegionList.size() > 0) {
+ staticRandomShuffle(secondaryRegionList, seed, layerName, "SecondaryRegionShuffle");
+ for (auto regionName : secondaryRegionList) {
+ if (secondaryRegionCount <= 0)
+ break;
+ Json secondaryRegionConfig = jsonMerge(regionDefaults, regionTypes.get(regionName.toString()));
+ layer.secondaryRegions.append(readRegion(secondaryRegionConfig, layerName, layer.layerBaseHeight));
+
+ auto subRegionList = secondaryRegionConfig.getArray("subRegion");
+ Json subRegionConfig;
+ if (subRegionList.size() > 0) {
+ String subRegionName = staticRandomFrom(subRegionList, seed, layerName, regionName.toString()).toString();
+ subRegionConfig = jsonMerge(regionDefaults, regionTypes.get(subRegionName));
+ } else {
+ subRegionConfig = secondaryRegionConfig;
+ }
+ layer.secondarySubRegions.append(readRegion(subRegionConfig, layerName, layer.layerBaseHeight));
+
+ --secondaryRegionCount;
+ }
+ }
+
+ layer.secondaryRegionSizeRange = jsonToVec2F(layerConfig.get("secondaryRegionSize"));
+ layer.subRegionSizeRange = jsonToVec2F(layerConfig.get("subRegionSize"));
+
+ WeightedPool<String> dungeonPool = jsonToWeightedPool<String>(layerConfig.get("dungeons"));
+ Vec2U dungeonCountRange = layerConfig.opt("dungeonCountRange").apply(jsonToVec2U).value();
+ unsigned dungeonCount = staticRandomU32Range(dungeonCountRange[0], dungeonCountRange[1], seed, layerName, "DungeonCount");
+ layer.dungeons.appendAll(dungeonPool.selectUniques(dungeonCount, staticRandomHash64(seed, layerName, "DungeonChoice")));
+ layer.dungeonXVariance = layerConfig.getInt("dungeonXVariance", 0);
+
+ return layer;
+ };
+
+ auto surfaceLayer = readLayer("surface").take();
+ String primaryBiome = surfaceLayer.primaryRegion.biome;
+
+ auto parameters = make_shared<TerrestrialWorldParameters>();
+
+ parameters->threatLevel = threatLevel;
+ parameters->typeName = typeName;
+ parameters->worldSize = jsonToVec2U(config.get("size"));
+ parameters->gravity = staticRandomFloatRange(gravityRange[0], gravityRange[1], seed, "WorldGravity");
+ parameters->airless = biomeDatabase->biomeIsAirless(primaryBiome);
+ parameters->environmentStatusEffects = biomeDatabase->biomeStatusEffects(primaryBiome);
+ parameters->overrideTech = config.opt("overrideTech").apply(jsonToStringList);
+ parameters->globalDirectives = config.opt("globalDirectives").apply(jsonToStringList);
+ parameters->beamUpRule = BeamUpRuleNames.getLeft(config.getString("beamUpRule", "Surface"));
+ parameters->disableDeathDrops = config.getBool("disableDeathDrops", false);
+ parameters->worldEdgeForceRegions = WorldEdgeForceRegionTypeNames.getLeft(config.getString("worldEdgeForceRegions", "Top"));
+
+ parameters->weatherPool = biomeDatabase->biomeWeathers(primaryBiome, seed, threatLevel);
+
+ parameters->primaryBiome = primaryBiome;
+ parameters->sizeName = sizeName;
+ parameters->hueShift = biomeDatabase->biomeHueShift(parameters->primaryBiome, surfaceBiomeSeed);
+
+ parameters->primarySurfaceLiquid = surfaceLayer.primaryRegion.oceanLiquid != EmptyLiquidId
+ ? surfaceLayer.primaryRegion.oceanLiquid
+ : surfaceLayer.primaryRegion.caveLiquid;
+
+ parameters->skyColoring = biomeDatabase->biomeSkyColoring(parameters->primaryBiome, seed);
+ parameters->dayLength = staticRandomFloatRange(dayLengthRange[0], dayLengthRange[1], seed, "DayLength");
+
+ parameters->blockNoiseConfig = config.get("blockNoise");
+ parameters->blendNoiseConfig = config.get("blendNoise");
+ parameters->blendSize = config.getFloat("blendSize");
+
+ parameters->spaceLayer = readLayer("space").take();
+ parameters->atmosphereLayer = readLayer("atmosphere").take();
+ parameters->surfaceLayer = surfaceLayer;
+ parameters->subsurfaceLayer = readLayer("subsurface").take();
+
+ while (auto undergroundLayer = readLayer(strf("underground%s", parameters->undergroundLayers.size() + 1)))
+ parameters->undergroundLayers.append(undergroundLayer.take());
+
+ parameters->coreLayer = readLayer("core").take();
+
+ return parameters;
+}
+
+AsteroidsWorldParametersPtr generateAsteroidsWorldParameters(uint64_t seed) {
+ auto& root = Root::singleton();
+ auto assets = root.assets();
+
+ auto parameters = make_shared<AsteroidsWorldParameters>();
+
+ auto asteroidsConfig = assets->json("/asteroids_worlds.config");
+ String biome = asteroidsConfig.getString("biome");
+ auto gravityRange = jsonToVec2F(asteroidsConfig.get("gravityRange"));
+
+ auto threatLevelRange = jsonToVec2F(asteroidsConfig.get("threatRange"));
+ parameters->threatLevel = staticRandomDouble(seed, "ThreatLevel") * (threatLevelRange[1] - threatLevelRange[0]) + threatLevelRange[0];
+ parameters->typeName = "asteroids";
+ parameters->worldSize = jsonToVec2U(asteroidsConfig.get("worldSize"));
+ parameters->gravity = staticRandomFloatRange(gravityRange[0], gravityRange[1], seed, "WorldGravity");
+ parameters->environmentStatusEffects = jsonToStringList(asteroidsConfig.getArray("environmentStatusEffects", JsonArray()));
+ parameters->overrideTech = asteroidsConfig.opt("overrideTech").apply(jsonToStringList);
+ parameters->globalDirectives = asteroidsConfig.opt("globalDirectives").apply(jsonToStringList);
+ parameters->beamUpRule = BeamUpRuleNames.getLeft(asteroidsConfig.getString("beamUpRule", "Surface"));
+ parameters->disableDeathDrops = asteroidsConfig.getBool("disableDeathDrops", false);
+ parameters->worldEdgeForceRegions = WorldEdgeForceRegionTypeNames.getLeft(asteroidsConfig.getString("worldEdgeForceRegions", "TopAndBottom"));
+
+ parameters->asteroidTopLevel = asteroidsConfig.getInt("asteroidsTop");
+ parameters->asteroidBottomLevel = asteroidsConfig.getInt("asteroidsBottom");
+ parameters->blendSize = asteroidsConfig.getFloat("blendSize");
+ parameters->asteroidBiome = biome;
+ parameters->ambientLightLevel = jsonToColor(asteroidsConfig.get("ambientLightLevel"));
+
+ return parameters;
+}
+
+FloatingDungeonWorldParametersPtr generateFloatingDungeonWorldParameters(String const& dungeonWorldName) {
+ auto& root = Root::singleton();
+ auto assets = root.assets();
+
+ auto worldConfig = assets->json("/dungeon_worlds.config:" + dungeonWorldName);
+
+ auto parameters = make_shared<FloatingDungeonWorldParameters>();
+
+ parameters->threatLevel = worldConfig.getFloat("threatLevel", 0);
+ parameters->typeName = dungeonWorldName;
+ parameters->worldSize = jsonToVec2U(worldConfig.get("worldSize"));
+ parameters->gravity = worldConfig.getFloat("gravity");
+ parameters->airless = worldConfig.getBool("airless", false);
+ parameters->environmentStatusEffects = jsonToStringList(worldConfig.getArray("environmentStatusEffects", JsonArray()));
+ parameters->overrideTech = worldConfig.opt("overrideTech").apply(jsonToStringList);
+ parameters->globalDirectives = worldConfig.opt("globalDirectives").apply(jsonToStringList);
+ if (auto weatherPoolConfig = worldConfig.optArray("weatherPool"))
+ parameters->weatherPool = jsonToWeightedPool<String>(*weatherPoolConfig);
+ parameters->beamUpRule = BeamUpRuleNames.getLeft(worldConfig.getString("beamUpRule", "Surface"));
+ parameters->disableDeathDrops = worldConfig.getBool("disableDeathDrops", false);
+ parameters->worldEdgeForceRegions = WorldEdgeForceRegionTypeNames.getLeft(worldConfig.getString("worldEdgeForceRegions", "Top"));
+
+ parameters->dungeonBaseHeight = worldConfig.getInt("dungeonBaseHeight");
+ parameters->dungeonSurfaceHeight = worldConfig.getInt("dungeonSurfaceHeight", parameters->dungeonBaseHeight);
+ parameters->dungeonUndergroundLevel = worldConfig.getInt("dungeonUndergroundLevel", 0);
+ parameters->primaryDungeon = worldConfig.getString("primaryDungeon");
+ parameters->biome = worldConfig.optString("biome");
+ parameters->ambientLightLevel = jsonToColor(worldConfig.get("ambientLightLevel"));
+ if (worldConfig.contains("musicTrack")) {
+ parameters->dayMusicTrack = worldConfig.optString("musicTrack");
+ parameters->nightMusicTrack = worldConfig.optString("musicTrack");
+ } else {
+ parameters->dayMusicTrack = worldConfig.optString("dayMusicTrack");
+ parameters->nightMusicTrack = worldConfig.optString("nightMusicTrack");
+ }
+ if (worldConfig.contains("ambientNoises")) {
+ parameters->dayAmbientNoises = worldConfig.optString("ambientNoises");
+ parameters->nightAmbientNoises = worldConfig.optString("ambientNoises");
+ } else {
+ parameters->dayAmbientNoises = worldConfig.optString("dayAmbientNoises");
+ parameters->nightAmbientNoises = worldConfig.optString("nightAmbientNoises");
+ }
+
+ return parameters;
+}
+
+}