summaryrefslogblamecommitdiffstats
path: root/src/Simulator/SandSimulator.cpp
blob: 42bb521ed96000846800fb3758ffcf1fe84abfae (plain) (tree)
1
2
3
4
5
6
7
8
9


                                                                                              
                          
                         
                     
                       
                                     
                     
                       
                        
 



 


                                                                        
 






                                                                                      
                                                                                                                





                                                                              
 





















                                                                                                                       
                          


                                                                                                

                          



                                                                                                                                        
                         

                                         
                                                                                    

                 
                                                            
                          

 



 
                                                          
 

                            
                                   
                                             
                                        

                                    


                                    



                                     
         

 



 
                                                                 
 
                                                        


                       


                                                                      


                       
 


                                                                                                      
         
                                                                                  





                               
                                                                  

 
 




                                                                  
                                            



                                  
                                  
                                  
                                             

                                              
                 
                                    

                 
                     


 


 
                                                                  
 
                                            


                                 
                                       
                                            
                                     



                                           
                                  
                                        
                                                           
                                           
                                  
                                   
                                                           

                                             
                                          
                                          

                                                   

                                                



                                           
                                  
                                             

                                              

                                                  




                                           
                                         

                                      

                                                   
                                           




                                    

 



 
                                                                         
 
                                            
                            
         
                                 

                                           
                                       
                                  
                                  
                                  
                                             
                                              
                                            
                                        
                                   


                                    
         

                     
 



 
                                                                                           

                            
         

                                                

                                         
                 
                                                                                                   
                 
         


                     
 








                                                                   
 




                                                                                                        

                                                        
                                                                                                                                  
                 

                       
 

                                   
                                                                                            
                                  




                                                    

 



 
                                                                                        
 



                                                                                              
                                                                    
 


                                             











                                                                                   




                                                                                                  
                    



                                                
 


                                                                            
                                                                                                    

                       
 
                                                                      
 



 

#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "SandSimulator.h"
#include "../BlockInfo.h"
#include "../World.h"
#include "../Defines.h"
#include "../Entities/FallingBlock.h"
#include "../Chunk.h"
#include "../IniFile.h"
#include "../EffectID.h"





cSandSimulator::cSandSimulator(cWorld & a_World, cIniFile & a_IniFile) :
	cSimulator(a_World),
	m_TotalBlocks(0)
{
	m_IsInstantFall = a_IniFile.GetValueSetB("Physics", "SandInstantFall", false);
}





void cSandSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
	cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData();
	if (ChunkData.empty())
	{
		return;
	}

	int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
	int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
	for (cSandSimulatorChunkData::const_iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr)
	{
		BLOCKTYPE BlockType = a_Chunk->GetBlock(itr->x, itr->y, itr->z);
		if (!IsAllowedBlock(BlockType) || (itr->y <= 0))
		{
			continue;
		}

		BLOCKTYPE BlockBelow = (itr->y > 0) ? a_Chunk->GetBlock(itr->x, itr->y - 1, itr->z) : E_BLOCK_AIR;
		if (CanStartFallingThrough(BlockBelow))
		{
			if (m_IsInstantFall)
			{
				DoInstantFall(a_Chunk, itr->x, itr->y, itr->z);
				continue;
			}
			Vector3i Pos;
			Pos.x = itr->x + BaseX;
			Pos.y = itr->y;
			Pos.z = itr->z + BaseZ;
			/*
			FLOGD(
				"Creating a falling block at {0} of type {1}, block below: {2}",
				Pos, ItemTypeToString(BlockType), ItemTypeToString(BlockBelow)
			);
			*/

			auto FallingBlock = cpp14::make_unique<cFallingBlock>(Pos, BlockType, a_Chunk->GetMeta(itr->x, itr->y, itr->z));
			auto FallingBlockPtr = FallingBlock.get();
			if (!FallingBlockPtr->Initialize(std::move(FallingBlock), m_World))
			{
				continue;
			}
			a_Chunk->SetBlock({itr->x, itr->y, itr->z}, E_BLOCK_AIR, 0);
		}
	}
	m_TotalBlocks -= static_cast<int>(ChunkData.size());
	ChunkData.clear();
}





bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
{
	switch (a_BlockType)
	{
		case E_BLOCK_ANVIL:
		case E_BLOCK_CONCRETE_POWDER:
		case E_BLOCK_DRAGON_EGG:
		case E_BLOCK_GRAVEL:
		case E_BLOCK_SAND:
		{
			return true;
		}
		default:
		{
			return false;
		}
	}
}





void cSandSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk)
{
	if ((a_Chunk == nullptr) || !a_Chunk->IsValid())
	{
		return;
	}
	int RelX = a_Block.x - a_Chunk->GetPosX() * cChunkDef::Width;
	int RelZ = a_Block.z - a_Chunk->GetPosZ() * cChunkDef::Width;
	if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_Block.y, RelZ)))
	{
		return;
	}

	// Check for duplicates:
	cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData();
	for (cSandSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
	{
		if ((itr->x == RelX) && (itr->y == a_Block.y) && (itr->z == RelZ))
		{
			return;
		}
	}

	m_TotalBlocks += 1;
	ChunkData.push_back(cCoordWithInt(RelX, a_Block.y, RelZ));
}





bool cSandSimulator::CanStartFallingThrough(BLOCKTYPE a_BlockType)
{
	// Please keep the list alpha-sorted
	switch (a_BlockType)
	{
		case E_BLOCK_AIR:
		case E_BLOCK_FIRE:
		case E_BLOCK_LAVA:
		case E_BLOCK_SNOW:
		case E_BLOCK_STATIONARY_LAVA:
		case E_BLOCK_STATIONARY_WATER:
		case E_BLOCK_WATER:
		{
			return true;
		}
	}
	return false;
}





bool cSandSimulator::CanContinueFallThrough(BLOCKTYPE a_BlockType)
{
	// Please keep the list alpha-sorted
	switch (a_BlockType)
	{
		case E_BLOCK_AIR:
		case E_BLOCK_BEETROOTS:
		case E_BLOCK_BROWN_MUSHROOM:
		case E_BLOCK_CARROTS:
		case E_BLOCK_COBWEB:
		case E_BLOCK_CROPS:
		case E_BLOCK_DEAD_BUSH:
		case E_BLOCK_DETECTOR_RAIL:
		case E_BLOCK_FIRE:
		case E_BLOCK_FLOWER_POT:
		case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
		case E_BLOCK_IRON_TRAPDOOR:
		case E_BLOCK_LAVA:
		case E_BLOCK_LEVER:
		case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
		case E_BLOCK_MINECART_TRACKS:
		case E_BLOCK_MELON_STEM:
		case E_BLOCK_POWERED_RAIL:
		case E_BLOCK_PUMPKIN_STEM:
		case E_BLOCK_REDSTONE_REPEATER_OFF:
		case E_BLOCK_REDSTONE_REPEATER_ON:
		case E_BLOCK_REDSTONE_TORCH_OFF:
		case E_BLOCK_REDSTONE_TORCH_ON:
		case E_BLOCK_REDSTONE_WIRE:
		case E_BLOCK_RED_MUSHROOM:
		case E_BLOCK_RED_ROSE:
		case E_BLOCK_SIGN_POST:
		case E_BLOCK_SNOW:
		case E_BLOCK_STANDING_BANNER:
		case E_BLOCK_STATIONARY_LAVA:
		case E_BLOCK_STATIONARY_WATER:
		case E_BLOCK_STONE_BUTTON:
		case E_BLOCK_STONE_PRESSURE_PLATE:
		case E_BLOCK_TALL_GRASS:
		case E_BLOCK_TORCH:
		case E_BLOCK_TRAPDOOR:
		case E_BLOCK_TRIPWIRE:
		case E_BLOCK_TRIPWIRE_HOOK:
		case E_BLOCK_WALL_BANNER:
		case E_BLOCK_WALLSIGN:
		case E_BLOCK_WATER:
		case E_BLOCK_WOODEN_BUTTON:
		case E_BLOCK_WOODEN_PRESSURE_PLATE:
		case E_BLOCK_YELLOW_FLOWER:
		{
			return true;
		}
	}
	return false;
}





bool cSandSimulator::IsReplacedOnRematerialization(BLOCKTYPE a_BlockType)
{
	// Please keep the list alpha-sorted
	switch (a_BlockType)
	{
		case E_BLOCK_AIR:
		case E_BLOCK_CHORUS_FLOWER:
		case E_BLOCK_CHORUS_PLANT:
		case E_BLOCK_DEAD_BUSH:
		case E_BLOCK_FIRE:
		case E_BLOCK_LAVA:
		case E_BLOCK_SNOW:
		case E_BLOCK_STATIONARY_LAVA:
		case E_BLOCK_STATIONARY_WATER:
		case E_BLOCK_STRUCTURE_VOID:
		case E_BLOCK_TALL_GRASS:
		case E_BLOCK_WATER:
		{
			return true;
		}
	}
	return false;
}





bool cSandSimulator::DoesBreakFallingThrough(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
	switch (a_BlockType)
	{
		case E_BLOCK_PURPUR_SLAB:
		case E_BLOCK_RED_SANDSTONE_SLAB:
		case E_BLOCK_STONE_SLAB:
		case E_BLOCK_WOODEN_SLAB:
		{
			return ((a_BlockMeta & 0x08) == 0);  // Only a bottom-slab breaks the block
		}
	}
	return false;
}





void cSandSimulator::FinishFalling(
	cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ,
	BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta
)
{
	ASSERT(a_BlockY < cChunkDef::Height);

	BLOCKTYPE CurrentBlockType = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
	if ((a_FallingBlockType == E_BLOCK_ANVIL) || IsReplacedOnRematerialization(CurrentBlockType))
	{
		// Rematerialize the material here:
		a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FallingBlockType, a_FallingBlockMeta);
		if (a_FallingBlockType == E_BLOCK_ANVIL)
		{
			a_World->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_ANVIL_LAND, {a_BlockX, a_BlockY, a_BlockZ}, 0);
		}
		return;
	}

	// Create a pickup instead:
	cItems Pickups;
	Pickups.Add(static_cast<ENUM_ITEM_TYPE>(a_FallingBlockType), 1, a_FallingBlockMeta);
	a_World->SpawnItemPickups(
		Pickups,
		static_cast<double>(a_BlockX) + 0.5,
		static_cast<double>(a_BlockY) + 0.5,
		static_cast<double>(a_BlockZ) + 0.5
	);
}





void cSandSimulator::DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
	// Remove the original block:
	BLOCKTYPE  FallingBlockType;
	NIBBLETYPE FallingBlockMeta;
	a_Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, FallingBlockType, FallingBlockMeta);
	a_Chunk->SetBlock({a_RelX, a_RelY, a_RelZ}, E_BLOCK_AIR, 0);

	// Search for a place to put it:
	for (int y = a_RelY - 1; y >= 0; y--)
	{
		BLOCKTYPE BlockType;
		NIBBLETYPE BlockMeta;
		a_Chunk->GetBlockTypeMeta(a_RelX, y, a_RelZ, BlockType, BlockMeta);
		int BlockY;
		if (DoesBreakFallingThrough(BlockType, BlockMeta))
		{
			BlockY = y;
		}
		else if (!CanContinueFallThrough(BlockType))
		{
			BlockY = y + 1;
		}
		else if ((FallingBlockType == E_BLOCK_CONCRETE_POWDER) && IsBlockWater(BlockType))
		{
			FallingBlockType = E_BLOCK_CONCRETE;
			BlockY = y;
		}
		else
		{
			// Can fall further down
			continue;
		}

		// Finish the fall at the found bottom:
		int BlockX = a_RelX + a_Chunk->GetPosX() * cChunkDef::Width;
		int BlockZ = a_RelZ + a_Chunk->GetPosZ() * cChunkDef::Width;
		FinishFalling(&m_World, BlockX, BlockY, BlockZ, FallingBlockType, FallingBlockMeta);
		return;
	}

	// The block just "fell off the world" without leaving a trace
}