diff options
Diffstat (limited to 'src/Simulator/SandSimulator.cpp')
-rw-r--r-- | src/Simulator/SandSimulator.cpp | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp new file mode 100644 index 000000000..87fb83357 --- /dev/null +++ b/src/Simulator/SandSimulator.cpp @@ -0,0 +1,309 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SandSimulator.h" +#include "../World.h" +#include "../BlockID.h" +#include "../Defines.h" +#include "../Entities/FallingBlock.h" +#include "../Chunk.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(float 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; + /* + LOGD( + "Creating a falling block at {%d, %d, %d} of type %s, block below: %s", + Pos.x, Pos.y, Pos.z, ItemTypeToString(BlockType).c_str(), ItemTypeToString(BlockBelow).c_str() + ); + */ + cFallingBlock * FallingBlock = new cFallingBlock(Pos, BlockType, a_Chunk->GetMeta(itr->x, itr->y, itr->z)); + FallingBlock->Initialize(&m_World); + a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0); + } + } + m_TotalBlocks -= ChunkData.size(); + ChunkData.clear(); +} + + + + + +bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) + { + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + case E_BLOCK_ANVIL: + case E_BLOCK_DRAGON_EGG: + { + return true; + } + } + return false; +} + + + + + +void cSandSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if ((a_Chunk == NULL) || !a_Chunk->IsValid()) + { + return; + } + int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, 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_BlockY) && (itr->z == RelZ)) + { + return; + } + } + + m_TotalBlocks += 1; + ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, 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_BROWN_MUSHROOM: + 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_LAVA: + case E_BLOCK_LEVER: + 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_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_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_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_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_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); + return; + } + + // Create a pickup instead: + cItems Pickups; + Pickups.Add((ENUM_ITEM_ID)a_FallingBlockType, 1, a_FallingBlockMeta); + a_World->SpawnItemPickups(Pickups, (double)a_BlockX + 0.5, (double)a_BlockY + 0.5, (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 + { + // 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 +} + + + + |