diff --git a/source/Chunk.h b/source/Chunk.h index 853f1e61a..4d88d1019 100644 --- a/source/Chunk.h +++ b/source/Chunk.h @@ -5,6 +5,7 @@ #include "ChunkDef.h" #include "Simulator/FireSimulator.h" +#include "Simulator/SandSimulator.h" @@ -271,6 +272,7 @@ public: cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; } cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; } cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; } + cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; } private: @@ -319,6 +321,7 @@ private: cFireSimulatorChunkData m_FireSimulatorData; cFluidSimulatorData * m_WaterSimulatorData; cFluidSimulatorData * m_LavaSimulatorData; + cSandSimulatorChunkData m_SandSimulatorData; void RemoveBlockEntity(cBlockEntity * a_BlockEntity); diff --git a/source/Entity.cpp b/source/Entity.cpp index c031c97af..bbca17fce 100644 --- a/source/Entity.cpp +++ b/source/Entity.cpp @@ -50,9 +50,9 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z) cEntity::~cEntity() { - LOGD("Deleting entity %d at pos {%.2f, %.2f} ~ [%d, %d]; ptr %p", + LOGD("Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p", m_UniqueID, - m_Pos.x, m_Pos.z, + m_Pos.x, m_Pos.y, m_Pos.z, (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width), this ); diff --git a/source/FallingBlock.cpp b/source/FallingBlock.cpp index 8f0320d9b..12362009d 100644 --- a/source/FallingBlock.cpp +++ b/source/FallingBlock.cpp @@ -3,15 +3,17 @@ #include "FallingBlock.h" #include "World.h" #include "ClientHandle.h" +#include "Simulator/SandSimulator.h" -cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType) - : super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f) - , m_BlockType(a_BlockType) - , m_OriginalPosition(a_BlockPosition) +cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : + super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta), + m_OriginalPosition(a_BlockPosition) { } @@ -21,7 +23,7 @@ cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_Block void cFallingBlock::Initialize(cWorld * a_World) { - super::Initialize( a_World ); + super::Initialize(a_World); a_World->BroadcastSpawn(*this); } @@ -44,13 +46,34 @@ void cFallingBlock::Tick(float a_Dt, MTRand & a_TickRandom) m_Speed.y -= MilliDt * 9.8f; m_Pos.y += m_Speed.y * MilliDt; - // GetWorld()->BroadcastTeleportEntity(*this); // Testing position - - Vector3i BlockPos( m_OriginalPosition.x, (int)(m_Pos.y - 0.5), m_OriginalPosition.z); - if (!IsPassable(GetWorld()->GetBlock(BlockPos))) + // GetWorld()->BroadcastTeleportEntity(*this); // Test position + + int BlockX = (int)m_OriginalPosition.x; + int BlockY = (int)(m_Pos.y - 0.5); + int BlockZ = (int)m_OriginalPosition.z; + + if (BlockY < 0) { - Destroy(); - GetWorld()->SetBlock(BlockPos.x, BlockPos.y + 1, BlockPos.z, m_BlockType, 0); + // Fallen out of this world, just continue falling until out of sight, then destroy: + if (BlockY < 100) + { + Destroy(); + } + return; + } + + if (BlockY < cChunkDef::Height - 1) + { + BLOCKTYPE BlockBelow = GetWorld()->GetBlock(BlockX, BlockY, BlockZ); + if ( + cSandSimulator::DoesBreakFallingThrough(BlockBelow) || // Fallen onto a block that breaks this into pickups (e. g. half-slab) + !cSandSimulator::CanContinueFallThrough(BlockBelow) // Fallen onto a solid block + ) + { + cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta); + Destroy(); + return; + } } } diff --git a/source/FallingBlock.h b/source/FallingBlock.h index abc3f8d9b..37fb4c4cc 100644 --- a/source/FallingBlock.h +++ b/source/FallingBlock.h @@ -23,9 +23,10 @@ class cFallingBlock : public: CLASS_PROTODEF(cFallingBlock); - cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType); + cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - BLOCKTYPE GetBlockType(void) const { return m_BlockType; } + BLOCKTYPE GetBlockType(void) const { return m_BlockType; } + NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; } // cEntity overrides: virtual void Initialize(cWorld * a_World) override; @@ -33,13 +34,9 @@ public: virtual void Tick(float a_Dt, MTRand & a_TickRandom) override; private: - BLOCKTYPE m_BlockType; - Vector3i m_OriginalPosition; - - static bool IsPassable(BLOCKTYPE a_BlockType) - { - return ((a_BlockType == E_BLOCK_AIR) || IsBlockLiquid(a_BlockType)); - } + BLOCKTYPE m_BlockType; + NIBBLETYPE m_BlockMeta; + Vector3i m_OriginalPosition; } ; 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,60 +12,72 @@ -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); } -cSandSimulator::~cSandSimulator() +void cSandSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { - delete m_Buffer; - delete m_Blocks; -} - - - - - -void cSandSimulator::Simulate(float a_Dt) -{ - m_Buffer->clear(); - std::swap( m_Blocks, m_Buffer ); - - for( BlockList::iterator itr = m_Buffer->begin(); itr != m_Buffer->end(); ++itr ) + cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData(); + if (ChunkData.empty()) { - Vector3i Pos = *itr; - BLOCKTYPE BlockID = m_World.GetBlock(Pos.x, Pos.y, Pos.z); - if(!IsAllowedBlock(BlockID)) - continue; + return; + } - BLOCKTYPE BottomBlock = m_World.GetBlock( Pos.x, Pos.y - 1, Pos.z ); - - if( IsPassable(BottomBlock) ) + 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)) { - cFallingBlock * FallingBlock = new cFallingBlock(Pos, BlockID); + 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); - m_World.SetBlock( Pos.x, Pos.y, Pos.z, E_BLOCK_AIR, 0 ); + 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 ) +bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType) { - return a_BlockType == E_BLOCK_SAND - || a_BlockType == E_BLOCK_GRAVEL; + switch (a_BlockType) + { + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + case E_BLOCK_ANVIL: + { + return true; + } + } + return false; } @@ -74,40 +86,169 @@ bool cSandSimulator::IsAllowedBlock( BLOCKTYPE a_BlockType ) void cSandSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) { - // 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))) + 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; } - Vector3i Block(a_BlockX, a_BlockY, a_BlockZ); - - //check for duplicates - for (BlockList::iterator itr = m_Blocks->begin(); itr != m_Blocks->end(); ++itr) + // Check for duplicates: + cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData(); + for (cSandSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) { - Vector3i Pos = *itr; - if ((Pos.x == a_BlockX) && (Pos.y == a_BlockY) && (Pos.z == a_BlockZ)) + if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == a_BlockZ)) { return; } } - m_Blocks->push_back(Block); + m_TotalBlocks += 1; + ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ)); } -bool cSandSimulator::IsPassable(BLOCKTYPE a_BlockType) +bool cSandSimulator::CanStartFallingThrough(BLOCKTYPE a_BlockType) { - return (a_BlockType == E_BLOCK_AIR) - || IsBlockWater(a_BlockType) - || IsBlockLava(a_BlockType) - || (a_BlockType == E_BLOCK_FIRE); + 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: + { + return true; + } + } + return false; } + + + + + +bool cSandSimulator::CanContinueFallThrough(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: + 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; +} + + + + + +bool cSandSimulator::IsReplacedOnRematerialization(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: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_DEAD_BUSH: + { + return true; + } + } + return false; +} + + + + + +bool cSandSimulator::DoesBreakFallingThrough(BLOCKTYPE a_BlockType) +{ + switch (a_BlockType) + { + case E_BLOCK_STONE_SLAB: + case E_BLOCK_WOODEN_SLAB: + { + return true; + } + } + 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, 0); +} + + + + + +void cSandSimulator::DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) +{ + // 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(); + cSandSimulator(cWorld & a_World, cIniFile & a_IniFile); - virtual void Simulate( float a_Dt ) override; - - virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override; - virtual bool IsPassable( BLOCKTYPE a_BlockType ); + // 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; + + + + diff --git a/source/World.cpp b/source/World.cpp index 84874258f..2cfe56d57 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -250,10 +250,10 @@ cWorld::cWorld(const AString & a_WorldName) : m_BlockTickQueueCopy.reserve(1000); // Simulators: - m_SimulatorManager = new cSimulatorManager(*this); + m_SimulatorManager = new cSimulatorManager(*this); m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER); m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); - m_SandSimulator = new cSandSimulator(*this); + m_SandSimulator = new cSandSimulator(*this, IniFile); m_FireSimulator = new cFireSimulator(*this, IniFile); m_RedstoneSimulator = new cRedstoneSimulator(*this); @@ -1166,9 +1166,9 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double float SpeedY = (float)(a_FlyAwaySpeed * r1.randInt(1000)); float SpeedZ = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); cPickup * Pickup = new cPickup( - (int)(a_BlockX * 32) + r1.randInt(16) + r1.randInt(16), - (int)(a_BlockY * 32) + r1.randInt(16) + r1.randInt(16), - (int)(a_BlockZ * 32) + r1.randInt(16) + r1.randInt(16), + (int)(a_BlockX * 32) + (r1.randInt(16) + r1.randInt(16) - 16), + (int)(a_BlockY * 32) + (r1.randInt(16) + r1.randInt(16) - 16), + (int)(a_BlockZ * 32) + (r1.randInt(16) + r1.randInt(16) - 16), *itr, SpeedX, SpeedY, SpeedZ ); Pickup->Initialize(this); diff --git a/source/World.h b/source/World.h index 2965ae1c3..ed0053db5 100644 --- a/source/World.h +++ b/source/World.h @@ -479,7 +479,7 @@ private: bool m_IsDeepSnowEnabled; // The cRedstone class simulates redstone and needs access to m_RSList - friend class cRedstone; + // friend class cRedstone; std::vector m_RSList; std::vector m_BlockTickQueue;