From befe132861b1995dfe776d4e134a90fc243d1a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Imrich?= Date: Thu, 8 Apr 2021 12:24:59 +0200 Subject: cPieceModifier interface and cPieceModifierRandomizeBlocks class (#5122) --- src/Generating/PieceModifier.cpp | 402 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 402 insertions(+) create mode 100644 src/Generating/PieceModifier.cpp (limited to 'src/Generating/PieceModifier.cpp') 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(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 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 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(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 & 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(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(PieceNoise.IntNoise2DInRange(a_PieceRot*2, static_cast(i), blockToRnd.m_MinNoiseMeta, blockToRnd.m_MaxNoiseMeta)), blockToRnd.m_MinMeta, blockToRnd.m_MaxMeta); + BlockMetas[i] = static_cast(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(blockToRnd.m_MaxMeta); + } + break; + } + } + + // All blocks meta params + if (m_MaxMeta > m_MinMeta) + { + int BlockMetaRnd = std::clamp(static_cast(PieceNoise.IntNoise2DInRange(a_PieceRot*2, static_cast(i), m_MinNoiseMeta, m_MaxNoiseMeta)), m_MinMeta, m_MaxMeta); + BlockMetas[i] = static_cast(BlockMetaRnd); + } + else if ((m_MaxMeta > -1) && (m_MaxMeta == m_MinMeta)) + { + // Change meta if at least minimum meta was specified + BlockMetas[i] = static_cast(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 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 & 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(); + } + + 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; +} + + + + -- cgit v1.2.3