void cFireSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) { if ((a_Chunk == nullptr) || !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)); }
void cSandSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) { if ((a_Chunk == nullptr) || !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)); }
void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData(); int NumMSecs = static_cast<int>(a_Dt.count()); for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();) { int x = itr->x; int y = itr->y; int z = itr->z; BLOCKTYPE BlockType = a_Chunk->GetBlock(x, y, z); 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(x, y, z); 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; } if ((itr->y > 0) && (!DoesBurnForever(a_Chunk->GetBlock(itr->x, itr->y - 1, itr->z)))) { a_Chunk->SetMeta(x, y, z, BlockMeta + 1); } itr->Data = GetBurnStepTime(a_Chunk, itr->x, itr->y, itr->z); // TODO: Add some randomness into this } // for itr - Data[] }
bool cFluidSimulator::IsPassableForFluid(BLOCKTYPE a_BlockType) { return ( (a_BlockType == E_BLOCK_AIR) || (a_BlockType == E_BLOCK_FIRE) || IsAllowedBlock(a_BlockType) || CanWashAway(a_BlockType) ); }
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[] }
void cSandSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData(); if (ChunkData.empty()) { return; } int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; for (cSandSimulatorChunkData::const_iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr) { BLOCKTYPE BlockType = a_Chunk->GetBlock(itr->x, itr->y, itr->z); if (!IsAllowedBlock(BlockType) || (itr->y <= 0)) { continue; } BLOCKTYPE BlockBelow = (itr->y > 0) ? a_Chunk->GetBlock(itr->x, itr->y - 1, itr->z) : E_BLOCK_AIR; if (CanStartFallingThrough(BlockBelow)) { if (m_IsInstantFall) { DoInstantFall(a_Chunk, itr->x, itr->y, itr->z); continue; } Vector3i Pos; Pos.x = itr->x + BaseX; Pos.y = itr->y; Pos.z = itr->z + BaseZ; /* 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 -= static_cast<int>(ChunkData.size()); ChunkData.clear(); }
// 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, 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; Z = Pos->z; } } else if (BlockID == E_BLOCK_AIR) { LowestPoint = 9; // This always dominates X = Pos->x; Z = Pos->z; } delete Pos; Pos = nullptr; } 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; }
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 a_NearChunk = a_NearChunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); if ((a_NearChunk == NULL) || (!a_NearChunk->IsValid())) { // Chunk not available return; } const int BlockX = a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX; const int BlockZ = a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ; BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; a_NearChunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta); 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->SetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); a_NearChunk->BroadcastSoundEffect("random.fizz", BlockX * 8, a_RelY * 8, BlockZ * 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->SetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); a_NearChunk->BroadcastSoundEffect("random.fizz", BlockX * 8, a_RelY * 8, BlockZ * 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()) { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockInServerPluginInterface PluginInterface(m_World); Handler->DropBlock( ChunkInterface, m_World, PluginInterface, NULL, BlockX, a_RelY, BlockZ ); } } // if (CanWashAway) // Spread: FLOG(" Spreading to {%d, %d, %d} with meta %d", BlockX, a_RelY, BlockZ, a_NewMeta); a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); m_World.GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, a_NearChunk); HardenBlock(a_NearChunk, a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); }
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 (size_t 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; }