summaryrefslogblamecommitdiffstats
path: root/src/Generating/IntGen.h
blob: 83a0e569a960cbf3d6dab0888ed15d239e9e8847 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406









                                                                                                    
             



                                           

                                                                            




















                                                                                                                   



                             


                                                                                                









                                                        



                                                                                                                            






                                                                                              



























































                                                                                                            
                                                                                                





                                                       
                                                                                                                        













                                                                                       
                        




                                                     
                                                       





                                        
                                                                                                





                                                       
                                                                                                
                                                                                                                               

                         





                                                                                                     









                        


                                                                                                       










                                                        
                                                                                








                                                             
                                                                                                



                                                                         

                                                                          

                                                            
                                               




                                                                       

                                                                       


                                                                  

                                                                                        

                                                               




                                                                                                                                 







                                                                                                  
                                                                                                                                










                                   

                                                                                                         




                                                     

                                                  

       
                                                                                








                                                            
                                                                                                

                                                  

                                                                         







                                                                                                                  




                                                                                      
 
                                                                        
                                 
                                                                                                              
                                         
                                                           


                                            
                                                            



                                         
                                                          
                                         
                                                           

                                         
                                                           
                                         
                                                            















                                                              

                                                                                    







                                                       
                                                                                          







                                                 
                                                                                                














































                                                                 

                                                                           





                                                        




                                                                                             

                                                       
                                                                                                                                    
                                         
                                                                                                                                                















                                                                                                 

                                                                                                       






                                                     
                                                                  

 
                                                                              
                              
                                   




                                          
                                                                                                





                                                                
                                                                       
                                 
                                                                                                           
                                                                  
                                         
                                                                                                        

                                         

                                   


          

                                                                          



















                                                                                                      
                                                                                          






                                                         
                                                                                       

                                                        

                                                                       





                                                                              





                                                                                             
                                 



                                                                                                                                       



                                                                                     

                                                 
                                                                          

                                                      
                                                           




                                                                                                



                                                                              

                                                 
                                                                          

                                                      
                                                        
                                 
                                                              







                                   






                                                        








                                             





  


                                                                                                         






                                                     
                                                                  








                                                            
                                                                                                

                                                     
                                                
                 






                                                                                                                  

                  
                                                 
                 
                                                                                                                   

                  




                                                      
                                                    
                 
                                                                                                                 

                  




                                                                                                   
                                                   



                                                                    
                                                       
                 
                                    


                                              
                 
                                                                                        

                  

                                                               
                 
                                                                                        

                  
                                                               
                 



                                                                                                            
                                                                                                      








                                                                                                                    











                                                                                  


                                                                                                                    
                                                                                                     





















                                                                                       
                                                                                      






                                                     
                                                                  











                                                                                                         
                                                                                                












                                                                
                                                                                                           
                                                                  









                                                                     
                                                 
                   

                                                         
                 

                                                           
                     
 







                                                          


                                                                                                           






                                            
                                                                  








                                                                   
                                                                                                


                                                            

                                                             












                                                                              
                                                              






































                                                                                                                                
                                                                                      








                                                          
                                                                                                















































                                                                                                        
                                                                                      










                                                                                            
                                                                                                


























                                                                                     
                                                        


                                                               
                                                       


                                                               
                                                        




                                                                                                 



                                                                                                                      























                                                                            




















































                                                                                                   































































































































































































































































































































































                                                                                                                   

// IntGen.h

// Declares the cIntGen class and descendants for generating and filtering various 2D arrays of ints

/*
The integers generated may be interpreted in several ways:
- land/see designators
	- 0 = ocean
	- >0 = land
- biome group
	- 0 = ocean
	- 1 = desert biomes
	- 2 = temperate biomes
	- 3 = mountains (hills and forests)
	- 4 = ice biomes
- biome group with "bgfRare" flag (for generating rare biomes for the group)
- biome IDs
The interpretation depends on the generator used and on the position in the chain.

The generators can be chained together - one produces data that another one consumes.
Some of such chain connections require changing the data dimensions between the two, which is handled automatically
by using templates.
*/





#pragma once

#include "../BiomeDef.h"





/** Constants representing the biome group designators. */
const int bgOcean        = 0;
const int bgDesert       = 1;
const int bgTemperate    = 2;
const int bgMountains    = 3;
const int bgIce          = 4;
const int bgLandOceanMax = 4;  // Maximum biome group value generated in the landOcean generator
const int bgfRare        = 1024;  // Flag added to values to generate rare biomes for the group





/** Interface that all the generator classes provide. */
template <int SizeX, int SizeZ = SizeX>
class cIntGen
{
public:
	/** Force a virtual destructor in all descendants.
	Descendants contain virtual functions and are referred to via pointer-to-base, so they need a virtual destructor. */
	virtual ~cIntGen() {}

	/** Holds the array of values generated by this class (descendant). */
	typedef int Values[SizeX * SizeZ];

	/** Generates the array of templated size into a_Values, based on given min coords. */
	virtual void GetInts(int a_MinX, int a_MinZ, Values & a_Values) = 0;
};





/** Provides additional cNoise member and its helper functions. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenWithNoise :
	public cIntGen<SizeX, SizeZ>
{
	typedef cIntGen<SizeX, SizeZ> super;

public:
	cIntGenWithNoise(int a_Seed) :
		m_Noise(a_Seed)
	{
	}

protected:
	cNoise m_Noise;

	/** Chooses one of a_Val1 or a_Val2, based on m_Noise and the coordinates for querying the noise. */
	int ChooseRandomOne(int a_RndX, int a_RndZ, int a_Val1, int a_Val2)
	{
		int rnd = m_Noise.IntNoise2DInt(a_RndX, a_RndZ) / 7;
		return ((rnd & 1) == 0) ? a_Val1 : a_Val2;
	}

	/** Chooses one of a_ValN, based on m_Noise and the coordinates for querying the noise. */
	int ChooseRandomOne(int a_RndX, int a_RndZ, int a_Val1, int a_Val2, int a_Val3, int a_Val4)
	{
		int rnd = m_Noise.IntNoise2DInt(a_RndX, a_RndZ) / 7;
		switch (rnd % 4)
		{
			case 0:  return a_Val1;
			case 1:  return a_Val2;
			case 2:  return a_Val3;
			default: return a_Val4;
		}
	}
};






/** Generates a 2D array of random integers in the specified range [0 .. Range). */
template <int Range, int SizeX, int SizeZ = SizeX>
class cIntGenChoice :
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	cIntGenChoice(int a_Seed) :
		super(a_Seed)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		for (int z = 0; z < SizeZ; z++)
		{
			int BaseZ = a_MinZ + z;
			for (int x = 0; x < SizeX; x++)
			{
				a_Values[x + SizeX * z] = (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7) % Range;
			}
		}  // for z
	}
};






/** Decides between the ocean and landmass biomes.
Has a threshold (in percent) of how much land, the larger the threshold, the more land.
Generates 0 for ocean, biome group ID for landmass. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenLandOcean :
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	cIntGenLandOcean(int a_Seed, int a_Threshold) :
		super(a_Seed),
		m_Threshold(a_Threshold)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		for (int z = 0; z < SizeZ; z++)
		{
			int BaseZ = a_MinZ + z;
			for (int x = 0; x < SizeX; x++)
			{
				int rnd = (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7);
				a_Values[x + SizeX * z] = ((rnd % 100) < m_Threshold) ? ((rnd / 101) % bgLandOceanMax + 1) : 0;
			}
		}

		// If the centerpoint of the world is within the area, set it to bgTemperate, always:
		if ((a_MinX <= 0) && (a_MinZ <= 0) && (a_MinX + SizeX > 0) && (a_MinZ + SizeZ > 0))
		{
			a_Values[-a_MinX - a_MinZ * SizeX] = bgTemperate;
		}
	}

protected:
	int m_Threshold;
};





/** Zooms the underlying value array to twice the size. Uses random-neighbor for the pixels in-between.
This means that the zoome out image is randomly distorted. Applying zoom several times provides all
the distortion that the generators need. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenZoom :
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

protected:
	static const int m_LowerSizeX = (SizeX / 2) + 2;
	static const int m_LowerSizeZ = (SizeZ / 2) + 2;

public:
	typedef std::shared_ptr<cIntGen<m_LowerSizeX, m_LowerSizeZ>> Underlying;


	cIntGenZoom(int a_Seed, Underlying a_UnderlyingGen) :
		super(a_Seed),
		m_UnderlyingGen(a_UnderlyingGen)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying data with half the resolution:
		int lowerMinX = a_MinX >> 1;
		int lowerMinZ = a_MinZ >> 1;
		int lowerData[m_LowerSizeX * m_LowerSizeZ];
		m_UnderlyingGen->GetInts(lowerMinX, lowerMinZ, lowerData);
		const int lowStepX = (m_LowerSizeX - 1) * 2;
		const int lowStepZ = (m_LowerSizeZ - 1) * 2;
		int cache[lowStepX * lowStepZ];

		// Discreet-interpolate the values into twice the size:
		for (int z = 0; z < m_LowerSizeZ - 1; ++z)
		{
			int idx = (z * 2) * lowStepX;
			int PrevZ0 = lowerData[z * m_LowerSizeX];
			int PrevZ1 = lowerData[(z + 1) * m_LowerSizeX];

			for (int x = 0; x < m_LowerSizeX - 1; ++x)
			{
				int ValX1Z0 = lowerData[x + 1 + z * m_LowerSizeX];
				int ValX1Z1 = lowerData[x + 1 + (z + 1) * m_LowerSizeX];
				int RndX = (x + lowerMinX) * 2;
				int RndZ = (z + lowerMinZ) * 2;
				cache[idx] = PrevZ0;
				cache[idx + lowStepX] = super::ChooseRandomOne(RndX, RndZ + 1, PrevZ0, PrevZ1);
				cache[idx + 1]        = super::ChooseRandomOne(RndX, RndZ - 1, PrevZ0, ValX1Z0);
				cache[idx + 1 + lowStepX] = super::ChooseRandomOne(RndX, RndZ, PrevZ0, ValX1Z0, PrevZ1, ValX1Z1);
				idx += 2;
				PrevZ0 = ValX1Z0;
				PrevZ1 = ValX1Z1;
			}
		}

		// Copy from Cache into a_Values; take into account the even/odd offsets in a_Min:
		for (int z = 0; z < SizeZ; ++z)
		{
			memcpy(a_Values + z * SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), SizeX * sizeof(int));
		}
	}

protected:
	Underlying m_UnderlyingGen;
};





/** Smoothes out some artifacts generated by the zooming - mostly single-pixel values.
Compares each pixel to its neighbors and if the neighbors are equal, changes the pixel to their value. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenSmooth :
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;
	static const int m_LowerSizeX = SizeX + 2;
	static const int m_LowerSizeZ = SizeZ + 2;

public:
	typedef std::shared_ptr<cIntGen<m_LowerSizeX, m_LowerSizeZ>> Underlying;


	cIntGenSmooth(int a_Seed, Underlying a_Underlying) :
		super(a_Seed),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying values:
		int lowerData[m_LowerSizeX * m_LowerSizeZ];
		m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerData);

		// Smooth - for each square check if the surroundings are the same, if so, expand them diagonally.
		// Also get rid of single-pixel irregularities (A-B-A):
		for (int z = 0; z < SizeZ; z++)
		{
			int NoiseZ = a_MinZ + z;
			for (int x = 0; x < SizeX; x++)
			{
				int val   = lowerData[x + 1 + (z + 1) * m_LowerSizeX];
				int above = lowerData[x + 1 +  z      * m_LowerSizeX];
				int below = lowerData[x + 1 + (z + 2) * m_LowerSizeX];
				int left  = lowerData[x     + (z + 1) * m_LowerSizeX];
				int right = lowerData[x + 2 + (z + 1) * m_LowerSizeX];

				if ((left == right) && (above == below))
				{
					if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 2) == 0)
					{
						val = left;
					}
					else
					{
						val = above;
					}
				}
				else
				{
					if (left == right)
					{
						val = left;
					}

					if (above == below)
					{
						val = above;
					}
				}

				a_Values[x + z * SizeX] = val;
			}
		}
	}

protected:
	Underlying m_Underlying;
};





/** Converts land biomes at the edge of an ocean into the respective beach biome. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenBeaches :
	public cIntGen<SizeX, SizeZ>
{
	typedef cIntGen<SizeX, SizeZ> super;
	static const int m_UnderlyingSizeX = SizeX + 2;
	static const int m_UnderlyingSizeZ = SizeZ + 2;

public:
	typedef std::shared_ptr<cIntGen<m_UnderlyingSizeX, m_UnderlyingSizeZ>> Underlying;


	cIntGenBeaches(Underlying a_Underlying) :
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Map for biome -> its beach:
		static const int ToBeach[] =
		{
			/* biOcean            */ biOcean,
			/* biPlains           */ biBeach,
			/* biDesert           */ biBeach,
			/* biExtremeHills     */ biStoneBeach,
			/* biForest           */ biBeach,
			/* biTaiga            */ biColdBeach,
			/* biSwampland        */ biSwampland,
			/* biRiver            */ biRiver,
			/* biNether           */ biNether,
			/* biEnd              */ biEnd,
			/* biFrozenOcean      */ biColdBeach,
			/* biFrozenRiver      */ biColdBeach,
			/* biIcePlains        */ biColdBeach,
			/* biIceMountains     */ biColdBeach,
			/* biMushroomIsland   */ biMushroomShore,
			/* biMushroomShore    */ biMushroomShore,
			/* biBeach            */ biBeach,
			/* biDesertHills      */ biBeach,
			/* biForestHills      */ biBeach,
			/* biTaigaHills       */ biColdBeach,
			/* biExtremeHillsEdge */ biStoneBeach,
			/* biJungle           */ biBeach,
			/* biJungleHills      */ biBeach,
			/* biJungleEdge       */ biBeach,
			/* biDeepOcean        */ biOcean,
			/* biStoneBeach       */ biStoneBeach,
			/* biColdBeach        */ biColdBeach,
			/* biBirchForest      */ biBeach,
			/* biBirchForestHills */ biBeach,
			/* biRoofedForest     */ biBeach,
			/* biColdTaiga        */ biColdBeach,
			/* biColdTaigaHills   */ biColdBeach,
			/* biMegaTaiga        */ biStoneBeach,
			/* biMegaTaigaHills   */ biStoneBeach,
			/* biExtremeHillsPlus */ biStoneBeach,
			/* biSavanna          */ biBeach,
			/* biSavannaPlateau   */ biBeach,
			/* biMesa             */ biMesa,
			/* biMesaPlateauF     */ biMesa,
			/* biMesaPlateau      */ biMesa,
		};

		// Generate the underlying values:
		int lowerValues[m_UnderlyingSizeX * m_UnderlyingSizeZ];
		m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerValues);

		// Add beaches between ocean and biomes:
		for (int z = 0; z < SizeZ; z++)
		{
			for (int x = 0; x < SizeX; x++)
			{
				int val   = lowerValues[x + 1 + (z + 1) * m_UnderlyingSizeX];
				int above = lowerValues[x + 1 + z       * m_UnderlyingSizeX];
				int below = lowerValues[x + 1 + (z + 2) * m_UnderlyingSizeX];
				int left  = lowerValues[x     + (z + 1) * m_UnderlyingSizeX];
				int right = lowerValues[x + 2 + (z + 1) * m_UnderlyingSizeX];
				if (!IsBiomeOcean(val))
				{
					if (IsBiomeOcean(above) || IsBiomeOcean(below) || IsBiomeOcean(left) || IsBiomeOcean(right))
					{
						// First convert the value to a regular biome (drop the M flag), then modulo by our biome count:
						val = ToBeach[(val % 128) % ARRAYCOUNT(ToBeach)];
					}
				}
				a_Values[x + z * SizeX] = val;
			}
		}
	}

protected:
	Underlying m_Underlying;
};





/** Generates the underlying numbers and then randomly changes some ocean group pixels into random land
biome group pixels, based on the predefined chance. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenAddIslands :
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying;


	cIntGenAddIslands(int a_Seed, int a_Chance, Underlying a_Underlying) :
		super(a_Seed),
		m_Chance(a_Chance),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		m_Underlying->GetInts(a_MinX, a_MinZ, a_Values);
		for (int z = 0; z < SizeZ; z++)
		{
			for (int x = 0; x < SizeX; x++)
			{
				if (a_Values[x + z * SizeX] == bgOcean)
				{
					int rnd = super::m_Noise.IntNoise2DInt(a_MinX + x, a_MinZ + z) / 7;
					if (rnd % 1000 < m_Chance)
					{
						a_Values[x + z * SizeX] = (rnd / 1003) % bgLandOceanMax;
					}
				}
			}  // for x
		}  // for z
	}

protected:
	/** Chance, in permille, of an island being generated in ocean. */
	int m_Chance;

	Underlying m_Underlying;
};





/** A filter that adds an edge biome group between two biome groups that need an edge between them. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenBiomeGroupEdges :
	public cIntGen<SizeX, SizeZ>
{
	typedef cIntGen<SizeX, SizeZ> super;

	static const int m_UnderlyingSizeX = SizeX + 2;
	static const int m_UnderlyingSizeZ = SizeZ + 2;

public:

	typedef std::shared_ptr<cIntGen<m_UnderlyingSizeX, m_UnderlyingSizeZ>> Underlying;

	cIntGenBiomeGroupEdges(Underlying a_Underlying) :
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values)
	{
		// Generate the underlying biome groups:
		int lowerValues[m_UnderlyingSizeX * m_UnderlyingSizeZ];
		m_Underlying->GetInts(a_MinX, a_MinZ, lowerValues);

		// Change the biomes on incompatible edges into an edge biome:
		for (int z = 0; z < SizeZ; z++)
		{
			for (int x = 0; x < SizeX; x++)
			{
				int val   = lowerValues[x + 1 + (z + 1) * m_UnderlyingSizeX];
				int above = lowerValues[x + 1 + z       * m_UnderlyingSizeX];
				int below = lowerValues[x + 1 + (z + 2) * m_UnderlyingSizeX];
				int left  = lowerValues[x     + (z + 1) * m_UnderlyingSizeX];
				int right = lowerValues[x + 2 + (z + 1) * m_UnderlyingSizeX];
				switch (val)
				{
					// Desert should neighbor only oceans, desert and temperates; change to temperate when another:
					case bgDesert:
					{
						if (
							!isDesertCompatible(above) ||
							!isDesertCompatible(below) ||
							!isDesertCompatible(left) ||
							!isDesertCompatible(right)
						)
						{
							val = bgTemperate;
						}
						break;
					}  // case bgDesert

					// Ice should not neighbor deserts; change to temperate:
					case bgIce:
					{
						if (
							(above == bgDesert) ||
							(below == bgDesert) ||
							(left  == bgDesert) ||
							(right == bgDesert)
						)
						{
							val = bgTemperate;
						}
						break;
					}  // case bgIce
				}
				a_Values[x + z * SizeX] = val;
			}  // for x
		}  // for z
	}

protected:
	Underlying m_Underlying;


	inline bool isDesertCompatible(int a_BiomeGroup)
	{
		switch (a_BiomeGroup)
		{
			case bgOcean:
			case bgDesert:
			case bgTemperate:
			{
				return true;
			}
			default:
			{
				return false;
			}
		}
	}
};





/** Turns biome group indices into real biomes.
For each pixel, takes its biome group and chooses a random biome from that group; replaces the value with
that biome. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenBiomes :
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying;


	cIntGenBiomes(int a_Seed, Underlying a_Underlying) :
		super(a_Seed),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Define the per-biome-group biomes:
		static const int oceanBiomes[] =
		{
			biOcean,  // biDeepOcean,
		};

		// Same as oceanBiomes, there are no rare oceanic biomes (mushroom islands are handled separately)
		static const int rareOceanBiomes[] =
		{
			biOcean,
		};

		static const int desertBiomes[] =
		{
			biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, biSavanna, biSavanna, biPlains,
		};

		static const int rareDesertBiomes[] =
		{
			biMesaPlateau, biMesaPlateauF,
		};

		static const int temperateBiomes[] =
		{
			biForest, biForest, biRoofedForest, biExtremeHills, biPlains, biBirchForest, biSwampland,
		};

		static const int rareTemperateBiomes[] =
		{
			biJungle,  // Jungle is not strictly temperate, but let's piggyback it here
		};

		static const int mountainBiomes[] =
		{
			biExtremeHills, biForest, biTaiga, biPlains,
		};

		static const int rareMountainBiomes[] =
		{
			biMegaTaiga,
		};

		static const int iceBiomes[] =
		{
			biIcePlains, biIcePlains, biIcePlains, biIcePlains, biColdTaiga,
		};

		// Same as iceBiomes, there's no rare ice biome
		static const int rareIceBiomes[] =
		{
			biIcePlains, biIcePlains, biIcePlains, biIcePlains, biColdTaiga,
		};

		static const cBiomesInGroups biomesInGroups[] =
		{
			/* bgOcean */     { static_cast<int>(ARRAYCOUNT(oceanBiomes)),     oceanBiomes},
			/* bgDesert */    { static_cast<int>(ARRAYCOUNT(desertBiomes)),    desertBiomes},
			/* bgTemperate */ { static_cast<int>(ARRAYCOUNT(temperateBiomes)), temperateBiomes},
			/* bgMountains */ { static_cast<int>(ARRAYCOUNT(mountainBiomes)),  mountainBiomes},
			/* bgIce */       { static_cast<int>(ARRAYCOUNT(iceBiomes)),       iceBiomes},
		};

		static const cBiomesInGroups rareBiomesInGroups[] =
		{
			/* bgOcean */     { static_cast<int>(ARRAYCOUNT(rareOceanBiomes)),     rareOceanBiomes},
			/* bgDesert */    { static_cast<int>(ARRAYCOUNT(rareDesertBiomes)),    rareDesertBiomes},
			/* bgTemperate */ { static_cast<int>(ARRAYCOUNT(rareTemperateBiomes)), rareTemperateBiomes},
			/* bgMountains */ { static_cast<int>(ARRAYCOUNT(rareMountainBiomes)),  rareMountainBiomes},
			/* bgIce */       { static_cast<int>(ARRAYCOUNT(rareIceBiomes)),       rareIceBiomes},
		};

		// Generate the underlying values, representing biome groups:
		m_Underlying->GetInts(a_MinX, a_MinZ, a_Values);

		// Overwrite each biome group with a random biome from that group:
		for (int z = 0; z < SizeZ; z++)
		{
			int IdxZ = z * SizeX;
			for (int x = 0; x < SizeX; x++)
			{
				int val = a_Values[x + IdxZ];
				const cBiomesInGroups & Biomes = (val > bgfRare) ?
					rareBiomesInGroups[(val & (bgfRare - 1)) % ARRAYCOUNT(rareBiomesInGroups)] :
					biomesInGroups[val % ARRAYCOUNT(biomesInGroups)];
				int rnd = (super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7);
				a_Values[x + IdxZ] = Biomes.Biomes[rnd % Biomes.Count];
			}
		}
	}

protected:

	struct cBiomesInGroups
	{
		const int Count;
		const int * Biomes;
	};


	/** The underlying int generator */
	Underlying m_Underlying;
};





/** Randomly replaces pixels of one value to another value, using the given chance. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenReplaceRandomly :
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying;


	cIntGenReplaceRandomly(int a_From, int a_To, int a_Chance, int a_Seed, Underlying a_Underlying) :
		super(a_Seed),
		m_From(a_From),
		m_To(a_To),
		m_Chance(a_Chance),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying values:
		m_Underlying->GetInts(a_MinX, a_MinZ, a_Values);

		// Replace some of the values:
		for (int z = 0; z < SizeZ; z++)
		{
			int idxZ = z * SizeX;
			for (int x = 0; x < SizeX; x++)
			{
				int idx = x + idxZ;
				if (a_Values[idx] == m_From)
				{
					int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7;
					if (rnd % 1000 < m_Chance)
					{
						a_Values[idx] = m_To;
					}
				}
			}
		}  // for z
	}


protected:
	/** The original value to be replaced. */
	int m_From;

	/** The destination value to which to replace. */
	int m_To;

	/** Chance, in permille, of replacing the value. */
	int m_Chance;

	Underlying m_Underlying;
};





/** Mixer that joins together finalized biomes and rivers.
It first checks for oceans, if there is an ocean in the Biomes, it keeps the ocean.
If there's no ocean, it checks Rivers for a river, if there is a river, it uses the Biomes to select either
regular river or frozen river, based on the biome. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenMixRivers:
	public cIntGen<SizeX, SizeZ>
{
	typedef cIntGen<SizeX, SizeZ> super;

public:
	typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying;


	cIntGenMixRivers(Underlying a_Biomes, Underlying a_Rivers):
		m_Biomes(a_Biomes),
		m_Rivers(a_Rivers)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying data:
		m_Biomes->GetInts(a_MinX, a_MinZ, a_Values);
		typename super::Values riverData;
		m_Rivers->GetInts(a_MinX, a_MinZ, riverData);

		// Mix the values:
		for (int z = 0; z < SizeZ; z++)
		{
			int idxZ = z * SizeX;
			for (int x = 0; x < SizeX; x++)
			{
				int idx = x + idxZ;
				if (IsBiomeOcean(a_Values[idx]))
				{
					// Oceans are kept without any changes
					continue;
				}
				if (riverData[idx] != biRiver)
				{
					// There's no river, keep the current value
					continue;
				}

				// There's a river, change the output to a river or a frozen river, based on the original biome:
				if (IsBiomeVeryCold((EMCSBiome)a_Values[idx]))
				{
					a_Values[idx] = biFrozenRiver;
				}
				else
				{
					a_Values[idx] = biRiver;
				}
			}  // for x
		}  // for z
	}

protected:
	Underlying m_Biomes;
	Underlying m_Rivers;
};





/** Generates a river based on the underlying data.
This is basically an edge detector over the underlying data. The rivers are the edges where the underlying data
changes from one pixel to its neighbor. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenRiver:
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;
	static const int UnderlyingSizeX = SizeX + 2;
	static const int UnderlyingSizeZ = SizeZ + 2;

public:
	typedef std::shared_ptr<cIntGen<UnderlyingSizeX, UnderlyingSizeZ>> Underlying;


	cIntGenRiver(int a_Seed, Underlying a_Underlying):
		super(a_Seed),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying data:
		int Cache[UnderlyingSizeX * UnderlyingSizeZ];
		m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, Cache);

		// Detect the edges:
		for (int z = 0; z < SizeZ; z++)
		{
			for (int x = 0; x < SizeX; x++)
			{
				int Above = Cache[x + 1 + z * UnderlyingSizeX];
				int Below = Cache[x + 1 + (z + 2) * UnderlyingSizeX];
				int Left  = Cache[x +     (z + 1) * UnderlyingSizeX];
				int Right = Cache[x + 2 + (z + 1) * UnderlyingSizeX];
				int val   = Cache[x + 1 + (z + 1) * UnderlyingSizeX];

				if ((val == Above) && (val == Below) && (val == Left) && (val == Right))
				{
					val = 0;
				}
				else
				{
					val = biRiver;
				}
				a_Values[x + z * SizeX] = val;
			}  // for x
		}  // for z
	}

protected:
	Underlying m_Underlying;
};





/** Turns some of the oceans into the specified biome. Used for mushroom and deep ocean.
The biome is only placed if at least 3 of its neighbors are ocean and only with the specified chance. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenAddToOcean:
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;
	static const int UnderlyingSizeX = SizeX + 2;
	static const int UnderlyingSizeZ = SizeZ + 2;

public:
	typedef std::shared_ptr<cIntGen<UnderlyingSizeX, UnderlyingSizeZ>> Underlying;


	cIntGenAddToOcean(int a_Seed, int a_Chance, int a_ToValue, Underlying a_Underlying):
		super(a_Seed),
		m_Chance(a_Chance),
		m_ToValue(a_ToValue),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying data:
		int Cache[UnderlyingSizeX * UnderlyingSizeZ];
		m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, Cache);

		// Add the mushroom islands:
		for (int z = 0; z < SizeZ; z++)
		{
			for (int x = 0; x < SizeX; x++)
			{
				int val = Cache[x + 1 + (z + 1) * UnderlyingSizeX];
				if (!IsBiomeOcean(val))
				{
					a_Values[x + z * SizeX] = val;
					continue;
				}

				// Count the ocean neighbors:
				int Above = Cache[x + 1 + z * UnderlyingSizeX];
				int Below = Cache[x + 1 + (z + 2) * UnderlyingSizeX];
				int Left  = Cache[x +     (z + 1) * UnderlyingSizeX];
				int Right = Cache[x + 2 + (z + 1) * UnderlyingSizeX];
				int NumOceanNeighbors = 0;
				if (IsBiomeOcean(Above))
				{
					NumOceanNeighbors += 1;
				}
				if (IsBiomeOcean(Below))
				{
					NumOceanNeighbors += 1;
				}
				if (IsBiomeOcean(Left))
				{
					NumOceanNeighbors += 1;
				}
				if (IsBiomeOcean(Right))
				{
					NumOceanNeighbors += 1;
				}

				// If at least 3 ocean neighbors and the chance is right, change:
				if (
					(NumOceanNeighbors >= 3) &&
					((super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7) % 1000 < m_Chance)
				)
				{
					a_Values[x + z * SizeX] = m_ToValue;
				}
				else
				{
					a_Values[x + z * SizeX] = val;
				}
			}  // for x
		}  // for z
	}

protected:
	/** Chance, in permille, of changing the biome. */
	int m_Chance;

	/** The value to change the ocean into. */
	int m_ToValue;

	Underlying m_Underlying;
};





/** Changes random pixels of the underlying data to the specified value. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenSetRandomly :
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying;

	cIntGenSetRandomly(int a_Seed, int a_Chance, int a_ToValue, Underlying a_Underlying) :
		super(a_Seed),
		m_Chance(a_Chance),
		m_ToValue(a_ToValue),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying data:
		m_Underlying->GetInts(a_MinX, a_MinZ, a_Values);

		// Change random pixels to bgOcean:
		for (int z = 0; z < SizeZ; z++)
		{
			for (int x = 0; x < SizeX; x++)
			{
				int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7;
				if (rnd % 1000 < m_Chance)
				{
					a_Values[x + z * SizeX] = m_ToValue;
				}
			}
		}
	}

protected:
	/** Chance, in permille, of changing each pixel. */
	int m_Chance;

	/** The value to which to set the pixel. */
	int m_ToValue;

	Underlying m_Underlying;
};






/** Adds a "rare" flag to random biome groups, based on the given chance. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenRareBiomeGroups:
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying;


	cIntGenRareBiomeGroups(int a_Seed, int a_Chance, Underlying a_Underlying):
		super(a_Seed),
		m_Chance(a_Chance),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying data:
		m_Underlying->GetInts(a_MinX, a_MinZ, a_Values);

		// Change some of the biome groups into rare biome groups:
		for (int z = 0; z < SizeZ; z++)
		{
			for (int x = 0; x < SizeX; x++)
			{
				int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7;
				if (rnd % 1000 < m_Chance)
				{
					int idx = x + SizeX * z;
					a_Values[idx] = a_Values[idx] | bgfRare;
				}
			}
		}
	}

protected:
	/** Chance, in permille, of changing each pixel into the rare biome group. */
	int m_Chance;

	/** The underlying generator. */
	Underlying m_Underlying;
};





/** Changes biomes in the parent data into an alternate versions (usually "hill" variants), in such places
that have their alterations set. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenAlternateBiomes:
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying;


	cIntGenAlternateBiomes(int a_Seed, Underlying a_Alterations, Underlying a_BaseBiomes):
		super(a_Seed),
		m_Alterations(a_Alterations),
		m_BaseBiomes(a_BaseBiomes)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the base biomes and the alterations:
		m_BaseBiomes->GetInts(a_MinX, a_MinZ, a_Values);
		super::Values alterations;
		m_Alterations->GetInts(a_MinX, a_MinZ, alterations);

		// Change the biomes into their alternate versions:
		for (int idx = 0; idx < SizeX * SizeZ; ++idx)
		{
			if (alterations[idx] == 0)
			{
				// No change
				continue;
			}

			// Change to alternate biomes:
			int val = a_Values[idx];
			switch (val)
			{
				case biBirchForest:   val = biBirchForestHills; break;
				case biDesert:        val = biDesertHills;      break;
				case biExtremeHills:  val = biExtremeHillsPlus; break;
				case biForest:        val = biForestHills;      break;
				case biIcePlains:     val = biIceMountains;     break;
				case biJungle:        val = biJungleHills;      break;
				case biMegaTaiga:     val = biMegaTaigaHills;   break;
				case biMesaPlateau:   val = biMesa;             break;
				case biMesaPlateauF:  val = biMesa;             break;
				case biMesaPlateauM:  val = biMesa;             break;
				case biMesaPlateauFM: val = biMesa;             break;
				case biPlains:        val = biForest;           break;
				case biRoofedForest:  val = biPlains;           break;
				case biSavanna:       val = biSavannaPlateau;   break;
				case biTaiga:         val = biTaigaHills;       break;
			}
			a_Values[idx] = val;
		}  // for idx - a_Values[]
	}

protected:
	Underlying m_Alterations;
	Underlying m_BaseBiomes;
};





/** Adds an edge between two specifically incompatible biomes, such as mesa and forest. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenBiomeEdges:
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;
	static const int m_LowerSizeX = SizeX + 2;
	static const int m_LowerSizeZ = SizeZ + 2;

public:
	typedef std::shared_ptr<cIntGen<m_LowerSizeX, m_LowerSizeZ>> Underlying;


	cIntGenBiomeEdges(int a_Seed, Underlying a_Underlying):
		super(a_Seed),
		m_Underlying(a_Underlying)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying biomes:
		Underlying::element_type::Values lowerValues;
		m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerValues);

		// Convert incompatible edges into neutral biomes:
		for (int z = 0; z < SizeZ; z++)
		{
			for (int x = 0; x < SizeX; x++)
			{
				int biome = lowerValues[x + 1 + (z + 1) * m_LowerSizeX];
				int above = lowerValues[x + 1 + z *       m_LowerSizeX];
				int below = lowerValues[x + 1 + (z + 2) * m_LowerSizeX];
				int left  = lowerValues[x +     (z + 1) * m_LowerSizeX];
				int right = lowerValues[x + 2 + (z + 1) * m_LowerSizeX];

				switch (biome)
				{
					case biDesert:
					case biDesertM:
					case biDesertHills:
					{
						if (
							IsBiomeVeryCold(static_cast<EMCSBiome>(above)) ||
							IsBiomeVeryCold(static_cast<EMCSBiome>(below)) ||
							IsBiomeVeryCold(static_cast<EMCSBiome>(left))  ||
							IsBiomeVeryCold(static_cast<EMCSBiome>(right))
						)
						{
							biome = biPlains;
						}
						break;
					}  // case biDesert

					case biMesaPlateau:
					case biMesaPlateauF:
					case biMesaPlateauFM:
					case biMesaPlateauM:
					{
						if (
							!isMesaCompatible(above) ||
							!isMesaCompatible(below) ||
							!isMesaCompatible(left)  ||
							!isMesaCompatible(right)
						)
						{
							biome = biDesert;
						}
						break;
					}  // Mesa biomes

					case biJungle:
					case biJungleM:
					{
						if (
							!isJungleCompatible(above) ||
							!isJungleCompatible(below) ||
							!isJungleCompatible(left)  ||
							!isJungleCompatible(right)
						)
						{
							biome = (biome == biJungle) ? biJungleEdge : biJungleEdgeM;
						}
						break;
					}  // Jungle biomes

					case biSwampland:
					case biSwamplandM:
					{
						if (
							IsBiomeNoDownfall(static_cast<EMCSBiome>(above)) ||
							IsBiomeNoDownfall(static_cast<EMCSBiome>(below)) ||
							IsBiomeNoDownfall(static_cast<EMCSBiome>(left))  ||
							IsBiomeNoDownfall(static_cast<EMCSBiome>(right))
						)
						{
							biome = biPlains;
						}
						break;
					}  // Swampland biomes
				}  // switch (biome)

				a_Values[x + z * SizeX] = biome;
			}  // for x
		}  // for z
	}


protected:
	Underlying m_Underlying;


	bool isMesaCompatible(int a_Biome)
	{
		switch (a_Biome)
		{
			case biDesert:
			case biMesa:
			case biMesaBryce:
			case biMesaPlateau:
			case biMesaPlateauF:
			case biMesaPlateauFM:
			case biMesaPlateauM:
			case biOcean:
			case biDeepOcean:
			{
				return true;
			}
			default:
			{
				return false;
			}
		}
	}


	bool isJungleCompatible(int a_Biome)
	{
		switch (a_Biome)
		{
			case biJungle:
			case biJungleM:
			case biJungleEdge:
			case biJungleEdgeM:
			case biJungleHills:
			{
				return true;
			}
			default:
			{
				return false;
			}
		}
	}
};





/** Changes biomes in the parent data into their alternate versions ("M" variants), in such places that
have their alterations set. */
template <int SizeX, int SizeZ = SizeX>
class cIntGenMBiomes:
	public cIntGenWithNoise<SizeX, SizeZ>
{
	typedef cIntGenWithNoise<SizeX, SizeZ> super;

public:
	typedef std::shared_ptr<cIntGen<SizeX, SizeZ>> Underlying;


	cIntGenMBiomes(int a_Seed, Underlying a_Alteration, Underlying a_Underlying):
		super(a_Seed),
		m_Underlying(a_Underlying),
		m_Alteration(a_Alteration)
	{
	}


	virtual void GetInts(int a_MinX, int a_MinZ, typename super::Values & a_Values) override
	{
		// Generate the underlying biomes and the alterations:
		m_Underlying->GetInts(a_MinX, a_MinZ, a_Values);
		super::Values alterations;
		m_Alteration->GetInts(a_MinX, a_MinZ, alterations);

		// Wherever alterations are nonzero, change into alternate biome, if available:
		for (int idx = 0; idx < SizeX * SizeZ; ++idx)
		{
			if (alterations[idx] == 0)
			{
				continue;
			}

			// Ice spikes biome was removed from here, because it was generated way too often
			switch (a_Values[idx])
			{
				case biPlains:               a_Values[idx] = biSunflowerPlains;      break;
				case biDesert:               a_Values[idx] = biDesertM;              break;
				case biExtremeHills:         a_Values[idx] = biExtremeHillsM;        break;
				case biForest:               a_Values[idx] = biFlowerForest;         break;
				case biTaiga:                a_Values[idx] = biTaigaM;               break;
				case biSwampland:            a_Values[idx] = biSwamplandM;           break;
				case biJungle:               a_Values[idx] = biJungleM;              break;
				case biJungleEdge:           a_Values[idx] = biJungleEdgeM;          break;
				case biBirchForest:          a_Values[idx] = biBirchForestM;         break;
				case biBirchForestHills:     a_Values[idx] = biBirchForestHillsM;    break;
				case biRoofedForest:         a_Values[idx] = biRoofedForestM;        break;
				case biColdTaiga:            a_Values[idx] = biColdTaigaM;           break;
				case biMegaSpruceTaiga:      a_Values[idx] = biMegaSpruceTaiga;      break;
				case biMegaSpruceTaigaHills: a_Values[idx] = biMegaSpruceTaigaHills; break;
				case biExtremeHillsPlus:     a_Values[idx] = biExtremeHillsPlusM;    break;
				case biSavanna:              a_Values[idx] = biSavannaM;             break;
				case biSavannaPlateau:       a_Values[idx] = biSavannaPlateauM;      break;
				case biMesa:                 a_Values[idx] = biMesaBryce;            break;
				case biMesaPlateauF:         a_Values[idx] = biMesaPlateauFM;        break;
				case biMesaPlateau:          a_Values[idx] = biMesaBryce;            break;
			}
		}  // for idx - a_Values[] / alterations[]
	}

protected:
	Underlying m_Underlying;
	Underlying m_Alteration;
};