summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Generating/CMakeLists.txt2
-rw-r--r--src/Generating/PieceModifier.cpp402
-rw-r--r--src/Generating/PieceModifier.h45
-rw-r--r--src/Generating/PiecePool.cpp24
-rw-r--r--src/Generating/PiecePool.h40
-rw-r--r--src/Generating/Prefab.cpp20
-rw-r--r--src/Generating/PrefabPiecePool.cpp14
7 files changed, 545 insertions, 2 deletions
diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt
index e299c97d7..df1726675 100644
--- a/src/Generating/CMakeLists.txt
+++ b/src/Generating/CMakeLists.txt
@@ -18,6 +18,7 @@ target_sources(
MineShafts.cpp
Noise3DGenerator.cpp
PieceGeneratorBFSTree.cpp
+ PieceModifier.cpp
PiecePool.cpp
PieceStructuresGen.cpp
Prefab.cpp
@@ -52,6 +53,7 @@ target_sources(
MineShafts.h
Noise3DGenerator.h
PieceGeneratorBFSTree.h
+ PieceModifier.h
PiecePool.h
PieceStructuresGen.h
Prefab.h
diff --git a/src/Generating/PieceModifier.cpp b/src/Generating/PieceModifier.cpp
new file mode 100644
index 000000000..54d6a2c21
--- /dev/null
+++ b/src/Generating/PieceModifier.cpp
@@ -0,0 +1,402 @@
+
+// PieceModifier.cpp
+
+// Implements the various classes descending from cPiece::cPieceModifier
+
+#include "Globals.h"
+#include "PieceModifier.h"
+#include "../Noise/Noise.h"
+
+
+
+
+
+// Constant that is added to seed
+static const int SEED_OFFSET = 135 * 13;
+
+
+
+
+
+// Emit a warning if the first param is true
+#define CONDWARNING(ShouldLog, Fmt, ...) \
+ do { \
+ if (ShouldLog) \
+ { \
+ LOGWARNING(Fmt, __VA_ARGS__); \
+ } \
+ } while (false)
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+/** A modifier which is pseudo-randomly replacing blocks to other types and metas. */
+class cPieceModifierRandomizeBlocks:
+ public cPiece::cPieceModifier
+{
+public:
+ cPieceModifierRandomizeBlocks(void) :
+ m_Seed()
+ {
+ }
+
+ virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) override
+ {
+ m_AllWeights = 0;
+ AString Params = a_Params;
+
+
+ /** BlocksToReplace parsing */
+ auto idxPipe = Params.find('|');
+ if (idxPipe != AString::npos)
+ {
+ AString blocksToReplaceStr = Params.substr(0, idxPipe);
+ auto blocksToReplace = StringSplitAndTrim(blocksToReplaceStr, ",");
+ for (size_t i = 0; i < blocksToReplace.size(); i++)
+ {
+ BLOCKTYPE blockType = static_cast<BLOCKTYPE>(BlockStringToType(blocksToReplace[i]));
+ if ((blockType == E_BLOCK_AIR) && !NoCaseCompare(blocksToReplace[i], "Air"))
+ {
+ CONDWARNING(a_LogWarnings, "Cannot parse block type from string \"%s\"!", blocksToReplace[i].c_str());
+ return false;
+ }
+ m_BlocksToReplace[blockType] = 1;
+ }
+
+ Params = Params.substr(idxPipe + 1, Params.length() - 1);
+ }
+ if (m_BlocksToReplace.size() == 0)
+ {
+ CONDWARNING(a_LogWarnings, "You must specify at least one block to replace%s!", "");
+ return false;
+ }
+
+
+ /** Meta params parsing */
+ auto idxSqBracketStart = Params.find('[');
+ auto idxSqBracketStartLast = Params.find_last_of('[');
+
+ bool isMultiMeta = false;
+ if ((idxSqBracketStart != idxSqBracketStartLast) && (idxSqBracketStartLast != Params.length() - 1))
+ {
+ // Meta per block type
+ isMultiMeta = true;
+ }
+ else
+ {
+ auto idxSqBracketEnd = Params.find(']');
+ if ((idxSqBracketEnd == Params.length() - 1) && (idxSqBracketStart != AString::npos))
+ {
+ AString metaParamsStr = Params.substr(idxSqBracketStart + 1, Params.length() - idxSqBracketStart - 2);
+ std::array<int, 4> metaParamsInt;
+ if (!ParseMeta(metaParamsStr, metaParamsInt, a_LogWarnings))
+ {
+ return false;
+ }
+
+ m_MinMeta = metaParamsInt[0];
+ m_MaxMeta = metaParamsInt[1];
+ m_MinNoiseMeta = metaParamsInt[2];
+ m_MaxNoiseMeta = metaParamsInt[3];
+
+ Params = Params.substr(0, idxSqBracketStart);
+ }
+ }
+
+
+ // BlocksToRandomize parsing
+ auto BlocksToRandomize = StringSplitAndTrim(Params, ";");
+ for (size_t i = 0; i < BlocksToRandomize.size(); i++)
+ {
+ AString block = BlocksToRandomize[i];
+
+ cRandomizedBlock Block{};
+
+ if (isMultiMeta)
+ {
+ auto sqBrStart = block.find('[');
+ if (sqBrStart != AString::npos)
+ {
+ auto sqBrEnd = block.find(']');
+ if (sqBrEnd != block.size() - 1)
+ {
+ CONDWARNING(a_LogWarnings, "If present, block meta params must be at the end of block to randomize definition \"%s\"!", block.c_str());
+ return false;
+
+ }
+ AString metaParamsStr = block.substr(sqBrStart + 1, block.size() - sqBrStart - 2);
+
+ std::array<int, 4> metaParamsInt;
+ if (!ParseMeta(metaParamsStr, metaParamsInt, a_LogWarnings))
+ {
+ return false;
+ }
+
+ Block.m_MinMeta = metaParamsInt[0];
+ Block.m_MaxMeta = metaParamsInt[1];
+ Block.m_MinNoiseMeta = metaParamsInt[2];
+ Block.m_MaxNoiseMeta = metaParamsInt[3];
+
+ block = block.substr(0, sqBrStart);
+
+ }
+ // No meta randomization for this block
+ }
+
+ auto BlockParams = StringSplitAndTrim(block, ",");
+ if (BlockParams.size() < 2)
+ {
+ CONDWARNING(a_LogWarnings, "Block weight is required param \"%s\"!", BlockParams[0].c_str());
+ return false;
+ }
+
+ BLOCKTYPE BlockType = static_cast<BLOCKTYPE>(BlockStringToType(BlockParams[0]));
+ int BlockWeight = 0;
+ if ((BlockType != E_BLOCK_AIR) && !NoCaseCompare(BlockParams[0], "Air"))
+ {
+ // Failed to parse block type
+ CONDWARNING(
+ a_LogWarnings, "Cannot parse block type from string \"%s\"!",
+ BlockParams[0].c_str());
+ return false;
+ }
+
+ if (!StringToInteger(BlockParams[1], BlockWeight))
+ {
+ // Failed to parse the crop weight:
+ CONDWARNING(
+ a_LogWarnings,
+ "Cannot parse block weight from string \"%s\"!",
+ BlockParams[1].c_str());
+ return false;
+ }
+
+
+ Block.m_Type = BlockType;
+ Block.m_Weight = BlockWeight;
+ m_AllWeights += BlockWeight;
+
+ m_BlocksToRandomize.push_back(Block);
+ }
+ if (m_BlocksToRandomize.size() == 0)
+ {
+ CONDWARNING(a_LogWarnings, "You must specify at least one block to randomize%s!", "");
+ return false;
+ }
+
+ return true;
+ }
+
+
+
+
+
+ bool ParseMeta(const AString & a_Meta, std::array<int, 4> & a_ParsedMeta, bool a_LogWarnings)
+ {
+ auto MetaParams = StringSplitAndTrim(a_Meta, ",");
+
+ for (size_t i = 0; i < MetaParams.size(); i++)
+ {
+ int Value;
+ if (!StringToInteger(MetaParams[i], Value))
+ {
+ // Failed to parse meta parameter from string:
+ CONDWARNING(a_LogWarnings, "Cannot parse meta param from string \"%s\", meta: %s!", MetaParams[i].c_str(), a_Meta.c_str());
+ return false;
+ }
+
+ if (i > 3)
+ {
+ CONDWARNING(a_LogWarnings, "Unsupported meta param \"%d\"!", Value);
+ return false;
+ }
+
+ a_ParsedMeta[i] = Value;
+ }
+
+ if (MetaParams.size() == 1)
+ {
+ // Noise is not used for meta
+ a_ParsedMeta[1] = a_ParsedMeta[0];
+ }
+ else if (MetaParams.size() == 2)
+ {
+ // Noise range is same as meta range
+ a_ParsedMeta[2] = a_ParsedMeta[0];
+ a_ParsedMeta[3] = a_ParsedMeta[1];
+ }
+ else if (MetaParams.size() == 3)
+ {
+ a_ParsedMeta[3] = a_ParsedMeta[1];
+ }
+
+ return true;
+ }
+
+
+
+
+
+ virtual void Modify(cBlockArea & a_Image, const Vector3i a_PiecePos, const int a_PieceRot) override
+ {
+ cNoise Noise(m_Seed);
+ cNoise PieceNoise(Noise.IntNoise3DInt(a_PiecePos));
+
+ size_t NumBlocks = a_Image.GetBlockCount();
+ BLOCKTYPE * BlockTypes = a_Image.GetBlockTypes();
+ BLOCKTYPE * BlockMetas = a_Image.GetBlockMetas();
+
+ for (size_t i = 0; i < NumBlocks; i++)
+ {
+ if (m_BlocksToReplace.count(BlockTypes[i]))
+ {
+ float BlockRnd = PieceNoise.IntNoise2DInRange(a_PieceRot, static_cast<int>(i), 0, m_AllWeights);
+
+ int weightDelta = 0;
+ for (auto & blockToRnd : m_BlocksToRandomize)
+ {
+ weightDelta += blockToRnd.m_Weight;
+ if (BlockRnd <= weightDelta)
+ {
+ BlockTypes[i] = blockToRnd.m_Type;
+
+ // Per block meta params
+ if (blockToRnd.m_MinMeta < blockToRnd.m_MaxMeta)
+ {
+ int BlockMetaRnd = std::clamp(static_cast<int>(PieceNoise.IntNoise2DInRange(a_PieceRot*2, static_cast<int>(i), blockToRnd.m_MinNoiseMeta, blockToRnd.m_MaxNoiseMeta)), blockToRnd.m_MinMeta, blockToRnd.m_MaxMeta);
+ BlockMetas[i] = static_cast<NIBBLETYPE>(BlockMetaRnd);
+ }
+ else if ((blockToRnd.m_MaxMeta > -1) && (blockToRnd.m_MaxMeta == blockToRnd.m_MinMeta))
+ {
+ // Change meta if at least minimum meta was specified
+ BlockMetas[i] = static_cast<NIBBLETYPE>(blockToRnd.m_MaxMeta);
+ }
+ break;
+ }
+ }
+
+ // All blocks meta params
+ if (m_MaxMeta > m_MinMeta)
+ {
+ int BlockMetaRnd = std::clamp(static_cast<int>(PieceNoise.IntNoise2DInRange(a_PieceRot*2, static_cast<int>(i), m_MinNoiseMeta, m_MaxNoiseMeta)), m_MinMeta, m_MaxMeta);
+ BlockMetas[i] = static_cast<NIBBLETYPE>(BlockMetaRnd);
+ }
+ else if ((m_MaxMeta > -1) && (m_MaxMeta == m_MinMeta))
+ {
+ // Change meta if at least minimum meta was specified
+ BlockMetas[i] = static_cast<NIBBLETYPE>(m_MaxMeta);
+ }
+ }
+ } // for i - BlockTypes[]
+ }
+
+ virtual void AssignSeed(int a_Seed) override
+ {
+ m_Seed = a_Seed + SEED_OFFSET;
+ }
+protected:
+ int m_Seed;
+ int m_AllWeights = 0;
+
+
+ /** Block types of a blocks which are being replaced by this strategy */
+ std::map<BLOCKTYPE, int> m_BlocksToReplace;
+
+ /** Randomized blocks with their weights and meta params */
+ cRandomizedBlocks m_BlocksToRandomize;
+
+ /** Minimum meta to randomize */
+ int m_MinMeta = 0;
+
+ /** Maximum meta to randomize */
+ int m_MaxMeta = -1;
+
+ /** Maximum meta in noise range */
+ int m_MaxNoiseMeta = 0;
+
+ /** Minimum meta in noise range */
+ int m_MinNoiseMeta = 0;
+};
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// CreatePieceModifierFromString:
+
+bool CreatePieceModifierFromString(const AString & a_Definition, std::shared_ptr<cPiece::cPieceModifiers> & a_Modifiers, bool a_LogWarnings)
+{
+
+ auto idxCurlyStart = a_Definition.find('{');
+ auto idxCurlyFirstEnd = a_Definition.find('}');
+ if ((idxCurlyStart == AString::npos) && (idxCurlyFirstEnd == AString::npos))
+ {
+ CONDWARNING(a_LogWarnings, "Piece metadata \"Modifiers\" needs at least one valid modifier definition \"%s\"!", a_Definition.c_str());
+ return false;
+ }
+
+ auto modifiersStr = StringSplitAndTrim(a_Definition, "{");
+
+ for (size_t i = 0; i < modifiersStr.size(); i++)
+ {
+ AString modifierStr = TrimString(modifiersStr[i]);
+
+ if (modifierStr.size() == 0)
+ {
+ continue;
+ }
+ auto idxCurlyEnd = modifierStr.find('}');
+ if (idxCurlyEnd == AString::npos)
+ {
+ CONDWARNING(a_LogWarnings, "Modifier definition must end with curly bracket \"%s\"!!", modifierStr.c_str());
+ return false;
+ }
+
+ modifierStr = modifierStr.substr(0, idxCurlyEnd);
+
+ // Break apart the modifier class, the first parameter before the first pipe char:
+ auto idxPipe = modifierStr.find('|');
+ if (idxPipe == AString::npos)
+ {
+ idxPipe = modifierStr.length();
+ }
+ AString ModifierClass = modifierStr.substr(0, idxPipe);
+
+ // Create a modifier class based on the class string:
+ cPiece::cPieceModifierPtr Modifier;
+ if (NoCaseCompare(ModifierClass, "RandomizeBlocks") == 0)
+ {
+ Modifier = std::make_shared<cPieceModifierRandomizeBlocks>();
+ }
+
+ if (Modifier == nullptr)
+ {
+ CONDWARNING(a_LogWarnings, "Unknown modifier class \"%s\" %s!", ModifierClass.c_str(), modifierStr.c_str());
+ return false;
+ }
+
+ // Initialize the modifier's parameters:
+ AString Params;
+ if (idxPipe < modifierStr.length())
+ {
+ Params = modifierStr.substr(idxPipe + 1);
+ }
+
+ if (!Modifier->InitializeFromString(Params, a_LogWarnings))
+ {
+ CONDWARNING(a_LogWarnings, "InitializeFromString error \"%s\" -- %!", Params.c_str(), modifierStr.c_str());
+ return false;
+ }
+
+ a_Modifiers->push_back(Modifier);
+ }
+
+ return true;
+}
+
+
+
+
diff --git a/src/Generating/PieceModifier.h b/src/Generating/PieceModifier.h
new file mode 100644
index 000000000..88fba5070
--- /dev/null
+++ b/src/Generating/PieceModifier.h
@@ -0,0 +1,45 @@
+
+// PieceModifier.h
+
+// Declares the public interface for cPiece's cPieceModifier implementations
+
+
+
+
+
+#pragma once
+
+#include "PiecePool.h"
+
+
+
+
+
+bool CreatePieceModifierFromString(const AString & a_Definition, std::shared_ptr<cPiece::cPieceModifiers> & a_Modifiers, bool a_LogWarnings);
+
+
+
+
+
+/** Used to store block type, meta, weight and some more params */
+class cRandomizedBlock
+{
+public:
+ BLOCKTYPE m_Type;
+
+ int m_Weight;
+
+ /** Minimum meta to randomize */
+ int m_MinMeta = 0;
+
+ /** Maximum meta to randomize */
+ int m_MaxMeta = -1;
+
+ /** Maximum meta in noise range */
+ int m_MaxNoiseMeta = 0;
+
+ /** Minimum meta in noise range */
+ int m_MinNoiseMeta = 0;
+};
+
+typedef std::vector<cRandomizedBlock> cRandomizedBlocks;
diff --git a/src/Generating/PiecePool.cpp b/src/Generating/PiecePool.cpp
index 4b4303516..a7c28e75a 100644
--- a/src/Generating/PiecePool.cpp
+++ b/src/Generating/PiecePool.cpp
@@ -8,6 +8,7 @@
#include "PiecePool.h"
#include "VerticalStrategy.h"
#include "VerticalLimit.h"
+#include "PieceModifier.h"
@@ -46,6 +47,29 @@ bool cPiece::SetVerticalLimitFromString(const AString & a_LimitDesc, bool a_LogW
+bool cPiece::SetPieceModifiersFromString(const AString & a_Definition, bool a_LogWarnings)
+{
+ auto modifiers = std::make_shared<cPieceModifiers>();
+ if (!CreatePieceModifierFromString(a_Definition, modifiers, a_LogWarnings))
+ {
+ return false;
+ }
+
+ cPieceModifiers Modifiers;
+ for (size_t i = 0; i < modifiers->size(); i++)
+ {
+ Modifiers.push_back(std::move(modifiers->at(i)));
+ }
+
+ m_Modifiers = Modifiers;
+
+ return true;
+}
+
+
+
+
+
Vector3i cPiece::RotatePos(const Vector3i & a_Pos, int a_NumCCWRotations) const
{
Vector3i Size = GetSize();
diff --git a/src/Generating/PiecePool.h b/src/Generating/PiecePool.h
index 8beaf0c31..5897f32c7 100644
--- a/src/Generating/PiecePool.h
+++ b/src/Generating/PiecePool.h
@@ -148,12 +148,41 @@ public:
typedef std::shared_ptr<cVerticalLimit> cVerticalLimitPtr;
+ /** Base class (interface) for piece modifiers. */
+ class cPieceModifier
+ {
+ public:
+ // Force a virtual destructor in descendants
+ virtual ~cPieceModifier() {}
+
+ /** Initializes the modifier's parameters from the string
+ representation. a_Params is the string containing only the parameters
+ If a_LogWarnings is true, logs any problems to the console.
+ Returns true if successful, false if the string parsing failed.
+ Used when loading the modifier from a file. */
+ virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) = 0;
+
+ /** Called prior to writing piece to a chunk, so that modifiers can modify blocks in the blockarea */
+ virtual void Modify(cBlockArea & a_Image, const Vector3i a_PiecePos, const int a_PieceNumRotations) = 0;
+
+ /** Called when the piece pool is assigned to a generator,
+ so that the modifiers can access world seed. */
+ virtual void AssignSeed(int a_Seed) {}
+ };
+
+
+ typedef std::shared_ptr<cPieceModifier> cPieceModifierPtr;
+
+ typedef std::vector<cPieceModifierPtr> cPieceModifiers;
+
/** The strategy used for vertical placement of this piece when it is used as a starting piece. */
cVerticalStrategyPtr m_VerticalStrategy;
/** The checker that verifies each placement's vertical position. */
cVerticalLimitPtr m_VerticalLimit;
+ /** The modifiers which are modifying piece's blocks. */
+ cPieceModifiers m_Modifiers;
/** Returns all of the available connectors that the piece has.
Each connector has a (relative) position in the piece, and a type associated with it. */
@@ -196,6 +225,11 @@ public:
return m_VerticalLimit;
}
+ cPieceModifiers GetModifiers(void) const
+ {
+ return m_Modifiers;
+ }
+
/** Sets the vertical strategy based on the description in the string.
If a_LogWarnings is true, logs the parsing problems into the server console.
Returns true if successful, false if strategy parsing failed (no strategy assigned). */
@@ -206,6 +240,12 @@ public:
If a_LogWarnings is true, any problem is reported into the server console. */
bool SetVerticalLimitFromString(const AString & a_LimitDesc, bool a_LogWarnings);
+ /** Sets the modifiers with their params in the string.
+ If a_LogWarnings is true, logs the parsing problems into the server console.
+ Returns true if successful, false if strategy parsing failed (no strategy
+ assigned). */
+ bool SetPieceModifiersFromString(const AString & a_Definition, bool a_LogWarnings);
+
/** Returns a copy of the a_Pos after rotating the piece the specified number of CCW rotations. */
Vector3i RotatePos(const Vector3i & a_Pos, int a_NumCCWRotations) const;
diff --git a/src/Generating/Prefab.cpp b/src/Generating/Prefab.cpp
index 7c8c3c0bc..0a7b2a89b 100644
--- a/src/Generating/Prefab.cpp
+++ b/src/Generating/Prefab.cpp
@@ -157,8 +157,24 @@ void cPrefab::Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumR
return;
}
- // Write the image:
- a_Dest.WriteBlockArea(Image, Placement.x, Placement.y, Placement.z, m_MergeStrategy);
+ if (m_Modifiers.size() == 0)
+ {
+ // Write the image:
+ a_Dest.WriteBlockArea(Image, Placement.x, Placement.y, Placement.z, m_MergeStrategy);
+ }
+ else
+ {
+ cBlockArea RandomizedImage;
+ Image.CopyTo(RandomizedImage);
+
+ for (size_t i = 0; i < m_Modifiers.size(); i++)
+ {
+ m_Modifiers[i]->Modify(RandomizedImage, a_Placement, a_NumRotations);
+ }
+
+ // Write the modified image:
+ a_Dest.WriteBlockArea(RandomizedImage, Placement.x, Placement.y, Placement.z, m_MergeStrategy);
+ }
// If requested, draw the floor (from the bottom of the prefab down to the nearest non-air)
switch (m_ExtendFloorStrategy)
diff --git a/src/Generating/PrefabPiecePool.cpp b/src/Generating/PrefabPiecePool.cpp
index 88e670026..67d464d13 100644
--- a/src/Generating/PrefabPiecePool.cpp
+++ b/src/Generating/PrefabPiecePool.cpp
@@ -615,6 +615,12 @@ bool cPrefabPiecePool::ReadPieceMetadataCubesetVer1(
}
a_Prefab->SetVerticalStrategyFromString(VerticalStrategy, a_LogWarnings);
+ AString ModifiersStr;
+ if (a_LuaState.GetNamedValue("Modifiers", ModifiersStr))
+ {
+ a_Prefab->SetPieceModifiersFromString(ModifiersStr, a_LogWarnings);
+ }
+
return true;
}
@@ -744,6 +750,14 @@ void cPrefabPiecePool::AssignGens(int a_Seed, cBiomeGen & a_BiomeGen, cTerrainHe
{
verticalLimit->AssignGens(a_Seed, a_BiomeGen, a_HeightGen, a_SeaLevel);
}
+ auto modifiers = piece->GetModifiers();
+ if (modifiers.size() > 0)
+ {
+ for (size_t i = 0; i < modifiers.size(); i++)
+ {
+ modifiers[i]->AssignSeed(a_Seed);
+ }
+ }
} // for piece - m_AllPieces[]
}