1
0
cuberite-2a/src/Simulator/SandSimulator.cpp
Mattes D 01b8ed5295
Pulled the BlockID and BlockInfo headers from Globals.h. (#4591)
The BlockID.h file was removed from Globals.h and renamed to BlockType.h (main change)
The BlockInfo.h file was removed from Globals.h (main change)
The ENUM_BLOCK_ID and ENUM_ITEM_ID enum names were replaced with ENUM_BLOCK_TYPE and ENUM_ITEM_TYPE (cosmetics)
The various enums, such as eDimension, eDamageType and eExplosionSource were moved from BlockType.h to Defines.h, together with the helper functions for converting between them and strings (StringToDimension et al.) (minor)
Many inline functions were moved from headers to their respective cpp files, so that BlockType.h could be included only into the cpp file, rather than the header.
That broke our tests a bit, since they pick bits and pieces out of the main code and provide stubs for the rest; they had to be re-stubbed and re-verified.
eMonsterType values are no longer tied to E_ITEM_SPAWN_EGG_META_* values
2020-04-03 08:57:01 +02:00

347 lines
7.7 KiB
C++

#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
}