From 6744738a85c60238585dcf72af211f852fd7e4c6 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sat, 2 Mar 2013 19:57:09 +0000 Subject: Rewritten SandSimulator to use direct chunk access; and sand falling on torches now creates a pickup. git-svn-id: http://mc-server.googlecode.com/svn/trunk@1240 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Simulator/SandSimulator.cpp | 235 +++++++++++++++++++++++++++++-------- source/Simulator/SandSimulator.h | 58 ++++++--- 2 files changed, 232 insertions(+), 61 deletions(-) (limited to 'source/Simulator') diff --git a/source/Simulator/SandSimulator.cpp b/source/Simulator/SandSimulator.cpp index 04bcc498c..27e137354 100644 --- a/source/Simulator/SandSimulator.cpp +++ b/source/Simulator/SandSimulator.cpp @@ -12,102 +12,243 @@ -cSandSimulator::cSandSimulator(cWorld & a_World) - : cSimulator(a_World) - , m_Blocks(new BlockList) - , m_Buffer(new BlockList) +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; + 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(); } -cSandSimulator::~cSandSimulator() +bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType) { - delete m_Buffer; - delete m_Blocks; + switch (a_BlockType) + { + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + case E_BLOCK_ANVIL: + { + return true; + } + } + return false; } -void cSandSimulator::Simulate(float a_Dt) +void cSandSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) { - m_Buffer->clear(); - std::swap( m_Blocks, m_Buffer ); + if (a_Chunk == NULL) + { + 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; + } - for( BlockList::iterator itr = m_Buffer->begin(); itr != m_Buffer->end(); ++itr ) + // Check for duplicates: + cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData(); + for (cSandSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) { - Vector3i Pos = *itr; - BLOCKTYPE BlockID = m_World.GetBlock(Pos.x, Pos.y, Pos.z); - if(!IsAllowedBlock(BlockID)) - continue; + if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == a_BlockZ)) + { + return; + } + } + + m_TotalBlocks += 1; + ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ)); +} + - BLOCKTYPE BottomBlock = m_World.GetBlock( Pos.x, Pos.y - 1, Pos.z ); - - if( IsPassable(BottomBlock) ) + + + +bool cSandSimulator::CanStartFallingThrough(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) + { + case E_BLOCK_AIR: + case E_BLOCK_FIRE: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: { - cFallingBlock * FallingBlock = new cFallingBlock(Pos, BlockID); - FallingBlock->Initialize(&m_World); - m_World.SetBlock( Pos.x, Pos.y, Pos.z, E_BLOCK_AIR, 0 ); + return true; } } - + return false; } -bool cSandSimulator::IsAllowedBlock( BLOCKTYPE a_BlockType ) +bool cSandSimulator::CanContinueFallThrough(BLOCKTYPE a_BlockType) { - return a_BlockType == E_BLOCK_SAND - || a_BlockType == E_BLOCK_GRAVEL; + switch (a_BlockType) + { + case E_BLOCK_AIR: + case E_BLOCK_FIRE: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_POWERED_RAIL: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_COBWEB: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_DEAD_BUSH: + case E_BLOCK_YELLOW_FLOWER: + case E_BLOCK_RED_ROSE: + case E_BLOCK_BROWN_MUSHROOM: + case E_BLOCK_RED_MUSHROOM: + case E_BLOCK_TORCH: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_CROPS: + case E_BLOCK_PUMPKIN_STEM: + case E_BLOCK_MELON_STEM: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + { + return true; + } + } + return false; } -void cSandSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +bool cSandSimulator::IsReplacedOnRematerialization(BLOCKTYPE a_BlockType) { - // TODO: Optimize this by passing the block type along - int RelX = a_BlockX; - int RelY = a_BlockY; - int RelZ = a_BlockZ; - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ); - if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, RelY, RelZ))) + switch (a_BlockType) { - return; + case E_BLOCK_AIR: + case E_BLOCK_FIRE: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_DEAD_BUSH: + { + return true; + } } + return false; +} - Vector3i Block(a_BlockX, a_BlockY, a_BlockZ); - - //check for duplicates - for (BlockList::iterator itr = m_Blocks->begin(); itr != m_Blocks->end(); ++itr) + + + + +bool cSandSimulator::DoesBreakFallingThrough(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) { - Vector3i Pos = *itr; - if ((Pos.x == a_BlockX) && (Pos.y == a_BlockY) && (Pos.z == a_BlockZ)) + case E_BLOCK_STONE_SLAB: + case E_BLOCK_WOODEN_SLAB: { - return; + return true; } } + return false; +} + - m_Blocks->push_back(Block); + + + +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, 0); } -bool cSandSimulator::IsPassable(BLOCKTYPE a_BlockType) +void cSandSimulator::DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) { - return (a_BlockType == E_BLOCK_AIR) - || IsBlockWater(a_BlockType) - || IsBlockLava(a_BlockType) - || (a_BlockType == E_BLOCK_FIRE); + // TODO } + + + + diff --git a/source/Simulator/SandSimulator.h b/source/Simulator/SandSimulator.h index ebd154f2d..571258049 100644 --- a/source/Simulator/SandSimulator.h +++ b/source/Simulator/SandSimulator.h @@ -2,32 +2,62 @@ #pragma once #include "Simulator.h" -#include "../BlockEntity.h" -#include "../Vector3i.h" -class cSandSimulator : public cSimulator +/// Despite the class name, this simulator takes care of all blocks that fall when suspended in the air. +class cSandSimulator : + public cSimulator { public: - cSandSimulator(cWorld & a_World); - ~cSandSimulator(); - - virtual void Simulate( float a_Dt ) override; - - virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override; - virtual bool IsPassable( BLOCKTYPE a_BlockType ); + cSandSimulator(cWorld & a_World, cIniFile & a_IniFile); + + // cSimulator overrides: + virtual void Simulate(float a_Dt) override {} // Unused in this simulator + virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override; + + /// Returns true if a falling-able block can start falling through the specified block type + static bool CanStartFallingThrough(BLOCKTYPE a_BlockType); + + /// Returns true if an already-falling block can pass through the specified block type (e. g. torch) + static bool CanContinueFallThrough(BLOCKTYPE a_BlockType); + + /// Returns true if the falling block rematerializing will replace the specified block type (e. g. tall grass) + static bool IsReplacedOnRematerialization(BLOCKTYPE a_BlockType); + + /// Returns true if the specified block breaks falling blocks while they fall through it (e. g. halfslabs) + static bool DoesBreakFallingThrough(BLOCKTYPE a_BlockType); + + /** Called when a block finishes falling at the specified coords, either by insta-fall, + or through cFallingBlock entity. + It either rematerializes the block (a_FallingBlockType) at the specified coords, or creates a pickup, + based on the block currently present in the world at the dest specified coords + */ + static void FinishFalling( + cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, + BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta + ); protected: + bool m_IsInstantFall; // If set to true, blocks don't fall using cFallingBlock entity, but instantly instead + + int m_TotalBlocks; // Total number of blocks currently in the queue for simulating + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; - - typedef std::list BlockList; - BlockList * m_Blocks; - BlockList * m_Buffer; + + /// Performs the instant fall of the block - removes it from top, Finishes it at the bottom + void DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); }; +/// Per-chunk data for the simulator, specified individual chunks to simulate; Data is not used +typedef cCoordWithIntList cSandSimulatorChunkData; + + + + -- cgit v1.2.3