summaryrefslogtreecommitdiffstats
path: root/src/Generating/SinglePieceStructuresGen.cpp
blob: fb6dcd67de7ba560465773c2bc9a90af4177731c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

#include "SinglePieceStructuresGen.h"

#include "PrefabStructure.h"
#include "../IniFile.h"
#include "../Item.h"


////////////////////////////////////////////////////////////////////////////////
// cSinglePieceStructuresGen::cGen

class cSinglePieceStructuresGen::cGen :
	public cGridStructGen
{
	using Super = cGridStructGen;
public:
	cGen(int a_Seed, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_SeaLevel, const AString & a_Name):
		Super(a_Seed),
		m_BiomeGen(a_BiomeGen),
		m_HeightGen(a_HeightGen),
		m_SeaLevel(a_SeaLevel),
		m_Name(a_Name)
	{
	}



	/** Loads the piecepool from a file.
	Returns true on success, logs warning and returns false on failure. */
	bool LoadFromFile(const AString & a_FileName)
	{
		m_PiecePool.Clear();

		// Load the piecepool from the file, log any warnings:
		if (!m_PiecePool.LoadFromFile(a_FileName, true))
		{
			return false;
		}
		if (NoCaseCompare(m_PiecePool.GetIntendedUse(), "SinglePieceStructures") != 0)
		{
			LOGWARNING("SinglePieceStructures generator: File %s is intended for use in \"%s\", rather than single piece structures. Loading the file, but the generator may behave unexpectedly.",
				a_FileName.c_str(), m_PiecePool.GetIntendedUse().c_str()
			);
		}
		m_PiecePool.AssignGens(m_Seed, m_BiomeGen, m_HeightGen, m_SeaLevel);

		// Apply generator params from the piecepool (in the metadata) into the generator:
		auto & generatorParams = m_PiecePool.GetAllMetadata();
		SetGeneratorParams(generatorParams);

		return true;
	}




	// cGridStructGen override
	virtual cStructurePtr CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ) override
	{
		// Generate the biomes for the chunk surrounding the origin:
		int ChunkX, ChunkZ;
		cChunkDef::BlockToChunk(a_OriginX, a_OriginZ, ChunkX, ChunkZ);
		cChunkDef::BiomeMap Biomes;
		m_BiomeGen.GenBiomes({ChunkX, ChunkZ}, Biomes);

		// Checks if the biome at the origin position is allowed
		auto Relative = cChunkDef::AbsoluteToRelative(Vector3i(a_OriginX, 1, a_OriginZ), {ChunkX, ChunkZ});
		if (!m_PiecePool.IsBiomeAllowed(Biomes[Relative.x + cChunkDef::Width * Relative.z]))
		{
			return cStructurePtr();
		}
		cPlacedPieces OutPiece;
		OutPiece.push_back(GetPiece(a_OriginX, a_OriginZ));
		return std::make_shared<cPrefabStructure>(a_GridX, a_GridZ, a_OriginX, a_OriginZ, std::move(OutPiece), m_HeightGen);
	}




	/** Determines which piece to place from the piece pool */
	cPlacedPiecePtr GetPiece(int a_BlockX, int a_BlockZ)
	{
		int rnd = m_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) / 7;

		// Choose a random one of the starting pieces:
		cPieces StartingPieces = m_PiecePool.GetStartingPieces();
		int Total = 0;
		for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr)
		{
			Total += m_PiecePool.GetStartingPieceWeight(**itr);
		}
		cPiece * StartingPiece;
		if (Total > 0)
		{
			int Chosen = rnd % Total;
			StartingPiece = StartingPieces.front();
			for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr)
			{
				Chosen -= m_PiecePool.GetStartingPieceWeight(**itr);
				if (Chosen <= 0)
				{
					StartingPiece = *itr;
					break;
				}
			}
		}
		else
		{
			// All pieces returned zero weight, but we need one to start. Choose with equal chance:
			StartingPiece = StartingPieces[static_cast<size_t>(rnd) % StartingPieces.size()];
		}
		rnd = rnd >> 16;

		// Choose a random supported rotation:
		int Rotations[4] = {0};
		int NumRotations = 1;
		for (size_t i = 1; i < ARRAYCOUNT(Rotations); i++)
		{
			if (StartingPiece->CanRotateCCW(static_cast<int>(i)))
			{
				Rotations[NumRotations] = static_cast<int>(i);
				NumRotations += 1;
			}
		}
		int Rotation = Rotations[rnd % NumRotations];
		int BlockY = StartingPiece->GetStartingPieceHeight(a_BlockX, a_BlockZ);
		ASSERT(BlockY >= 0);  // The vertical strategy should have been provided and should give valid coords

		cPlacedPiece * Piece = new cPlacedPiece(nullptr, *StartingPiece, Vector3i(a_BlockX, BlockY, a_BlockZ), Rotation);
		return cPlacedPiecePtr(Piece);
	}

protected:
	/** The underlying biome generator that defines whether the structure is created or not */
	cBiomeGen & m_BiomeGen;

	/** The underlying height generator, used to position the prefabs crossing chunk borders if they are set to FitGround. */
	cTerrainHeightGen & m_HeightGen;

	/** The world's sea level, if available. Used for some cVerticalStrategy descendants. */
	int m_SeaLevel;

	/** The name that is used for reporting. */
	AString m_Name;

	/** All available prefabs. */
	cPrefabPiecePool m_PiecePool;
};


////////////////////////////////////////////////////////////////////////////////
// cSinglePieceStructuresGen

cSinglePieceStructuresGen::cSinglePieceStructuresGen(int a_Seed) :
	m_Seed(a_Seed)
{
}





bool cSinglePieceStructuresGen::Initialize(const AString & a_Prefabs, int a_SeaLevel, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen)
{
	// Load each piecepool:
	auto Structures = StringSplitAndTrim(a_Prefabs, "|");
	for (const auto & S: Structures)
	{
		auto FileName = Printf("Prefabs%cSinglePieceStructures%c%s.cubeset", cFile::PathSeparator(), cFile::PathSeparator(), S.c_str());
		if (!cFile::IsFile(FileName))
		{
			FileName.append(".gz");
			if (!cFile::IsFile(FileName))
			{
				LOGWARNING("Cannot load SinglePieceStructure cubeset file %s", FileName.c_str());
				continue;
			}
		}

		auto Gen = std::make_shared<cGen>(m_Seed, a_BiomeGen, a_HeightGen, a_SeaLevel, S);
		if (Gen->LoadFromFile(FileName))
		{
			m_Gens.push_back(Gen);
		}
	}

	// Report a warning if no generators available:
	if (m_Gens.empty())
	{
		LOGWARNING("The PieceStructures generator was asked to generate \"%s\", but none of the prefabs are valid.", a_Prefabs.c_str());
		return false;
	}
	return true;
}





void cSinglePieceStructuresGen::GenFinish(cChunkDesc & a_Chunk)
{
	for (auto & Gen: m_Gens)
	{
		Gen->GenFinish(a_Chunk);
	}
}