summaryrefslogtreecommitdiffstats
path: root/src/Generating/VerticalStrategy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Generating/VerticalStrategy.cpp')
-rw-r--r--src/Generating/VerticalStrategy.cpp342
1 files changed, 342 insertions, 0 deletions
diff --git a/src/Generating/VerticalStrategy.cpp b/src/Generating/VerticalStrategy.cpp
new file mode 100644
index 000000000..08af5b272
--- /dev/null
+++ b/src/Generating/VerticalStrategy.cpp
@@ -0,0 +1,342 @@
+
+// VerticalStrategy.cpp
+
+// Implements the various classes descending from cPiece::cVerticalStrategy
+
+#include "Globals.h"
+#include "VerticalStrategy.h"
+
+
+
+
+
+// Constant that is added to random seed
+static const int SEED_OFFSET = 135;
+
+
+
+
+
+// Emit a warning if the first param is true
+#define CONDWARNING(ShouldLog, Fmt, ...) \
+ if (ShouldLog) \
+ { \
+ LOGWARNING(Fmt, __VA_ARGS__); \
+ }
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Globals:
+
+/** Parses a string containing a range in which both values are optional ("<MinHeight>|<MaxHeight>") into Min, Range.
+Returns true if successful, false on failure.
+If a_LogWarnings is true, outputs failure reasons to console.
+The range is returned in a_Min and a_Range, they are left unchanged if the range value is not present in the string. */
+static bool ParseRange(const AString & a_Params, int & a_Min, int & a_Range, bool a_LogWarnings)
+{
+ auto params = StringSplitAndTrim(a_Params, "|");
+ if (params.size() == 0)
+ {
+ // No params, generate directly on top:
+ return true;
+ }
+ if (!StringToInteger(params[0], a_Min))
+ {
+ // Failed to parse the min rel height:
+ CONDWARNING(a_LogWarnings, "Cannot parse minimum height from string \"%s\"!", params[0].c_str());
+ return false;
+ }
+ if (params.size() == 1)
+ {
+ // Only one param was given, there's no range
+ return true;
+ }
+ int maxHeight = a_Min;
+ if (!StringToInteger(params[1], maxHeight))
+ {
+ CONDWARNING(a_LogWarnings, "Cannot parse maximum height from string \"%s\"!", params[1].c_str());
+ return false;
+ }
+ if (maxHeight < a_Min)
+ {
+ std::swap(maxHeight, a_Min);
+ }
+ a_Range = maxHeight - a_Min + 1;
+ return true;
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+/** A vertical strategy that places the piece at a predefined height. */
+class cVerticalStrategyFixed:
+ public cPiece::cVerticalStrategy
+{
+public:
+ cVerticalStrategyFixed(void):
+ m_Height(-1000) // Default to "unassigned" height
+ {
+ }
+
+
+ virtual int GetVerticalPlacement(int a_BlockX, int a_BlockZ) override
+ {
+ return m_Height;
+ }
+
+
+ virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) override
+ {
+ // Params: "<Height>", compulsory
+ if (!StringToInteger(a_Params, m_Height))
+ {
+ CONDWARNING(a_LogWarnings, "Cannot parse the fixed height from string \"%s\"!", a_Params.c_str());
+ return false;
+ }
+ return true;
+ }
+
+protected:
+ /** Height at which the pieces are placed.
+ Note that this height may be outside the world, so that only a part of the piece is generated. */
+ int m_Height;
+};
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+/** A vertical strategy that places the piece in a random height between two heights. */
+class cVerticalStrategyRange:
+ public cPiece::cVerticalStrategy
+{
+public:
+ cVerticalStrategyRange(void):
+ m_Seed(0),
+ m_Min(-1), // Default to "unassigned" height
+ m_Range(1)
+ {
+ }
+
+
+ virtual int GetVerticalPlacement(int a_BlockX, int a_BlockZ) override
+ {
+ cNoise Noise(m_Seed);
+ return m_Min + (Noise.IntNoise2DInt(a_BlockX, a_BlockZ) / 7) % m_Range;
+ }
+
+
+ virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) override
+ {
+ // Params: "<MinHeight>|<MaxHeight>", all compulsory
+ auto params = StringSplitAndTrim(a_Params, "|");
+ if (params.size() != 2)
+ {
+ CONDWARNING(a_LogWarnings, "Cannot parse the range parameters from string \"%s\"!", a_Params.c_str());
+ return false;
+ }
+ int Max = 0;
+ if (!StringToInteger(params[0], m_Min) || !StringToInteger(params[1], Max))
+ {
+ CONDWARNING(a_LogWarnings, "Cannot parse the minimum or maximum height from string \"%s\"!", a_Params.c_str());
+ return false;
+ }
+ if (m_Min > Max)
+ {
+ std::swap(m_Min, Max);
+ }
+ m_Range = Max - m_Min + 1;
+ return true;
+ }
+
+
+ virtual void AssignGens(int a_Seed, cBiomeGenPtr & a_BiomeGen, cTerrainHeightGenPtr & a_TerrainHeightGen, int a_SeaLevel) override
+ {
+ m_Seed = a_Seed + SEED_OFFSET;
+ }
+
+protected:
+ /** Seed for the random generator. Received in AssignGens(). */
+ int m_Seed;
+
+ /** Range for the random generator. Received in InitializeFromString(). */
+ int m_Min, m_Range;
+};
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+/** A vertical strategy that places the piece in a specified range relative to the top of the terrain. */
+class cVerticalStrategyTerrainTop:
+ public cPiece::cVerticalStrategy
+{
+public:
+
+ virtual int GetVerticalPlacement(int a_BlockX, int a_BlockZ) override
+ {
+ ASSERT(m_HeightGen != nullptr);
+
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkDef::HeightMap HeightMap;
+ m_HeightGen->GenHeightMap(ChunkX, ChunkZ, HeightMap);
+ cNoise noise(m_Seed);
+ int rel = m_MinRelHeight + (noise.IntNoise2DInt(a_BlockX, a_BlockZ) / 7) % m_RelHeightRange + 1;
+ return cChunkDef::GetHeight(HeightMap, a_BlockX - ChunkX * cChunkDef::Width, a_BlockZ - ChunkZ * cChunkDef::Width) + rel;
+ }
+
+
+ virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) override
+ {
+ // Params: "<MinRelativeHeight>|<MaxRelativeHeight>", all optional
+ m_MinRelHeight = 0;
+ m_RelHeightRange = 1;
+ return ParseRange(a_Params, m_MinRelHeight, m_RelHeightRange, a_LogWarnings);
+ }
+
+
+ virtual void AssignGens(int a_Seed, cBiomeGenPtr & a_BiomeGen, cTerrainHeightGenPtr & a_HeightGen, int a_SeaLevel) override
+ {
+ m_Seed = a_Seed + SEED_OFFSET;
+ m_HeightGen = a_HeightGen;
+ }
+
+protected:
+ /** Seed for the random generator. */
+ int m_Seed;
+
+ /** Height generator from which the top of the terrain is read. */
+ cTerrainHeightGenPtr m_HeightGen;
+
+ /** Minimum relative height at which the prefab is placed. */
+ int m_MinRelHeight;
+
+ /** Range of the relative heights at which the prefab can be placed above the minimum. */
+ int m_RelHeightRange;
+};
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+/** A vertical strategy that places the piece within a range on top of the terrain or ocean, whichever's higher. */
+class cVerticalStrategyTerrainOrOceanTop:
+ public cPiece::cVerticalStrategy
+{
+public:
+
+ virtual int GetVerticalPlacement(int a_BlockX, int a_BlockZ) override
+ {
+ ASSERT(m_HeightGen != nullptr);
+
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
+ cChunkDef::HeightMap HeightMap;
+ m_HeightGen->GenHeightMap(ChunkX, ChunkZ, HeightMap);
+ int terrainHeight = static_cast<int>(cChunkDef::GetHeight(HeightMap, a_BlockX - ChunkX * cChunkDef::Width, a_BlockZ - ChunkZ * cChunkDef::Width));
+ terrainHeight = std::max(1 + terrainHeight, m_SeaLevel);
+ cNoise noise(m_Seed);
+ int rel = m_MinRelHeight + (noise.IntNoise2DInt(a_BlockX, a_BlockZ) / 7) % m_RelHeightRange + 1;
+ return terrainHeight + rel;
+ }
+
+
+ virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) override
+ {
+ // Params: "<MinRelativeHeight>|<MaxRelativeHeight>", all optional
+ m_MinRelHeight = 0;
+ m_RelHeightRange = 1;
+ return ParseRange(a_Params, m_MinRelHeight, m_RelHeightRange, a_LogWarnings);
+ }
+
+
+ virtual void AssignGens(int a_Seed, cBiomeGenPtr & a_BiomeGen, cTerrainHeightGenPtr & a_HeightGen, int a_SeaLevel) override
+ {
+ m_Seed = a_Seed + SEED_OFFSET;
+ m_HeightGen = a_HeightGen;
+ m_SeaLevel = a_SeaLevel;
+ }
+
+protected:
+ /** Seed for the random generator. */
+ int m_Seed;
+
+ /** Height generator from which the top of the terrain is read. */
+ cTerrainHeightGenPtr m_HeightGen;
+
+ /** The sea level used by the world. */
+ int m_SeaLevel;
+
+ /** Minimum relative height at which the prefab is placed. */
+ int m_MinRelHeight;
+
+ /** Range of the relative heights at which the prefab can be placed above the minimum. */
+ int m_RelHeightRange;
+};
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// CreateVerticalStrategyFromString:
+
+cPiece::cVerticalStrategyPtr CreateVerticalStrategyFromString(const AString & a_StrategyDesc, bool a_LogWarnings)
+{
+ // Break apart the strategy class, the first parameter before the first pipe char:
+ auto idxPipe = a_StrategyDesc.find('|');
+ if (idxPipe == AString::npos)
+ {
+ idxPipe = a_StrategyDesc.length();
+ }
+ AString StrategyClass = a_StrategyDesc.substr(0, idxPipe);
+
+ // Create a strategy class based on the class string:
+ cPiece::cVerticalStrategyPtr Strategy;
+ if (NoCaseCompare(StrategyClass, "Fixed") == 0)
+ {
+ Strategy = std::make_shared<cVerticalStrategyFixed>();
+ }
+ else if (NoCaseCompare(StrategyClass, "Range") == 0)
+ {
+ Strategy = std::make_shared<cVerticalStrategyRange>();
+ }
+ else if (NoCaseCompare(StrategyClass, "TerrainTop") == 0)
+ {
+ Strategy = std::make_shared<cVerticalStrategyTerrainTop>();
+ }
+ else if (NoCaseCompare(StrategyClass, "TerrainOrOceanTop") == 0)
+ {
+ Strategy = std::make_shared<cVerticalStrategyTerrainOrOceanTop>();
+ }
+ else
+ {
+ return nullptr;
+ }
+
+ // Initialize the strategy's parameters:
+ AString Params;
+ if (idxPipe < a_StrategyDesc.length())
+ {
+ Params = a_StrategyDesc.substr(idxPipe + 1);
+ }
+ if (!Strategy->InitializeFromString(Params, a_LogWarnings))
+ {
+ return nullptr;
+ }
+
+ return Strategy;
+}
+
+
+
+