summaryrefslogtreecommitdiffstats
path: root/src/Generating/VillageGen.cpp
blob: 3358bc5310ef3b6d2b9aca370ccf490348b1ed21 (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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

// VillageGen.cpp

// Implements the cVillageGen class representing the village generator

#include "Globals.h"
#include "VillageGen.h"
#include "Prefabs/PlainsVillagePrefabs.h"
#include "Prefabs/SandVillagePrefabs.h"
#include "PieceGenerator.h"





/*
How village generating works:
By descending from a cGridStructGen, a semi-random grid is generated. A village may be generated for each of
the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
biomes are village-friendly. If yes, the entire village structure is built for that cell. If not, the cell
is left village-less.

A village is generated using the regular BFS piece generator. The well piece is used as the starting piece,
the roads and houses are then used as the following pieces. Only the houses are read from the prefabs,
though, the roads are generated by code and their content is ignored. A special subclass of the cPiecePool
class is used, so that the roads connect to each other and to the well only in predefined manners.

The well has connectors of type "1". The houses have connectors of type "-1". The roads have connectors of
both types, type "-1" at the far ends and type "1" on the long edges.

When the village is about to be drawn into a chunk, it queries the heights for each piece intersecting the
chunk. The pieces are shifted so that their pivot points lie on the surface, and the roads are drawn
directly by turning the surface blocks into gravel / sandstone.
*/

class cVillagePiecePool :
	public cPrefabPiecePool
{
	typedef cPrefabPiecePool super;
public:
	cVillagePiecePool(
		const cPrefab::sDef * a_PieceDefs,         size_t a_NumPieceDefs,
		const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs
	) :
		super(a_PieceDefs, a_NumPieceDefs, a_StartingPieceDefs, a_NumStartingPieceDefs)
	{
		// Add the road piece:
		cBlockArea BA;
		BA.Create(5, 1, 3, cBlockArea::baTypes | cBlockArea::baMetas);
		BA.Fill(cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_GRAVEL, 0);
		cPrefab * RoadPiece = new cPrefab(BA, 7);
		RoadPiece->AddConnector(0, 0, 1, BLOCK_FACE_XM, -1);
		RoadPiece->AddConnector(4, 0, 1, BLOCK_FACE_XP, -1);
		RoadPiece->AddConnector(4, 0, 1, BLOCK_FACE_XP, 1);
		RoadPiece->AddConnector(1, 0, 0, BLOCK_FACE_ZM, 1);
		RoadPiece->AddConnector(3, 0, 0, BLOCK_FACE_ZM, 1);
		RoadPiece->AddConnector(1, 0, 2, BLOCK_FACE_ZP, 1);
		RoadPiece->AddConnector(3, 0, 2, BLOCK_FACE_ZP, 1);
		RoadPiece->SetAddWeightIfSame(10000);
		m_AllPieces.push_back(RoadPiece);
		m_PiecesByConnector[-1].push_back(RoadPiece);
		m_PiecesByConnector[1].push_back(RoadPiece);
	}
	
	
	// cPrefabPiecePool overrides:
	virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override
	{
		// Only roads are allowed to connect to the well:
		if ((a_PlacedPiece.GetDepth() == 0) && (a_NewPiece.GetSize().y != 1))
		{
			return 0;
		}
		
		// Roads cannot branch T-wise:
		if (
			(a_PlacedPiece.GetPiece().GetSize().y == 1) &&  // Connecting to a road
			(
				(a_ExistingConnector.m_Direction == BLOCK_FACE_ZP) ||
				(a_ExistingConnector.m_Direction == BLOCK_FACE_ZM) 
			) &&            // Through the long-edge connector
			(a_NewPiece.GetSize().y == 1)                   // And the new piece is a road
		)
		{
			return 0;
		}
		
		return ((const cPrefab &)a_NewPiece).GetPieceWeight(a_PlacedPiece, a_ExistingConnector);
	}
}	;





class cVillageGen::cVillage :
	public cGridStructGen::cStructure
{
	typedef cGridStructGen::cStructure super;
	
public:
	cVillage(
		int a_Seed,
		int a_OriginX, int a_OriginZ,
		int a_MaxRoadDepth,
		int a_MaxSize,
		cPrefabPiecePool & a_Prefabs,
		cTerrainHeightGen & a_HeightGen
	) :
		super(a_OriginX, a_OriginZ),
		m_Seed(a_Seed),
		m_Noise(a_Seed),
		m_MaxSize(a_MaxSize),
		m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize),
		m_Prefabs(a_Prefabs),
		m_HeightGen(a_HeightGen)
	{
		cBFSPieceGenerator pg(m_Prefabs, a_Seed);
		pg.PlacePieces(a_OriginX, 10, a_OriginZ, a_MaxRoadDepth + 1, m_Pieces);
	}
	
protected:
	/** Seed for the random functions */
	int m_Seed;
	
	/** The noise used as a pseudo-random generator */
	cNoise m_Noise;
	
	/** Maximum size, in X/Z blocks, of the village (radius from the origin) */
	int m_MaxSize;
	
	/** Borders of the vilalge - no item may reach out of this cuboid. */
	cCuboid m_Borders;
	
	/** Prefabs to use for buildings */
	cPrefabPiecePool & m_Prefabs;
	
	/** The underlying height generator, used for placing the structures on top of the terrain. */
	cTerrainHeightGen & m_HeightGen;
	
	/** The village pieces, placed by the generator. */
	cPlacedPieces m_Pieces;
	
	
	// cGrdStructGen::cStructure overrides:
	virtual void DrawIntoChunk(cChunkDesc & a_Chunk) override
	{
		// TODO
		// Iterate over all items
		// Each intersecting prefab is placed on ground (if not already placed), then drawn
		// Each intersecting road is drawn by replacing top soil blocks with gravel / sandstone blocks
		for (cPlacedPieces::const_iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
		{
			const cPrefab & Prefab = (const cPrefab &)((*itr)->GetPiece());
			Prefab.Draw(a_Chunk, *itr);
		}  // for itr - m_PlacedPieces[]
	}
} ;





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cVillageGen:

/** The prefabs for the sand village. */
static cVillagePiecePool g_SandVillage  (g_SandVillagePrefabs,   g_SandVillagePrefabsCount,   g_SandVillageStartingPrefabs,   g_SandVillageStartingPrefabsCount);

/** The prefabs for the plains village. */
static cVillagePiecePool g_PlainsVillage(g_PlainsVillagePrefabs, g_PlainsVillagePrefabsCount, g_PlainsVillageStartingPrefabs, g_PlainsVillageStartingPrefabsCount);





cVillageGen::cVillageGen(int a_Seed, int a_GridSize, int a_MaxDepth, int a_MaxSize, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen) :
	super(a_Seed, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 100),
	m_MaxDepth(a_MaxDepth),
	m_MaxSize(a_MaxSize),
	m_BiomeGen(a_BiomeGen),
	m_HeightGen(a_HeightGen)
{
}





cGridStructGen::cStructurePtr cVillageGen::CreateStructure(int a_OriginX, int a_OriginZ)
{
	// 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);

	// Check if all the biomes are village-friendly:
	// If just one is not, no village is created, because it's likely that an unfriendly biome is too close
	cVillagePiecePool * VillagePrefabs = NULL;
	for (size_t i = 0; i < ARRAYCOUNT(Biomes); i++)
	{
		switch (Biomes[i])
		{
			case biDesert:
			case biDesertM:
			{
				// These biomes allow sand villages
				VillagePrefabs = &g_SandVillage;
				break;
			}
			case biPlains:
			case biSavanna:
			case biSavannaM:
			case biSunflowerPlains:
			{
				// These biomes allow plains-style villages
				VillagePrefabs = &g_PlainsVillage;
				break;
			}
			default:
			{
				// Village-unfriendly biome, bail out with zero structure:
				return cStructurePtr();
			}
		}  // switch (Biomes[i])
	}  // for i - Biomes[]

	// Create a village based on the chosen prefabs:
	if (VillagePrefabs == NULL)
	{
		return cStructurePtr();
	}
	return cStructurePtr(new cVillage(m_Seed, a_OriginX, a_OriginZ, m_MaxDepth, m_MaxSize, *VillagePrefabs, m_HeightGen));
}