Also made the dimension in world.ini specifiable by a string. Exported StringToDimension() and StringToBiome() to Lua API. git-svn-id: http://mc-server.googlecode.com/svn/trunk@1621 0a769ca7-a7f5-676a-18bf-c427514a06d6
		
			
				
	
	
		
			665 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			665 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
// FinishGen.cpp
 | 
						|
 | 
						|
/* Implements the various finishing generators:
 | 
						|
	- cFinishGenSnow
 | 
						|
	- cFinishGenIce
 | 
						|
	- cFinishGenSprinkleFoliage
 | 
						|
*/
 | 
						|
 | 
						|
#include "Globals.h"
 | 
						|
 | 
						|
#include "FinishGen.h"
 | 
						|
#include "../Noise.h"
 | 
						|
#include "../BlockID.h"
 | 
						|
#include "../Simulator/FluidSimulator.h"  // for cFluidSimulator::CanWashAway()
 | 
						|
#include "../World.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"
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
static inline bool IsWater(BLOCKTYPE a_BlockType)
 | 
						|
{
 | 
						|
	return (a_BlockType == E_BLOCK_STATIONARY_WATER) || (a_BlockType == E_BLOCK_WATER);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
// 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 = (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;
 | 
						|
			}
 | 
						|
			int 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 = (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, (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)
 | 
						|
					)
 | 
						|
					{
 | 
						|
						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
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
// 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++)
 | 
						|
		{
 | 
						|
			switch (a_ChunkDesc.GetBiome(x, z))
 | 
						|
			{
 | 
						|
				case biIcePlains:
 | 
						|
				case biIceMountains:
 | 
						|
				case biTaiga:
 | 
						|
				case biTaigaHills:
 | 
						|
				case biFrozenRiver:
 | 
						|
				case biFrozenOcean:
 | 
						|
				{
 | 
						|
					int Height = a_ChunkDesc.GetHeight(x, z);
 | 
						|
					if (g_BlockIsSnowable[a_ChunkDesc.GetBlockType(x, Height, z)])
 | 
						|
					{
 | 
						|
						a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
 | 
						|
						a_ChunkDesc.SetHeight(x, z, Height + 1);
 | 
						|
					}
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}  // 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++)
 | 
						|
		{
 | 
						|
			switch (a_ChunkDesc.GetBiome(x, z))
 | 
						|
			{
 | 
						|
				case biIcePlains:
 | 
						|
				case biIceMountains:
 | 
						|
				case biTaiga:
 | 
						|
				case biTaigaHills:
 | 
						|
				case biFrozenRiver:
 | 
						|
				case biFrozenOcean:
 | 
						|
				{
 | 
						|
					int Height = a_ChunkDesc.GetHeight(x, z);
 | 
						|
					switch (a_ChunkDesc.GetBlockType(x, Height, z))
 | 
						|
					{
 | 
						|
						case E_BLOCK_WATER:
 | 
						|
						case E_BLOCK_STATIONARY_WATER:
 | 
						|
						{
 | 
						|
							a_ChunkDesc.SetBlockType(x, Height, z, E_BLOCK_ICE);
 | 
						|
							break;
 | 
						|
						}
 | 
						|
					}
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}  // for z
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
// cFinishGenLilypads:
 | 
						|
 | 
						|
int cFinishGenSingleBiomeSingleTopBlock::GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap)
 | 
						|
{
 | 
						|
	int res = 0;
 | 
						|
	for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
 | 
						|
	{
 | 
						|
		if (a_BiomeMap[i] == m_Biome)
 | 
						|
		{
 | 
						|
			res++;
 | 
						|
		}
 | 
						|
	}  // for i - a_BiomeMap[]
 | 
						|
	return m_Amount * res / 256;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFinishGenSingleBiomeSingleTopBlock::GenFinish(cChunkDesc & a_ChunkDesc)
 | 
						|
{
 | 
						|
	// Add Lilypads on top of water surface in Swampland
 | 
						|
 | 
						|
	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:
 | 
						|
		if (a_ChunkDesc.GetBiome(x, z) != m_Biome)
 | 
						|
		{
 | 
						|
			// Incorrect biome
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		int Height = a_ChunkDesc.GetHeight(x, z);
 | 
						|
		if (Height >= cChunkDef::Height)
 | 
						|
		{
 | 
						|
			// 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 ((BlockBelow == m_AllowedBelow1) || (BlockBelow == m_AllowedBelow2))
 | 
						|
		{
 | 
						|
			a_ChunkDesc.SetBlockType(x, Height + 1, z, m_BlockType);
 | 
						|
			a_ChunkDesc.SetHeight(x, z, Height + 1);
 | 
						|
		}
 | 
						|
	}  // for i
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
// 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(void)
 | 
						|
{
 | 
						|
	// Nothing needed yet
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFinishGenPreSimulator::GenFinish(cChunkDesc & a_ChunkDesc)
 | 
						|
{
 | 
						|
	CollapseSandGravel(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
 | 
						|
	StationarizeFluid(a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap(), E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
 | 
						|
	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, 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 (int 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, const cWorld & a_World) :
 | 
						|
	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;
 | 
						|
	switch (a_World.GetDimension())
 | 
						|
	{
 | 
						|
		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 (int 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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 |