summaryrefslogtreecommitdiffstats
path: root/src/ChunkMap.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/ChunkMap.cpp585
1 files changed, 403 insertions, 182 deletions
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 5797eb453..e695f0ab2 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -15,6 +15,9 @@
#include "Blocks/BlockHandler.h"
#include "MobCensus.h"
#include "MobSpawner.h"
+#include "BoundingBox.h"
+
+#include "Entities/Pickup.h"
#ifndef _WIN32
#include <cstdlib> // abs
@@ -131,7 +134,7 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ)
-cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
+cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
// No need to lock m_CSLayers, since it's already locked by the operation that called us
ASSERT(m_CSLayers.IsLockedByCurrentThread());
@@ -802,6 +805,10 @@ void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
/// Wakes up the simulators for the specified area of blocks
void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ)
{
+ // Limit the Y coords:
+ a_MinBlockY = std::max(a_MinBlockY, 0);
+ a_MaxBlockY = std::min(a_MaxBlockY, cChunkDef::Height - 1);
+
cSimulatorManager * SimMgr = m_World->GetSimulatorManager();
int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ;
cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ);
@@ -894,17 +901,34 @@ void cChunkMap::SetChunkData(
bool a_MarkDirty
)
{
- cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
- if (Chunk == NULL)
{
- return;
- }
- Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_BlockEntities);
-
- if (a_MarkDirty)
- {
- Chunk->MarkDirty();
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_BlockEntities);
+
+ if (a_MarkDirty)
+ {
+ Chunk->MarkDirty();
+ }
+
+ // Notify relevant ChunkStays:
+ for (cChunkStays::iterator itr = m_ChunkStays.begin(); itr != m_ChunkStays.end(); )
+ {
+ if ((*itr)->ChunkAvailable(a_ChunkX, a_ChunkZ))
+ {
+ cChunkStays::iterator cur = itr;
+ ++itr;
+ m_ChunkStays.erase(cur);
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_ChunkStays[]
}
// Notify plugins of the chunk becoming available
@@ -1117,6 +1141,21 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player)
BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
{
+ // First check if it isn't queued in the m_FastSetBlockQueue:
+ {
+ int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
+ int ChunkX, ChunkY, ChunkZ;
+ cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
+ ChunkY = 0;
+ cCSLock Lock(m_CSFastSetBlock);
+ for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
+ {
+ if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
+ {
+ return itr->BlockType;
+ }
+ } // for itr - m_FastSetBlockQueue[]
+ }
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ );
@@ -1135,6 +1174,17 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ)
{
+ // First check if it isn't queued in the m_FastSetBlockQueue:
+ {
+ cCSLock Lock(m_CSFastSetBlock);
+ for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
+ {
+ if ((itr->x == a_BlockX) && (itr->y == a_BlockY) && (itr->z == a_BlockZ))
+ {
+ return itr->BlockMeta;
+ }
+ } // for itr - m_FastSetBlockQueue[]
+ }
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ );
@@ -1207,8 +1257,14 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP
-void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
+void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
{
+ cChunkInterface ChunkInterface(this);
+ if (a_BlockType == E_BLOCK_AIR)
+ {
+ BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(ChunkInterface, a_WorldInterface, a_BlockX, a_BlockY, a_BlockZ);
+ }
+
int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ );
@@ -1219,6 +1275,7 @@ void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_B
Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta );
m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, Chunk);
}
+ BlockHandler(a_BlockType)->OnPlaced(ChunkInterface, a_WorldInterface, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
}
@@ -1319,8 +1376,9 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks)
break;
}
case E_BLOCK_LEAVES:
+ case E_BLOCK_NEW_LEAVES:
{
- if (itr->BlockType == E_BLOCK_LOG)
+ if ((itr->BlockType == E_BLOCK_LOG) || (itr->BlockType == E_BLOCK_NEW_LOG))
{
Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
}
@@ -1337,10 +1395,10 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks)
EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ)
{
int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ;
- cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ );
+ cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
return Chunk->GetBiomeAt(X, Z);
@@ -1355,6 +1413,63 @@ EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ)
+bool cChunkMap::SetBiomeAt(int a_BlockX, int a_BlockZ, EMCSBiome a_Biome)
+{
+ int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ Chunk->SetBiomeAt(X, Z, a_Biome);
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+bool cChunkMap::SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome)
+{
+ // Translate coords to relative:
+ int Y = 0;
+ int MinChunkX, MinChunkZ, MinX = a_MinX, MinZ = a_MinZ;
+ int MaxChunkX, MaxChunkZ, MaxX = a_MaxX, MaxZ = a_MaxZ;
+ cChunkDef::AbsoluteToRelative(MinX, Y, MinZ, MinChunkX, MinChunkZ);
+ cChunkDef::AbsoluteToRelative(MaxX, Y, MaxZ, MaxChunkX, MaxChunkZ);
+
+ // Go through all chunks, set:
+ bool res = true;
+ cCSLock Lock(m_CSLayers);
+ for (int x = MinChunkX; x <= MaxChunkX; x++)
+ {
+ int MinRelX = (x == MinChunkX) ? MinX : 0;
+ int MaxRelX = (x == MaxChunkX) ? MaxX : cChunkDef::Width - 1;
+ for (int z = MinChunkZ; z <= MaxChunkZ; z++)
+ {
+ int MinRelZ = (z == MinChunkZ) ? MinZ : 0;
+ int MaxRelZ = (z == MaxChunkZ) ? MaxZ : cChunkDef::Width - 1;
+ cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z);
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ Chunk->SetAreaBiome(MinRelX, MaxRelX, MinRelZ, MaxRelZ, a_Biome);
+ }
+ else
+ {
+ res = false;
+ }
+ } // for z
+ } // for x
+ return res;
+}
+
+
+
+
+
bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
{
bool res = true;
@@ -1624,105 +1739,203 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
{
return;
}
-
+
+ bool ShouldDestroyBlocks = true;
+
// Don't explode if the explosion center is inside a liquid block:
- switch (m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ)))
+ if (IsBlockLiquid(m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ))))
{
- case E_BLOCK_WATER:
- case E_BLOCK_STATIONARY_WATER:
- case E_BLOCK_LAVA:
- case E_BLOCK_STATIONARY_LAVA:
- {
- return;
- }
+ ShouldDestroyBlocks = false;
}
-
- cBlockArea area;
+
+ int ExplosionSizeInt = (int)ceil(a_ExplosionSize);
+ int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
+
int bx = (int)floor(a_BlockX);
int by = (int)floor(a_BlockY);
int bz = (int)floor(a_BlockZ);
- int ExplosionSizeInt = (int) ceil(a_ExplosionSize);
- int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
- a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt);
+
int MinY = std::max((int)floor(a_BlockY - ExplosionSizeInt), 0);
int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1);
- area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt));
- for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++)
+
+ if (ShouldDestroyBlocks)
{
- for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++)
+ cBlockArea area;
+
+ a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt);
+
+ area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt));
+ for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++)
{
- if ((by + y >= cChunkDef::Height) || (by + y < 0))
- {
- // Outside of the world
- continue;
- }
- for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++)
+ for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++)
{
- if ((x * x + y * y + z * z) > ExplosionSizeSq)
+ if ((by + y >= cChunkDef::Height) || (by + y < 0))
{
- // Too far away
+ // Outside of the world
continue;
}
-
- BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z);
- switch (Block)
+ for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++)
{
- case E_BLOCK_TNT:
- {
- // Activate the TNT, with a random fuse between 10 to 30 game ticks
- double FuseTime = (double)(10 + m_World->GetTickRandomNumber(20)) / 20;
- m_World->SpawnPrimedTNT(a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5, FuseTime);
- area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
- a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
- break;
- }
- case E_BLOCK_OBSIDIAN:
- case E_BLOCK_BEDROCK:
- case E_BLOCK_WATER:
- case E_BLOCK_LAVA:
+ if ((x * x + y * y + z * z) > ExplosionSizeSq)
{
- // These blocks are not affected by explosions
- break;
+ // Too far away
+ continue;
}
- case E_BLOCK_STATIONARY_WATER:
+ BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z);
+ switch (Block)
{
- // Turn into simulated water:
- area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER);
- break;
- }
-
- case E_BLOCK_STATIONARY_LAVA:
- {
- // Turn into simulated lava:
- area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA);
- break;
- }
-
- case E_BLOCK_AIR:
- {
- // No pickups for air
- break;
- }
-
- default:
- {
- if (m_World->GetTickRandomNumber(100) <= 25) // 25% chance of pickups
+ case E_BLOCK_TNT:
+ {
+ // Activate the TNT, with a random fuse between 10 to 30 game ticks
+ int FuseTime = 10 + m_World->GetTickRandomNumber(20);
+ m_World->SpawnPrimedTNT(a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5, FuseTime);
+ area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
+ a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
+ break;
+ }
+
+ case E_BLOCK_OBSIDIAN:
+ case E_BLOCK_BEDROCK:
+ case E_BLOCK_WATER:
+ case E_BLOCK_LAVA:
{
- cItems Drops;
- cBlockHandler * Handler = BlockHandler(Block);
+ // These blocks are not affected by explosions
+ break;
+ }
- Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); // Stone becomes cobblestone, coal ore becomes coal, etc.
- m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z);
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ // Turn into simulated water:
+ area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER);
+ break;
}
- area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
- a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
- }
- } // switch (BlockType)
- } // for z
- } // for y
- } // for x
- area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt);
+
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ // Turn into simulated lava:
+ area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA);
+ break;
+ }
+
+ case E_BLOCK_AIR:
+ {
+ // No pickups for air
+ break;
+ }
+
+ default:
+ {
+ if (m_World->GetTickRandomNumber(100) <= 25) // 25% chance of pickups
+ {
+ cItems Drops;
+ cBlockHandler * Handler = BlockHandler(Block);
+
+ Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); // Stone becomes cobblestone, coal ore becomes coal, etc.
+ m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z);
+ }
+ else if ((m_World->GetTNTShrapnelLevel() > slNone) && (m_World->GetTickRandomNumber(100) < 20)) // 20% chance of flinging stuff around
+ {
+ if (!cBlockInfo::FullyOccupiesVoxel(Block))
+ {
+ break;
+ }
+ else if ((m_World->GetTNTShrapnelLevel() == slGravityAffectedOnly) && ((Block != E_BLOCK_SAND) && (Block != E_BLOCK_GRAVEL)))
+ {
+ break;
+ }
+ m_World->SpawnFallingBlock(bx + x, by + y + 5, bz + z, Block, area.GetBlockMeta(bx + x, by + y, bz + z));
+ }
+
+ area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
+ a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
+ break;
+
+ }
+ } // switch (BlockType)
+ } // for z
+ } // for y
+ } // for x
+ area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt);
+ }
+
+ class cTNTDamageCallback :
+ public cEntityCallback
+ {
+ public:
+ cTNTDamageCallback(cBoundingBox & a_bbTNT, Vector3d a_ExplosionPos, int a_ExplosionSize) :
+ m_bbTNT(a_bbTNT),
+ m_ExplosionPos(a_ExplosionPos),
+ m_ExplosionSize(a_ExplosionSize)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (a_Entity->IsPickup())
+ {
+ if (((cPickup *)a_Entity)->GetAge() < 20) // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
+ {
+ return false;
+ }
+ }
+
+ Vector3d EntityPos = a_Entity->GetPosition();
+ cBoundingBox bbEntity(EntityPos, a_Entity->GetWidth() / 2, a_Entity->GetHeight());
+
+ if (!m_bbTNT.IsInside(bbEntity)) // IsInside actually acts like DoesSurround
+ {
+ return false;
+ }
+
+ Vector3d AbsoluteEntityPos(abs(EntityPos.x), abs(EntityPos.y), abs(EntityPos.z));
+
+ // Work out how far we are from the edge of the TNT's explosive effect
+ AbsoluteEntityPos -= m_ExplosionPos;
+
+ // All to positive
+ AbsoluteEntityPos.x = abs(AbsoluteEntityPos.x);
+ AbsoluteEntityPos.y = abs(AbsoluteEntityPos.y);
+ AbsoluteEntityPos.z = abs(AbsoluteEntityPos.z);
+
+ double FinalDamage = (((1 / AbsoluteEntityPos.x) + (1 / AbsoluteEntityPos.y) + (1 / AbsoluteEntityPos.z)) * 2) * m_ExplosionSize;
+
+ // Clip damage values
+ if (FinalDamage > a_Entity->GetMaxHealth())
+ FinalDamage = a_Entity->GetMaxHealth();
+ else if (FinalDamage < 0)
+ FinalDamage = 0;
+
+ if (!a_Entity->IsTNT() && !a_Entity->IsFallingBlock()) // Don't apply damage to other TNT entities and falling blocks, they should be invincible
+ {
+ a_Entity->TakeDamage(dtExplosion, NULL, (int)FinalDamage, 0);
+ }
+
+ // Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
+ Vector3d distance_explosion = a_Entity->GetPosition() - m_ExplosionPos;
+ if (distance_explosion.SqrLength() < 4096.0)
+ {
+ distance_explosion.Normalize();
+ distance_explosion *= m_ExplosionSize * m_ExplosionSize;
+
+ a_Entity->AddSpeed(distance_explosion);
+ }
+
+ return false;
+ }
+
+ protected:
+ cBoundingBox & m_bbTNT;
+ Vector3d m_ExplosionPos;
+ int m_ExplosionSize;
+ };
+
+ cBoundingBox bbTNT(Vector3d(a_BlockX, a_BlockY, a_BlockZ), 0.5, 1);
+ bbTNT.Expand(ExplosionSizeInt * 2, ExplosionSizeInt * 2, ExplosionSizeInt * 2);
+
+
+ cTNTDamageCallback TNTDamageCallback(bbTNT, Vector3d(a_BlockX, a_BlockY, a_BlockZ), ExplosionSizeInt);
+ ForEachEntity(TNTDamageCallback);
// Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391):
WakeUpSimulatorsInArea(
@@ -1986,6 +2199,42 @@ bool cChunkMap::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, c
+bool cChunkMap::DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
+bool cChunkMap::DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlowerPotCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithFlowerPotAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{
int ChunkX, ChunkZ;
@@ -2088,24 +2337,6 @@ bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const ASt
-void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay)
-{
- cCSLock Lock(m_CSLayers);
- for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr)
- {
- cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
- if (Chunk == NULL)
- {
- continue;
- }
- Chunk->Stay(a_Stay);
- }
-}
-
-
-
-
-
void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
@@ -2692,113 +2923,103 @@ void cChunkMap::cChunkLayer::UnloadUnusedChunks(void)
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cChunkStay:
-
-cChunkStay::cChunkStay(cWorld * a_World) :
- m_World(a_World),
- m_IsEnabled(false)
-{
-}
-
-
-
-
-
-cChunkStay::~cChunkStay()
+void cChunkMap::FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
- Clear();
+ cCSLock Lock(m_CSFastSetBlock);
+ m_FastSetBlockQueue.push_back(sSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta));
}
-void cChunkStay::Clear(void)
+void cChunkMap::FastSetQueuedBlocks()
{
- if (m_IsEnabled)
+ // Asynchronously set blocks:
+ sSetBlockList FastSetBlockQueueCopy;
{
- Disable();
+ cCSLock Lock(m_CSFastSetBlock);
+ std::swap(FastSetBlockQueueCopy, m_FastSetBlockQueue);
+ }
+ this->FastSetBlocks(FastSetBlockQueueCopy);
+ if (!FastSetBlockQueueCopy.empty())
+ {
+ // Some blocks failed, store them for next tick:
+ cCSLock Lock(m_CSFastSetBlock);
+ m_FastSetBlockQueue.splice(m_FastSetBlockQueue.end(), FastSetBlockQueueCopy);
}
- m_Chunks.clear();
}
-void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cChunkMap::AddChunkStay(cChunkStay & a_ChunkStay)
{
- ASSERT(!m_IsEnabled);
-
- for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ cCSLock Lock(m_CSLayers);
+
+ // Add it to the list:
+ ASSERT(std::find(m_ChunkStays.begin(), m_ChunkStays.end(), &a_ChunkStay) == m_ChunkStays.end()); // Has not yet been added
+ m_ChunkStays.push_back(&a_ChunkStay);
+
+ // Schedule all chunks to be loaded / generated, and mark each as locked:
+ const cChunkCoordsVector & WantedChunks = a_ChunkStay.GetChunks();
+ for (cChunkCoordsVector::const_iterator itr = WantedChunks.begin(); itr != WantedChunks.end(); ++itr)
{
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ if (Chunk == NULL)
{
- // Already present
- return;
+ continue;
}
- } // for itr - Chunks[]
- m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ Chunk->Stay(true);
+ if (Chunk->IsValid())
+ {
+ a_ChunkStay.ChunkAvailable(itr->m_ChunkX, itr->m_ChunkZ);
+ }
+ } // for itr - WantedChunks[]
}
-void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+/** Removes the specified cChunkStay descendant from the internal list of ChunkStays. */
+void cChunkMap::DelChunkStay(cChunkStay & a_ChunkStay)
{
- ASSERT(!m_IsEnabled);
-
- for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ cCSLock Lock(m_CSLayers);
+
+ // Remove from the list of active chunkstays:
+ bool HasFound = false;
+ for (cChunkStays::iterator itr = m_ChunkStays.begin(), end = m_ChunkStays.end(); itr != end; ++itr)
{
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
+ if (*itr == &a_ChunkStay)
{
- // Found, un-"stay"
- m_Chunks.erase(itr);
- return;
+ m_ChunkStays.erase(itr);
+ HasFound = true;
+ break;
}
- } // for itr - m_Chunks[]
-}
-
-
-
-
-
-void cChunkStay::Enable(void)
-{
- ASSERT(!m_IsEnabled);
+ } // for itr - m_ChunkStays[]
- m_World->ChunksStay(*this, true);
- m_IsEnabled = true;
-}
-
-
-
-
-
-void cChunkStay::Load(void)
-{
- for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ if (!HasFound)
{
- m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
- } // for itr - m_Chunks[]
-}
-
-
-
-
-
-void cChunkStay::Disable(void)
-{
- ASSERT(m_IsEnabled);
+ ASSERT(!"Removing a cChunkStay that hasn't been added!");
+ return;
+ }
- m_World->ChunksStay(*this, false);
- m_IsEnabled = false;
+ // Unmark all contained chunks:
+ const cChunkCoordsVector & Chunks = a_ChunkStay.GetChunks();
+ for (cChunkCoordsVector::const_iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr)
+ {
+ cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ if (Chunk == NULL)
+ {
+ continue;
+ }
+ Chunk->Stay(false);
+ } // for itr - Chunks[]
}
+