osb/source/game/StarSkyParameters.cpp
2023-06-20 14:33:09 +10:00

219 lines
7.6 KiB
C++

#include "StarSkyParameters.hpp"
#include "StarCelestialDatabase.hpp"
#include "StarCelestialGraphics.hpp"
#include "StarCasting.hpp"
#include "StarJsonExtra.hpp"
#include "StarDataStreamExtra.hpp"
namespace Star {
SkyParameters::SkyParameters() : seed(), skyType(SkyType::Barren), skyColoring(makeRight(Color::Black)) {}
SkyParameters::SkyParameters(CelestialCoordinate const& coordinate, CelestialDatabasePtr const& celestialDatabase)
: SkyParameters() {
if (!coordinate || coordinate.isSystem())
return;
auto params = celestialDatabase->parameters(coordinate);
if (!params)
return;
seed = staticRandomU64(params->seed(), "SkySeed");
// Gather up all the CelestialParameters and scales for all the celestial
// objects to draw in the sky, we should draw the parent planet if we are a
// satellite, as well as all the other satellites.
auto selfCoordinate = params->coordinate();
if (selfCoordinate.isSatelliteBody()) {
if (auto planet = celestialDatabase->parameters(selfCoordinate.parent())) {
Vec2F pos;
pos[0] = staticRandomFloat(params->seed(), planet->seed(), "x");
pos[1] = staticRandomFloat(params->seed(), planet->seed(), "y");
// My parent's parent is no one.
nearbyPlanet = {{CelestialGraphics::drawWorld(*planet, {}), pos}};
}
}
for (auto satelliteCoordinate : celestialDatabase->children(selfCoordinate.planet())) {
if (satelliteCoordinate != selfCoordinate) {
if (auto satellite = celestialDatabase->parameters(satelliteCoordinate)) {
Vec2F pos;
pos[0] = staticRandomFloat(params->seed(), satellite->seed(), "x");
pos[1] = staticRandomFloat(params->seed(), satellite->seed(), "y");
nearbyMoons.append(
{CelestialGraphics::drawWorld(*satellite, celestialDatabase->parameters(satelliteCoordinate.parent())),
pos});
}
}
}
horizonImages = CelestialGraphics::worldHorizonImages(*params);
readVisitableParameters(params->visitableParameters());
}
SkyParameters::SkyParameters(SkyParameters const& oldSkyParameters, VisitableWorldParametersConstPtr newVisitableParameters) : SkyParameters() {
*this = oldSkyParameters;
readVisitableParameters(newVisitableParameters);
}
SkyParameters::SkyParameters(Json const& config) : SkyParameters() {
if (config.isNull())
return;
seed = config.getUInt("seed");
dayLength = config.optFloat("dayLength");
auto extractLayerData = [](Json const& v, uint64_t selfSeed) -> pair<List<pair<String, float>>, Vec2F> {
Vec2F pos;
if (v.contains("pos")) {
pos = jsonToVec2F(v.get("pos"));
} else if (v.contains("seed")) {
pos[0] = staticRandomFloat(selfSeed, v.getUInt("seed"), "x");
pos[1] = staticRandomFloat(selfSeed, v.getUInt("seed"), "y");
}
List<pair<String, float>> layers;
for (auto const& l : v.get("layers").iterateArray())
layers.append({l.getString("image"), l.getFloat("scale")});
return {layers, pos};
};
if (config.contains("planet") && config.get("planet"))
nearbyPlanet = extractLayerData(config.get("planet"), seed);
if (config.contains("satellites"))
nearbyMoons =
jsonToList<pair<List<pair<String, float>>, Vec2F>>(config.get("satellites"), bind(extractLayerData, _1, seed));
if (config.contains("horizonImages")) {
horizonImages = jsonToList<pair<String, String>>(config.get("horizonImages"),
[](Json const& v) -> pair<String, String> {
return {v.getString("left"), v.getString("right")};
});
}
horizonClouds = config.getBool("horizonClouds", true);
skyType = SkyTypeNames.getLeft(config.getString("skyType", "barren"));
if (auto colors = config.opt("skyColoring")) {
skyColoring.setLeft(SkyColoring{*colors});
} else if (auto ambientLightLevel = config.opt("ambientLightLevel")) {
skyColoring.setRight(jsonToColor(*ambientLightLevel));
} else {
skyColoring.setRight(Color::Black);
}
spaceLevel = config.optFloat("spaceLevel");
surfaceLevel = config.optFloat("surfaceLevel");
}
Json SkyParameters::toJson() const {
return JsonObject{
{"seed", seed},
{"dayLength", jsonFromMaybe<float>(dayLength)},
{"planet",
jsonFromMaybe<pair<List<pair<String, float>>, Vec2F>>(nearbyPlanet,
[](pair<List<pair<String, float>>, Vec2F> p) -> Json {
return JsonObject{
{"layers",
p.first.transformed([](pair<String, float> const& p) -> Json {
return JsonObject{{"image", p.first}, {"scale", p.second}};
})},
{"pos", jsonFromVec2F(p.second)},
};
})},
{"satellites",
jsonFromList<pair<List<pair<String, float>>, Vec2F>>(nearbyMoons,
[](pair<List<pair<String, float>>, Vec2F> const& p) {
return JsonObject{
{"layers",
p.first.transformed([](pair<String, float> const& p) -> Json {
return JsonObject{{"image", p.first}, {"scale", p.second}};
})},
{"pos", jsonFromVec2F(p.second)},
};
})},
{"horizonImages",
jsonFromList<pair<String, String>>(horizonImages,
[](pair<String, String> p) {
return JsonObject{
{"left", p.first}, {"right", p.second},
};
})},
{"horizonClouds", horizonClouds},
{"skyType", SkyTypeNames.getRight(skyType)},
{"skyColoring", jsonFromMaybe<SkyColoring>(skyColoring.maybeLeft(), [](SkyColoring c) { return c.toJson(); })},
{"ambientLightLevel", jsonFromMaybe<Color>(skyColoring.maybeRight(), [](Color c) { return jsonFromColor(c); })},
{"spaceLevel", jsonFromMaybe<float>(spaceLevel)},
{"surfaceLevel", jsonFromMaybe<float>(surfaceLevel)},
};
}
void SkyParameters::read(DataStream& ds) {
ds >> seed;
ds >> dayLength;
ds >> nearbyPlanet;
ds >> nearbyMoons;
ds >> horizonImages;
ds >> horizonClouds;
ds >> skyType;
ds >> skyColoring;
ds >> spaceLevel;
ds >> surfaceLevel;
}
void SkyParameters::write(DataStream& ds) const {
ds << seed;
ds << dayLength;
ds << nearbyPlanet;
ds << nearbyMoons;
ds << horizonImages;
ds << horizonClouds;
ds << skyType;
ds << skyColoring;
ds << spaceLevel;
ds << surfaceLevel;
}
void SkyParameters::readVisitableParameters(VisitableWorldParametersConstPtr visitableParameters) {
if (auto terrestrialParameters = as<TerrestrialWorldParameters>(visitableParameters)) {
dayLength = terrestrialParameters->dayLength;
if (terrestrialParameters->airless) {
skyType = SkyType::Atmosphereless;
horizonClouds = false;
} else {
skyType = SkyType::Atmospheric;
horizonClouds = true;
}
skyColoring.setLeft(terrestrialParameters->skyColoring);
spaceLevel = terrestrialParameters->spaceLayer.layerMinHeight;
surfaceLevel = terrestrialParameters->atmosphereLayer.layerMinHeight;
} else {
skyType = SkyType::Barren;
horizonClouds = false;
if (auto asteroidsParameters = as<AsteroidsWorldParameters>(visitableParameters)) {
skyColoring.setRight(asteroidsParameters->ambientLightLevel);
} else if (auto floatingDungeonParameters = as<FloatingDungeonWorldParameters>(visitableParameters)) {
skyColoring.setRight(floatingDungeonParameters->ambientLightLevel);
} else {
skyColoring.setRight(Color::Black);
}
}
}
DataStream& operator>>(DataStream& ds, SkyParameters& sky) {
sky.read(ds);
return ds;
}
DataStream& operator<<(DataStream& ds, SkyParameters const& sky) {
sky.write(ds);
return ds;
}
}