summaryrefslogblamecommitdiffstats
path: root/src/Generating/FinishGen.cpp
blob: 8066a497ed481e97a598a88bd2af428b99c18d63 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764











                                               
                       
                                                                               
                                       
                     
                       




 
                                                  


                                                                                                                                       

                                                  

                                      













                                                                                           
                                                                                
                                
 
                                                                      
 

                                             
 

                                                                           
 

                             
 

                                                   
                                                                           


                                 
 
                                                                                                                     


                                 
 
                                            
                                                                                                                     
                                     
                 
                                                                                          
                 
                                       
                 
                                                                                        
                 
                                         
                 
                                                                                





                 
 
 
                                                                                                                                 
 

                                                   




































                                                                                                                
         
                                                                        
                                                 
                 
                                                                                
                                                         
                         








                                                                                
                                                                                                                       


                                                 

                                                                                             
                                                                                                               



                                                 
                                                                                                    






                                                                                         

                                                                                
                                 
                                                                                   





                                 



 
                                                                                














                                                                                                                                                     
                                              










































                                                                                                                                       
                                                                 



















                                                                                                 
                                                                                                                                                                                             
                                                                                                              



                                                                                                                              
                                                                                                                                                                                                       
























                                                                                                                                                                                                                 
                       
 
                                                             
 
                                                  
         
                                                                        
                                                          
                 


                                                                                       
                                                                                            
                                                                                                                                             
                         

                                         
 
                                                                                                           
                                                                
 



                                         
 
                                                             


                                                                                                                                                     
 
                                 
                         

                                         
 
                                                                     








                                                                                             
                                                                              
                         
                                                                                                  
                                                                                         
                                 


                                                                                                                             

                                 




                                                                                                        






                 
 
                                                                                




































                                                                                
                                                                       




                                                                 
                                                              






                                                                                     
                                                                                  





























                                                                                                                                        
                                                                                                                                










                                                                                           

                             
                                                                                                             











                                                                                                                 
                                                                 














                                                                   

                                                                                 

                                                                                     



                             
 
                                                      
                                                                                






                    
                                                                   

                                            
 
                                                           
                                                  
         
                                                                            
                                                            

                                                          

                                                                                      


                                         
                                                                     




                                                            
                                                                                   




                                                                                                               
 
                                                                    

                                                                                  
                                                                    


                                                   

                                                                                                            

                                                               
                                                                                                             


                                                                    
                                                                                                        


                                                                    
                                                                                                            


                                                                    
                                                                                                              


                                                                                  
                                                                                                                                       
                                         
                                                                                         




                                                                               
                                                                                                                                           


                                                        
 






                                                                                        


                                                                                                             
                                                                                                             
                                                                                           

                                         
                                                                                                      
                                         
                                                                                         





                                                      
                                                         







                           
                                                                  
 





                                             





 
                                                                                







                                                                
                                    










                                                                                    
                                                                



                                                 





                                                                                                   



                                                 


                                                                                                                           













                                                                                            

                  
                                                        





                                                                               
                                                                        
                                                                                    
                         

                                                                                      
                         
 
                                                                                                                                 







                                                                                 






                   
                                                                                

                 
                                                       





                                                          

                                                                                    
                         

                                                                                      
                         














                                                                                  





                   
 
                                                                                
                            
 
                                                                                 

                    
                                                           
         
                                                  



                                  
                                    





 
                                                                  
 



                                                              
         

                                                                                                    
 
                                                         

                                                             
                 
                                          

                                 
 
                                                                
                                                    



                                      
                                                                              
                 
                                             

                                 
 
                                                                              
                                                     
                 
                                 
                 



                                                                        





 
                                                                                

                        
                                                              
 
                                                                         




                                                                                                     
                                                             
                         
                                                                            







                                  
 
                                                                                

                          



                                                                                                                                  







                             
                                                                
 













                                                                                                                                    











                                                                                            




                                                          
                                        








                                                                                                               
                                                            


                                                         



                                                             
                                                          



                                                                      
                                         

                                                                                                               










                                                                                                                  



                                                                        



                                                       
                                                                                                  






                           































                                                                                                                   
                                                                               
















                                                                                                                                                  
 


                                                                   
                                                                                             























                                                                                                               
 
                                                                                

                          
                                                                                                                             






                                                                                                                                          
                              
                            
         
                               




                                                                                                                 
                                  




                                                                                                                       
                            











                                                                                                                         

                                                                                                           
                                                                                                      




                                                                             






                                                                                
                                                                
 
                                                                                                                              




                                    
 
                                          
                                                                                                                    

                                                       
 







                                                                                  
                                                                          

                                                                

                                                           
                                                                                         














                                                                              
                                                                                          


                                                                                                             
                                                                 


                             
 











                                
                                                       
         
                                                                                                    














                                                        
 
                                                        
                                                          





                    




                                                                                                        
                       
 



                                                                         













                                                                                     
                                                                           
                              
                 
                                
                                                                                                                                   
                                                                     
         
                                                                                                                                                
                                                                        
         







                                                               

                                             
                                                                          

                                            
                       




                                                           
                                                                                                                              
                       

         
                                                                                       

                                                

                                                                                                         

                                                                                                                                       
                                                   
                         

                                                                                                                         

                                                                                                                                   

                               

                                                     







                                                                                                                                     




                                                               



                                                                                        
                                                                    
                                                                         
         
                             
         

                                             


                                                            

         
                             
         

                                                    
                                                                                                                                                                                                
         
         

                             
                                                                                   

                             

         


                                                                                                        
 
                                                                        
                                                  
                                                       

                                                                                                                               

                    








                                                                          
                                                

                                             

                                                                                        
 
                                                      

                                           
                                                                    

                              


                                   
                 
                                             
                 
 



                                                                     
                                           
                 
 
                                            




                                   
                 
                                                         
                              
                 
 
                                               




                                   
                 
                                                          
                              
                 

                                                   





                                       
                 
                                                         
                              
                 
 
                                                          




                                  
                 
                                                        
                              
                 
                                                   
                        
                 
                              
                 
         



                                           
 
                                     
         
                                     

         
                                                


                                                                         
                                                                                                
                                           
                                       

                        




 














































































































































































































































































































































































                                                                                                                                                

// FinishGen.cpp

/* Implements the various finishing generators:
	- cFinishGenSnow
	- cFinishGenIce
	- cFinishGenSprinkleFoliage
*/

#include "Globals.h"

#include "FinishGen.h"
#include "../BlockID.h"
#include "../Simulator/FluidSimulator.h"  // for cFluidSimulator::CanWashAway()
#include "../Simulator/FireSimulator.h"
#include "../World.h"
#include "../IniFile.h"





#define DEF_NETHER_WATER_SPRINGS    "0, 1; 255, 1"
#define DEF_NETHER_LAVA_SPRINGS     "0, 0; 30, 0; 31, 50; 120, 50; 127, 0"
#define DEF_OVERWORLD_WATER_SPRINGS "0, 0; 10, 10; 11, 75; 16, 83; 20, 83; 24, 78; 32, 62; 40, 40; 44, 15; 48, 7; 56, 2; 64, 1; 255, 0"
#define DEF_OVERWORLD_LAVA_SPRINGS  "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0"
#define DEF_END_WATER_SPRINGS       "0, 1; 255, 1"
#define DEF_END_LAVA_SPRINGS        "0, 1; 255, 1"
#define DEF_ANIMAL_SPAWN_PERCENT    10
#define DEF_NO_ANIMALS              0





static inline bool IsWater(BLOCKTYPE a_BlockType)
{
	return (a_BlockType == E_BLOCK_STATIONARY_WATER) || (a_BlockType == E_BLOCK_WATER);
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenNetherClumpFoliage:

void cFinishGenNetherClumpFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
	int ChunkX = a_ChunkDesc.GetChunkX();
	int ChunkZ = a_ChunkDesc.GetChunkZ();

	int Val1 = m_Noise.IntNoise2DInt(ChunkX ^ ChunkZ, ChunkZ + ChunkX);
	int Val2 = m_Noise.IntNoise2DInt(ChunkZ ^ ChunkX, ChunkZ - ChunkX);

	int PosX = Val1 % 16;
	int PosZ = Val2 % 16;

	for (int y = 1; y < cChunkDef::Height; y++)
	{
		if (a_ChunkDesc.GetBlockType(PosX, y, PosZ) != E_BLOCK_AIR)
		{
			continue;
		}

		if (!cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(PosX, y - 1, PosZ)))  // Only place on solid blocks
		{
			continue;
		}

		// Choose what block to use.
		NOISE_DATATYPE BlockType = m_Noise.IntNoise3D(static_cast<int>(ChunkX), y, static_cast<int>(ChunkZ));
		if (BlockType < -0.7)
		{
			TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_BROWN_MUSHROOM);
		}
		else if (BlockType < 0)
		{
			TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_RED_MUSHROOM);
		}
		else if (BlockType < 0.7)
		{
			TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_FIRE);
		}
	}
}





void cFinishGenNetherClumpFoliage::TryPlaceClump(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block)
{
	bool IsFireBlock = a_Block == E_BLOCK_FIRE;

	int MinX = a_RelX - 4;
	if (MinX < 0)  // Check if the coordinate is outside the chunk. If it it then adjust it.
	{
		MinX = 0;
	}

	int MaxX = a_RelX + 4;
	if (MaxX > cChunkDef::Width)  // Check if the coordinate is outside the chunk. If it it then adjust it.
	{
		MaxX = cChunkDef::Width;
	}

	int MinZ = a_RelZ - 4;
	if (MinZ < 0)  // Check if the coordinate is outside the chunk. If it it then adjust it.
	{
		MinZ = 0;
	}

	int MaxZ = a_RelZ + 4;
	if (MaxZ > cChunkDef::Width)  // Check if the coordinate is outside the chunk. If it it then adjust it.
	{
		MaxZ = cChunkDef::Width;
	}

	int MinY = a_RelY - 2;
	if (MinY < 0)  // Check if the coordinate is outside the chunk. If it it then adjust it.
	{
		MinY = 0;
	}

	int MaxY = a_RelY + 2;
	if (MaxY > cChunkDef::Height)  // Check if the coordinate is outside the chunk. If it it then adjust it.
	{
		MaxY = cChunkDef::Height;
	}

	for (int x = MinX; x < MaxX; x++)
	{
		int xx = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x;
		for (int z = MinZ; z < MaxZ; z++)
		{
			int zz = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
			for (int y = MinY; y < MaxY; y++)
			{
				if (
					((x < 0) || (x >= cChunkDef::Width)) ||
					((y < 0) || (y >= cChunkDef::Height)) ||
					((z < 0) || (z >= cChunkDef::Width))
					)
				{
					continue;
				}

				if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)  // Don't replace non air blocks.
				{
					continue;
				}

				BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, y - 1, z);
				if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow))  // Only place on solid blocks
				{
					continue;
				}

				if (IsFireBlock)  // don't place fire on non-forever burning blocks.
				{
					if (!cFireSimulator::DoesBurnForever(BlockBelow))
					{
						continue;
					}
				}

				NOISE_DATATYPE Val = m_Noise.IntNoise2D(xx, zz);
				if (Val < -0.5)
				{
					a_ChunkDesc.SetBlockType(x, y, z, a_Block);
				}
			}
		}
	}
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenGlowStone:

void cFinishGenGlowStone::GenFinish(cChunkDesc & a_ChunkDesc)
{
	int ChunkX = a_ChunkDesc.GetChunkX();
	int ChunkZ = a_ChunkDesc.GetChunkZ();
	
	// Change the number of attempts to create a vein depending on the maximum height of the chunk. A standard Nether could have 5 veins at most.
	int NumGlowStone = m_Noise.IntNoise2DInt(ChunkX, ChunkZ) % a_ChunkDesc.GetMaxHeight() / 23;

	for (int i = 1; i <= NumGlowStone; i++)
	{
		// The maximum size for a string of glowstone can get 3 - 5 blocks long
		int Size = 3 + m_Noise.IntNoise3DInt(ChunkX, i, ChunkZ) % 3;

		// Generate X / Z coordinates.
		int X = Size + (m_Noise.IntNoise2DInt(i, Size) % (cChunkDef::Width - Size * 2));
		int Z = Size + (m_Noise.IntNoise2DInt(X, i)    % (cChunkDef::Width - Size * 2));
		
		int Height = a_ChunkDesc.GetHeight(X, Z);
		for (int y = Height; y > Size; y--)
		{
			if (!cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(X, y, Z)))
			{
				// Current block isn't solid, bail out
				continue;
			}

			if (a_ChunkDesc.GetBlockType(X, y - 1, Z) != E_BLOCK_AIR)
			{
				// The block below isn't air, bail out
				continue;
			}

			if ((m_Noise.IntNoise3DInt(X, y, Z) % 100) < 95)
			{
				// Have a 5% chance of creating the glowstone
				continue;
			}

			TryPlaceGlowstone(a_ChunkDesc, X, y, Z, Size, 5 + m_Noise.IntNoise3DInt(X, y, Z) % 7);
			break;
		}
	}
}





void cFinishGenGlowStone::TryPlaceGlowstone(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, int a_Size, int a_NumStrings)
{
	// The starting point of every glowstone string
	Vector3i StartPoint = Vector3i(a_RelX, a_RelY, a_RelZ);
	
	// Array with possible directions for a string of glowstone to go to.
	const Vector3i AvailableDirections[] =
	{
		{ -1,  0,  0 }, { 1, 0, 0 },
		{  0, -1,  0 },  // Don't let the glowstone go up
		{  0,  0, -1 }, { 0, 0, 1 },
		
		// Diagonal direction. Only X or Z with Y.
		// If all were changed the glowstone string looks awkward
		{ 0, -1,  1 }, {  1, -1, 0 },
		{ 0, -1, -1 }, { -1, -1, 0 },

	};

	for (int i = 1; i <= a_NumStrings; i++)
	{
		// The current position of the string that is being generated
		Vector3i CurrentPos = Vector3i(StartPoint);

		// A vector where the previous direction of a glowstone string is stored.
		// This is used to make the strings change direction when going one block further
		Vector3i PreviousDirection = Vector3i();

		for (int j = 0; j < a_Size; j++)
		{
			Vector3i Direction = AvailableDirections[static_cast<size_t>(m_Noise.IntNoise3DInt(CurrentPos.x, CurrentPos.y * i, CurrentPos.z)) % ARRAYCOUNT(AvailableDirections)];
			int Attempts = 2;  // multiply by 1 would make no difference, so multiply by 2 instead

			while (Direction.Equals(PreviousDirection))
			{
				// To make the glowstone branches look better we want to make the direction change every time.
				Direction = AvailableDirections[static_cast<size_t>(m_Noise.IntNoise3DInt(CurrentPos.x, CurrentPos.y * i * Attempts, CurrentPos.z)) % ARRAYCOUNT(AvailableDirections)];
				Attempts++;
			}

			// Update the previous direction variable
			PreviousDirection = Direction;

			// Update the position of the glowstone string
			CurrentPos += Direction;
			if (cBlockInfo::IsSolid(a_ChunkDesc.GetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z)) && (a_ChunkDesc.GetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z) != E_BLOCK_GLOWSTONE))
			{
				// The glowstone hit something solid, and it wasn't glowstone. Stop the string.
				break;
			}

			// Place a glowstone block.
			a_ChunkDesc.SetBlockType(CurrentPos.x, CurrentPos.y, CurrentPos.z, E_BLOCK_GLOWSTONE);
		}
	}
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenTallGrass:

void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc)
{
	for (int x = 0; x < cChunkDef::Width; x++)
	{
		int xx = x + a_ChunkDesc.GetChunkX() * cChunkDef::Width;
		for (int z = 0; z < cChunkDef::Width; z++)
		{
			int zz = z + a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
			int BiomeDensity = GetBiomeDensity(a_ChunkDesc.GetBiome(x, z));

			// Choose if we want to place long grass here. If not then bail out:
			if ((m_Noise.IntNoise2DInt(xx + m_Noise.IntNoise1DInt(xx), zz + m_Noise.IntNoise1DInt(zz)) / 7 % 100) > BiomeDensity)
			{
				continue;
			}

			// Get the top block + 1. This is the place where the grass would finaly be placed:
			int y = a_ChunkDesc.GetHeight(x, z) + 1;

			if (y >= 255)
			{
				continue;
			}

			// Check if long grass can be placed:
			if (
				(a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) ||
				((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_GRASS) && (a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_DIRT))

				)
			{
				continue;
			}

			// Choose what long grass meta we should use:
			int GrassType = m_Noise.IntNoise2DInt(xx * 50, zz * 50) / 7 % 100;
			if (GrassType < 60)
			{
				a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_TALL_GRASS, 1);
			}
			else if (GrassType < 90)
			{
				a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_TALL_GRASS, 2);
			}
			else if (!IsBiomeVeryCold(a_ChunkDesc.GetBiome(x, z)))
			{
				// If double long grass we have to choose what type we should use:
				if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR)
				{
					NIBBLETYPE Meta = (m_Noise.IntNoise2DInt(xx * 100, zz * 100) / 7 % 100) > 25 ? 2 : 3;
					a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_BIG_FLOWER, Meta);
					a_ChunkDesc.SetBlockTypeMeta(x, y + 1, z, E_BLOCK_BIG_FLOWER, 8);
				}
			}
			else
			{
				NIBBLETYPE meta = (m_Noise.IntNoise2DInt(xx * 50, zz * 50) / 7 % 2) + 1;
				a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_TALL_GRASS, meta);
			}
		}
	}
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenVines

bool cFinishGenVines::IsJungleVariant(EMCSBiome a_Biome)
{
	switch (a_Biome)
	{
		case biJungle:
		case biJungleEdge:
		case biJungleEdgeM:
		case biJungleHills:
		case biJungleM:
		{
			return true;
		}
	}

	return false;
}





void cFinishGenVines::GenFinish(cChunkDesc & a_ChunkDesc)
{
	for (int x = 0; x < cChunkDef::Width; x++)
	{
		int xx = x + a_ChunkDesc.GetChunkX() * cChunkDef::Width;
		for (int z = 0; z < cChunkDef::Width; z++)
		{
			int zz = z + a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
			if (!IsJungleVariant(a_ChunkDesc.GetBiome(x, z)))
			{
				// Current biome isn't a jungle
				continue;
			}

			if ((m_Noise.IntNoise2DInt(xx, zz) % 101) < 50)
			{
				continue;
			}

			int Height = a_ChunkDesc.GetHeight(x, z);
			for (int y = Height; y > m_Level; y--)
			{
				if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR)
				{
					// Can't place vines in non-air blocks
					continue;
				}

				if ((m_Noise.IntNoise3DInt(xx, y, zz) % 101) < 50)
				{
					continue;
				}

				std::vector<NIBBLETYPE> Places;
				if ((x + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x + 1, y, z)))
				{
					Places.push_back(8);
				}

				if ((x - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x - 1, y, z)))
				{
					Places.push_back(2);
				}

				if ((z + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z + 1)))
				{
					Places.push_back(1);
				}

				if ((z - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z - 1)))
				{
					Places.push_back(4);
				}

				if (Places.size() == 0)
				{
					continue;
				}

				NIBBLETYPE Meta = Places[static_cast<size_t>(m_Noise.IntNoise3DInt(xx, y, zz)) % Places.size()];
				a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_VINES, Meta);
			}
		}
	}
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenSprinkleFoliage:

bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ)
{
	// We'll be doing comparison to neighbors, so require the coords to be 1 block away from the chunk edges:
	if (
		(a_RelX < 1) || (a_RelX >= cChunkDef::Width  - 1) ||
		(a_RelY < 1) || (a_RelY >= cChunkDef::Height - 2) ||
		(a_RelZ < 1) || (a_RelZ >= cChunkDef::Width  - 1)
	)
	{
		return false;
	}

	// Only allow dirt, grass or sand below sugarcane:
	switch (a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ))
	{
		case E_BLOCK_DIRT:
		case E_BLOCK_GRASS:
		case E_BLOCK_SAND:
		{
			break;
		}
		default:
		{
			return false;
		}
	}

	// Water is required next to the block below the sugarcane:
	if (
		!IsWater(a_ChunkDesc.GetBlockType(a_RelX - 1, a_RelY, a_RelZ)) &&
		!IsWater(a_ChunkDesc.GetBlockType(a_RelX + 1, a_RelY, a_RelZ)) &&
		!IsWater(a_ChunkDesc.GetBlockType(a_RelX,     a_RelY, a_RelZ - 1)) &&
		!IsWater(a_ChunkDesc.GetBlockType(a_RelX,     a_RelY, a_RelZ + 1))
	)
	{
		return false;
	}

	// All conditions met, place a sugarcane here:
	a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE);
	return true;
}





void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
{
	// Generate small foliage (1-block):

	// TODO: Update heightmap with 1-block-tall foliage
	for (int z = 0; z < cChunkDef::Width; z++)
	{
		int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
		const float zz = static_cast<float>(BlockZ);
		for (int x = 0; x < cChunkDef::Width; x++)
		{
			int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width + x;
			if (((m_Noise.IntNoise2DInt(BlockX, BlockZ) / 8) % 128) < 124)
			{
				continue;
			}
			HEIGHTTYPE Top = a_ChunkDesc.GetHeight(x, z);
			if (Top > 250)
			{
				// Nothing grows above Y=250
				continue;
			}
			if (a_ChunkDesc.GetBlockType(x, Top + 1, z) != E_BLOCK_AIR)
			{
				// Space already taken by something else, don't grow here
				// WEIRD, since we're using heightmap, so there should NOT be anything above it
				continue;
			}

			const float xx = static_cast<float>(BlockX);
			float val1 = m_Noise.CubicNoise2D(xx * 0.1f,  zz * 0.1f);
			float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f);
			switch (a_ChunkDesc.GetBlockType(x, Top, z))
			{
				case E_BLOCK_GRASS:
				{
					float val3 = m_Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10);
					float val4 = m_Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20);
					if (val1 + val2 > 0.2f)
					{
						a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_YELLOW_FLOWER);
					}
					else if (val2 + val3 > 0.2f)
					{
						a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_ROSE);
					}
					else if (val3 + val4 > 0.2f)
					{
						a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_RED_MUSHROOM);
					}
					else if (val1 + val4 > 0.2f)
					{
						a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_BROWN_MUSHROOM);
					}
					else if (val1 + val2 + val3 + val4 < -0.1)
					{
						a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_TALL_GRASS, E_META_TALL_GRASS_GRASS);
					}
					else if (TryAddSugarcane(a_ChunkDesc, x, Top, z))
					{
						++Top;
					}
					else if ((val1 > 0.5) && (val2 < -0.5))
					{
						a_ChunkDesc.SetBlockTypeMeta(x, ++Top, z, E_BLOCK_PUMPKIN, static_cast<int>(val3 * 8) % 4);
					}
					break;
				}  // case E_BLOCK_GRASS

				case E_BLOCK_SAND:
				{
					int y = Top + 1;
					if (
						(x > 0) && (x < cChunkDef::Width - 1) &&
						(z > 0) && (z < cChunkDef::Width - 1) &&
						(val1 + val2 > 0.5f) &&
						(a_ChunkDesc.GetBlockType(x + 1, y, z)     == E_BLOCK_AIR) &&
						(a_ChunkDesc.GetBlockType(x - 1, y, z)     == E_BLOCK_AIR) &&
						(a_ChunkDesc.GetBlockType(x,     y, z + 1) == E_BLOCK_AIR) &&
						(a_ChunkDesc.GetBlockType(x,     y, z - 1) == E_BLOCK_AIR) &&
						IsDesertVariant(a_ChunkDesc.GetBiome(x, z))
					)
					{
						a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS);
					}
					else if (TryAddSugarcane(a_ChunkDesc, x, Top, z))
					{
						++Top;
					}
					break;
				}
			}  // switch (TopBlock)
			a_ChunkDesc.SetHeight(x, z, Top);
		}  // for y
	}  // for z
}





bool cFinishGenSprinkleFoliage::IsDesertVariant(EMCSBiome a_Biome)
{
	return
	(
		(a_Biome == biDesertHills) ||
		(a_Biome == biDesert) ||
		(a_Biome == biDesertM)
	);
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenSoulsandRims

void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc)
{
	int ChunkX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
	int ChunkZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
	HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();

	for (int x = 0; x < 16; x++)
	{
		int xx = ChunkX + x;
		for (int z = 0; z < 16; z++)
		{
			int zz = ChunkZ + z;

			// Place soulsand rims when netherrack gets thin
			for (int y = 2; y < MaxHeight - 2; y++)
			{
				// The current block is air. Let's bail ut.
				BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z);
				if (Block != E_BLOCK_NETHERRACK)
				{
					continue;
				}

				if (
					((a_ChunkDesc.GetBlockType(x, y + 1, z) != E_BLOCK_AIR) &&
					( a_ChunkDesc.GetBlockType(x, y + 2, z) != E_BLOCK_AIR)) ||
					((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_AIR) &&
					( a_ChunkDesc.GetBlockType(x, y - 2, z) != E_BLOCK_AIR))
				)
				{
					continue;
				}

				NOISE_DATATYPE NoiseX = (static_cast<NOISE_DATATYPE>(xx)) / 32;
				NOISE_DATATYPE NoiseY = (static_cast<NOISE_DATATYPE>(zz)) / 32;
				NOISE_DATATYPE CompBlock = m_Noise.CubicNoise3D(NoiseX, static_cast<float>(y) / 4, NoiseY);
				if (CompBlock < 0)
				{
					a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SOULSAND);
				}
			}
		}
	}
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenSnow:

void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc)
{
	// Add a snow block in snowy biomes onto blocks that can be snowed over
	for (int z = 0; z < cChunkDef::Width; z++)
	{
		for (int x = 0; x < cChunkDef::Width; x++)
		{
			HEIGHTTYPE Height = a_ChunkDesc.GetHeight(x, z);
			if (GetSnowStartHeight(a_ChunkDesc.GetBiome(x, z)) > Height)
			{
				// Height isn't high enough for snow to start forming.
				continue;
			}

			if (!cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)) || (Height >= cChunkDef::Height - 1))
			{
				// The top block can't be snown over.
				continue;
			}

			a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
			a_ChunkDesc.SetHeight(x, z, Height + 1);
		}  // for x
	}  // for z
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenIce:

void cFinishGenIce::GenFinish(cChunkDesc & a_ChunkDesc)
{
	// Turn surface water into ice in icy biomes
	for (int z = 0; z < cChunkDef::Width; z++)
	{
		for (int x = 0; x < cChunkDef::Width; x++)
		{
			int Height = a_ChunkDesc.GetHeight(x, z);
			if (GetSnowStartHeight(a_ChunkDesc.GetBiome(x, z)) > Height)
			{
				// Height isn't high enough for snow to start forming.
				continue;
			}

			if (!IsBlockWater(a_ChunkDesc.GetBlockType(x, Height, z)))
			{
				// The block isn't a water block.
				continue;
			}

			if (a_ChunkDesc.GetBlockMeta(x, Height, z) != 0)
			{
				// The water block isn't a source block.
				continue;
			}

			a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE);
		}  // for x
	}  // for z
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenSingleTopBlock:

int cFinishGenSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap)
{
	int res = 0;
	for (size_t i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
	{
		if (IsAllowedBiome(a_BiomeMap[i]))
		{
			res++;
		}
	}  // for i - a_BiomeMap[]
	return m_Amount * res / 256;
}





void cFinishGenSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
{
	int NumToGen = GetNumToGen(a_ChunkDesc.GetBiomeMap());
	int ChunkX = a_ChunkDesc.GetChunkX();
	int ChunkZ = a_ChunkDesc.GetChunkZ();
	for (int i = 0; i < NumToGen; i++)
	{
		int x = (m_Noise.IntNoise3DInt(ChunkX + ChunkZ, ChunkZ, i) / 13) % cChunkDef::Width;
		int z = (m_Noise.IntNoise3DInt(ChunkX - ChunkZ, i, ChunkZ) / 11) % cChunkDef::Width;

		// Place the block at {x, z} if possible:
		EMCSBiome Biome = a_ChunkDesc.GetBiome(x, z);
		if (!IsAllowedBiome(Biome))
		{
			// Incorrect biome
			continue;
		}

		HEIGHTTYPE Height = a_ChunkDesc.GetHeight(x, z);
		if (Height >= cChunkDef::Height - 1)
		{
			// Too high up
			continue;
		}
		if (a_ChunkDesc.GetBlockType(x, Height + 1, z) != E_BLOCK_AIR)
		{
			// Not an empty block
			continue;
		}

		BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, Height, z);
		if (!IsAllowedBlockBelow(BlockBelow))
		{
			continue;
		}

		a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType);
		a_ChunkDesc.SetHeight(x, z, Height + 1);
	}
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenBottomLava:

void cFinishGenBottomLava::GenFinish(cChunkDesc & a_ChunkDesc)
{
	cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes();
	for (int y = m_Level; y > 0; y--)
	{
		for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
		{
			int Index = cChunkDef::MakeIndexNoCheck(x, y, z);
			if (BlockTypes[Index] == E_BLOCK_AIR)
			{
				BlockTypes[Index] = E_BLOCK_STATIONARY_LAVA;
			}
		}  // for x, for z
	}  // for y
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenPreSimulator:

cFinishGenPreSimulator::cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava) :
	m_PreSimulateFallingBlocks(a_PreSimulateFallingBlocks),
	m_PreSimulateWater(a_PreSimulateWater),
	m_PreSimulateLava(a_PreSimulateLava)
{
	// Nothing needed yet
}





void cFinishGenPreSimulator::GenFinish(cChunkDesc & a_ChunkDesc)
{
	if (m_PreSimulateFallingBlocks)
	{
		CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
	}

	if (m_PreSimulateWater)
	{
		StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
	}

	if (m_PreSimulateLava)
	{
		StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
	}
	// TODO: other operations
}





void cFinishGenPreSimulator::CollapseSandGravel(
	cChunkDef::BlockTypes & a_BlockTypes,    // Block types to read and change
	cChunkDef::HeightMap & a_HeightMap       // Height map to update by the current data
)
{
	for (int z = 0; z < cChunkDef::Width; z++)
	{
		for (int x = 0; x < cChunkDef::Width; x++)
		{
			int LastY = -1;
			int HeightY = 0;
			for (int y = 0; y < cChunkDef::Height; y++)
			{
				BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z);
				switch (Block)
				{
					default:
					{
						// Set the last block onto which stuff can fall to this height:
						LastY = y;
						HeightY = y;
						break;
					}
					case E_BLOCK_AIR:
					{
						// Do nothing
						break;
					}
					case E_BLOCK_FIRE:
					case E_BLOCK_WATER:
					case E_BLOCK_STATIONARY_WATER:
					case E_BLOCK_LAVA:
					case E_BLOCK_STATIONARY_LAVA:
					{
						// Do nothing, only remember this height as potentially highest
						HeightY = y;
						break;
					}
					case E_BLOCK_SAND:
					case E_BLOCK_GRAVEL:
					{
						if (LastY < y - 1)
						{
							cChunkDef::SetBlock(a_BlockTypes, x, LastY + 1, z, Block);
							cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
						}
						LastY++;
						if (LastY > HeightY)
						{
							HeightY = LastY;
						}
						break;
					}
				}  // switch (GetBlock)
			}  // for y
			cChunkDef::SetHeight(a_HeightMap, x, z, static_cast<HEIGHTTYPE>(HeightY));
		}  // for x
	}  // for z
}





void cFinishGenPreSimulator::StationarizeFluid(
	cChunkDef::BlockTypes & a_BlockTypes,    // Block types to read and change
	cChunkDef::HeightMap & a_HeightMap,      // Height map to read
	BLOCKTYPE a_Fluid,
	BLOCKTYPE a_StationaryFluid
)
{
	// Turn fluid in the middle to stationary, unless it has air or washable block next to it:
	for (int z = 1; z < cChunkDef::Width - 1; z++)
	{
		for (int x = 1; x < cChunkDef::Width - 1; x++)
		{
			for (int y = cChunkDef::GetHeight(a_HeightMap, x, z); y >= 0; y--)
			{
				BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, x, y, z);
				if ((Block != a_Fluid) && (Block != a_StationaryFluid))
				{
					continue;
				}
				static const struct
				{
					int x, y, z;
				} Coords[] =
				{
					{1, 0, 0},
					{-1, 0, 0},
					{0, 0, 1},
					{0, 0, -1},
					{0, -1, 0}
				} ;
				BLOCKTYPE BlockToSet = a_StationaryFluid;  // By default, don't simulate this block
				for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
				{
					if ((y == 0) && (Coords[i].y < 0))
					{
						continue;
					}
					BLOCKTYPE Neighbor = cChunkDef::GetBlock(a_BlockTypes, x + Coords[i].x, y + Coords[i].y, z + Coords[i].z);
					if ((Neighbor == E_BLOCK_AIR) || cFluidSimulator::CanWashAway(Neighbor))
					{
						// There is an air / washable neighbor, simulate this block
						BlockToSet = a_Fluid;
						break;
					}
				}  // for i - Coords[]
				cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockToSet);
			}  // for y
		}  // for x
	}  // for z

	// Turn fluid at the chunk edges into non-stationary fluid:
	for (int y = 0; y < cChunkDef::Height; y++)
	{
		for (int i = 0; i < cChunkDef::Width; i++)  // i stands for both x and z here
		{
			if (cChunkDef::GetBlock(a_BlockTypes, 0, y, i) == a_StationaryFluid)
			{
				cChunkDef::SetBlock(a_BlockTypes, 0, y, i, a_Fluid);
			}
			if (cChunkDef::GetBlock(a_BlockTypes, i, y, 0) == a_StationaryFluid)
			{
				cChunkDef::SetBlock(a_BlockTypes, i, y, 0, a_Fluid);
			}
			if (cChunkDef::GetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i) == a_StationaryFluid)
			{
				cChunkDef::SetBlock(a_BlockTypes, cChunkDef::Width - 1, y, i, a_Fluid);
			}
			if (cChunkDef::GetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1) == a_StationaryFluid)
			{
				cChunkDef::SetBlock(a_BlockTypes, i, y, cChunkDef::Width - 1, a_Fluid);
			}
		}
	}
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenFluidSprings:

cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension) :
	m_Noise(a_Seed + a_Fluid * 100),  // Need to take fluid into account, otherwise water and lava springs generate next to each other
	m_HeightDistribution(255),
	m_Fluid(a_Fluid)
{
	bool IsWater = (a_Fluid == E_BLOCK_WATER);
	AString SectionName = IsWater ? "WaterSprings" : "LavaSprings";
	AString DefaultHeightDistribution;
	int DefaultChance = 0;
	switch (a_Dimension)
	{
		case dimNether:
		{
			DefaultHeightDistribution = IsWater ? DEF_NETHER_WATER_SPRINGS : DEF_NETHER_LAVA_SPRINGS;
			DefaultChance = IsWater ? 0 : 15;
			break;
		}
		case dimOverworld:
		{
			DefaultHeightDistribution = IsWater ? DEF_OVERWORLD_WATER_SPRINGS : DEF_OVERWORLD_LAVA_SPRINGS;
			DefaultChance = IsWater ? 24 : 9;
			break;
		}
		case dimEnd:
		{
			DefaultHeightDistribution = IsWater ? DEF_END_WATER_SPRINGS : DEF_END_LAVA_SPRINGS;
			DefaultChance = 0;
			break;
		}
		default:
		{
			ASSERT(!"Unhandled world dimension");
			break;
		}
	}  // switch (dimension)
	AString HeightDistribution = a_IniFile.GetValueSet(SectionName, "HeightDistribution", DefaultHeightDistribution);
	if (!m_HeightDistribution.SetDefString(HeightDistribution) || (m_HeightDistribution.GetSum() <= 0))
	{
		LOGWARNING("[%sSprings]: HeightDistribution is invalid, using the default of \"%s\".",
			(a_Fluid == E_BLOCK_WATER) ? "Water" : "Lava",
			DefaultHeightDistribution.c_str()
		);
		m_HeightDistribution.SetDefString(DefaultHeightDistribution);
	}
	m_Chance = a_IniFile.GetValueSetI(SectionName, "Chance", DefaultChance);
}





void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc)
{
	int ChanceRnd = (m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 512, 256 * a_ChunkDesc.GetChunkZ()) / 13) % 100;
	if (ChanceRnd > m_Chance)
	{
		// Not in this chunk
		return;
	}

	// Get the height at which to try:
	int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11;
	Height %= m_HeightDistribution.GetSum();
	Height = m_HeightDistribution.MapValue(Height);

	// Try adding the spring at the height, if unsuccessful, move lower:
	for (int y = Height; y > 1; y--)
	{
		// TODO: randomize the order in which the coords are being checked
		for (int z = 1; z < cChunkDef::Width - 1; z++)
		{
			for (int x = 1; x < cChunkDef::Width - 1; x++)
			{
				switch (a_ChunkDesc.GetBlockType(x, y, z))
				{
					case E_BLOCK_NETHERRACK:
					case E_BLOCK_STONE:
					{
						if (TryPlaceSpring(a_ChunkDesc, x, y, z))
						{
							// Succeeded, bail out
							return;
						}
					}
				}  // switch (BlockType)
			}  // for x
		}  // for y
	}  // for y
}





bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z)
{
	// In order to place a spring, it needs exactly one of the XZ neighbors or a below neighbor to be air
	// Also, its neighbor on top of it must be non-air
	if (a_ChunkDesc.GetBlockType(x, y + 1, z) == E_BLOCK_AIR)
	{
		return false;
	}

	static const struct
	{
		int x, y, z;
	} Coords[] =
	{
		{-1,  0,  0},
		{ 1,  0,  0},
		{ 0, -1,  0},
		{ 0,  0, -1},
		{ 0,  0,  1},
	} ;
	int NumAirNeighbors = 0;
	for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
	{
		switch (a_ChunkDesc.GetBlockType(x + Coords[i].x, y + Coords[i].y, z + Coords[i].z))
		{
			case E_BLOCK_AIR:
			{
				NumAirNeighbors += 1;
				if (NumAirNeighbors > 1)
				{
					return false;
				}
			}
		}
	}
	if (NumAirNeighbors == 0)
	{
		return false;
	}

	// Has exactly one air neighbor, place a spring:
	a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0);
	return true;
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenPassiveMobs:

cFinishGenPassiveMobs::cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension) :
	m_Noise(a_Seed)
{
	AString SectionName = "Animals";
	int DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
	switch (a_Dimension)
	{
		case dimOverworld:
		{
			DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT;
			break;
		}
		case dimNether:
		case dimEnd:  // No nether or end animals (currently)
		{
			DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
			break;
		}
		default:
		{
			ASSERT(!"Unhandled world dimension");
			DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS;
			break;
		}
	}  // switch (dimension)
	m_AnimalProbability = a_IniFile.GetValueSetI(SectionName, "AnimalSpawnChunkPercentage", DefaultAnimalSpawnChunkPercentage);
	if ((m_AnimalProbability < 0) || (m_AnimalProbability > 100))
	{
		LOGWARNING("[Animals]: AnimalSpawnChunkPercentage is invalid, using the default of \"%d\".", DefaultAnimalSpawnChunkPercentage);
		m_AnimalProbability = DefaultAnimalSpawnChunkPercentage;
	}
}





void cFinishGenPassiveMobs::GenFinish(cChunkDesc & a_ChunkDesc)
{
	int chunkX = a_ChunkDesc.GetChunkX();
	int chunkZ = a_ChunkDesc.GetChunkZ();
	int ChanceRnd = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % 100;
	if (ChanceRnd > m_AnimalProbability)
	{
		return;
	}

	eMonsterType RandomMob = GetRandomMob(a_ChunkDesc);
	if (RandomMob == mtInvalidType)
	{
		// No mobs here. Don't send an error, because if the biome was a desert it would return mtInvalidType as well.
		return;
	}

	// Try spawning a pack center 10 times, should get roughly the same probability
	for (int Tries = 0; Tries < 10; Tries++)
	{
		int PackCenterX = (m_Noise.IntNoise2DInt(chunkX + chunkZ, Tries) / 7) % cChunkDef::Width;
		int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries) / 7) % cChunkDef::Width;
		if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob))
		{
			for (int i = 0; i < 3; i++)
			{
				int OffsetX = (m_Noise.IntNoise2DInt(chunkX + chunkZ + i, Tries) / 7) % cChunkDef::Width;
				int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries + i) / 7) % cChunkDef::Width;
				TrySpawnAnimals(a_ChunkDesc, OffsetX, a_ChunkDesc.GetHeight(OffsetX, OffsetZ), OffsetZ, RandomMob);
			}
			return;

		}  // if pack center spawn successful
	}  // for tries
}





bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, eMonsterType AnimalToSpawn)
{
	if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0))
	{
		return false;
	}

	BLOCKTYPE BlockAtHead    = a_ChunkDesc.GetBlockType(a_RelX, a_RelY + 1, a_RelZ);
	BLOCKTYPE BlockAtFeet    = a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ);
	BLOCKTYPE BlockUnderFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY - 1, a_RelZ);

	// Check block below (opaque, grass, water), and above (air)
	if ((AnimalToSpawn == mtSquid) && (BlockAtFeet != E_BLOCK_WATER))
	{
		return false;
	}
	if (
		(AnimalToSpawn != mtSquid) &&
		(BlockAtHead != E_BLOCK_AIR) &&
		(BlockAtFeet != E_BLOCK_AIR) &&
		(!cBlockInfo::IsTransparent(BlockUnderFeet))
	)
	{
		return false;
	}
	if (
		(BlockUnderFeet != E_BLOCK_GRASS) &&
		((AnimalToSpawn == mtWolf) || (AnimalToSpawn == mtRabbit) || (AnimalToSpawn == mtCow) || (AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig))
	)
	{
		return false;
	}
	if ((AnimalToSpawn == mtMooshroom) && (BlockUnderFeet != E_BLOCK_MYCELIUM))
	{
		return false;
	}

	double AnimalX = static_cast<double>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX + 0.5);
	double AnimalY = a_RelY;
	double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5);

	cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn);
	NewMob->SetHealth(NewMob->GetMaxHealth());
	NewMob->SetPosition(AnimalX, AnimalY, AnimalZ);
	a_ChunkDesc.GetEntities().push_back(NewMob);
	LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ);

	return true;
}





eMonsterType cFinishGenPassiveMobs::GetRandomMob(cChunkDesc & a_ChunkDesc)
{

	std::set<eMonsterType> ListOfSpawnables;
	int chunkX = a_ChunkDesc.GetChunkX();
	int chunkZ = a_ChunkDesc.GetChunkZ();
	int x = (m_Noise.IntNoise2DInt(chunkX, chunkZ + 10) / 7) % cChunkDef::Width;
	int z = (m_Noise.IntNoise2DInt(chunkX + chunkZ, chunkZ) / 7) % cChunkDef::Width;

	// Check biomes first to get a list of animals
	switch (a_ChunkDesc.GetBiome(x, z))
	{
		// No animals in deserts or non-overworld dimensions
		case biNether:
		case biEnd:
		case biDesertHills:
		case biDesert:
		case biDesertM:
		{
			return mtInvalidType;
		}

		// Mooshroom only - no other mobs on mushroom islands
		case biMushroomIsland:
		case biMushroomShore:
		{
			return mtMooshroom;
		}

		// Add squid in ocean biomes
		case biOcean:
		case biFrozenOcean:
		case biFrozenRiver:
		case biRiver:
		case biDeepOcean:
		{
			ListOfSpawnables.insert(mtSquid);
			break;
		}

		// Add ocelots in jungle biomes
		case biJungle:
		case biJungleHills:
		case biJungleEdge:
		case biJungleM:
		case biJungleEdgeM:
		{
			ListOfSpawnables.insert(mtOcelot);
			break;
		}

		// Add horses in plains-like biomes
		case biPlains:
		case biSunflowerPlains:
		case biSavanna:
		case biSavannaPlateau:
		case biSavannaM:
		case biSavannaPlateauM:
		{
			ListOfSpawnables.insert(mtHorse);
			break;
		}

		// Add wolves in forest and spruce forests
		case biForest:
		case biTaiga:
		case biMegaTaiga:
		case biColdTaiga:
		case biColdTaigaM:
		{
			ListOfSpawnables.insert(mtWolf);
			break;
		}
		// Nothing special about this biome
		default:
		{
			break;
		}
	}
	ListOfSpawnables.insert(mtChicken);
	ListOfSpawnables.insert(mtCow);
	ListOfSpawnables.insert(mtPig);
	ListOfSpawnables.insert(mtSheep);

	if (ListOfSpawnables.empty())
	{
		return mtInvalidType;
	}

	auto MobIter = ListOfSpawnables.begin();
	using diff_type =
		std::iterator_traits<decltype(MobIter)>::difference_type;
	diff_type RandMob = static_cast<diff_type>
		(static_cast<size_t>(m_Noise.IntNoise2DInt(chunkX - chunkZ + 2, chunkX + 5) / 7)
		% ListOfSpawnables.size());
	std::advance(MobIter, RandMob);

	return *MobIter;
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenOres:

void cFinishGenOres::GenFinish(cChunkDesc & a_ChunkDesc)
{
	int seq = 1;

	// Generate the ores from the ore list.
	for (const auto & ore: m_OreInfos)
	{
		GenerateOre(
			a_ChunkDesc,
			ore.m_BlockType, ore.m_BlockMeta,
			ore.m_MaxHeight, ore.m_NumNests, ore.m_NestSize,
			seq
		);
		seq++;
	}
}





const cFinishGenOres::OreInfos & cFinishGenOres::DefaultOverworldOres(void)
{
	static OreInfos res
	{
		// OreType,            OreMeta, MaxHeight, NumNests, NestSize
		{E_BLOCK_COAL_ORE,     0,       127,       20,       16},
		{E_BLOCK_IRON_ORE,     0,        64,       20,        8},
		{E_BLOCK_GOLD_ORE,     0,        32,        2,        8},
		{E_BLOCK_REDSTONE_ORE, 0,        16,        8,        7},
		{E_BLOCK_DIAMOND_ORE,  0,        15,        1,        7},
		{E_BLOCK_LAPIS_ORE,    0,        30,        1,        6},
	};
	return res;
}





const cFinishGenOres::OreInfos & cFinishGenOres::DefaultNetherOres(void)
{
	static OreInfos res;
	if (res.empty())
	{
		// Hasn't been initialized yet, emplace the defaults:
		//               OreType,                   OreMeta, MaxHeight, NumNests, NestSize
		res.emplace_back(E_BLOCK_NETHER_QUARTZ_ORE, 0,       127,       20,       8);
	}
	return res;
}





const cFinishGenOres::OreInfos & cFinishGenOres::DefaultNaturalPatches(void)
{
	static OreInfos res;
	if (res.empty())
	{
		// Hasn't been initialized yet, emplace the defaults:
		//               OreType,        OreMeta,               MaxHeight, NumNests, NestSize
		res.emplace_back(E_BLOCK_DIRT,   0,                     127,       20,       32);
		res.emplace_back(E_BLOCK_GRAVEL, 0,                     127,       10,       32);
		res.emplace_back(E_BLOCK_STONE,  E_META_STONE_GRANITE,  127,       20,       32);
		res.emplace_back(E_BLOCK_STONE,  E_META_STONE_DIORITE,  127,       20,       32);
		res.emplace_back(E_BLOCK_STONE,  E_META_STONE_ANDESITE, 127,       20,       32);
	}
	return res;
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenOreNests:

void cFinishGenOreNests::GenerateOre(
	cChunkDesc & a_ChunkDesc,
	BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta,
	int a_MaxHeight, int a_NumNests, int a_NestSize,
	int a_Seq
)
{
	// This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other.
	// It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes
	// Only "terraformable" blocks get replaced with ore, all other blocks stay (so the nest can actually be smaller than specified).

	auto chunkX = a_ChunkDesc.GetChunkX();
	auto chunkZ = a_ChunkDesc.GetChunkZ();
	auto & blockTypes = a_ChunkDesc.GetBlockTypes();
	auto & blockMetas = a_ChunkDesc.GetBlockMetasUncompressed();
	for (int i = 0; i < a_NumNests; i++)
	{
		int nestRnd = m_Noise.IntNoise3DInt(chunkX + i, a_Seq, chunkZ + 64 * i) / 8;
		int BaseX = nestRnd % cChunkDef::Width;
		nestRnd /= cChunkDef::Width;
		int BaseZ = nestRnd % cChunkDef::Width;
		nestRnd /= cChunkDef::Width;
		int BaseY = nestRnd % a_MaxHeight;
		nestRnd /= a_MaxHeight;
		int NestSize = a_NestSize + (nestRnd % (a_NestSize / 4));  // The actual nest size may be up to 1 / 4 larger
		int Num = 0;
		while (Num < NestSize)
		{
			// Put a cuboid around [BaseX, BaseY, BaseZ]
			int rnd = m_Noise.IntNoise3DInt(chunkX + 64 * i, 2 * a_Seq + Num, chunkZ + 32 * i) / 8;
			int xsize = rnd % 2;
			int ysize = (rnd / 4) % 2;
			int zsize = (rnd / 16) % 2;
			rnd >>= 8;
			for (int x = xsize; x >= 0; --x)
			{
				int BlockX = BaseX + x;
				if (cChunkDef::IsValidWidth(BlockX))
				{
					Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
					continue;
				}
				for (int y = ysize; y >= 0; --y)
				{
					int BlockY = BaseY + y;
					if (!cChunkDef::IsValidHeight(BlockY))
					{
						Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
						continue;
					}
					for (int z = zsize; z >= 0; --z)
					{
						int BlockZ = BaseZ + z;
						if (cChunkDef::IsValidWidth(BlockZ))
						{
							Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
							continue;
						}

						int Index = cChunkDef::MakeIndexNoCheck(BlockX, BlockY, BlockZ);
						auto blockType = blockTypes[Index];
						if ((blockType == E_BLOCK_STONE) || (blockType == E_BLOCK_NETHERRACK))
						{
							blockTypes[Index] = a_OreType;
							blockMetas[Index] = a_OreMeta;
						}
						Num++;
					}  // for z
				}  // for y
			}  // for x

			// Move the base to a neighbor voxel
			switch (rnd % 4)
			{
				case 0: BaseX--; break;
				case 1: BaseX++; break;
			}
			switch ((rnd >> 3) % 4)
			{
				case 0: BaseY--; break;
				case 1: BaseY++; break;
			}
			switch ((rnd >> 6) % 4)
			{
				case 0: BaseZ--; break;
				case 1: BaseZ++; break;
			}
		}  // while (Num < NumBlocks)
	}  // for i - NumNests
}





////////////////////////////////////////////////////////////////////////////////
// cFinishGenOrePockets:

bool cFinishGenOrePockets::Initialize(cIniFile & a_IniFile)
{
	// TODO
	return true;
}





void cFinishGenOrePockets::GenerateOre(
	cChunkDesc & a_ChunkDesc,
	BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta,
	int a_MaxHeight, int a_NumNests, int a_NestSize,
	int a_Seq
)
{
	// This function generates several "pockets" of the specified ore
	// Each chunk can contain only pockets that are generated for that chunk, or for its XM / ZM neighbors.

	// Generate for the 3 neighbors in the XP / ZP direction as well, so that pockets crossing the boundaries are accounted for as well:
	int chunkZ = a_ChunkDesc.GetChunkZ();
	int chunkX = a_ChunkDesc.GetChunkX();
	imprintChunkOrePockets(chunkX - 1, chunkZ - 1, a_ChunkDesc, a_OreType, a_OreMeta, a_MaxHeight, a_NumNests, a_NestSize, a_Seq);
	imprintChunkOrePockets(chunkX - 1, chunkZ,     a_ChunkDesc, a_OreType, a_OreMeta, a_MaxHeight, a_NumNests, a_NestSize, a_Seq);
	imprintChunkOrePockets(chunkX,     chunkZ - 1, a_ChunkDesc, a_OreType, a_OreMeta, a_MaxHeight, a_NumNests, a_NestSize, a_Seq);
	imprintChunkOrePockets(chunkX,     chunkZ,     a_ChunkDesc, a_OreType, a_OreMeta, a_MaxHeight, a_NumNests, a_NestSize, a_Seq);
}





void cFinishGenOrePockets::imprintChunkOrePockets(
	int a_ChunkX, int a_ChunkZ,
	cChunkDesc & a_ChunkDesc,
	BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta,
	int a_MaxHeight, int a_NumNests, int a_NestSize,
	int a_Seq
)
{
	// Pick a starting coord for each nest:
	int baseBlockX = a_ChunkX * cChunkDef::Width;
	int baseBlockZ = a_ChunkZ * cChunkDef::Width;
	for (int i = 0; i < a_NumNests; i++)
	{
		int nestRnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 7;
		int baseX = (nestRnd % cChunkDef::Width) + baseBlockX;
		nestRnd /= cChunkDef::Width;
		int baseZ = (nestRnd % cChunkDef::Width) + baseBlockZ;
		nestRnd /= cChunkDef::Width;
		int baseY = nestRnd % a_MaxHeight;
		nestRnd /= a_MaxHeight;
		imprintPocket(
			a_ChunkDesc,
			baseX, baseY, baseZ,
			a_NestSize, i + 200 * a_Seq,
			a_OreType, a_OreMeta
		);
	}  // for i - NumNests
}





void cFinishGenOrePockets::imprintPocket(
	cChunkDesc & a_ChunkDesc,
	int a_MinPocketX, int a_PocketY, int a_MinPocketZ,
	int a_NestSize, int a_Seq,
	BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta
)
{
	// A line segment in a random direction is chosen. Then, several spheres are formed along this line segment,
	// with their diameters diminishing towards the line ends (one half of a sinusoid)

	double x1 = static_cast<double>(a_MinPocketX) + 0.5;
	double y1 = static_cast<double>(a_PocketY)    + 0.5;
	double z1 = static_cast<double>(a_MinPocketZ) + 0.5;
	int rnd = m_Noise.IntNoise2DInt(a_MinPocketX + 7 * a_Seq, a_MinPocketZ + a_PocketY * 11) / 7;
	double angle = static_cast<double>(rnd % 256) / (256.0 * M_PI / 2.0);  // range [0 .. pi / 2]
	rnd /= 256;
	double length = static_cast<double>(a_NestSize) / 4.0;
	double x2 = x1 + sin(angle) * length;  // Always larger than x1
	double z2 = z1 + cos(angle) * length;  // Always larger than z1
	double y2 = y1 + static_cast<double>((rnd % 3) - 1);  // Up to 1 block away from y1

	// Iterate over the line segment in a total of a_NestSize steps:
	double stepX = (x2 - x1) / static_cast<double>(a_NestSize);
	double stepY = (y2 - y1) / static_cast<double>(a_NestSize);
	double stepZ = (z2 - z1) / static_cast<double>(a_NestSize);
	double stepR = M_PI / static_cast<double>(a_NestSize);
	double size = static_cast<double>(a_NestSize) / 16.0;
	for (int i = 0; i < a_NestSize; ++i)
	{
		double iDbl = static_cast<double>(i);
		double sphereX = x1 + stepX * iDbl;
		double sphereY = y1 + stepY * iDbl;
		double sphereZ = z1 + stepZ * iDbl;
		double radius = (sin(stepR * iDbl) + 1.0) * size + 1.0;
		imprintSphere(a_ChunkDesc, sphereX, sphereY, sphereZ, radius, a_OreType, a_OreMeta);
	}  // for i
}





void cFinishGenOrePockets::imprintSphere(
	cChunkDesc & a_ChunkDesc,
	double a_SphereX, double a_SphereY, double a_SphereZ, double a_Radius,
	BLOCKTYPE a_OreType, NIBBLETYPE a_OreMeta
)
{
	// Get the sphere's bounding box, unioned with the chunk's bounding box (possibly empty):
	int baseX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
	int baseZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
	int minX = std::max(FloorC(a_SphereX - a_Radius), baseX);
	int minY = std::max(FloorC(a_SphereY - a_Radius), 0);
	int minZ = std::max(FloorC(a_SphereZ - a_Radius), baseZ);
	int maxX = std::min(CeilC(a_SphereX + a_Radius), baseX + cChunkDef::Width - 1);
	int maxY = std::min(CeilC(a_SphereY + a_Radius), cChunkDef::Height - 1);
	int maxZ = std::min(CeilC(a_SphereZ + a_Radius), baseZ + cChunkDef::Width - 1);

	/*
	// DEBUG:
	int blockX = FloorC(a_SphereX);
	int blockY = FloorC(a_SphereY);
	int blockZ = FloorC(a_SphereZ);
	if (
		(blockX >= baseX) && (blockX < baseX + cChunkDef::Width) &&
		(blockY >= 0) && (blockY < cChunkDef::Height) &&
		(blockZ >= baseZ) && (blockZ < baseZ + cChunkDef::Width)
	)
	{
		// LOGD("Imprinting a sphere center at {%d, %d, %d}", blockX, blockY, blockZ);
		a_ChunkDesc.SetBlockTypeMeta(blockX - baseX, blockY, blockZ - baseZ, a_OreType, a_OreMeta);
	}
	return;
	//*/

	// Imprint the parts of the sphere intersecting the chunk:
	double radiusSq = a_Radius * a_Radius / 4.0;
	for (int y = minY; y <= maxY; y++)
	{
		double relY = static_cast<double>(y) + 0.5 - a_SphereY;
		double relYSq = relY * relY;
		if (relYSq > radiusSq)
		{
			// outside the sphere, bail out
			continue;
		}
		for (int z = minZ; z <= maxZ; z++)
		{
			double relZ = static_cast<double>(z) + 0.5 - a_SphereZ;
			double relZSq = relZ * relZ;
			if (relZSq + relYSq > radiusSq)
			{
				// outside the sphere, bail out
				continue;
			}
			for (int x = minX; x <= maxX; x++)
			{
				double relX = static_cast<double>(x) + 0.5 - a_SphereX;
				double relXSq = relX * relX;
				if (relZSq + relYSq + relXSq > radiusSq)
				{
					// outside the sphere, bail out
					continue;
				}
				int bX = x - baseX;
				int bZ = z - baseZ;
				auto blockType = a_ChunkDesc.GetBlockType(bX, y, bZ);
				if ((blockType == E_BLOCK_STONE) || (blockType == E_BLOCK_NETHERRACK))
				{
					a_ChunkDesc.SetBlockTypeMeta(bX, y, bZ, a_OreType, a_OreMeta);
				}
			}  // for x
		}  // for z
	}  // for y
}