Moved source to src
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
|
||||
// DelayedFluidSimulator.cpp
|
||||
|
||||
// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay
|
||||
// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot.
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#include "DelayedFluidSimulator.h"
|
||||
#include "../World.h"
|
||||
#include "../Chunk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cDelayedFluidSimulatorChunkData::cSlot
|
||||
|
||||
bool cDelayedFluidSimulatorChunkData::cSlot::Add(int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
ASSERT(a_RelZ >= 0);
|
||||
ASSERT(a_RelZ < ARRAYCOUNT(m_Blocks));
|
||||
|
||||
cCoordWithIntVector & Blocks = m_Blocks[a_RelZ];
|
||||
int Index = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
for (cCoordWithIntVector::const_iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr)
|
||||
{
|
||||
if (itr->Data == Index)
|
||||
{
|
||||
// Already present
|
||||
return false;
|
||||
}
|
||||
} // for itr - Blocks[]
|
||||
Blocks.push_back(cCoordWithInt(a_RelX, a_RelY, a_RelZ, Index));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cDelayedFluidSimulatorChunkData:
|
||||
|
||||
cDelayedFluidSimulatorChunkData::cDelayedFluidSimulatorChunkData(int a_TickDelay) :
|
||||
m_Slots(new cSlot[a_TickDelay])
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cDelayedFluidSimulatorChunkData::~cDelayedFluidSimulatorChunkData()
|
||||
{
|
||||
delete[] m_Slots;
|
||||
m_Slots = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cDelayedFluidSimulator:
|
||||
|
||||
cDelayedFluidSimulator::cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay) :
|
||||
super(a_World, a_Fluid, a_StationaryFluid),
|
||||
m_TickDelay(a_TickDelay),
|
||||
m_AddSlotNum(a_TickDelay - 1),
|
||||
m_SimSlotNum(0),
|
||||
m_TotalBlocks(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cDelayedFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
|
||||
{
|
||||
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
// Not inside the world (may happen when rclk with a full bucket - the client sends Y = -1)
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
|
||||
if (BlockType != m_FluidBlock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData();
|
||||
cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw;
|
||||
cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_AddSlotNum];
|
||||
|
||||
// Add, if not already present:
|
||||
if (!Slot.Add(RelX, a_BlockY, RelZ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
++m_TotalBlocks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cDelayedFluidSimulator::Simulate(float a_Dt)
|
||||
{
|
||||
m_AddSlotNum = m_SimSlotNum;
|
||||
m_SimSlotNum += 1;
|
||||
if (m_SimSlotNum >= m_TickDelay)
|
||||
{
|
||||
m_SimSlotNum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cDelayedFluidSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
|
||||
{
|
||||
void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData();
|
||||
cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw;
|
||||
cDelayedFluidSimulatorChunkData::cSlot & Slot = ChunkData->m_Slots[m_SimSlotNum];
|
||||
|
||||
// Simulate all the blocks in the scheduled slot:
|
||||
for (int i = 0; i < ARRAYCOUNT(Slot.m_Blocks); i++)
|
||||
{
|
||||
cCoordWithIntVector & Blocks = Slot.m_Blocks[i];
|
||||
if (Blocks.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (cCoordWithIntVector::iterator itr = Blocks.begin(), end = Blocks.end(); itr != end; ++itr)
|
||||
{
|
||||
SimulateBlock(a_Chunk, itr->x, itr->y, itr->z);
|
||||
}
|
||||
m_TotalBlocks -= Blocks.size();
|
||||
Blocks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
// DelayedFluidSimulator.h
|
||||
|
||||
// Interfaces to the cDelayedFluidSimulator class representing a fluid simulator that has a configurable delay
|
||||
// before simulating a block. Each tick it takes a consecutive delay "slot" and simulates only blocks in that slot.
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FluidSimulator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cDelayedFluidSimulatorChunkData :
|
||||
public cFluidSimulatorData
|
||||
{
|
||||
public:
|
||||
class cSlot
|
||||
{
|
||||
public:
|
||||
/// Returns true if the specified block is stored
|
||||
bool HasBlock(int a_RelX, int a_RelY, int a_RelZ);
|
||||
|
||||
/// Adds the specified block unless already present; returns true if added, false if the block was already present
|
||||
bool Add(int a_RelX, int a_RelY, int a_RelZ);
|
||||
|
||||
/** Array of block containers, each item stores blocks for one Z coord
|
||||
Int param is the block index (for faster duplicate comparison in Add())
|
||||
*/
|
||||
cCoordWithIntVector m_Blocks[16];
|
||||
} ;
|
||||
|
||||
cDelayedFluidSimulatorChunkData(int a_TickDelay);
|
||||
virtual ~cDelayedFluidSimulatorChunkData();
|
||||
|
||||
/// Slots, one for each delay tick, each containing the blocks to simulate
|
||||
cSlot * m_Slots;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cDelayedFluidSimulator :
|
||||
public cFluidSimulator
|
||||
{
|
||||
typedef cFluidSimulator super;
|
||||
|
||||
public:
|
||||
cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay);
|
||||
|
||||
// cSimulator overrides:
|
||||
virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
|
||||
virtual void Simulate(float a_Dt) override;
|
||||
virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
|
||||
virtual cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); }
|
||||
|
||||
protected:
|
||||
|
||||
int m_TickDelay; // Count of the m_Slots array in each ChunkData
|
||||
int m_AddSlotNum; // Index into m_Slots[] where to add new blocks in each ChunkData
|
||||
int m_SimSlotNum; // Index into m_Slots[] where to simulate blocks in each ChunkData
|
||||
|
||||
int m_TotalBlocks; // Statistics only: the total number of blocks currently queued
|
||||
|
||||
/*
|
||||
Slots:
|
||||
| 0 | 1 | ... | m_AddSlotNum | m_SimSlotNum | ... | m_TickDelay - 1 |
|
||||
adding blocks here ^ | ^ simulating here
|
||||
*/
|
||||
|
||||
/// Called from SimulateChunk() to simulate each block in one slot of blocks. Descendants override this method to provide custom simulation.
|
||||
virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "FireSimulator.h"
|
||||
#include "../World.h"
|
||||
#include "../BlockID.h"
|
||||
#include "../Defines.h"
|
||||
#include "../Chunk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Easy switch for turning on debugging logging:
|
||||
#if 0
|
||||
#define FLOG LOGD
|
||||
#else
|
||||
#define FLOG(...)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define MAX_CHANCE_REPLACE_FUEL 100000
|
||||
#define MAX_CHANCE_FLAMMABILITY 100000
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static const struct
|
||||
{
|
||||
int x, y, z;
|
||||
} gCrossCoords[] =
|
||||
{
|
||||
{ 1, 0, 0},
|
||||
{-1, 0, 0},
|
||||
{ 0, 0, 1},
|
||||
{ 0, 0, -1},
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static const struct
|
||||
{
|
||||
int x, y, z;
|
||||
} gNeighborCoords[] =
|
||||
{
|
||||
{ 1, 0, 0},
|
||||
{-1, 0, 0},
|
||||
{ 0, 1, 0},
|
||||
{ 0, -1, 0},
|
||||
{ 0, 0, 1},
|
||||
{ 0, 0, -1},
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cFireSimulator:
|
||||
|
||||
cFireSimulator::cFireSimulator(cWorld & a_World, cIniFile & a_IniFile) :
|
||||
cSimulator(a_World)
|
||||
{
|
||||
// Read params from the ini file:
|
||||
m_BurnStepTimeFuel = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeFuel", 500);
|
||||
m_BurnStepTimeNonfuel = a_IniFile.GetValueSetI("FireSimulator", "BurnStepTimeNonfuel", 100);
|
||||
m_Flammability = a_IniFile.GetValueSetI("FireSimulator", "Flammability", 50);
|
||||
m_ReplaceFuelChance = a_IniFile.GetValueSetI("FireSimulator", "ReplaceFuelChance", 50000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cFireSimulator::~cFireSimulator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
|
||||
{
|
||||
cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData();
|
||||
|
||||
int NumMSecs = (int)a_Dt;
|
||||
for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();)
|
||||
{
|
||||
int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z);
|
||||
BLOCKTYPE BlockType = a_Chunk->GetBlock(idx);
|
||||
|
||||
if (!IsAllowedBlock(BlockType))
|
||||
{
|
||||
// The block is no longer eligible (not a fire block anymore; a player probably placed a block over the fire)
|
||||
FLOG("FS: Removing block {%d, %d, %d}",
|
||||
itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
|
||||
);
|
||||
itr = Data.erase(itr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to spread the fire:
|
||||
TrySpreadFire(a_Chunk, itr->x, itr->y, itr->z);
|
||||
|
||||
itr->Data -= NumMSecs;
|
||||
if (itr->Data >= 0)
|
||||
{
|
||||
// Not yet, wait for it longer
|
||||
++itr;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Burn out the fire one step by increasing the meta:
|
||||
/*
|
||||
FLOG("FS: Fire at {%d, %d, %d} is stepping",
|
||||
itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
|
||||
);
|
||||
*/
|
||||
NIBBLETYPE BlockMeta = a_Chunk->GetMeta(idx);
|
||||
if (BlockMeta == 0x0f)
|
||||
{
|
||||
// The fire burnt out completely
|
||||
FLOG("FS: Fire at {%d, %d, %d} burnt out, removing the fire block",
|
||||
itr->x + a_ChunkX * cChunkDef::Width, itr->y, itr->z + a_ChunkZ * cChunkDef::Width
|
||||
);
|
||||
a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0);
|
||||
RemoveFuelNeighbors(a_Chunk, itr->x, itr->y, itr->z);
|
||||
itr = Data.erase(itr);
|
||||
continue;
|
||||
}
|
||||
a_Chunk->SetMeta(idx, BlockMeta + 1);
|
||||
itr->Data = GetBurnStepTime(a_Chunk, itr->x, itr->y, itr->z); // TODO: Add some randomness into this
|
||||
} // for itr - Data[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFireSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
return (a_BlockType == E_BLOCK_FIRE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFireSimulator::IsFuel(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
switch (a_BlockType)
|
||||
{
|
||||
case E_BLOCK_PLANKS:
|
||||
case E_BLOCK_LEAVES:
|
||||
case E_BLOCK_LOG:
|
||||
case E_BLOCK_WOOL:
|
||||
case E_BLOCK_BOOKCASE:
|
||||
case E_BLOCK_FENCE:
|
||||
case E_BLOCK_TNT:
|
||||
case E_BLOCK_VINES:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFireSimulator::IsForever(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
return (a_BlockType == E_BLOCK_NETHERRACK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireSimulator::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;
|
||||
BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
|
||||
if (!IsAllowedBlock(BlockType))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for duplicates:
|
||||
cFireSimulatorChunkData & ChunkData = a_Chunk->GetFireSimulatorData();
|
||||
for (cCoordWithIntList::iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr)
|
||||
{
|
||||
if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
|
||||
{
|
||||
// Already present, skip adding
|
||||
return;
|
||||
}
|
||||
} // for itr - ChunkData[]
|
||||
|
||||
FLOG("FS: Adding block {%d, %d, %d}", a_BlockX, a_BlockY, a_BlockZ);
|
||||
ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ, 100));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
bool IsBlockBelowSolid = false;
|
||||
if (a_RelY > 0)
|
||||
{
|
||||
BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
|
||||
if (IsForever(BlockBelow))
|
||||
{
|
||||
// Is burning atop of netherrack, burn forever (re-check in 10 sec)
|
||||
return 10000;
|
||||
}
|
||||
if (IsFuel(BlockBelow))
|
||||
{
|
||||
return m_BurnStepTimeFuel;
|
||||
}
|
||||
IsBlockBelowSolid = g_BlockIsSolid[BlockBelow];
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
|
||||
{
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (a_Chunk->UnboundedRelGetBlock(a_RelX + gCrossCoords[i].x, a_RelY, a_RelZ + gCrossCoords[i].z, BlockType, BlockMeta))
|
||||
{
|
||||
if (IsFuel(BlockType))
|
||||
{
|
||||
return m_BurnStepTimeFuel;
|
||||
}
|
||||
}
|
||||
} // for i - gCrossCoords[]
|
||||
|
||||
if (!IsBlockBelowSolid && (a_RelY >= 0))
|
||||
{
|
||||
// Checked through everything, nothing was flammable
|
||||
// If block below isn't solid, we can't have fire, it would be a non-fueled fire
|
||||
// SetBlock just to make sure fire doesn't spawn
|
||||
a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
|
||||
return 0;
|
||||
}
|
||||
return m_BurnStepTimeNonfuel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
/*
|
||||
if (m_World.GetTickRandomNumber(10000) > 100)
|
||||
{
|
||||
// Make the chance to spread 100x smaller
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
for (int x = a_RelX - 1; x <= a_RelX + 1; x++)
|
||||
{
|
||||
for (int z = a_RelZ - 1; z <= a_RelZ + 1; z++)
|
||||
{
|
||||
for (int y = a_RelY - 1; y <= a_RelY + 2; y++) // flames spread up one more block than around
|
||||
{
|
||||
// No need to check the coords for equality with the parent block,
|
||||
// it cannot catch fire anyway (because it's not an air block)
|
||||
|
||||
if (m_World.GetTickRandomNumber(MAX_CHANCE_FLAMMABILITY) > m_Flammability)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start the fire in the neighbor {x, y, z}
|
||||
/*
|
||||
FLOG("FS: Trying to start fire at {%d, %d, %d}.",
|
||||
x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width
|
||||
);
|
||||
*/
|
||||
if (CanStartFireInBlock(a_Chunk, x, y, z))
|
||||
{
|
||||
FLOG("FS: Starting new fire at {%d, %d, %d}.",
|
||||
x + a_Chunk->GetPosX() * cChunkDef::Width, y, z + a_Chunk->GetPosZ() * cChunkDef::Width
|
||||
);
|
||||
a_Chunk->UnboundedRelSetBlock(x, y, z, E_BLOCK_FIRE, 0);
|
||||
}
|
||||
} // for y
|
||||
} // for z
|
||||
} // for x
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++)
|
||||
{
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_Chunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta))
|
||||
{
|
||||
// Neighbor not accessible, ignore it
|
||||
continue;
|
||||
}
|
||||
if (!IsFuel(BlockType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance);
|
||||
a_Chunk->UnboundedRelSetBlock(
|
||||
a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z,
|
||||
ShouldReplaceFuel ? E_BLOCK_FIRE : E_BLOCK_AIR, 0
|
||||
);
|
||||
} // for i - Coords[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFireSimulator::CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
|
||||
{
|
||||
// The chunk is not accessible
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BlockType != E_BLOCK_AIR)
|
||||
{
|
||||
// Only an air block can be replaced by a fire block
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAYCOUNT(gNeighborCoords); i++)
|
||||
{
|
||||
if (!a_NearChunk->UnboundedRelGetBlock(a_RelX + gNeighborCoords[i].x, a_RelY + gNeighborCoords[i].y, a_RelZ + gNeighborCoords[i].z, BlockType, BlockMeta))
|
||||
{
|
||||
// Neighbor inaccessible, skip it while evaluating
|
||||
continue;
|
||||
}
|
||||
if (IsFuel(BlockType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} // for i - Coords[]
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Simulator.h"
|
||||
#include "../BlockEntities/BlockEntity.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** The fire simulator takes care of the fire blocks.
|
||||
It periodically increases their meta ("steps") until they "burn out"; it also supports the forever burning netherrack.
|
||||
Each individual fire block gets stored in per-chunk data; that list is then used for fast retrieval.
|
||||
The data value associated with each coord is used as the number of msec that the fire takes until
|
||||
it progresses to the next step (blockmeta++). This value is updated if a neighbor is changed.
|
||||
The simulator reads its parameters from the ini file given to the constructor.
|
||||
*/
|
||||
class cFireSimulator :
|
||||
public cSimulator
|
||||
{
|
||||
public:
|
||||
cFireSimulator(cWorld & a_World, cIniFile & a_IniFile);
|
||||
~cFireSimulator();
|
||||
|
||||
virtual void Simulate(float a_Dt) override {} // not used
|
||||
virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
|
||||
|
||||
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override;
|
||||
|
||||
bool IsFuel (BLOCKTYPE a_BlockType);
|
||||
bool IsForever(BLOCKTYPE a_BlockType);
|
||||
|
||||
protected:
|
||||
/// Time (in msec) that a fire block takes to burn with a fuel block into the next step
|
||||
unsigned m_BurnStepTimeFuel;
|
||||
|
||||
/// Time (in msec) that a fire block takes to burn without a fuel block into the next step
|
||||
unsigned m_BurnStepTimeNonfuel;
|
||||
|
||||
/// Chance [0..100000] of an adjacent fuel to catch fire on each tick
|
||||
int m_Flammability;
|
||||
|
||||
/// Chance [0..100000] of a fuel burning out being replaced by a new fire block instead of an air block
|
||||
int m_ReplaceFuelChance;
|
||||
|
||||
|
||||
virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
|
||||
|
||||
/// Returns the time [msec] after which the specified fire block is stepped again; based on surrounding fuels
|
||||
int GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
|
||||
|
||||
/// Tries to spread fire to a neighborhood of the specified block
|
||||
void TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
|
||||
|
||||
/// Removes all burnable blocks neighboring the specified block
|
||||
void RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
|
||||
|
||||
/** Returns true if a fire can be started in the specified block,
|
||||
that is, it is an air block and has fuel next to it.
|
||||
Note that a_NearChunk may be a chunk neighbor to the block specified!
|
||||
The coords are relative to a_NearChunk but not necessarily in it.
|
||||
*/
|
||||
bool CanStartFireInBlock(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Stores individual fire blocks in the chunk; the int data is used as the time [msec] the fire takes to step to another stage (blockmeta++)
|
||||
typedef cCoordWithIntList cFireSimulatorChunkData;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
|
||||
// FloodyFluidSimulator.cpp
|
||||
|
||||
// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :)
|
||||
// http://forum.mc-server.org/showthread.php?tid=565
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#include "FloodyFluidSimulator.h"
|
||||
#include "../World.h"
|
||||
#include "../Chunk.h"
|
||||
#include "../BlockArea.h"
|
||||
#include "../Blocks/BlockHandler.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Enable or disable detailed logging
|
||||
#if 0
|
||||
#define FLOG LOGD
|
||||
#else
|
||||
#define FLOG(...)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cFloodyFluidSimulator::cFloodyFluidSimulator(
|
||||
cWorld & a_World,
|
||||
BLOCKTYPE a_Fluid,
|
||||
BLOCKTYPE a_StationaryFluid,
|
||||
NIBBLETYPE a_Falloff,
|
||||
int a_TickDelay,
|
||||
int a_NumNeighborsForSource
|
||||
) :
|
||||
super(a_World, a_Fluid, a_StationaryFluid, a_TickDelay),
|
||||
m_Falloff(a_Falloff),
|
||||
m_NumNeighborsForSource(a_NumNeighborsForSource)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
FLOG("Simulating block {%d, %d, %d}: block %d, meta %d",
|
||||
a_Chunk->GetPosX() * cChunkDef::Width + a_RelX, a_RelY, a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ,
|
||||
a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ),
|
||||
a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ)
|
||||
);
|
||||
|
||||
NIBBLETYPE MyMeta = a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ);
|
||||
if (!IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ)))
|
||||
{
|
||||
// Can happen - if a block is scheduled for simulating and gets replaced in the meantime.
|
||||
FLOG(" BadBlockType exit");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MyMeta != 0)
|
||||
{
|
||||
// Source blocks aren't checked for tributaries, others are.
|
||||
if (CheckTributaries(a_Chunk, a_RelX, a_RelY, a_RelZ, MyMeta))
|
||||
{
|
||||
// Has no tributary, has been decreased (in CheckTributaries()),
|
||||
// no more processing needed (neighbors have been scheduled by the decrease)
|
||||
FLOG(" CheckTributaries exit");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// New meta for the spreading to neighbors:
|
||||
// If this is a source block or was falling, the new meta is just the falloff
|
||||
// Otherwise it is the current meta plus falloff (may be larger than max height, will be checked later)
|
||||
NIBBLETYPE NewMeta = ((MyMeta == 0) || ((MyMeta & 0x08) != 0)) ? m_Falloff : (MyMeta + m_Falloff);
|
||||
bool SpreadFurther = true;
|
||||
if (a_RelY > 0)
|
||||
{
|
||||
BLOCKTYPE Below = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);
|
||||
if (IsPassableForFluid(Below) || IsBlockLava(Below) || IsBlockWater(Below))
|
||||
{
|
||||
// Spread only down, possibly washing away what's there or turning lava to stone / cobble / obsidian:
|
||||
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY - 1, a_RelZ, 8);
|
||||
SpreadFurther = false;
|
||||
}
|
||||
// If source creation is on, check for it here:
|
||||
else if (
|
||||
(m_NumNeighborsForSource > 0) && // Source creation is on
|
||||
(MyMeta == m_Falloff) && // Only exactly one block away from a source (fast bail-out)
|
||||
!IsPassableForFluid(Below) && // Only exactly 1 block deep
|
||||
CheckNeighborsForSource(a_Chunk, a_RelX, a_RelY, a_RelZ) // Did we create a source?
|
||||
)
|
||||
{
|
||||
// We created a source, no more spreading is to be done now
|
||||
// Also has been re-scheduled for ticking in the next wave, so no marking is needed
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (SpreadFurther && (NewMeta < 8))
|
||||
{
|
||||
// Spread to the neighbors:
|
||||
SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, NewMeta);
|
||||
SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, NewMeta);
|
||||
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, NewMeta);
|
||||
SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, NewMeta);
|
||||
}
|
||||
|
||||
// Mark as processed:
|
||||
a_Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, MyMeta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta)
|
||||
{
|
||||
// If we have a section above, check if there's fluid above this block that would feed it:
|
||||
if (a_RelY < cChunkDef::Height - 1)
|
||||
{
|
||||
if (IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ)))
|
||||
{
|
||||
// This block is fed from above, no more processing needed
|
||||
FLOG(" Fed from above");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Not fed from above, check if there's a feed from the side (but not if it's a downward-flowing block):
|
||||
if (a_MyMeta != 8)
|
||||
{
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
static const Vector3i Coords[] =
|
||||
{
|
||||
Vector3i( 1, 0, 0),
|
||||
Vector3i(-1, 0, 0),
|
||||
Vector3i( 0, 0, 1),
|
||||
Vector3i( 0, 0, -1),
|
||||
} ;
|
||||
for (int i = 0; i < ARRAYCOUNT(Coords); i++)
|
||||
{
|
||||
if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (IsAllowedBlock(BlockType) && IsHigherMeta(BlockMeta, a_MyMeta))
|
||||
{
|
||||
// This block is fed, no more processing needed
|
||||
FLOG(" Fed from {%d, %d, %d}, type %d, meta %d",
|
||||
a_Chunk->GetPosX() * cChunkDef::Width + a_RelX + Coords[i].x,
|
||||
a_RelY,
|
||||
a_Chunk->GetPosZ() * cChunkDef::Width + a_RelZ + Coords[i].z,
|
||||
BlockType, BlockMeta
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} // for i - Coords[]
|
||||
} // if not fed from above
|
||||
|
||||
// Block is not fed, decrease by m_Falloff levels:
|
||||
if (a_MyMeta >= 8)
|
||||
{
|
||||
FLOG(" Not fed and downwards, turning into non-downwards meta %d", m_Falloff);
|
||||
a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, m_Falloff);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_MyMeta += m_Falloff;
|
||||
if (a_MyMeta < 8)
|
||||
{
|
||||
FLOG(" Not fed, decreasing from %d to %d", a_MyMeta - m_Falloff, a_MyMeta);
|
||||
a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, a_MyMeta);
|
||||
}
|
||||
else
|
||||
{
|
||||
FLOG(" Not fed, meta %d, erasing altogether", a_MyMeta);
|
||||
a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta)
|
||||
{
|
||||
ASSERT(a_NewMeta <= 8); // Invalid meta values
|
||||
ASSERT(a_NewMeta > 0); // Source blocks aren't spread
|
||||
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta))
|
||||
{
|
||||
// Chunk not available
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsAllowedBlock(BlockType))
|
||||
{
|
||||
if ((BlockMeta == a_NewMeta) || IsHigherMeta(BlockMeta, a_NewMeta))
|
||||
{
|
||||
// Don't spread there, there's already a higher or same level there
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check water - lava interaction:
|
||||
if (m_FluidBlock == E_BLOCK_LAVA)
|
||||
{
|
||||
if (IsBlockWater(BlockType))
|
||||
{
|
||||
// Lava flowing into water, change to stone / cobblestone based on direction:
|
||||
BLOCKTYPE NewBlock = (a_NewMeta == 8) ? E_BLOCK_STONE : E_BLOCK_COBBLESTONE;
|
||||
FLOG(" Lava flowing into water, turning water at rel {%d, %d, %d} into stone",
|
||||
a_RelX, a_RelY, a_RelZ,
|
||||
ItemTypeToString(NewBlock).c_str()
|
||||
);
|
||||
a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0);
|
||||
m_World.BroadcastSoundEffect("random.fizz", a_RelX * 8, a_RelY * 8, a_RelZ * 8, 0.5f, 1.5f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (m_FluidBlock == E_BLOCK_WATER)
|
||||
{
|
||||
if (IsBlockLava(BlockType))
|
||||
{
|
||||
// Water flowing into lava, change to cobblestone / obsidian based on dest block:
|
||||
BLOCKTYPE NewBlock = (BlockMeta == 0) ? E_BLOCK_OBSIDIAN : E_BLOCK_COBBLESTONE;
|
||||
FLOG(" Water flowing into lava, turning lava at rel {%d, %d, %d} into %s",
|
||||
a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str()
|
||||
);
|
||||
a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0);
|
||||
m_World.BroadcastSoundEffect("random.fizz", a_RelX * 8, a_RelY * 8, a_RelZ * 8, 0.5f, 1.5f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(!"Unknown fluid!");
|
||||
}
|
||||
|
||||
if (!IsPassableForFluid(BlockType))
|
||||
{
|
||||
// Can't spread there
|
||||
return;
|
||||
}
|
||||
|
||||
// Wash away the block there, if possible:
|
||||
if (CanWashAway(BlockType))
|
||||
{
|
||||
cBlockHandler * Handler = BlockHandler(BlockType);
|
||||
if (Handler->DoesDropOnUnsuitable())
|
||||
{
|
||||
Handler->DropBlock(
|
||||
&m_World, NULL,
|
||||
a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX,
|
||||
a_RelY,
|
||||
a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ
|
||||
);
|
||||
}
|
||||
} // if (CanWashAway)
|
||||
|
||||
// Spread:
|
||||
FLOG(" Spreading to {%d, %d, %d} with meta %d",
|
||||
a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX,
|
||||
a_RelY,
|
||||
a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ,
|
||||
a_NewMeta
|
||||
);
|
||||
a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
FLOG(" Checking neighbors for source creation");
|
||||
|
||||
static const Vector3i NeighborCoords[] =
|
||||
{
|
||||
Vector3i(-1, 0, 0),
|
||||
Vector3i( 1, 0, 0),
|
||||
Vector3i( 0, 0, -1),
|
||||
Vector3i( 0, 0, 1),
|
||||
} ;
|
||||
|
||||
int NumNeeded = m_NumNeighborsForSource;
|
||||
for (int i = 0; i < ARRAYCOUNT(NeighborCoords); i++)
|
||||
{
|
||||
int x = a_RelX + NeighborCoords[i].x;
|
||||
int y = a_RelY + NeighborCoords[i].y;
|
||||
int z = a_RelZ + NeighborCoords[i].z;
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_Chunk->UnboundedRelGetBlock(x, y, z, BlockType, BlockMeta))
|
||||
{
|
||||
// Neighbor not available, skip it
|
||||
continue;
|
||||
}
|
||||
// FLOG(" Neighbor at {%d, %d, %d}: %s", x, y, z, ItemToFullString(cItem(BlockType, 1, BlockMeta)).c_str());
|
||||
if ((BlockMeta == 0) && IsAnyFluidBlock(BlockType))
|
||||
{
|
||||
NumNeeded--;
|
||||
// FLOG(" Found a neighbor source at {%d, %d, %d}, NumNeeded := %d", x, y, z, NumNeeded);
|
||||
if (NumNeeded == 0)
|
||||
{
|
||||
// Found enough, turn into a source and bail out
|
||||
// FLOG(" Found enough neighbor sources, turning into a source");
|
||||
a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// FLOG(" Not enough neighbors for turning into a source, NumNeeded = %d", NumNeeded);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
// FloodyFluidSimulator.h
|
||||
|
||||
// Interfaces to the cFloodyFluidSimulator that represents a fluid simulator that tries to flood everything :)
|
||||
// http://forum.mc-server.org/showthread.php?tid=565
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DelayedFluidSimulator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class cBlockArea;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cFloodyFluidSimulator :
|
||||
public cDelayedFluidSimulator
|
||||
{
|
||||
typedef cDelayedFluidSimulator super;
|
||||
|
||||
public:
|
||||
cFloodyFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, NIBBLETYPE a_Falloff, int a_TickDelay, int a_NumNeighborsForSource);
|
||||
|
||||
protected:
|
||||
NIBBLETYPE m_Falloff;
|
||||
int m_NumNeighborsForSource;
|
||||
|
||||
// cDelayedFluidSimulator overrides:
|
||||
virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override;
|
||||
|
||||
/// Checks tributaries, if not fed, decreases the block's level and returns true
|
||||
bool CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta);
|
||||
|
||||
/// Spreads into the specified block, if the blocktype there allows. a_Area is for checking.
|
||||
void SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta);
|
||||
|
||||
/// Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so
|
||||
bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#include "FluidSimulator.h"
|
||||
#include "../World.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cFluidSimulator::cFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
|
||||
super(a_World),
|
||||
m_FluidBlock(a_Fluid),
|
||||
m_StationaryFluidBlock(a_StationaryFluid)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFluidSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
return ((a_BlockType == m_FluidBlock) || (a_BlockType == m_StationaryFluidBlock));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFluidSimulator::CanWashAway(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
switch (a_BlockType)
|
||||
{
|
||||
case E_BLOCK_BROWN_MUSHROOM:
|
||||
case E_BLOCK_CACTUS:
|
||||
case E_BLOCK_COBWEB:
|
||||
case E_BLOCK_CROPS:
|
||||
case E_BLOCK_DEAD_BUSH:
|
||||
case E_BLOCK_RAIL:
|
||||
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_SNOW:
|
||||
case E_BLOCK_SUGARCANE:
|
||||
case E_BLOCK_TALL_GRASS:
|
||||
case E_BLOCK_TORCH:
|
||||
case E_BLOCK_YELLOW_FLOWER:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFluidSimulator::IsSolidBlock(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
return !IsPassableForFluid(a_BlockType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFluidSimulator::IsPassableForFluid(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
return (
|
||||
(a_BlockType == E_BLOCK_AIR) ||
|
||||
(a_BlockType == E_BLOCK_FIRE) ||
|
||||
IsAllowedBlock(a_BlockType) ||
|
||||
CanWashAway(a_BlockType)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFluidSimulator::IsHigherMeta(NIBBLETYPE a_Meta1, NIBBLETYPE a_Meta2)
|
||||
{
|
||||
if (a_Meta1 == 0)
|
||||
{
|
||||
// Source block is higher than anything, even itself.
|
||||
return true;
|
||||
}
|
||||
if ((a_Meta1 & 0x08) != 0)
|
||||
{
|
||||
// Falling fluid is higher than anything, including self
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a_Meta2 == 0)
|
||||
{
|
||||
// Second block is a source and first block isn't
|
||||
return false;
|
||||
}
|
||||
if ((a_Meta2 & 0x08) != 0)
|
||||
{
|
||||
// Second block is falling and the first one is neither a source nor falling
|
||||
return false;
|
||||
}
|
||||
|
||||
// All special cases have been handled, now it's just a raw comparison:
|
||||
return (a_Meta1 < a_Meta2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO Not working very well yet :s
|
||||
Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over)
|
||||
{
|
||||
if ((a_Y < 0) || (a_Y >= cChunkDef::Height))
|
||||
{
|
||||
return NONE;
|
||||
}
|
||||
BLOCKTYPE BlockID = m_World.GetBlock(a_X, a_Y, a_Z);
|
||||
if (!IsAllowedBlock(BlockID)) // No Fluid -> No Flowing direction :D
|
||||
{
|
||||
return NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
Disabled because of causing problems and being useless atm
|
||||
char BlockBelow = m_World.GetBlock(a_X, a_Y - 1, a_Z); //If there is nothing or fluid below it -> dominating flow is down :D
|
||||
if (BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow))
|
||||
return Y_MINUS;
|
||||
*/
|
||||
|
||||
NIBBLETYPE LowestPoint = m_World.GetBlockMeta(a_X, a_Y, a_Z); //Current Block Meta so only lower points will be counted
|
||||
int X = 0, Y = 0, Z = 0; //Lowest Pos will be stored here
|
||||
|
||||
if (IsAllowedBlock(m_World.GetBlock(a_X, a_Y + 1, a_Z)) && a_Over) //check for upper block to flow because this also affects the flowing direction
|
||||
{
|
||||
return GetFlowingDirection(a_X, a_Y + 1, a_Z, false);
|
||||
}
|
||||
|
||||
std::vector< Vector3i * > Points;
|
||||
|
||||
Points.reserve(4); //Already allocate 4 places :D
|
||||
|
||||
//add blocks around the checking pos
|
||||
Points.push_back(new Vector3i(a_X - 1, a_Y, a_Z));
|
||||
Points.push_back(new Vector3i(a_X + 1, a_Y, a_Z));
|
||||
Points.push_back(new Vector3i(a_X, a_Y, a_Z + 1));
|
||||
Points.push_back(new Vector3i(a_X, a_Y, a_Z - 1));
|
||||
|
||||
for (std::vector<Vector3i *>::iterator it = Points.begin(); it < Points.end(); it++)
|
||||
{
|
||||
Vector3i *Pos = (*it);
|
||||
char BlockID = m_World.GetBlock(Pos->x, Pos->y, Pos->z);
|
||||
if(IsAllowedBlock(BlockID))
|
||||
{
|
||||
char Meta = m_World.GetBlockMeta(Pos->x, Pos->y, Pos->z);
|
||||
|
||||
if(Meta > LowestPoint)
|
||||
{
|
||||
LowestPoint = Meta;
|
||||
X = Pos->x;
|
||||
Y = Pos->y;
|
||||
Z = Pos->z;
|
||||
}
|
||||
}else if(BlockID == E_BLOCK_AIR)
|
||||
{
|
||||
LowestPoint = 9; //This always dominates
|
||||
X = Pos->x;
|
||||
Y = Pos->y;
|
||||
Z = Pos->z;
|
||||
|
||||
}
|
||||
delete Pos;
|
||||
}
|
||||
|
||||
if (LowestPoint == m_World.GetBlockMeta(a_X, a_Y, a_Z))
|
||||
return NONE;
|
||||
|
||||
if (a_X - X > 0)
|
||||
{
|
||||
return X_MINUS;
|
||||
}
|
||||
|
||||
if (a_X - X < 0)
|
||||
{
|
||||
return X_PLUS;
|
||||
}
|
||||
|
||||
if (a_Z - Z > 0)
|
||||
{
|
||||
return Z_MINUS;
|
||||
}
|
||||
|
||||
if (a_Z - Z < 0)
|
||||
{
|
||||
return Z_PLUS;
|
||||
}
|
||||
|
||||
return NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Simulator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum Direction
|
||||
{
|
||||
X_PLUS,
|
||||
X_MINUS,
|
||||
Y_PLUS,
|
||||
Y_MINUS,
|
||||
Z_PLUS,
|
||||
Z_MINUS,
|
||||
NONE
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** This is a base class for all fluid simulator data classes.
|
||||
Needed so that cChunk can properly delete instances of fluid simulator data, no matter what simulator it's using
|
||||
*/
|
||||
class cFluidSimulatorData
|
||||
{
|
||||
public:
|
||||
virtual ~cFluidSimulatorData() {}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cFluidSimulator :
|
||||
public cSimulator
|
||||
{
|
||||
typedef cSimulator super;
|
||||
|
||||
public:
|
||||
cFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid);
|
||||
|
||||
// cSimulator overrides:
|
||||
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override;
|
||||
|
||||
/// Gets the flowing direction. If a_Over is true also the block over the current block affects the direction (standard)
|
||||
virtual Direction GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over = true);
|
||||
|
||||
/// Creates a ChunkData object for the simulator to use. The simulator returns the correct object type.
|
||||
virtual cFluidSimulatorData * CreateChunkData(void) { return NULL; }
|
||||
|
||||
bool IsFluidBlock (BLOCKTYPE a_BlockType) const { return (a_BlockType == m_FluidBlock); }
|
||||
bool IsStationaryFluidBlock(BLOCKTYPE a_BlockType) const { return (a_BlockType == m_StationaryFluidBlock); }
|
||||
bool IsAnyFluidBlock (BLOCKTYPE a_BlockType) const { return ((a_BlockType == m_FluidBlock) || (a_BlockType == m_StationaryFluidBlock)); }
|
||||
|
||||
static bool CanWashAway(BLOCKTYPE a_BlockType);
|
||||
|
||||
bool IsSolidBlock (BLOCKTYPE a_BlockType);
|
||||
bool IsPassableForFluid(BLOCKTYPE a_BlockType);
|
||||
|
||||
/// Returns true if a_Meta1 is a higher fluid than a_Meta2. Takes source blocks into account.
|
||||
bool IsHigherMeta(NIBBLETYPE a_Meta1, NIBBLETYPE a_Meta2);
|
||||
|
||||
protected:
|
||||
BLOCKTYPE m_FluidBlock; // The fluid block type that needs simulating
|
||||
BLOCKTYPE m_StationaryFluidBlock; // The fluid block type that indicates no simulation is needed
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
// NoopFluidSimulator.h
|
||||
|
||||
// Declares the cNoopFluidSimulator class representing a fluid simulator that performs nothing, it ignores all blocks
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FluidSimulator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cNoopFluidSimulator :
|
||||
public cFluidSimulator
|
||||
{
|
||||
typedef cFluidSimulator super;
|
||||
|
||||
public:
|
||||
cNoopFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
|
||||
super(a_World, a_Fluid, a_StationaryFluid)
|
||||
{
|
||||
}
|
||||
|
||||
// cSimulator overrides:
|
||||
virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override {}
|
||||
virtual void Simulate(float a_Dt) override {}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,86 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Simulator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cRedstoneSimulator :
|
||||
public cSimulator
|
||||
{
|
||||
typedef cSimulator super;
|
||||
public:
|
||||
cRedstoneSimulator(cWorld & a_World);
|
||||
~cRedstoneSimulator();
|
||||
|
||||
virtual void Simulate( float a_Dt ) override;
|
||||
virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override { return true; }
|
||||
|
||||
virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
|
||||
|
||||
enum eRedstoneDirection
|
||||
{
|
||||
REDSTONE_NONE = 0,
|
||||
REDSTONE_X_POS = 0x1,
|
||||
REDSTONE_X_NEG = 0x2,
|
||||
REDSTONE_Z_POS = 0x4,
|
||||
REDSTONE_Z_NEG = 0x8,
|
||||
};
|
||||
eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
eRedstoneDirection GetWireDirection(const Vector3i & a_Pos) { return GetWireDirection(a_Pos.x, a_Pos.y, a_Pos.z); }
|
||||
|
||||
static bool IsRepeaterPointingTo (const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos);
|
||||
static bool IsRepeaterPointingAway(const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos);
|
||||
static NIBBLETYPE RepeaterRotationToMetaData(double a_Rotation);
|
||||
static Vector3i GetRepeaterDirection(NIBBLETYPE a_MetaData);
|
||||
static NIBBLETYPE LeverDirectionToMetaData(char a_Dir);
|
||||
static bool IsLeverOn(cWorld * a_World, const Vector3i & a_BlockPos);
|
||||
static bool IsLeverOn(NIBBLETYPE a_BlockMeta);
|
||||
|
||||
|
||||
private:
|
||||
struct sRepeaterChange
|
||||
{
|
||||
Vector3i Position;
|
||||
int Ticks;
|
||||
bool bPowerOn;
|
||||
bool bPowerOffNextTime;
|
||||
};
|
||||
|
||||
typedef std::deque <Vector3i> BlockList;
|
||||
|
||||
typedef std::deque< sRepeaterChange > RepeaterList;
|
||||
RepeaterList m_SetRepeaters;
|
||||
|
||||
void SetRepeater(const Vector3i & a_Position, int a_Ticks, bool a_bPowerOn);
|
||||
|
||||
virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override {}
|
||||
|
||||
void HandleChange( const Vector3i & a_BlockPos );
|
||||
BlockList RemoveCurrent( const Vector3i & a_BlockPos );
|
||||
|
||||
bool PowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock, char a_Power );
|
||||
int UnPowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock );
|
||||
|
||||
bool IsPowered( const Vector3i & a_BlockPos, bool a_bOnlyByWire = false );
|
||||
bool IsPowering( const Vector3i & a_PowerPos, const Vector3i & a_BlockPos, eRedstoneDirection a_WireDirection, bool a_bOnlyByWire );
|
||||
|
||||
BlockList m_Blocks;
|
||||
BlockList m_BlocksBuffer;
|
||||
|
||||
BlockList m_RefreshPistons;
|
||||
BlockList m_RefreshDropSpensers;
|
||||
|
||||
BlockList m_RefreshTorchesAround;
|
||||
|
||||
void RefreshTorchesAround( const Vector3i & a_BlockPos );
|
||||
|
||||
// TODO: The entire simulator is synchronized, no need to lock data structures; remove this
|
||||
cCriticalSection m_CS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Simulator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// 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, 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, NIBBLETYPE a_BlockMeta);
|
||||
|
||||
/** 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Simulator.h"
|
||||
#include "../World.h"
|
||||
#include "../Vector3i.h"
|
||||
#include "../BlockID.h"
|
||||
#include "../Defines.h"
|
||||
#include "../Chunk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSimulator::cSimulator(cWorld & a_World)
|
||||
: m_World(a_World)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSimulator::~cSimulator()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
|
||||
{
|
||||
AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
|
||||
AddBlock(a_BlockX - 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 1, a_BlockZ));
|
||||
AddBlock(a_BlockX + 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 1, a_BlockZ));
|
||||
AddBlock(a_BlockX, a_BlockY, a_BlockZ - 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 1));
|
||||
AddBlock(a_BlockX, a_BlockY, a_BlockZ + 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 1));
|
||||
if (a_BlockY > 0)
|
||||
{
|
||||
AddBlock(a_BlockX, a_BlockY - 1, a_BlockZ, a_Chunk);
|
||||
}
|
||||
if (a_BlockY < cChunkDef::Height - 1)
|
||||
{
|
||||
AddBlock(a_BlockX, a_BlockY + 1, a_BlockZ, a_Chunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Vector3i.h"
|
||||
#include "../../iniFile/iniFile.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cWorld;
|
||||
class cChunk;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cSimulator
|
||||
{
|
||||
public:
|
||||
cSimulator(cWorld & a_World);
|
||||
virtual ~cSimulator();
|
||||
|
||||
/// Called in each tick, a_Dt is the time passed since the last tick, in msec
|
||||
virtual void Simulate(float a_Dt) = 0;
|
||||
|
||||
/// Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available
|
||||
virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) {};
|
||||
|
||||
/// Called when a block changes
|
||||
virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk);
|
||||
|
||||
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) = 0;
|
||||
|
||||
protected:
|
||||
friend class cChunk; // Calls AddBlock() in its WakeUpSimulators() function, to speed things up
|
||||
|
||||
/// Called to simulate a new block
|
||||
virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) = 0;
|
||||
|
||||
cWorld & m_World;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "SimulatorManager.h"
|
||||
#include "../World.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSimulatorManager::cSimulatorManager(cWorld & a_World) :
|
||||
m_World(a_World),
|
||||
m_Ticks(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSimulatorManager::~cSimulatorManager()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSimulatorManager::Simulate(float a_Dt)
|
||||
{
|
||||
m_Ticks++;
|
||||
for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr )
|
||||
{
|
||||
if ((m_Ticks % itr->second) == 0)
|
||||
{
|
||||
itr->first->Simulate(a_Dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSimulatorManager::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
|
||||
{
|
||||
// m_Ticks has already been increased in Simulate()
|
||||
for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr )
|
||||
{
|
||||
if ((m_Ticks % itr->second) == 0)
|
||||
{
|
||||
itr->first->SimulateChunk(a_Dt, a_ChunkX, a_ChunkZ, a_Chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSimulatorManager::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
|
||||
{
|
||||
for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr )
|
||||
{
|
||||
itr->first->WakeUp(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSimulatorManager::RegisterSimulator(cSimulator * a_Simulator, int a_Rate)
|
||||
{
|
||||
m_Simulators.push_back(std::make_pair(a_Simulator, a_Rate));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
// cSimulatorManager.h
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
#include "Simulator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd: Chunk.h
|
||||
class cChunk;
|
||||
|
||||
// fwd: World.h
|
||||
class cWorld;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cSimulatorManager
|
||||
{
|
||||
public:
|
||||
cSimulatorManager(cWorld & a_World);
|
||||
~cSimulatorManager();
|
||||
|
||||
void Simulate(float a_Dt);
|
||||
|
||||
void SimulateChunk(float a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk);
|
||||
|
||||
void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk);
|
||||
|
||||
void RegisterSimulator(cSimulator * a_Simulator, int a_Rate); // Takes ownership of the simulator object!
|
||||
|
||||
protected:
|
||||
typedef std::vector <std::pair<cSimulator *, int> > cSimulators;
|
||||
|
||||
cWorld & m_World;
|
||||
cSimulators m_Simulators;
|
||||
long long m_Ticks;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
// VaporizeFluidSimulator.cpp
|
||||
|
||||
// Implements the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air
|
||||
|
||||
#include "Globals.h"
|
||||
#include "VaporizeFluidSimulator.h"
|
||||
#include "../Chunk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cVaporizeFluidSimulator::cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid) :
|
||||
super(a_World, a_Fluid, a_StationaryFluid)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cVaporizeFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
|
||||
{
|
||||
if (a_Chunk == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
|
||||
int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
|
||||
BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_BlockY, RelZ);
|
||||
if (
|
||||
(BlockType == m_FluidBlock) ||
|
||||
(BlockType == m_StationaryFluidBlock)
|
||||
)
|
||||
{
|
||||
a_Chunk->SetBlock(RelX, a_BlockY, RelZ, E_BLOCK_AIR, 0);
|
||||
a_Chunk->BroadcastSoundEffect("random.fizz", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.6f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cVaporizeFluidSimulator::Simulate(float a_Dt)
|
||||
{
|
||||
// Nothing needed
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
// VaporizeFluidSimulator.h
|
||||
|
||||
// Declares the cVaporizeFluidSimulator class representing a fluid simulator that replaces all fluid blocks with air
|
||||
// Useful for water simulation in the Nether
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FluidSimulator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cVaporizeFluidSimulator :
|
||||
public cFluidSimulator
|
||||
{
|
||||
typedef cFluidSimulator super;
|
||||
|
||||
public:
|
||||
cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid);
|
||||
|
||||
// cSimulator overrides:
|
||||
virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
|
||||
virtual void Simulate(float a_Dt) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user