summaryrefslogtreecommitdiffstats
path: root/src/BlockArea.cpp
diff options
context:
space:
mode:
authorAlexander Harkness <bearbin@gmail.com>2013-11-24 15:19:41 +0100
committerAlexander Harkness <bearbin@gmail.com>2013-11-24 15:19:41 +0100
commit675b4aa878f16291ce33fced48a2bc7425f635ae (patch)
tree409914df27a98f65adf866da669429c4de141b6f /src/BlockArea.cpp
parentLineBlockTracer: Using the coord-based block faces. (diff)
downloadcuberite-675b4aa878f16291ce33fced48a2bc7425f635ae.tar
cuberite-675b4aa878f16291ce33fced48a2bc7425f635ae.tar.gz
cuberite-675b4aa878f16291ce33fced48a2bc7425f635ae.tar.bz2
cuberite-675b4aa878f16291ce33fced48a2bc7425f635ae.tar.lz
cuberite-675b4aa878f16291ce33fced48a2bc7425f635ae.tar.xz
cuberite-675b4aa878f16291ce33fced48a2bc7425f635ae.tar.zst
cuberite-675b4aa878f16291ce33fced48a2bc7425f635ae.zip
Diffstat (limited to 'src/BlockArea.cpp')
-rw-r--r--src/BlockArea.cpp2124
1 files changed, 2124 insertions, 0 deletions
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
new file mode 100644
index 000000000..5c15adfef
--- /dev/null
+++ b/src/BlockArea.cpp
@@ -0,0 +1,2124 @@
+
+// BlockArea.cpp
+
+// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
+// The object also supports writing the blockdata back into cWorld, even into other coords
+
+#include "Globals.h"
+#include "BlockArea.h"
+#include "World.h"
+#include "OSSupport/GZipFile.h"
+#include "WorldStorage/FastNBT.h"
+#include "Blocks/BlockHandler.h"
+
+
+
+
+
+// This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
+/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
+template<typename Combinator> void InternalMergeBlocks(
+ BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
+ NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
+ int a_SizeX, int a_SizeY, int a_SizeZ,
+ int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ,
+ int a_DstOffX, int a_DstOffY, int a_DstOffZ,
+ int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
+ int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ,
+ Combinator a_Combinator
+)
+{
+ for (int y = 0; y < a_SizeY; y++)
+ {
+ int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ;
+ int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ;
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX;
+ int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX;
+ int SrcIdx = SrcBaseZ + a_SrcOffX;
+ int DstIdx = DstBaseZ + a_DstOffX;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
+ ++DstIdx;
+ ++SrcIdx;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msOverwrite merging
+static void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msFillAir merging
+static void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ if (a_DstType == E_BLOCK_AIR)
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+ }
+ // "else" is the default, already in place
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msImprint merging
+static void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ if (a_SrcType != E_BLOCK_AIR)
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_SrcMeta;
+ }
+ // "else" is the default, already in place
+}
+
+
+
+
+
+/// Combinator used for cBlockArea::msLake merging
+static void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
+{
+ // Sponge is the NOP block
+ if (a_SrcType == E_BLOCK_SPONGE)
+ {
+ return;
+ }
+
+ // Air is always hollowed out
+ if (a_SrcType == E_BLOCK_AIR)
+ {
+ a_DstType = E_BLOCK_AIR;
+ a_DstMeta = 0;
+ return;
+ }
+
+ // Water and lava are never overwritten
+ switch (a_DstType)
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ return;
+ }
+ }
+
+ // Water and lava always overwrite
+ switch (a_SrcType)
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ a_DstType = a_SrcType;
+ a_DstMeta = a_DstMeta;
+ return;
+ }
+ }
+
+ if (a_SrcType == E_BLOCK_STONE)
+ {
+ switch (a_DstType)
+ {
+ case E_BLOCK_DIRT:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_MYCELIUM:
+ {
+ a_DstType = E_BLOCK_STONE;
+ a_DstMeta = 0;
+ return;
+ }
+ }
+ }
+ // Everything else is left as it is
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBlockArea:
+
+cBlockArea::cBlockArea(void) :
+ m_SizeX(0),
+ m_SizeY(0),
+ m_SizeZ(0),
+ m_BlockTypes(NULL),
+ m_BlockMetas(NULL),
+ m_BlockLight(NULL),
+ m_BlockSkyLight(NULL)
+{
+}
+
+
+
+
+
+cBlockArea::~cBlockArea()
+{
+ Clear();
+}
+
+
+
+
+
+void cBlockArea::Clear(void)
+{
+ delete[] m_BlockTypes; m_BlockTypes = NULL;
+ delete[] m_BlockMetas; m_BlockMetas = NULL;
+ delete[] m_BlockLight; m_BlockLight = NULL;
+ delete[] m_BlockSkyLight; m_BlockSkyLight = NULL;
+ m_SizeX = 0;
+ m_SizeY = 0;
+ m_SizeZ = 0;
+}
+
+
+
+
+
+void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
+{
+ Clear();
+ int BlockCount = a_SizeX * a_SizeY * a_SizeZ;
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ m_BlockTypes = new BLOCKTYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockTypes[i] = E_BLOCK_AIR;
+ }
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ m_BlockMetas = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockMetas[i] = 0;
+ }
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ m_BlockLight = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockLight[i] = 0;
+ }
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ m_BlockSkyLight = new NIBBLETYPE[BlockCount];
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockSkyLight[i] = 0x0f;
+ }
+ }
+ m_SizeX = a_SizeX;
+ m_SizeY = a_SizeY;
+ m_SizeZ = a_SizeZ;
+ m_OriginX = 0;
+ m_OriginY = 0;
+ m_OriginZ = 0;
+}
+
+
+
+
+
+void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
+{
+ m_OriginX = a_OriginX;
+ m_OriginY = a_OriginY;
+ m_OriginZ = a_OriginZ;
+}
+
+
+
+
+
+bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
+{
+ // Normalize the coords:
+ if (a_MinBlockX > a_MaxBlockX)
+ {
+ std::swap(a_MinBlockX, a_MaxBlockX);
+ }
+ if (a_MinBlockY > a_MaxBlockY)
+ {
+ std::swap(a_MinBlockY, a_MaxBlockY);
+ }
+ if (a_MinBlockZ > a_MaxBlockZ)
+ {
+ std::swap(a_MinBlockZ, a_MaxBlockZ);
+ }
+
+ // Include the Max coords:
+ a_MaxBlockX += 1;
+ a_MaxBlockY += 1;
+ a_MaxBlockZ += 1;
+
+ // Check coords validity:
+ if (a_MinBlockY < 0)
+ {
+ LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MinBlockY = 0;
+ }
+ else if (a_MinBlockY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MinBlockY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MinBlockY = cChunkDef::Height - 1;
+ }
+ if (a_MaxBlockY < 0)
+ {
+ LOGWARNING("%s: MaxBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MaxBlockY = 0;
+ }
+ else if (a_MaxBlockY >= cChunkDef::Height)
+ {
+ LOGWARNING("%s: MaxBlockY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MaxBlockY = cChunkDef::Height - 1;
+ }
+
+ // Allocate the needed memory:
+ Clear();
+ if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes))
+ {
+ return false;
+ }
+ m_OriginX = a_MinBlockX;
+ m_OriginY = a_MinBlockY;
+ m_OriginZ = a_MinBlockZ;
+ cChunkReader Reader(*this);
+
+ // Convert block coords to chunks coords:
+ int MinChunkX, MaxChunkX;
+ int MinChunkZ, MaxChunkZ;
+ cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ);
+ cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
+
+ // Query block data:
+ if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
+ {
+ Clear();
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
+{
+ ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have?
+ a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have
+
+ // Check coords validity:
+ if (a_MinBlockY < 0)
+ {
+ LOGWARNING("%s: MinBlockY less than zero, adjusting to zero", __FUNCTION__);
+ a_MinBlockY = 0;
+ }
+ else if (a_MinBlockY >= cChunkDef::Height - m_SizeY)
+ {
+ LOGWARNING("%s: MinBlockY + m_SizeY more than chunk height, adjusting to chunk height", __FUNCTION__);
+ a_MinBlockY = cChunkDef::Height - m_SizeY - 1;
+ }
+
+ return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
+}
+
+
+
+
+
+void cBlockArea::CopyTo(cBlockArea & a_Into) const
+{
+ if (&a_Into == this)
+ {
+ LOGWARNING("Trying to copy a cBlockArea into self, ignoring.");
+ return;
+ }
+
+ a_Into.Clear();
+ a_Into.SetSize(m_SizeX, m_SizeY, m_SizeZ, GetDataTypes());
+ a_Into.m_OriginX = m_OriginX;
+ a_Into.m_OriginY = m_OriginY;
+ a_Into.m_OriginZ = m_OriginZ;
+ int BlockCount = GetBlockCount();
+ if (HasBlockTypes())
+ {
+ memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE));
+ }
+ if (HasBlockMetas())
+ {
+ memcpy(a_Into.m_BlockMetas, m_BlockMetas, BlockCount * sizeof(NIBBLETYPE));
+ }
+ if (HasBlockLights())
+ {
+ memcpy(a_Into.m_BlockLight, m_BlockLight, BlockCount * sizeof(NIBBLETYPE));
+ }
+ if (HasBlockSkyLights())
+ {
+ memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE));
+ }
+}
+
+
+
+
+
+void cBlockArea::CopyFrom(const cBlockArea & a_From)
+{
+ a_From.CopyTo(*this);
+}
+
+
+
+
+
+void cBlockArea::DumpToRawFile(const AString & a_FileName)
+{
+ cFile f;
+ if (!f.Open(a_FileName, cFile::fmWrite))
+ {
+ LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str());
+ return;
+ }
+ UInt32 SizeX = ntohl(m_SizeX);
+ UInt32 SizeY = ntohl(m_SizeY);
+ UInt32 SizeZ = ntohl(m_SizeZ);
+ f.Write(&SizeX, 4);
+ f.Write(&SizeY, 4);
+ f.Write(&SizeZ, 4);
+ unsigned char DataTypes = GetDataTypes();
+ f.Write(&DataTypes, 1);
+ int NumBlocks = GetBlockCount();
+ if (HasBlockTypes())
+ {
+ f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE));
+ }
+ if (HasBlockMetas())
+ {
+ f.Write(m_BlockMetas, NumBlocks);
+ }
+ if (HasBlockLights())
+ {
+ f.Write(m_BlockLight, NumBlocks);
+ }
+ if (HasBlockSkyLights())
+ {
+ f.Write(m_BlockSkyLight, NumBlocks);
+ }
+}
+
+
+
+
+
+bool cBlockArea::LoadFromSchematicFile(const AString & a_FileName)
+{
+ // Un-GZip the contents:
+ AString Contents;
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmRead))
+ {
+ LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ int NumBytesRead = File.ReadRestOfFile(Contents);
+ if (NumBytesRead < 0)
+ {
+ LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead);
+ return false;
+ }
+ File.Close();
+
+ // Parse the NBT:
+ cParsedNBT NBT(Contents.data(), Contents.size());
+ if (!NBT.IsValid())
+ {
+ LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+
+ return LoadFromSchematicNBT(NBT);
+}
+
+
+
+
+
+bool cBlockArea::SaveToSchematicFile(const AString & a_FileName)
+{
+ cFastNBTWriter Writer("Schematic");
+ Writer.AddShort("Width", m_SizeX);
+ Writer.AddShort("Height", m_SizeY);
+ Writer.AddShort("Length", m_SizeZ);
+ Writer.AddString("Materials", "Alpha");
+ if (HasBlockTypes())
+ {
+ Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(GetBlockCount(), 0);
+ Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
+ }
+ if (HasBlockMetas())
+ {
+ Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(GetBlockCount(), 0);
+ Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
+ }
+ // TODO: Save entities and block entities
+ Writer.BeginList("Entities", TAG_Compound);
+ Writer.EndList();
+ Writer.BeginList("TileEntities", TAG_Compound);
+ Writer.EndList();
+ Writer.Finish();
+
+ // Save to file
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmWrite))
+ {
+ LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str());
+ return false;
+ }
+ if (!File.Write(Writer.GetResult()))
+ {
+ LOG("Cannot write data to file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ if (
+ (a_AddMinX + a_SubMaxX >= m_SizeX) ||
+ (a_AddMinY + a_SubMaxY >= m_SizeY) ||
+ (a_AddMinZ + a_SubMaxZ >= m_SizeZ)
+ )
+ {
+ LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d",
+ m_SizeX, m_SizeY, m_SizeZ,
+ a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ
+ );
+ return;
+ }
+
+ if (HasBlockTypes())
+ {
+ CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockMetas())
+ {
+ CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockLights())
+ {
+ CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ if (HasBlockSkyLights())
+ {
+ CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
+ }
+ m_OriginX += a_AddMinX;
+ m_OriginY += a_AddMinY;
+ m_OriginZ += a_AddMinZ;
+ m_SizeX -= a_AddMinX + a_SubMaxX;
+ m_SizeY -= a_AddMinY + a_SubMaxY;
+ m_SizeZ -= a_AddMinZ + a_SubMaxZ;
+}
+
+
+
+
+
+void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ if (HasBlockTypes())
+ {
+ ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockMetas())
+ {
+ ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockLights())
+ {
+ ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ if (HasBlockSkyLights())
+ {
+ ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
+ }
+ m_OriginX -= a_SubMinX;
+ m_OriginY -= a_SubMinY;
+ m_OriginZ -= a_SubMinZ;
+ m_SizeX += a_SubMinX + a_AddMaxX;
+ m_SizeY += a_SubMinY + a_AddMaxY;
+ m_SizeZ += a_SubMinZ + a_AddMaxZ;
+}
+
+
+
+
+
+void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy)
+{
+ // Block types are compulsory, block metas are voluntary
+ if (!HasBlockTypes() || !a_Src.HasBlockTypes())
+ {
+ LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
+ return;
+ }
+
+ // Dst is *this, Src is a_Src
+ int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
+ int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
+ int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
+
+ int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
+ int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
+ int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
+
+ int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
+ int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
+ int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
+
+ const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas();
+ NIBBLETYPE * DstMetas = m_BlockMetas;
+ bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL));
+
+ if (IsDummyMetas)
+ {
+ SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()];
+ DstMetas = new NIBBLETYPE[GetBlockCount()];
+ }
+
+ switch (a_Strategy)
+ {
+ case msOverwrite:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorOverwrite
+ );
+ break;
+ } // case msOverwrite
+
+ case msFillAir:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorFillAir
+ );
+ break;
+ } // case msFillAir
+
+ case msImprint:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorImprint
+ );
+ break;
+ } // case msImprint
+
+ case msLake:
+ {
+ InternalMergeBlocks(
+ m_BlockTypes, a_Src.GetBlockTypes(),
+ DstMetas, SrcMetas,
+ SizeX, SizeY, SizeZ,
+ SrcOffX, SrcOffY, SrcOffZ,
+ DstOffX, DstOffY, DstOffZ,
+ a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
+ m_SizeX, m_SizeY, m_SizeZ,
+ MergeCombinatorLake
+ );
+ break;
+ } // case msLake
+
+ default:
+ {
+ LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
+ ASSERT(!"Unknown block area merge strategy");
+ break;
+ }
+ } // switch (a_Strategy)
+
+ if (IsDummyMetas)
+ {
+ delete[] SrcMetas;
+ delete[] DstMetas;
+ }
+}
+
+
+
+
+
+void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight)
+{
+ if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
+ {
+ LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
+ __FUNCTION__, a_DataTypes, GetDataTypes()
+ );
+ a_DataTypes = a_DataTypes & GetDataTypes();
+ }
+
+ int BlockCount = GetBlockCount();
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockTypes[i] = a_BlockType;
+ }
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockMetas[i] = a_BlockMeta;
+ }
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockLight[i] = a_BlockLight;
+ }
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ for (int i = 0; i < BlockCount; i++)
+ {
+ m_BlockSkyLight[i] = a_BlockSkyLight;
+ }
+ }
+}
+
+
+
+
+
+void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
+ {
+ LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
+ __FUNCTION__, a_DataTypes, GetDataTypes()
+ );
+ a_DataTypes = a_DataTypes & GetDataTypes();
+ }
+
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight;
+ } // for x, z, y
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
+ {
+ m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight;
+ } // for x, z, y
+ }
+}
+
+
+
+
+
+void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ // Bresenham-3D algorithm for drawing lines:
+ int dx = abs(a_RelX2 - a_RelX1);
+ int dy = abs(a_RelY2 - a_RelY1);
+ int dz = abs(a_RelZ2 - a_RelZ1);
+ int sx = (a_RelX1 < a_RelX2) ? 1 : -1;
+ int sy = (a_RelY1 < a_RelY2) ? 1 : -1;
+ int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1;
+ int err = dx - dz;
+
+ if (dx >= std::max(dy, dz)) // x dominant
+ {
+ int yd = dy - dx / 2;
+ int zd = dz - dx / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelX1 == a_RelX2)
+ {
+ break;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ a_RelY1 += sy;
+ yd -= dx;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ a_RelZ1 += sz;
+ zd -= dx;
+ }
+
+ // move along x
+ a_RelX1 += sx;
+ yd += dy;
+ zd += dz;
+ }
+ }
+ else if (dy >= std::max(dx, dz)) // y dominant
+ {
+ int xd = dx - dy / 2;
+ int zd = dz - dy / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelY1 == a_RelY2)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ a_RelX1 += sx;
+ xd -= dy;
+ }
+
+ if (zd >= 0) // move along z
+ {
+ a_RelZ1 += sz;
+ zd -= dy;
+ }
+
+ // move along y
+ a_RelY1 += sy;
+ xd += dx;
+ zd += dz;
+ }
+ }
+ else
+ {
+ // z dominant
+ ASSERT(dz >= std::max(dx, dy));
+ int xd = dx - dz / 2;
+ int yd = dy - dz / 2;
+
+ while (true)
+ {
+ RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
+
+ if (a_RelZ1 == a_RelZ2)
+ {
+ break;
+ }
+
+ if (xd >= 0) // move along x
+ {
+ a_RelX1 += sx;
+ xd -= dz;
+ }
+
+ if (yd >= 0) // move along y
+ {
+ a_RelY1 += sy;
+ yd -= dz;
+ }
+
+ // move along z
+ a_RelZ1 += sz;
+ xd += dx;
+ yd += dy;
+ }
+ } // if (which dimension is dominant)
+}
+
+
+
+
+
+void cBlockArea::RotateCCW(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to rotate, just use the NoMeta function
+ RotateCCWNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int OldIdx = MakeIndex(x, y, z);
+ NewTypes[NewIdx] = m_BlockTypes[OldIdx];
+ NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]);
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewTypes;
+ delete[] NewMetas;
+
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::RotateCW(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to rotate, just use the NoMeta function
+ RotateCWNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ;
+ int OldIdx = MakeIndex(x, y, z);
+ NewTypes[NewIdx] = m_BlockTypes[OldIdx];
+ NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]);
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewTypes;
+ delete[] NewMetas;
+
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::MirrorXY(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorXYNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfZ = m_SizeZ / 2;
+ int MaxZ = m_SizeZ - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(x, y, MaxZ - z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::MirrorXZ(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorXZNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfY = m_SizeY / 2;
+ int MaxY = m_SizeY - 1;
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(x, MaxY - y, z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::MirrorYZ(void)
+{
+ if (!HasBlockTypes())
+ {
+ LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
+ return;
+ }
+
+ if (!HasBlockMetas())
+ {
+ // There are no blockmetas to mirror, just use the NoMeta function
+ MirrorYZNoMeta();
+ return;
+ }
+
+ // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
+ int HalfX = m_SizeX / 2;
+ int MaxX = m_SizeX - 1;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ int Idx1 = MakeIndex(x, y, z);
+ int Idx2 = MakeIndex(MaxX - x, y, z);
+ std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
+ NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]);
+ NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]);
+ m_BlockMetas[Idx1] = Meta2;
+ m_BlockMetas[Idx2] = Meta1;
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::RotateCCWNoMeta(void)
+{
+ if (HasBlockTypes())
+ {
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockTypes, NewTypes);
+ delete[] NewTypes;
+ }
+ if (HasBlockMetas())
+ {
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = m_SizeX - x - 1;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = z;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ } // for y
+ } // for z
+ } // for x
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewMetas;
+ }
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::RotateCWNoMeta(void)
+{
+ if (HasBlockTypes())
+ {
+ BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewTypes[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockTypes[MakeIndex(x, y, z)];
+ } // for y
+ } // for x
+ } // for z
+ std::swap(m_BlockTypes, NewTypes);
+ delete[] NewTypes;
+ }
+ if (HasBlockMetas())
+ {
+ NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ];
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int NewX = m_SizeZ - z - 1;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ int NewZ = x;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ NewMetas[NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ] = m_BlockMetas[MakeIndex(x, y, z)];
+ } // for y
+ } // for x
+ } // for z
+ std::swap(m_BlockMetas, NewMetas);
+ delete[] NewMetas;
+ }
+ std::swap(m_SizeX, m_SizeZ);
+}
+
+
+
+
+
+void cBlockArea::MirrorXYNoMeta(void)
+{
+ int HalfZ = m_SizeZ / 2;
+ int MaxZ = m_SizeZ - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < HalfZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::MirrorXZNoMeta(void)
+{
+ int HalfY = m_SizeY / 2;
+ int MaxY = m_SizeY - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < HalfY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::MirrorYZNoMeta(void)
+{
+ int HalfX = m_SizeX / 2;
+ int MaxX = m_SizeX - 1;
+ if (HasBlockTypes())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockTypes)
+
+ if (HasBlockMetas())
+ {
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ for (int x = 0; x < HalfX; x++)
+ {
+ std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]);
+ } // for x
+ } // for z
+ } // for y
+ } // if (HasBlockMetas)
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
+{
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ return;
+ }
+ m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_BlockType;
+}
+
+
+
+
+
+void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
+{
+ SetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, m_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, m_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, m_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, m_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight)
+{
+ SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, m_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight)
+{
+ SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, m_BlockSkyLight);
+}
+
+
+
+
+
+BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ return E_BLOCK_AIR;
+ }
+ return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)];
+}
+
+
+
+
+
+BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetRelBlockType(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockMetas);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockMetas);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockSkyLight);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
+{
+ return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ SetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cBlockArea::SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__);
+ }
+ else
+ {
+ m_BlockTypes[idx] = a_BlockType;
+ }
+ if (m_BlockMetas == NULL)
+ {
+ LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__);
+ }
+ else
+ {
+ m_BlockMetas[idx] = a_BlockMeta;
+ }
+}
+
+
+
+
+
+void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
+{
+ return GetRelBlockTypeMeta(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_BlockType, a_BlockMeta);
+}
+
+
+
+
+
+void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
+{
+ int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if (m_BlockTypes == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockTypes have not been read!");
+ a_BlockType = E_BLOCK_AIR;
+ }
+ else
+ {
+ a_BlockType = m_BlockTypes[idx];
+ }
+
+ if (m_BlockMetas == NULL)
+ {
+ LOGWARNING("cBlockArea: BlockMetas have not been read!");
+ a_BlockMeta = 0;
+ }
+ else
+ {
+ a_BlockMeta = m_BlockMetas[idx];
+ }
+}
+
+
+
+
+
+int cBlockArea::GetDataTypes(void) const
+{
+ int res = 0;
+ if (m_BlockTypes != NULL)
+ {
+ res |= baTypes;
+ }
+ if (m_BlockMetas != NULL)
+ {
+ res |= baMetas;
+ }
+ if (m_BlockLight != NULL)
+ {
+ res |= baLight;
+ }
+ if (m_BlockSkyLight != NULL)
+ {
+ res |= baSkyLight;
+ }
+ return res;
+}
+
+
+
+
+
+bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
+{
+ ASSERT(m_BlockTypes == NULL); // Has been cleared
+
+ if (a_DataTypes & baTypes)
+ {
+ m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockTypes == NULL)
+ {
+ return false;
+ }
+ }
+ if (a_DataTypes & baMetas)
+ {
+ m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockMetas == NULL)
+ {
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ if (a_DataTypes & baLight)
+ {
+ m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockLight == NULL)
+ {
+ delete[] m_BlockMetas;
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ if (a_DataTypes & baSkyLight)
+ {
+ m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
+ if (m_BlockSkyLight == NULL)
+ {
+ delete[] m_BlockLight;
+ delete[] m_BlockMetas;
+ delete[] m_BlockTypes;
+ return false;
+ }
+ }
+ m_SizeX = a_SizeX;
+ m_SizeY = a_SizeY;
+ m_SizeZ = a_SizeZ;
+ return true;
+}
+
+
+
+
+
+int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const
+{
+ ASSERT(a_RelX >= 0);
+ ASSERT(a_RelX < m_SizeX);
+ ASSERT(a_RelY >= 0);
+ ASSERT(a_RelY < m_SizeY);
+ ASSERT(a_RelZ >= 0);
+ ASSERT(a_RelZ < m_SizeZ);
+
+ return a_RelX + a_RelZ * m_SizeX + a_RelY * m_SizeX * m_SizeZ;
+}
+
+
+
+
+
+void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
+{
+ if (a_Array == NULL)
+ {
+ LOGWARNING("cBlockArea: datatype has not been read!");
+ return;
+ }
+ a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value;
+}
+
+
+
+
+
+void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
+{
+ SetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Value, a_Array);
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const
+{
+ if (a_Array == NULL)
+ {
+ LOGWARNING("cBlockArea: datatype has not been read!");
+ return 16;
+ }
+ return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)];
+}
+
+
+
+
+
+NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const
+{
+ return GetRelNibble(a_BlockX - m_OriginX, a_BlockY - m_OriginY, a_BlockZ - m_OriginZ, a_Array);
+}
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBlockArea::cChunkReader:
+
+cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
+ m_Area(a_Area),
+ m_OriginX(a_Area.m_OriginX),
+ m_OriginY(a_Area.m_OriginY),
+ m_OriginZ(a_Area.m_OriginZ)
+{
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc)
+{
+ int SizeY = m_Area.m_SizeY;
+ int MinY = m_OriginY;
+
+ // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
+ // OffX, OffZ are the offsets of the current chunk data from the area origin
+ // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
+ int SizeX = cChunkDef::Width;
+ int SizeZ = cChunkDef::Width;
+ int OffX, OffZ;
+ int BaseX, BaseZ;
+ OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX;
+ if (OffX < 0)
+ {
+ BaseX = -OffX;
+ SizeX += OffX; // SizeX is decreased, OffX is negative
+ OffX = 0;
+ }
+ else
+ {
+ BaseX = 0;
+ }
+ OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ;
+ if (OffZ < 0)
+ {
+ BaseZ = -OffZ;
+ SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
+ OffZ = 0;
+ }
+ else
+ {
+ BaseZ = 0;
+ }
+ // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
+ if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX)
+ {
+ SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX);
+ }
+ if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ)
+ {
+ SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ);
+ }
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int ChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int ChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int ChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
+{
+ m_CurrentChunkX = a_ChunkX;
+ m_CurrentChunkZ = a_ChunkZ;
+ return true;
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockTypes(const BLOCKTYPE * a_BlockTypes)
+{
+ if (m_Area.m_BlockTypes == NULL)
+ {
+ // Don't want BlockTypes
+ return;
+ }
+
+ int SizeY = m_Area.m_SizeY;
+ int MinY = m_OriginY;
+
+ // SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
+ // OffX, OffZ are the offsets of the current chunk data from the area origin
+ // BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
+ int SizeX = cChunkDef::Width;
+ int SizeZ = cChunkDef::Width;
+ int OffX, OffZ;
+ int BaseX, BaseZ;
+ OffX = m_CurrentChunkX * cChunkDef::Width - m_OriginX;
+ if (OffX < 0)
+ {
+ BaseX = -OffX;
+ SizeX += OffX; // SizeX is decreased, OffX is negative
+ OffX = 0;
+ }
+ else
+ {
+ BaseX = 0;
+ }
+ OffZ = m_CurrentChunkZ * cChunkDef::Width - m_OriginZ;
+ if (OffZ < 0)
+ {
+ BaseZ = -OffZ;
+ SizeZ += OffZ; // SizeZ is decreased, OffZ is negative
+ OffZ = 0;
+ }
+ else
+ {
+ BaseZ = 0;
+ }
+ // If the chunk extends beyond the area in the X or Z axis, cut off the Size:
+ if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_OriginX + m_Area.m_SizeX)
+ {
+ SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_OriginX + m_Area.m_SizeX);
+ }
+ if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_OriginZ + m_Area.m_SizeZ)
+ {
+ SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_OriginZ + m_Area.m_SizeZ);
+ }
+
+ for (int y = 0; y < SizeY; y++)
+ {
+ int ChunkY = MinY + y;
+ int AreaY = y;
+ for (int z = 0; z < SizeZ; z++)
+ {
+ int ChunkZ = BaseZ + z;
+ int AreaZ = OffZ + z;
+ for (int x = 0; x < SizeX; x++)
+ {
+ int ChunkX = BaseX + x;
+ int AreaX = OffX + x;
+ m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetBlock(a_BlockTypes, ChunkX, ChunkY, ChunkZ);
+ } // for x
+ } // for z
+ } // for y
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockMeta(const NIBBLETYPE * a_BlockMetas)
+{
+ if (m_Area.m_BlockMetas == NULL)
+ {
+ // Don't want metas
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockMetas, a_BlockMetas);
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockLight(const NIBBLETYPE * a_BlockLight)
+{
+ if (m_Area.m_BlockLight == NULL)
+ {
+ // Don't want light
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockLight, a_BlockLight);
+}
+
+
+
+
+
+void cBlockArea::cChunkReader::BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight)
+{
+ if (m_Area.m_BlockSkyLight == NULL)
+ {
+ // Don't want skylight
+ return;
+ }
+ CopyNibbles(m_Area.m_BlockSkyLight, a_BlockSkyLight);
+}
+
+
+
+
+
+void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
+ int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
+ int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
+ BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ];
+ int idx = 0;
+ for (int y = 0; y < NewSizeY; y++)
+ {
+ for (int z = 0; z < NewSizeZ; z++)
+ {
+ for (int x = 0; x < NewSizeX; x++)
+ {
+ int OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ);
+ NewBlockTypes[idx++] = m_BlockTypes[OldIndex];
+ } // for x
+ } // for z
+ } // for y
+ delete m_BlockTypes;
+ m_BlockTypes = NewBlockTypes;
+}
+
+
+
+
+
+void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
+{
+ int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
+ int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
+ int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
+ NIBBLETYPE * NewNibbles = new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ];
+ int idx = 0;
+ for (int y = 0; y < NewSizeY; y++)
+ {
+ for (int z = 0; z < NewSizeZ; z++)
+ {
+ for (int x = 0; x < NewSizeX; x++)
+ {
+ NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)];
+ } // for x
+ } // for z
+ } // for y
+ delete a_Array;
+ a_Array = NewNibbles;
+}
+
+
+
+
+
+void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX;
+ int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY;
+ int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ;
+ int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
+ BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount];
+ memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE));
+ int OldIndex = 0;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX;
+ int idx = IndexBaseZ + a_SubMinX;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ NewBlockTypes[idx++] = m_BlockTypes[OldIndex++];
+ } // for x
+ } // for z
+ } // for y
+ delete m_BlockTypes;
+ m_BlockTypes = NewBlockTypes;
+}
+
+
+
+
+
+void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
+{
+ int NewSizeX = m_SizeX + a_SubMinX + a_AddMaxX;
+ int NewSizeY = m_SizeY + a_SubMinY + a_AddMaxY;
+ int NewSizeZ = m_SizeZ + a_SubMinZ + a_AddMaxZ;
+ int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
+ NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount];
+ memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE));
+ int OldIndex = 0;
+ for (int y = 0; y < m_SizeY; y++)
+ {
+ int IndexBaseY = (y + a_SubMinY) * m_SizeX * m_SizeZ;
+ for (int z = 0; z < m_SizeZ; z++)
+ {
+ int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_SizeX;
+ int idx = IndexBaseZ + a_SubMinX;
+ for (int x = 0; x < m_SizeX; x++)
+ {
+ NewNibbles[idx++] = a_Array[OldIndex++];
+ } // for x
+ } // for z
+ } // for y
+ delete a_Array;
+ a_Array = NewNibbles;
+}
+
+
+
+
+
+bool cBlockArea::LoadFromSchematicNBT(cParsedNBT & a_NBT)
+{
+ int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
+ if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
+ {
+ AString Materials = a_NBT.GetString(TMaterials);
+ if (Materials.compare("Alpha") != 0)
+ {
+ LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str());
+ return false;
+ }
+ }
+ int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
+ int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
+ int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
+ if (
+ (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
+ (a_NBT.GetType(TSizeX) != TAG_Short) ||
+ (a_NBT.GetType(TSizeY) != TAG_Short) ||
+ (a_NBT.GetType(TSizeZ) != TAG_Short)
+ )
+ {
+ LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)",
+ TSizeX, TSizeY, TSizeZ,
+ a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ)
+ );
+ return false;
+ }
+
+ int SizeX = a_NBT.GetShort(TSizeX);
+ int SizeY = a_NBT.GetShort(TSizeY);
+ int SizeZ = a_NBT.GetShort(TSizeZ);
+ if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1))
+ {
+ LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ);
+ return false;
+ }
+
+ int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
+ int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
+ if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
+ {
+ LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes);
+ return false;
+ }
+ bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
+
+ Clear();
+ SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes);
+
+ // Copy the block types and metas:
+ int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
+ if (a_NBT.GetDataLength(TBlockTypes) < NumBytes)
+ {
+ LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockTypes)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockTypes);
+ }
+ memcpy(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
+
+ if (AreMetasPresent)
+ {
+ int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
+ if (a_NBT.GetDataLength(TBlockMetas) < NumBytes)
+ {
+ LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockMetas)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockMetas);
+ }
+ memcpy(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
+ }
+
+ return true;
+}
+
+
+
+
+void cBlockArea::RelSetData(
+ int a_RelX, int a_RelY, int a_RelZ,
+ int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
+ NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
+)
+{
+ int Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
+ if ((a_DataTypes & baTypes) != 0)
+ {
+ m_BlockTypes[Index] = a_BlockType;
+ }
+ if ((a_DataTypes & baMetas) != 0)
+ {
+ m_BlockMetas[Index] = a_BlockMeta;
+ }
+ if ((a_DataTypes & baLight) != 0)
+ {
+ m_BlockLight[Index] = a_BlockLight;
+ }
+ if ((a_DataTypes & baSkyLight) != 0)
+ {
+ m_BlockSkyLight[Index] = a_BlockSkyLight;
+ }
+}
+
+
+
+