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

summaryrefslogtreecommitdiff
path: root/source/game/StarCelestialGraphics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/StarCelestialGraphics.cpp')
-rw-r--r--source/game/StarCelestialGraphics.cpp288
1 files changed, 288 insertions, 0 deletions
diff --git a/source/game/StarCelestialGraphics.cpp b/source/game/StarCelestialGraphics.cpp
new file mode 100644
index 0000000..3e0c02e
--- /dev/null
+++ b/source/game/StarCelestialGraphics.cpp
@@ -0,0 +1,288 @@
+#include "StarCelestialGraphics.hpp"
+#include "StarJsonExtra.hpp"
+#include "StarLexicalCast.hpp"
+#include "StarFormat.hpp"
+#include "StarImageProcessing.hpp"
+#include "StarCelestialDatabase.hpp"
+#include "StarParallax.hpp"
+#include "StarRoot.hpp"
+#include "StarBiomeDatabase.hpp"
+#include "StarTerrainDatabase.hpp"
+#include "StarLiquidsDatabase.hpp"
+#include "StarAssets.hpp"
+
+namespace Star {
+
+List<pair<String, float>> CelestialGraphics::drawSystemPlanetaryObject(CelestialParameters const& parameters) {
+ return {{parameters.getParameter("smallImage").toString(), parameters.getParameter("smallImageScale").toFloat()}};
+}
+
+List<pair<String, float>> CelestialGraphics::drawSystemCentralBody(CelestialParameters const& parameters) {
+ return {{parameters.getParameter("image").toString(), parameters.getParameter("imageScale").toFloat()}};
+}
+
+List<pair<String, float>> CelestialGraphics::drawWorld(
+ CelestialParameters const& celestialParameters, Maybe<CelestialParameters> const& overrideShadowParameters) {
+ auto& root = Root::singleton();
+ auto assets = root.assets();
+ auto liquidsDatabase = root.liquidsDatabase();
+
+ CelestialParameters shadowParameters = overrideShadowParameters.value(celestialParameters);
+
+ String type = celestialParameters.getParameter("worldType").toString();
+
+ List<pair<String, float>> layers;
+
+ if (type == "Terrestrial") {
+ auto terrestrialParameters = as<TerrestrialWorldParameters>(celestialParameters.visitableParameters());
+ if (!terrestrialParameters)
+ return {};
+
+ auto gfxConfig = jsonMerge(assets->json("/celestial.config:terrestrialGraphics").get("default"),
+ assets->json("/celestial.config:terrestrialGraphics").get(terrestrialParameters->typeName, JsonObject()));
+
+ auto liquidImages = gfxConfig.getString("liquidImages", "");
+ auto baseImages = gfxConfig.getString("baseImages", "");
+ auto shadowImages = gfxConfig.getString("shadowImages", "");
+ auto baseCount = gfxConfig.getInt("baseCount", 0);
+ auto dynamicsImages = gfxConfig.getString("dynamicsImages", "");
+ float imageScale = celestialParameters.getParameter("imageScale", 1.0f).toFloat();
+
+ // If the planet has water, then draw the corresponding water image as the
+ // base layer, otherwise use the bottom most mask image.
+ if (terrestrialParameters->primarySurfaceLiquid != EmptyLiquidId && !liquidImages.empty()) {
+ String liquidBaseImage = liquidImages.replace("<liquid>", liquidsDatabase->liquidName(terrestrialParameters->primarySurfaceLiquid));
+ layers.append({move(liquidBaseImage), imageScale});
+ } else {
+ if (baseCount > 0) {
+ String baseLayer = strf("%s?hueshift=%s", baseImages.replace("<biome>",
+ terrestrialParameters->primaryBiome).replace("<num>", toString(baseCount)), terrestrialParameters->hueShift);
+ layers.append({move(baseLayer), imageScale});
+ }
+ }
+
+ // Then draw all the biome layers
+ for (int i = 0; i < baseCount; ++i) {
+ String baseImage = baseImages.replace("<num>", toString(baseCount - i));
+ String hueShiftString, dynamicMaskString;
+ if (!dynamicsImages.empty())
+ dynamicMaskString = "?addmask=" + dynamicsImages.replace("<num>", toString(celestialParameters.randomizeParameterRange(gfxConfig.getArray("dynamicsRange"), i).toInt()));
+ if (terrestrialParameters->hueShift != 0)
+ hueShiftString = strf("?hueshift=%s", terrestrialParameters->hueShift);
+ String layer = baseImage + hueShiftString + dynamicMaskString;
+ layers.append({move(layer), imageScale});
+ }
+
+ if (!shadowImages.empty()) {
+ String shadow = shadowImages.replace("<num>", toString(shadowParameters.randomizeParameterRange(gfxConfig.getArray("shadowNumber")).toInt()));
+ layers.append({move(shadow), imageScale});
+ }
+
+ } else if (type == "Asteroids") {
+ String maskImages = celestialParameters.getParameter("maskImages").toString();
+ int maskCount = celestialParameters.getParameter("masks").toInt();
+ String dynamicsImages = celestialParameters.getParameter("dynamicsImages").toString();
+ float imageScale = celestialParameters.getParameter("imageScale", 1.0f).toFloat();
+
+ for (int i = 0; i < maskCount; ++i) {
+ String biomeMaskBase = maskImages.replace("<num>", toString(maskCount - i));
+ String dynamicMask = dynamicsImages.replace("<num>", toString(celestialParameters.randomizeParameterRange("dynamicsRange", i).toInt()));
+ String layer = strf("%s?addmask=%s", biomeMaskBase, dynamicMask);
+ layers.append({move(layer), imageScale});
+ }
+
+ } else if (type == "FloatingDungeon") {
+ String image = celestialParameters.getParameter("image").toString();
+ float imageScale = celestialParameters.getParameter("imageScale", 1.0f).toFloat();
+ layers.append({move(image), imageScale});
+
+ if (!celestialParameters.getParameter("dynamicsImages").toString().empty()) {
+ String dynamicsImages = celestialParameters.getParameter("dynamicsImages", "").toString();
+ String dynamicsImage = dynamicsImages.replace("<num>", toString(celestialParameters.randomizeParameterRange("dynamicsRange").toInt()));
+ layers.append({move(dynamicsImage), imageScale});
+ }
+
+ } else if (type == "GasGiant") {
+ auto gfxConfig = assets->json("/celestial.config:gasGiantGraphics");
+
+ auto baseImage = gfxConfig.getString("baseImage", "");
+ auto shadowImages = gfxConfig.getString("shadowImages", "");
+ auto dynamicsImages = gfxConfig.getString("dynamicsImages", "");
+ auto overlayImages = gfxConfig.getString("overlayImages", "");
+ auto overlayCount = gfxConfig.getInt("overlayCount", 0);
+ float imageScale = celestialParameters.getParameter("imageScale", 1.0f).toFloat();
+
+ float hueShift = celestialParameters.randomizeParameterRange(gfxConfig.getArray("primaryHueShiftRange")).toFloat();
+ if (!baseImage.empty())
+ layers.append({strf("%s?hueshift=%s", baseImage, hueShift), imageScale});
+
+ if (!overlayImages.empty()) {
+ for (int i = 0; i < overlayCount; ++i) {
+ hueShift += celestialParameters.randomizeParameterRange(gfxConfig.getArray("hueShiftOffsetRange")).toFloat();
+ String maskImage = dynamicsImages.replace("<num>", toString(celestialParameters.randomizeParameterRange(gfxConfig.getArray("dynamicsRange"), i).toInt()));
+ String overlayImage = overlayImages.replace("<num>", toString(i));
+ layers.append({strf("%s?hueshift=%s?addmask=%s", overlayImage, hueShift, maskImage), imageScale});
+ }
+ }
+
+ if (!shadowImages.empty()) {
+ String shadow = shadowImages.replace("<num>", toString(shadowParameters.randomizeParameterRange(gfxConfig.getArray("shadowNumber")).toInt()));
+ layers.append({move(shadow), imageScale});
+ }
+ }
+
+ return layers;
+}
+
+List<pair<String, String>> CelestialGraphics::worldHorizonImages(CelestialParameters const& celestialParameters) {
+ auto& root = Root::singleton();
+ auto assets = root.assets();
+ auto liquidsDatabase = root.liquidsDatabase();
+
+ auto getLR = [](String const& base) -> pair<String, String> {
+ return pair<String, String>(base.replace("<selector>", "l"), base.replace("<selector>", "r"));
+ };
+
+ String type = celestialParameters.getParameter("worldType").toString();
+
+ List<pair<String, String>> res;
+
+ if (type == "Terrestrial") {
+ auto terrestrialParameters = as<TerrestrialWorldParameters>(celestialParameters.visitableParameters());
+ if (!terrestrialParameters)
+ return {};
+
+ auto gfxConfig = jsonMerge(assets->json("/celestial.config:terrestrialHorizonGraphics").get("default"),
+ assets->json("/celestial.config:terrestrialHorizonGraphics").get(terrestrialParameters->typeName, JsonObject()));
+
+ String baseImages = gfxConfig.getString("baseImages");
+ String atmoTextures = gfxConfig.getString("atmosphereTextures");
+ String shadowTextures = gfxConfig.getString("shadowTextures");
+ String maskTextures = gfxConfig.getString("maskTextures");
+ String liquidTextures = gfxConfig.getString("liquidTextures");
+ auto numMasks = jsonToVec2I(gfxConfig.get("maskRange"));
+ auto maskPerPlanetRange = jsonToVec2I(gfxConfig.get("maskPerPlanetRange"));
+
+ auto biomeHueShift = "?" + imageOperationToString(HueShiftImageOperation::hueShiftDegrees(terrestrialParameters->hueShift));
+
+ if (terrestrialParameters->primarySurfaceLiquid != EmptyLiquidId) {
+ auto seed = celestialParameters.seed();
+ RandomSource rand(seed);
+
+ int numPlanetMasks = rand.randInt(maskPerPlanetRange[0], maskPerPlanetRange[1]);
+ List<int> masks;
+ for (int i = 0; i < numPlanetMasks; ++i)
+ masks.append(rand.randInt(numMasks[0], numMasks[1]));
+
+ String liquidBase = liquidTextures.replace("<liquid>", liquidsDatabase->liquidName(terrestrialParameters->primarySurfaceLiquid));
+ res.append(getLR(liquidBase));
+
+ StringList planetMaskListL;
+ StringList planetMaskListR;
+ for (auto m : masks) {
+ String base = maskTextures.replace("<mask>", toString(m));
+ auto lr = getLR(base);
+ planetMaskListL.append(lr.first);
+ planetMaskListR.append(lr.second);
+ }
+
+ String leftMask, rightMask;
+ if (!planetMaskListL.empty())
+ leftMask = "?" + imageOperationToString(AlphaMaskImageOperation{AlphaMaskImageOperation::Additive, planetMaskListL, {0, 0}});
+ if (!planetMaskListR.empty())
+ rightMask = "?" + imageOperationToString(AlphaMaskImageOperation{AlphaMaskImageOperation::Additive, planetMaskListR, {0, 0}});
+
+ auto toAppend = getLR(baseImages + biomeHueShift);
+ res.append({toAppend.first + leftMask, toAppend.second + rightMask});
+ } else {
+ res.append(getLR(baseImages + biomeHueShift));
+ }
+
+ if (celestialParameters.getParameter("atmosphere", true).toBool())
+ res.append(getLR(atmoTextures));
+
+ res.append(getLR(shadowTextures));
+
+ } else if (type == "Asteroids") {
+ res.append(getLR(assets->json("/celestial.config:asteroidsHorizons").toString()));
+
+ } else if (type == "FloatingDungeon") {
+ auto dungeonParameters = as<FloatingDungeonWorldParameters>(celestialParameters.visitableParameters());
+ auto dungeonHorizons = assets->json("/celestial.config:floatingDungeonHorizons");
+ if (dungeonHorizons.contains(dungeonParameters->primaryDungeon))
+ res.append(getLR(dungeonHorizons.get(dungeonParameters->primaryDungeon).toString()));
+ }
+
+ return res;
+}
+
+int CelestialGraphics::worldRadialPosition(CelestialParameters const& parameters) {
+ if (parameters.coordinate().isPlanetaryBody())
+ return staticRandomU32(parameters.seed(), "RadialNumber") % planetRadialPositions();
+ if (parameters.coordinate().isSatelliteBody())
+ return staticRandomU32(parameters.seed(), "RadialNumber") % satelliteRadialPositions();
+ return 0;
+}
+
+int CelestialGraphics::planetRadialPositions() {
+ return Root::singleton().assets()->json("/celestial.config:planetRadialSlots").toInt();
+}
+
+int CelestialGraphics::satelliteRadialPositions() {
+ return Root::singleton().assets()->json("/celestial.config:satelliteRadialSlots").toInt();
+}
+
+List<pair<String, float>> CelestialGraphics::drawSystemTwinkle(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& system, double time) {
+ auto parameters = celestialDatabase->parameters(system);
+ if (!parameters)
+ return {};
+
+ auto assets = Root::singleton().assets();
+
+ int twinkleFrameCount = assets->json("/celestial.config:twinkleFrames").toInt();
+ float twinkleScale = assets->json("/celestial.config:twinkleScale").toFloat();
+ String twinkleFrameset = parameters->getParameter("twinkleFrames").toString();
+ float twinkleTime = parameters->randomizeParameterRange("twinkleTime").toFloat();
+ String twinkleBackground = parameters->getParameter("twinkleBackground").toString();
+
+ String twinkleFrame = strf("%s:%s", twinkleFrameset, (int)(std::fmod<double>(time / twinkleTime, 1.0f) * twinkleFrameCount));
+
+ return {{move(twinkleBackground), 1.0f}, {move(twinkleFrame), twinkleScale}};
+}
+
+List<pair<String, float>> CelestialGraphics::drawSystemPlanetaryObject(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) {
+ if (auto params = celestialDatabase->parameters(coordinate))
+ return drawSystemPlanetaryObject(params.take());
+ return {};
+}
+
+List<pair<String, float>> CelestialGraphics::drawSystemCentralBody(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) {
+ if (auto params = celestialDatabase->parameters(coordinate))
+ return drawSystemCentralBody(params.take());
+ return {};
+}
+
+List<pair<String, float>> CelestialGraphics::drawWorld(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) {
+ auto params = celestialDatabase->parameters(coordinate);
+ if (!params)
+ return {};
+
+ if (coordinate.isSatelliteBody())
+ return drawWorld(params.take(), celestialDatabase->parameters(coordinate.parent()));
+ else
+ return drawWorld(params.take());
+}
+
+List<pair<String, String>> CelestialGraphics::worldHorizonImages(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) {
+ if (auto params = celestialDatabase->parameters(coordinate))
+ return worldHorizonImages(params.take());
+ return {};
+}
+
+int CelestialGraphics::worldRadialPosition(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) {
+ if (auto params = celestialDatabase->parameters(coordinate))
+ return worldRadialPosition(params.take());
+ return 0;
+}
+
+}