Moved source to src

This commit is contained in:
Alexander Harkness
2013-11-24 14:19:41 +00:00
parent 1480d6d64d
commit 675b4aa878
469 changed files with 0 additions and 0 deletions
+158
View File
@@ -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();
}
}
+82
View File
@@ -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;
} ;
+374
View File
@@ -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;
}
+75
View File
@@ -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;
+330
View File
@@ -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;
}
+53
View File
@@ -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);
} ;
+212
View File
@@ -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;
}
+75
View File
@@ -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
} ;
+36
View File
@@ -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
+86
View File
@@ -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;
};
+309
View File
@@ -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
}
+63
View File
@@ -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;
+51
View File
@@ -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);
}
}
+46
View File
@@ -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;
} ;
+80
View File
@@ -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));
}
+52
View File
@@ -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;
};
+53
View File
@@ -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
}
+34
View File
@@ -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;
} ;