From 53e22b11857fed62e2313d6d84d90f88ed412ffb Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Mon, 29 Jul 2013 12:13:03 +0100 Subject: Changed everyting to Unix line endings. --- source/AllToLua.bat | 6 +- source/BlockArea.cpp | 4248 ++++++++++++------------- source/BlockArea.h | 620 ++-- source/BlockEntities/BlockEntityWithItems.h | 172 +- source/BlockEntities/DispenserEntity.cpp | 450 +-- source/BlockEntities/DispenserEntity.h | 82 +- source/BlockEntities/DropSpenserEntity.cpp | 490 +-- source/BlockEntities/DropSpenserEntity.h | 178 +- source/BlockEntities/DropperEntity.cpp | 84 +- source/BlockEntities/DropperEntity.h | 98 +- source/BlockEntities/HopperEntity.cpp | 1046 +++--- source/BlockEntities/HopperEntity.h | 204 +- source/BlockEntities/JukeboxEntity.cpp | 246 +- source/BlockEntities/JukeboxEntity.h | 86 +- source/BlockEntities/NoteEntity.cpp | 318 +- source/BlockEntities/NoteEntity.h | 104 +- source/Blocks/BlockBed.cpp | 170 +- source/Blocks/BlockBed.h | 146 +- source/Blocks/BlockBrewingStand.h | 64 +- source/Blocks/BlockCactus.h | 176 +- source/Blocks/BlockCauldron.h | 118 +- source/Blocks/BlockChest.h | 444 +-- source/Blocks/BlockCloth.h | 68 +- source/Blocks/BlockCobWeb.h | 60 +- source/Blocks/BlockCrops.h | 216 +- source/Blocks/BlockDeadBush.h | 94 +- source/Blocks/BlockDirt.h | 176 +- source/Blocks/BlockDoor.cpp | 176 +- source/Blocks/BlockDoor.h | 210 +- source/Blocks/BlockDropSpenser.h | 78 +- source/Blocks/BlockEnderchest.h | 56 +- source/Blocks/BlockEntity.h | 62 +- source/Blocks/BlockFarmland.h | 198 +- source/Blocks/BlockFenceGate.h | 120 +- source/Blocks/BlockFire.h | 84 +- source/Blocks/BlockFlower.h | 106 +- source/Blocks/BlockFlowerPot.h | 210 +- source/Blocks/BlockFluid.h | 100 +- source/Blocks/BlockFurnace.h | 94 +- source/Blocks/BlockGlass.h | 52 +- source/Blocks/BlockGlowstone.h | 60 +- source/Blocks/BlockGravel.h | 54 +- source/Blocks/BlockHandler.cpp | 906 +++--- source/Blocks/BlockHandler.h | 316 +- source/Blocks/BlockHopper.h | 92 +- source/Blocks/BlockIce.h | 74 +- source/Blocks/BlockLadder.h | 230 +- source/Blocks/BlockLeaves.h | 368 +-- source/Blocks/BlockLever.cpp | 96 +- source/Blocks/BlockLever.h | 122 +- source/Blocks/BlockMelon.h | 70 +- source/Blocks/BlockMushroom.h | 142 +- source/Blocks/BlockMycelium.h | 54 +- source/Blocks/BlockNote.h | 26 +- source/Blocks/BlockOre.h | 160 +- source/Blocks/BlockPiston.cpp | 138 +- source/Blocks/BlockPiston.h | 56 +- source/Blocks/BlockRail.h | 842 ++--- source/Blocks/BlockRedstone.cpp | 54 +- source/Blocks/BlockRedstone.h | 92 +- source/Blocks/BlockRedstoneRepeater.cpp | 94 +- source/Blocks/BlockRedstoneRepeater.h | 120 +- source/Blocks/BlockRedstoneTorch.h | 72 +- source/Blocks/BlockSand.h | 56 +- source/Blocks/BlockSapling.h | 138 +- source/Blocks/BlockSign.h | 146 +- source/Blocks/BlockSlab.h | 140 +- source/Blocks/BlockSnow.h | 104 +- source/Blocks/BlockStairs.h | 306 +- source/Blocks/BlockStems.h | 116 +- source/Blocks/BlockStone.h | 58 +- source/Blocks/BlockSugarcane.h | 192 +- source/Blocks/BlockTallGrass.h | 102 +- source/Blocks/BlockTorch.h | 518 +-- source/Blocks/BlockVine.h | 400 +-- source/Blocks/BlockWood.h | 54 +- source/Blocks/BlockWorkbench.h | 86 +- source/ByteBuffer.cpp | 1322 ++++---- source/ByteBuffer.h | 242 +- source/CommandOutput.cpp | 142 +- source/CommandOutput.h | 164 +- source/Enchantments.cpp | 596 ++-- source/Enchantments.h | 228 +- source/FallingBlock.cpp | 206 +- source/FallingBlock.h | 90 +- source/FastRandom.cpp | 348 +- source/FastRandom.h | 114 +- source/Generating/Caves.cpp | 1940 +++++------ source/Generating/Caves.h | 204 +- source/Generating/ChunkDesc.cpp | 1156 +++---- source/Generating/ComposableGenerator.cpp | 1048 +++--- source/Generating/ComposableGenerator.h | 350 +- source/Generating/DistortedHeightmap.cpp | 838 ++--- source/Generating/DistortedHeightmap.h | 204 +- source/Generating/EndGen.cpp | 434 +-- source/Generating/EndGen.h | 138 +- source/Generating/MineShafts.cpp | 2846 ++++++++--------- source/Generating/MineShafts.h | 122 +- source/Generating/Noise3DGenerator.cpp | 1162 +++---- source/Generating/Noise3DGenerator.h | 212 +- source/Generating/Ravines.cpp | 1062 +++---- source/Generating/Ravines.h | 92 +- source/ItemGrid.cpp | 1324 ++++---- source/ItemGrid.h | 382 +-- source/Items/ItemBed.h | 112 +- source/Items/ItemBrewingStand.h | 82 +- source/Items/ItemBucket.h | 320 +- source/Items/ItemCauldron.h | 82 +- source/Items/ItemCloth.h | 46 +- source/Items/ItemDoor.h | 90 +- source/Items/ItemDye.h | 88 +- source/Items/ItemFlowerPot.h | 82 +- source/Items/ItemFood.h | 112 +- source/Items/ItemHandler.cpp | 1000 +++--- source/Items/ItemHandler.h | 192 +- source/Items/ItemHoe.h | 60 +- source/Items/ItemLeaves.h | 82 +- source/Items/ItemLighter.h | 112 +- source/Items/ItemMinecart.h | 160 +- source/Items/ItemPickaxe.h | 150 +- source/Items/ItemRedstoneDust.h | 76 +- source/Items/ItemRedstoneRepeater.h | 80 +- source/Items/ItemSapling.h | 84 +- source/Items/ItemSeeds.h | 130 +- source/Items/ItemShears.h | 126 +- source/Items/ItemShovel.h | 80 +- source/Items/ItemSign.h | 102 +- source/Items/ItemSlab.h | 104 +- source/Items/ItemSpawnEgg.h | 104 +- source/Items/ItemSugarcane.h | 78 +- source/Items/ItemSword.h | 40 +- source/Items/ItemWood.h | 44 +- source/LinearInterpolation.cpp | 502 +-- source/LinearInterpolation.h | 120 +- source/LinearUpscale.h | 488 +-- source/LuaWindow.cpp | 350 +- source/LuaWindow.h | 192 +- source/Minecart.cpp | 320 +- source/Minecart.h | 234 +- source/Mobs/Bat.h | 54 +- source/Mobs/Blaze.cpp | 54 +- source/Mobs/Blaze.h | 50 +- source/Mobs/Magmacube.cpp | 54 +- source/Mobs/Magmacube.h | 62 +- source/Mobs/Mooshroom.cpp | 66 +- source/Mobs/Mooshroom.h | 50 +- source/Mobs/Ocelot.h | 54 +- source/Mobs/Villager.cpp | 34 +- source/Mobs/Villager.h | 46 +- source/Mobs/Witch.cpp | 64 +- source/Mobs/Witch.h | 50 +- source/OSSupport/GZipFile.cpp | 214 +- source/OSSupport/GZipFile.h | 104 +- source/OSSupport/ListenThread.cpp | 462 +-- source/OSSupport/ListenThread.h | 166 +- source/Player.cpp | 2846 ++++++++--------- source/Plugin_Squirrel.cpp | 818 ++--- source/Plugin_Squirrel.h | 138 +- source/ProbabDistrib.cpp | 284 +- source/ProbabDistrib.h | 148 +- source/Protocol/ChunkDataSerializer.cpp | 352 +- source/Protocol/ChunkDataSerializer.h | 96 +- source/Protocol/Protocol.h | 384 +-- source/Protocol/Protocol125.cpp | 3272 +++++++++---------- source/Protocol/Protocol125.h | 304 +- source/Protocol/Protocol132.cpp | 1804 +++++------ source/Protocol/Protocol132.h | 200 +- source/Protocol/Protocol14x.cpp | 506 +-- source/Protocol/Protocol14x.h | 126 +- source/Protocol/Protocol15x.cpp | 274 +- source/Protocol/Protocol15x.h | 76 +- source/Protocol/Protocol16x.cpp | 490 +-- source/Protocol/Protocol16x.h | 146 +- source/Protocol/ProtocolRecognizer.cpp | 1542 ++++----- source/Protocol/ProtocolRecognizer.h | 260 +- source/RCONServer.cpp | 664 ++-- source/RCONServer.h | 218 +- source/SQLite/urls.txt | 16 +- source/Simulator/DelayedFluidSimulator.cpp | 316 +- source/Simulator/DelayedFluidSimulator.h | 164 +- source/Simulator/FloodyFluidSimulator.cpp | 668 ++-- source/Simulator/FloodyFluidSimulator.h | 106 +- source/Simulator/NoopFluidSimulator.h | 72 +- source/Simulator/VaporizeFluidSimulator.cpp | 106 +- source/Simulator/VaporizeFluidSimulator.h | 68 +- source/SquirrelCommandBinder.cpp | 236 +- source/SquirrelCommandBinder.h | 102 +- source/TNTEntity.cpp | 144 +- source/TNTEntity.h | 68 +- source/UI/SlotArea.cpp | 1630 +++++----- source/UI/SlotArea.h | 606 ++-- source/WorldStorage/NBTChunkSerializer.cpp | 918 +++--- source/WorldStorage/NBTChunkSerializer.h | 220 +- source/XMLParser.h | 1402 ++++---- source/squirrelbindings/SquirrelArray.h | 112 +- source/squirrelbindings/SquirrelBaseClass.h | 132 +- source/squirrelbindings/SquirrelBindings.cpp | 390 +-- source/squirrelbindings/SquirrelBindings.h | 64 +- source/squirrelbindings/SquirrelFunctions.cpp | 188 +- source/squirrelbindings/SquirrelFunctions.h | 56 +- source/squirrelbindings/SquirrelObject.h | 110 +- source/virtual_method_hooks.lua | 1012 +++--- 202 files changed, 33396 insertions(+), 33396 deletions(-) (limited to 'source') diff --git a/source/AllToLua.bat b/source/AllToLua.bat index 584f57423..60520e6f3 100644 --- a/source/AllToLua.bat +++ b/source/AllToLua.bat @@ -1,3 +1,3 @@ -"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg - -if %ALLTOLUA_WAIT%N == N pause +"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg + +if %ALLTOLUA_WAIT%N == N pause diff --git a/source/BlockArea.cpp b/source/BlockArea.cpp index 6134843bb..5c15adfef 100644 --- a/source/BlockArea.cpp +++ b/source/BlockArea.cpp @@ -1,2124 +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 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; - } -} - - - - + +// 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 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; + } +} + + + + diff --git a/source/BlockArea.h b/source/BlockArea.h index 76c4f749a..075cc99ec 100644 --- a/source/BlockArea.h +++ b/source/BlockArea.h @@ -1,310 +1,310 @@ - -// BlockArea.h - -// Interfaces to 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 - -// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting! - - - - - -#pragma once - - - - - -// fwd: World.h -class cWorld; - -// fwd: FastNBT.h -class cParsedNBT; - - - - - -// tolua_begin -class cBlockArea -{ - // tolua_end - DISALLOW_COPY_AND_ASSIGN(cBlockArea); - // tolua_begin - -public: - - /// What data is to be queried (bit-mask) - enum - { - baTypes = 1, - baMetas = 2, - baLight = 4, - baSkyLight = 8, - } ; - - enum eMergeStrategy - { - msOverwrite, - msFillAir, - msImprint, - msLake, - } ; - - cBlockArea(void); - ~cBlockArea(); - - /// Clears the data stored to reclaim memory - void Clear(void); - - /** Creates a new area of the specified size and contents. - Origin is set to all zeroes. - BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light. - */ - void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas); - - /// Resets the origin. No other changes are made, contents are untouched. - void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ); - - /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive. - bool 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 = baTypes | baMetas); - - // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write - // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again - - /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all - bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas); - - /// Copies this object's contents into the specified BlockArea. - void CopyTo(cBlockArea & a_Into) const; - - /// Copies the contents from the specified BlockArea into this object. - void CopyFrom(const cBlockArea & a_From); - - /// For testing purposes only, dumps the area into a file. - void DumpToRawFile(const AString & a_FileName); - - /// Loads an area from a .schematic file. Returns true if successful - bool LoadFromSchematicFile(const AString & a_FileName); - - /// Saves the area into a .schematic file. Returns true if successful - bool SaveToSchematicFile(const AString & a_FileName); - - /// Crops the internal contents by the specified amount of blocks from each border. - void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); - - /// Expands the internal contents by the specified amount of blocks from each border - void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); - - /** Merges another block area into this one, using the specified block combinating strategy - This function combines another BlockArea into the current object. - The strategy parameter specifies how individual blocks are combined together, using the table below. - - | area block | result | - | this | Src | msOverwrite | msFillAir | msImprint | - +------+-----+-------------+-----------+-----------+ - | air | air | air | air | air | - | A | air | air | A | A | - | air | B | B | B | B | - | A | B | B | A | B | - - So to sum up: - - msOverwrite completely overwrites all blocks with the Src's blocks - - msFillAir overwrites only those blocks that were air - - msImprint overwrites with only those blocks that are non-air - - Special strategies: - msLake (evaluate top-down, first match wins): - | area block | | - | this | Src | result | - +----------+--------+--------+ - | A | sponge | A | Sponge is the NOP block - | * | air | air | Air always gets hollowed out, even under the oceans - | water | * | water | Water is never overwritten - | lava | * | lava | Lava is never overwritten - | * | water | water | Water always overwrites anything - | * | lava | lava | Lava always overwrites anything - | dirt | stone | stone | Stone overwrites dirt - | grass | stone | stone | ... and grass - | mycelium | stone | stone | ... and mycelium - | A | stone | A | ... but nothing else - | A | * | A | Everything else is left as it is - - */ - void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy); - - /// Fills the entire block area with the specified data - void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f); - - /// Fills a cuboid inside the block area with the specified data - void 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 = 0, - NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f - ); - - /// Draws a line from between two points with the specified data - void 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 = 0, - NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f - ); - - /// Rotates the entire area counter-clockwise around the Y axis - void RotateCCW(void); - - /// Rotates the entire area clockwise around the Y axis - void RotateCW(void); - - /// Mirrors the entire area around the XY plane - void MirrorXY(void); - - /// Mirrors the entire area around the XZ plane - void MirrorXZ(void); - - /// Mirrors the entire area around the YZ plane - void MirrorYZ(void); - - /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta - void RotateCCWNoMeta(void); - - /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta - void RotateCWNoMeta(void); - - /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta - void MirrorXYNoMeta(void); - - /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta - void MirrorXZNoMeta(void); - - /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta - void MirrorYZNoMeta(void); - - // Setters: - void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); - void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); - void SetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta); - void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta); - void SetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight); - void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight); - void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight); - void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight); - - // Getters: - BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const; - BLOCKTYPE GetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ) const; - NIBBLETYPE GetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ) const; - NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) const; - NIBBLETYPE GetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ) const; - NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; - NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const; - NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; - - void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; - void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; - - int GetSizeX(void) const { return m_SizeX; } - int GetSizeY(void) const { return m_SizeY; } - int GetSizeZ(void) const { return m_SizeZ; } - - int GetOriginX(void) const { return m_OriginX; } - int GetOriginY(void) const { return m_OriginY; } - int GetOriginZ(void) const { return m_OriginZ; } - - /// Returns the datatypes that are stored in the object (bitmask of baXXX values) - int GetDataTypes(void) const; - - bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); } - bool HasBlockMetas (void) const { return (m_BlockMetas != NULL); } - bool HasBlockLights (void) const { return (m_BlockLight != NULL); } - bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != NULL); } - - // tolua_end - - // Clients can use these for faster access to all blocktypes. Be careful though! - /// Returns the internal pointer to the block types - BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; } - NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block! - NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block! - NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block! - int GetBlockCount(void) const { return m_SizeX * m_SizeY * m_SizeZ; } - int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const; - -protected: - friend class cChunkDesc; - - class cChunkReader : - public cChunkDataCallback - { - public: - cChunkReader(cBlockArea & a_Area); - - protected: - cBlockArea & m_Area; - int m_OriginX; - int m_OriginY; - int m_OriginZ; - int m_CurrentChunkX; - int m_CurrentChunkZ; - - void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc); - - // cChunkDataCallback overrides: - virtual bool Coords (int a_ChunkX, int a_ChunkZ) override; - virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override; - virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override; - virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override; - virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override; - } ; - - typedef NIBBLETYPE * NIBBLEARRAY; - - - int m_OriginX; - int m_OriginY; - int m_OriginZ; - int m_SizeX; - int m_SizeY; - int m_SizeZ; - - BLOCKTYPE * m_BlockTypes; - NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access - NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access - NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access - - /// Clears the data stored and prepares a fresh new block area with the specified dimensions - bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes); - - // Basic Setters: - void SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); - void SetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); - - // Basic Getters: - NIBBLETYPE GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const; - NIBBLETYPE GetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const; - - // Crop helpers: - void CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); - void CropNibbles (NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); - - // Expand helpers: - void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); - void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); - - /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful. - bool LoadFromSchematicNBT(cParsedNBT & a_NBT); - - /// Sets the specified datatypes at the specified location. - void 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 - ); - // tolua_begin -} ; -// tolua_end - - - - + +// BlockArea.h + +// Interfaces to 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 + +// NOTE: All Nibble values (meta, blocklight, skylight) are stored one-nibble-per-byte for faster access / editting! + + + + + +#pragma once + + + + + +// fwd: World.h +class cWorld; + +// fwd: FastNBT.h +class cParsedNBT; + + + + + +// tolua_begin +class cBlockArea +{ + // tolua_end + DISALLOW_COPY_AND_ASSIGN(cBlockArea); + // tolua_begin + +public: + + /// What data is to be queried (bit-mask) + enum + { + baTypes = 1, + baMetas = 2, + baLight = 4, + baSkyLight = 8, + } ; + + enum eMergeStrategy + { + msOverwrite, + msFillAir, + msImprint, + msLake, + } ; + + cBlockArea(void); + ~cBlockArea(); + + /// Clears the data stored to reclaim memory + void Clear(void); + + /** Creates a new area of the specified size and contents. + Origin is set to all zeroes. + BlockTypes are set to air, block metas to zero, blocklights to zero and skylights to full light. + */ + void Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes = baTypes | baMetas); + + /// Resets the origin. No other changes are made, contents are untouched. + void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ); + + /// Reads an area of blocks specified. Returns true if successful. All coords are inclusive. + bool 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 = baTypes | baMetas); + + // TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write + // A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again + + /// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all + bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas); + + /// Copies this object's contents into the specified BlockArea. + void CopyTo(cBlockArea & a_Into) const; + + /// Copies the contents from the specified BlockArea into this object. + void CopyFrom(const cBlockArea & a_From); + + /// For testing purposes only, dumps the area into a file. + void DumpToRawFile(const AString & a_FileName); + + /// Loads an area from a .schematic file. Returns true if successful + bool LoadFromSchematicFile(const AString & a_FileName); + + /// Saves the area into a .schematic file. Returns true if successful + bool SaveToSchematicFile(const AString & a_FileName); + + /// Crops the internal contents by the specified amount of blocks from each border. + void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + + /// Expands the internal contents by the specified amount of blocks from each border + void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + + /** Merges another block area into this one, using the specified block combinating strategy + This function combines another BlockArea into the current object. + The strategy parameter specifies how individual blocks are combined together, using the table below. + + | area block | result | + | this | Src | msOverwrite | msFillAir | msImprint | + +------+-----+-------------+-----------+-----------+ + | air | air | air | air | air | + | A | air | air | A | A | + | air | B | B | B | B | + | A | B | B | A | B | + + So to sum up: + - msOverwrite completely overwrites all blocks with the Src's blocks + - msFillAir overwrites only those blocks that were air + - msImprint overwrites with only those blocks that are non-air + + Special strategies: + msLake (evaluate top-down, first match wins): + | area block | | + | this | Src | result | + +----------+--------+--------+ + | A | sponge | A | Sponge is the NOP block + | * | air | air | Air always gets hollowed out, even under the oceans + | water | * | water | Water is never overwritten + | lava | * | lava | Lava is never overwritten + | * | water | water | Water always overwrites anything + | * | lava | lava | Lava always overwrites anything + | dirt | stone | stone | Stone overwrites dirt + | grass | stone | stone | ... and grass + | mycelium | stone | stone | ... and mycelium + | A | stone | A | ... but nothing else + | A | * | A | Everything else is left as it is + + */ + void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy); + + /// Fills the entire block area with the specified data + void Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta = 0, NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f); + + /// Fills a cuboid inside the block area with the specified data + void 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 = 0, + NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f + ); + + /// Draws a line from between two points with the specified data + void 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 = 0, + NIBBLETYPE a_BlockLight = 0, NIBBLETYPE a_BlockSkyLight = 0x0f + ); + + /// Rotates the entire area counter-clockwise around the Y axis + void RotateCCW(void); + + /// Rotates the entire area clockwise around the Y axis + void RotateCW(void); + + /// Mirrors the entire area around the XY plane + void MirrorXY(void); + + /// Mirrors the entire area around the XZ plane + void MirrorXZ(void); + + /// Mirrors the entire area around the YZ plane + void MirrorYZ(void); + + /// Rotates the entire area counter-clockwise around the Y axis, doesn't use blockhandlers for block meta + void RotateCCWNoMeta(void); + + /// Rotates the entire area clockwise around the Y axis, doesn't use blockhandlers for block meta + void RotateCWNoMeta(void); + + /// Mirrors the entire area around the XY plane, doesn't use blockhandlers for block meta + void MirrorXYNoMeta(void); + + /// Mirrors the entire area around the XZ plane, doesn't use blockhandlers for block meta + void MirrorXZNoMeta(void); + + /// Mirrors the entire area around the YZ plane, doesn't use blockhandlers for block meta + void MirrorYZNoMeta(void); + + // Setters: + void SetRelBlockType (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType); + void SetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + void SetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta); + void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta); + void SetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight); + void SetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight); + void SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight); + void SetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight); + + // Getters: + BLOCKTYPE GetRelBlockType (int a_RelX, int a_RelY, int a_RelZ) const; + BLOCKTYPE GetBlockType (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockMeta (int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockLight (int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; + NIBBLETYPE GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const; + NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ) const; + + void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + + int GetSizeX(void) const { return m_SizeX; } + int GetSizeY(void) const { return m_SizeY; } + int GetSizeZ(void) const { return m_SizeZ; } + + int GetOriginX(void) const { return m_OriginX; } + int GetOriginY(void) const { return m_OriginY; } + int GetOriginZ(void) const { return m_OriginZ; } + + /// Returns the datatypes that are stored in the object (bitmask of baXXX values) + int GetDataTypes(void) const; + + bool HasBlockTypes (void) const { return (m_BlockTypes != NULL); } + bool HasBlockMetas (void) const { return (m_BlockMetas != NULL); } + bool HasBlockLights (void) const { return (m_BlockLight != NULL); } + bool HasBlockSkyLights(void) const { return (m_BlockSkyLight != NULL); } + + // tolua_end + + // Clients can use these for faster access to all blocktypes. Be careful though! + /// Returns the internal pointer to the block types + BLOCKTYPE * GetBlockTypes (void) const { return m_BlockTypes; } + NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block! + NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block! + NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block! + int GetBlockCount(void) const { return m_SizeX * m_SizeY * m_SizeZ; } + int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const; + +protected: + friend class cChunkDesc; + + class cChunkReader : + public cChunkDataCallback + { + public: + cChunkReader(cBlockArea & a_Area); + + protected: + cBlockArea & m_Area; + int m_OriginX; + int m_OriginY; + int m_OriginZ; + int m_CurrentChunkX; + int m_CurrentChunkZ; + + void CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc); + + // cChunkDataCallback overrides: + virtual bool Coords (int a_ChunkX, int a_ChunkZ) override; + virtual void BlockTypes (const BLOCKTYPE * a_BlockTypes) override; + virtual void BlockMeta (const NIBBLETYPE * a_BlockMetas) override; + virtual void BlockLight (const NIBBLETYPE * a_BlockLight) override; + virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override; + } ; + + typedef NIBBLETYPE * NIBBLEARRAY; + + + int m_OriginX; + int m_OriginY; + int m_OriginZ; + int m_SizeX; + int m_SizeY; + int m_SizeZ; + + BLOCKTYPE * m_BlockTypes; + NIBBLETYPE * m_BlockMetas; // Each meta is stored as a separate byte for faster access + NIBBLETYPE * m_BlockLight; // Each light value is stored as a separate byte for faster access + NIBBLETYPE * m_BlockSkyLight; // Each light value is stored as a separate byte for faster access + + /// Clears the data stored and prepares a fresh new block area with the specified dimensions + bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes); + + // Basic Setters: + void SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); + void SetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array); + + // Basic Getters: + NIBBLETYPE GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const; + NIBBLETYPE GetNibble (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const; + + // Crop helpers: + void CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + void CropNibbles (NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ); + + // Expand helpers: + void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ); + + /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful. + bool LoadFromSchematicNBT(cParsedNBT & a_NBT); + + /// Sets the specified datatypes at the specified location. + void 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 + ); + // tolua_begin +} ; +// tolua_end + + + + diff --git a/source/BlockEntities/BlockEntityWithItems.h b/source/BlockEntities/BlockEntityWithItems.h index 587b10461..0846ae17e 100644 --- a/source/BlockEntities/BlockEntityWithItems.h +++ b/source/BlockEntities/BlockEntityWithItems.h @@ -1,86 +1,86 @@ - -// BlockEntityWithItems.h - -// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid - - - - - -#pragma once - -#include "BlockEntity.h" -#include "../ItemGrid.h" - - - - - -// tolua_begin -class cBlockEntityWithItems : - public cBlockEntity - // tolua_end - // tolua doesn't seem to support multiple inheritance? - , public cItemGrid::cListener - // tolua_begin -{ - typedef cBlockEntity super; - -public: - // tolua_end - - cBlockEntityWithItems( - BLOCKTYPE a_BlockType, // Type of the block that the entity represents - int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity - int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid - cWorld * a_World // Optional world to assign to the entity - ) : - super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World), - m_Contents(a_ItemGridWidth, a_ItemGridHeight) - { - m_Contents.AddListener(*this); - } - - virtual void Destroy(void) override - { - // Drop the contents as pickups: - ASSERT(m_World != NULL); - cItems Pickups; - m_Contents.CopyToItems(Pickups); - m_Contents.Clear(); - m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); - } - - // tolua_begin - - const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); } - const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); } - - void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); } - void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); } - - /// Returns the ItemGrid used for storing the contents - cItemGrid & GetContents(void) { return m_Contents; } - - // tolua_end - - /// Const version of the GetContents() function for C++ type-safety - const cItemGrid & GetContents(void) const { return m_Contents; } - -protected: - cItemGrid m_Contents; - - // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) - { - ASSERT(a_Grid == &m_Contents); - if (m_World != NULL) - { - m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); - } - } -} ; // tolua_export - - - - + +// BlockEntityWithItems.h + +// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid + + + + + +#pragma once + +#include "BlockEntity.h" +#include "../ItemGrid.h" + + + + + +// tolua_begin +class cBlockEntityWithItems : + public cBlockEntity + // tolua_end + // tolua doesn't seem to support multiple inheritance? + , public cItemGrid::cListener + // tolua_begin +{ + typedef cBlockEntity super; + +public: + // tolua_end + + cBlockEntityWithItems( + BLOCKTYPE a_BlockType, // Type of the block that the entity represents + int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity + int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid + cWorld * a_World // Optional world to assign to the entity + ) : + super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Contents(a_ItemGridWidth, a_ItemGridHeight) + { + m_Contents.AddListener(*this); + } + + virtual void Destroy(void) override + { + // Drop the contents as pickups: + ASSERT(m_World != NULL); + cItems Pickups; + m_Contents.CopyToItems(Pickups); + m_Contents.Clear(); + m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); + } + + // tolua_begin + + const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); } + const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); } + + void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); } + void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); } + + /// Returns the ItemGrid used for storing the contents + cItemGrid & GetContents(void) { return m_Contents; } + + // tolua_end + + /// Const version of the GetContents() function for C++ type-safety + const cItemGrid & GetContents(void) const { return m_Contents; } + +protected: + cItemGrid m_Contents; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum) + { + ASSERT(a_Grid == &m_Contents); + if (m_World != NULL) + { + m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); + } + } +} ; // tolua_export + + + + diff --git a/source/BlockEntities/DispenserEntity.cpp b/source/BlockEntities/DispenserEntity.cpp index ab4c8e519..cde67bb41 100644 --- a/source/BlockEntities/DispenserEntity.cpp +++ b/source/BlockEntities/DispenserEntity.cpp @@ -1,225 +1,225 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "DispenserEntity.h" -#include "../Player.h" -#include "../Simulator/FluidSimulator.h" -#include "../Chunk.h" - - - - - -cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ) : - super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, NULL) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) -{ - int DispX = m_RelX; - int DispY = m_PosY; - int DispZ = m_RelZ; - NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - AddDropSpenserDir(DispX, DispY, DispZ, Meta); - cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ); - if (DispChunk == NULL) - { - // Would dispense into / interact with a non-loaded chunk, ignore the tick - return; - } - BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ); - - // Dispense the item: - switch (m_Contents.GetSlot(a_SlotNum).m_ItemType) - { - case E_ITEM_BUCKET: - { - LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); - switch (DispBlock) - { - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_WATER: - { - if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET)) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); - } - break; - } - case E_BLOCK_STATIONARY_LAVA: - case E_BLOCK_LAVA: - { - if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET)) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); - } - break; - } - default: - { - DropFromSlot(a_Chunk, a_SlotNum); - break; - } - } - break; - } // E_ITEM_BUCKET - - case E_ITEM_WATER_BUCKET: - { - LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); - if (EmptyLiquidBucket(DispBlock, a_SlotNum)) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0); - } - else - { - DropFromSlot(a_Chunk, a_SlotNum); - } - break; - } - - case E_ITEM_LAVA_BUCKET: - { - LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); - if (EmptyLiquidBucket(DispBlock, a_SlotNum)) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0); - } - else - { - DropFromSlot(a_Chunk, a_SlotNum); - } - break; - } - - case E_ITEM_SPAWN_EGG: - { - double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); - double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); - if (m_World->SpawnMob(MobX, DispY, MobZ, m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0) - { - m_Contents.ChangeSlotCount(a_SlotNum, -1); - } - break; - } - - case E_BLOCK_TNT: - { - // Spawn a primed TNT entity, if space allows: - if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) - { - double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); - double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); - m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity - m_Contents.ChangeSlotCount(a_SlotNum, -1); - } - break; - } - - case E_ITEM_FLINT_AND_STEEL: - { - // Spawn fire if the block in front is air. - if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) - { - DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0); - m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1); - // If the durability has run out destroy the item. - if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64) - { - m_Contents.ChangeSlotCount(a_SlotNum, -1); - } - } - break; - } - - default: - { - DropFromSlot(a_Chunk, a_SlotNum); - break; - } - } // switch (ItemType) -} - - - - - - -bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType) -{ - cItem LiquidBucket(a_BucketItemType, 1); - if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) - { - // Special case: replacing one empty bucket with one full bucket - m_Contents.SetSlot(a_SlotNum, LiquidBucket); - return true; - } - - // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else - if (m_Contents.HowManyCanFit(LiquidBucket) < 1) - { - // Cannot fit into m_Contents - return false; - } - - m_Contents.ChangeSlotCount(a_SlotNum, -1); - m_Contents.AddItem(LiquidBucket); - return true; -} - - - - - -bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum) -{ - if ( - (a_BlockInFront != E_BLOCK_AIR) && - !IsBlockLiquid(a_BlockInFront) && - !cFluidSimulator::CanWashAway(a_BlockInFront) - ) - { - // Not a suitable block in front - return false; - } - - cItem EmptyBucket(E_ITEM_BUCKET, 1); - if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) - { - // Change the single full bucket present into a single empty bucket - m_Contents.SetSlot(a_SlotNum, EmptyBucket); - return true; - } - - // There are full buckets stacked at this slot, check if we can fit in the empty bucket - if (m_Contents.HowManyCanFit(EmptyBucket) < 1) - { - // The empty bucket wouldn't fit into m_Contents - return false; - } - - // The empty bucket fits in, remove one full bucket and add the empty one - m_Contents.ChangeSlotCount(a_SlotNum, -1); - m_Contents.AddItem(EmptyBucket); - return true; -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "DispenserEntity.h" +#include "../Player.h" +#include "../Simulator/FluidSimulator.h" +#include "../Chunk.h" + + + + + +cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ) : + super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, NULL) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + int DispX = m_RelX; + int DispY = m_PosY; + int DispZ = m_RelZ; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + AddDropSpenserDir(DispX, DispY, DispZ, Meta); + cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ); + if (DispChunk == NULL) + { + // Would dispense into / interact with a non-loaded chunk, ignore the tick + return; + } + BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ); + + // Dispense the item: + switch (m_Contents.GetSlot(a_SlotNum).m_ItemType) + { + case E_ITEM_BUCKET: + { + LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + switch (DispBlock) + { + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); + } + break; + } + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_LAVA: + { + if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0); + } + break; + } + default: + { + DropFromSlot(a_Chunk, a_SlotNum); + break; + } + } + break; + } // E_ITEM_BUCKET + + case E_ITEM_WATER_BUCKET: + { + LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + if (EmptyLiquidBucket(DispBlock, a_SlotNum)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0); + } + else + { + DropFromSlot(a_Chunk, a_SlotNum); + } + break; + } + + case E_ITEM_LAVA_BUCKET: + { + LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock); + if (EmptyLiquidBucket(DispBlock, a_SlotNum)) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0); + } + else + { + DropFromSlot(a_Chunk, a_SlotNum); + } + break; + } + + case E_ITEM_SPAWN_EGG: + { + double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); + double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); + if (m_World->SpawnMob(MobX, DispY, MobZ, m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_BLOCK_TNT: + { + // Spawn a primed TNT entity, if space allows: + if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) + { + double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width); + double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width); + m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 4, 0); // 4 seconds fuse, no initial velocity + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + break; + } + + case E_ITEM_FLINT_AND_STEEL: + { + // Spawn fire if the block in front is air. + if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR) + { + DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0); + m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1); + // If the durability has run out destroy the item. + if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64) + { + m_Contents.ChangeSlotCount(a_SlotNum, -1); + } + } + break; + } + + default: + { + DropFromSlot(a_Chunk, a_SlotNum); + break; + } + } // switch (ItemType) +} + + + + + + +bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType) +{ + cItem LiquidBucket(a_BucketItemType, 1); + if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) + { + // Special case: replacing one empty bucket with one full bucket + m_Contents.SetSlot(a_SlotNum, LiquidBucket); + return true; + } + + // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else + if (m_Contents.HowManyCanFit(LiquidBucket) < 1) + { + // Cannot fit into m_Contents + return false; + } + + m_Contents.ChangeSlotCount(a_SlotNum, -1); + m_Contents.AddItem(LiquidBucket); + return true; +} + + + + + +bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum) +{ + if ( + (a_BlockInFront != E_BLOCK_AIR) && + !IsBlockLiquid(a_BlockInFront) && + !cFluidSimulator::CanWashAway(a_BlockInFront) + ) + { + // Not a suitable block in front + return false; + } + + cItem EmptyBucket(E_ITEM_BUCKET, 1); + if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1) + { + // Change the single full bucket present into a single empty bucket + m_Contents.SetSlot(a_SlotNum, EmptyBucket); + return true; + } + + // There are full buckets stacked at this slot, check if we can fit in the empty bucket + if (m_Contents.HowManyCanFit(EmptyBucket) < 1) + { + // The empty bucket wouldn't fit into m_Contents + return false; + } + + // The empty bucket fits in, remove one full bucket and add the empty one + m_Contents.ChangeSlotCount(a_SlotNum, -1); + m_Contents.AddItem(EmptyBucket); + return true; +} + + + + diff --git a/source/BlockEntities/DispenserEntity.h b/source/BlockEntities/DispenserEntity.h index 09270e7f8..5e3327f18 100644 --- a/source/BlockEntities/DispenserEntity.h +++ b/source/BlockEntities/DispenserEntity.h @@ -1,41 +1,41 @@ - -#pragma once - -#include "DropSpenserEntity.h" - - - - - -// tolua_begin -class cDispenserEntity : - public cDropSpenserEntity -{ - typedef cDropSpenserEntity super; - -public: - - /// Constructor used while generating a chunk; sets m_World to NULL - cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ); - - // tolua_end - - /// Constructor used for normal operation - cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - - static const char * GetClassStatic(void) { return "cDispenserEntity"; } - -private: - // cDropSpenser overrides: - virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; - - /// If such a bucket can fit, adds it to m_Contents and returns true - bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType); - - /// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true - bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum); -} ; // tolua_export - - - - + +#pragma once + +#include "DropSpenserEntity.h" + + + + + +// tolua_begin +class cDispenserEntity : + public cDropSpenserEntity +{ + typedef cDropSpenserEntity super; + +public: + + /// Constructor used while generating a chunk; sets m_World to NULL + cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_end + + /// Constructor used for normal operation + cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + static const char * GetClassStatic(void) { return "cDispenserEntity"; } + +private: + // cDropSpenser overrides: + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; + + /// If such a bucket can fit, adds it to m_Contents and returns true + bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType); + + /// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true + bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum); +} ; // tolua_export + + + + diff --git a/source/BlockEntities/DropSpenserEntity.cpp b/source/BlockEntities/DropSpenserEntity.cpp index a9860d812..eb8257a21 100644 --- a/source/BlockEntities/DropSpenserEntity.cpp +++ b/source/BlockEntities/DropSpenserEntity.cpp @@ -1,245 +1,245 @@ - -// DropSpenserEntity.cpp - -// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity -// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior - -#include "Globals.h" -#include "DropSpenserEntity.h" -#include "../Player.h" -#include "../Chunk.h" - - - - - -cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), - m_ShouldDropSpense(false), - m_IsPowered(false) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -cDropSpenserEntity::~cDropSpenserEntity() -{ - // Tell window its owner is destroyed - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->OwnerDestroyed(); - } -} - - - - - -void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction) -{ - switch (a_Direction) - { - case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return; - case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return; - case E_META_DROPSPENSER_FACING_ZM: a_BlockZ--; return; - case E_META_DROPSPENSER_FACING_ZP: a_BlockZ++; return; - case E_META_DROPSPENSER_FACING_XM: a_BlockX--; return; - case E_META_DROPSPENSER_FACING_XP: a_BlockX++; return; - } - LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction); - return; -} - - - - - -void cDropSpenserEntity::DropSpense(cChunk & a_Chunk) -{ - // Pick one of the occupied slots: - int OccupiedSlots[9]; - int SlotsCnt = 0; - for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) - { - if (!m_Contents.GetSlot(i).IsEmpty()) - { - OccupiedSlots[SlotsCnt] = i; - SlotsCnt++; - } - } // for i - m_Contents[] - - if (SlotsCnt == 0) - { - // Nothing in the dropspenser, play the click sound - m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f); - return; - } - - int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1); - - // DropSpense the item, using the specialized behavior in the subclasses: - DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]); - - // Broadcast a smoke and click effects: - NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - int SmokeDir = 0; - switch (Meta) - { - case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break; - case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break; - case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break; - case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break; - } - m_World->BroadcastSoundParticleEffect(2000, m_PosX * 8, m_PosY * 8, m_PosZ * 8, SmokeDir); - m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f); - - // Update the UI window, if open: - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastWholeWindow(); - } -} - - - - - -void cDropSpenserEntity::Activate(void) -{ - m_ShouldDropSpense = true; -} - - - - - -void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered) -{ - if (a_IsPowered && !m_IsPowered) - { - Activate(); - } - m_IsPowered = a_IsPowered; -} - - - - - -bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (!m_ShouldDropSpense) - { - return false; - } - - m_ShouldDropSpense = false; - DropSpense(a_Chunk); - return true; -} - - - - - -bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - Json::Value AllSlots = a_Value.get("Slots", 0); - int SlotIdx = 0; - for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) - { - cItem Contents; - Contents.FromJson(*itr); - m_Contents.SetSlot(SlotIdx, Contents); - SlotIdx++; - if (SlotIdx >= m_Contents.GetNumSlots()) - { - return true; - } - } - - return true; -} - - - - - -void cDropSpenserEntity::SaveToJson(Json::Value & a_Value) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - Json::Value AllSlots; - int NumSlots = m_Contents.GetNumSlots(); - for (int i = 0; i < NumSlots; i++) - { - Json::Value Slot; - m_Contents.GetSlot(i).GetJson(Slot); - AllSlots.append(Slot); - } - a_Value["Slots"] = AllSlots; -} - - - - - -void cDropSpenserEntity::SendTo(cClientHandle & a_Client) -{ - // Nothing needs to be sent - UNUSED(a_Client); -} - - - - - -void cDropSpenserEntity::UsedBy(cPlayer * a_Player) -{ - cWindow * Window = GetWindow(); - if (Window == NULL) - { - OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this)); - Window = GetWindow(); - } - - if (Window != NULL) - { - if (a_Player->GetWindow() != Window) - { - a_Player->OpenWindow(Window); - } - } -} - - - - - -void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum) -{ - int DispX = m_PosX; - int DispY = m_PosY; - int DispZ = m_PosZ; - NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - AddDropSpenserDir(DispX, DispY, DispZ, Meta); - - cItems Pickups; - Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum)); - m_World->SpawnItemPickups(Pickups, DispX, DispY, DispZ); -} - - - - + +// DropSpenserEntity.cpp + +// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity +// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior + +#include "Globals.h" +#include "DropSpenserEntity.h" +#include "../Player.h" +#include "../Chunk.h" + + + + + +cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_ShouldDropSpense(false), + m_IsPowered(false) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +cDropSpenserEntity::~cDropSpenserEntity() +{ + // Tell window its owner is destroyed + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction) +{ + switch (a_Direction) + { + case E_META_DROPSPENSER_FACING_YM: a_BlockY--; return; + case E_META_DROPSPENSER_FACING_YP: a_BlockY++; return; + case E_META_DROPSPENSER_FACING_ZM: a_BlockZ--; return; + case E_META_DROPSPENSER_FACING_ZP: a_BlockZ++; return; + case E_META_DROPSPENSER_FACING_XM: a_BlockX--; return; + case E_META_DROPSPENSER_FACING_XP: a_BlockX++; return; + } + LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction); + return; +} + + + + + +void cDropSpenserEntity::DropSpense(cChunk & a_Chunk) +{ + // Pick one of the occupied slots: + int OccupiedSlots[9]; + int SlotsCnt = 0; + for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) + { + if (!m_Contents.GetSlot(i).IsEmpty()) + { + OccupiedSlots[SlotsCnt] = i; + SlotsCnt++; + } + } // for i - m_Contents[] + + if (SlotsCnt == 0) + { + // Nothing in the dropspenser, play the click sound + m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f); + return; + } + + int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1); + + // DropSpense the item, using the specialized behavior in the subclasses: + DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]); + + // Broadcast a smoke and click effects: + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + int SmokeDir = 0; + switch (Meta) + { + case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break; + case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break; + case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break; + case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break; + } + m_World->BroadcastSoundParticleEffect(2000, m_PosX * 8, m_PosY * 8, m_PosZ * 8, SmokeDir); + m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f); + + // Update the UI window, if open: + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->BroadcastWholeWindow(); + } +} + + + + + +void cDropSpenserEntity::Activate(void) +{ + m_ShouldDropSpense = true; +} + + + + + +void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered) +{ + if (a_IsPowered && !m_IsPowered) + { + Activate(); + } + m_IsPowered = a_IsPowered; +} + + + + + +bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (!m_ShouldDropSpense) + { + return false; + } + + m_ShouldDropSpense = false; + DropSpense(a_Chunk); + return true; +} + + + + + +bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) + { + cItem Contents; + Contents.FromJson(*itr); + m_Contents.SetSlot(SlotIdx, Contents); + SlotIdx++; + if (SlotIdx >= m_Contents.GetNumSlots()) + { + return true; + } + } + + return true; +} + + + + + +void cDropSpenserEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + int NumSlots = m_Contents.GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + Json::Value Slot; + m_Contents.GetSlot(i).GetJson(Slot); + AllSlots.append(Slot); + } + a_Value["Slots"] = AllSlots; +} + + + + + +void cDropSpenserEntity::SendTo(cClientHandle & a_Client) +{ + // Nothing needs to be sent + UNUSED(a_Client); +} + + + + + +void cDropSpenserEntity::UsedBy(cPlayer * a_Player) +{ + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this)); + Window = GetWindow(); + } + + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } +} + + + + + +void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + int DispX = m_PosX; + int DispY = m_PosY; + int DispZ = m_PosZ; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + AddDropSpenserDir(DispX, DispY, DispZ, Meta); + + cItems Pickups; + Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum)); + m_World->SpawnItemPickups(Pickups, DispX, DispY, DispZ); +} + + + + diff --git a/source/BlockEntities/DropSpenserEntity.h b/source/BlockEntities/DropSpenserEntity.h index ca9f7dfa3..f2f1eba36 100644 --- a/source/BlockEntities/DropSpenserEntity.h +++ b/source/BlockEntities/DropSpenserEntity.h @@ -1,89 +1,89 @@ - -// DropSpenser.h - -// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity -// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior - - - - - -#pragma once - -#include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" - - - - - -namespace Json -{ - class Value; -} - -class cClientHandle; -class cServer; - - - - - -// tolua_begin -class cDropSpenserEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner -{ - typedef cBlockEntityWithItems super; - -public: - enum { - ContentsHeight = 3, - ContentsWidth = 3, - } ; - - // tolua_end - - cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - virtual ~cDropSpenserEntity(); - - static const char * GetClassStatic(void) { return "cDropSpenserEntity"; } - - bool LoadFromJson(const Json::Value & a_Value); - - // cBlockEntity overrides: - virtual void SaveToJson(Json::Value & a_Value) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void SendTo(cClientHandle & a_Client) override; - virtual void UsedBy(cPlayer * a_Player) override; - - // tolua_begin - - /// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize) - void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction); - - /// Sets the dropspenser to dropspense an item in the next tick - void Activate(void); - - /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate - void SetRedstonePower(bool a_IsPowered); - - // tolua_end - -protected: - bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick - bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power. - - /// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects - void DropSpense(cChunk & a_Chunk); - - /// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...) - virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0; - - /// Helper function, drops one item from the specified slot (like a dropper) - void DropFromSlot(cChunk & a_Chunk, int a_SlotNum); -} ; // tolua_export - - - - + +// DropSpenser.h + +// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity +// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior + + + + + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +namespace Json +{ + class Value; +} + +class cClientHandle; +class cServer; + + + + + +// tolua_begin +class cDropSpenserEntity : + public cBlockEntityWithItems, + public cBlockEntityWindowOwner +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 3, + ContentsWidth = 3, + } ; + + // tolua_end + + cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + virtual ~cDropSpenserEntity(); + + static const char * GetClassStatic(void) { return "cDropSpenserEntity"; } + + bool LoadFromJson(const Json::Value & a_Value); + + // cBlockEntity overrides: + virtual void SaveToJson(Json::Value & a_Value) override; + virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual void UsedBy(cPlayer * a_Player) override; + + // tolua_begin + + /// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize) + void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction); + + /// Sets the dropspenser to dropspense an item in the next tick + void Activate(void); + + /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate + void SetRedstonePower(bool a_IsPowered); + + // tolua_end + +protected: + bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick + bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power. + + /// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects + void DropSpense(cChunk & a_Chunk); + + /// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...) + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0; + + /// Helper function, drops one item from the specified slot (like a dropper) + void DropFromSlot(cChunk & a_Chunk, int a_SlotNum); +} ; // tolua_export + + + + diff --git a/source/BlockEntities/DropperEntity.cpp b/source/BlockEntities/DropperEntity.cpp index ebb52c8a1..44d679e02 100644 --- a/source/BlockEntities/DropperEntity.cpp +++ b/source/BlockEntities/DropperEntity.cpp @@ -1,42 +1,42 @@ - -// DropperEntity.cpp - -// Implements the cRtopperEntity class representing a Dropper block entity - -#include "Globals.h" -#include "DropperEntity.h" -#include "../Player.h" -#include "../Simulator/FluidSimulator.h" - - - - - -cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) : - super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, NULL) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) -{ - DropFromSlot(a_Chunk, a_SlotNum); -} - - - - + +// DropperEntity.cpp + +// Implements the cRtopperEntity class representing a Dropper block entity + +#include "Globals.h" +#include "DropperEntity.h" +#include "../Player.h" +#include "../Simulator/FluidSimulator.h" + + + + + +cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) : + super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, NULL) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) +{ + DropFromSlot(a_Chunk, a_SlotNum); +} + + + + diff --git a/source/BlockEntities/DropperEntity.h b/source/BlockEntities/DropperEntity.h index ed29bfe95..af74e7f7c 100644 --- a/source/BlockEntities/DropperEntity.h +++ b/source/BlockEntities/DropperEntity.h @@ -1,49 +1,49 @@ - -// DropperEntity.h - -// Declares the cDropperEntity class representing a dropper block entity - - - - - -#pragma once - -#include "DropSpenserEntity.h" - - - - - -// tolua_begin -class cDropperEntity : - public cDropSpenserEntity -{ - typedef cDropSpenserEntity super; - -public: - - /// Constructor used while generating a chunk; sets m_World to NULL - cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ); - - // tolua_end - - /// Constructor used for normal operation - cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - - static const char * GetClassStatic(void) { return "cDropperEntity"; } - -protected: - // cDropSpenserEntity overrides: - virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; - - /** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper. - Called when there's a container directly in front of the dropper, - so the dropper should store items there, rather than dropping. - */ - void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ); -} ; // tolua_export - - - - + +// DropperEntity.h + +// Declares the cDropperEntity class representing a dropper block entity + + + + + +#pragma once + +#include "DropSpenserEntity.h" + + + + + +// tolua_begin +class cDropperEntity : + public cDropSpenserEntity +{ + typedef cDropSpenserEntity super; + +public: + + /// Constructor used while generating a chunk; sets m_World to NULL + cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_end + + /// Constructor used for normal operation + cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + static const char * GetClassStatic(void) { return "cDropperEntity"; } + +protected: + // cDropSpenserEntity overrides: + virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override; + + /** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper. + Called when there's a container directly in front of the dropper, + so the dropper should store items there, rather than dropping. + */ + void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ); +} ; // tolua_export + + + + diff --git a/source/BlockEntities/HopperEntity.cpp b/source/BlockEntities/HopperEntity.cpp index 3fadf727c..23e4b8096 100644 --- a/source/BlockEntities/HopperEntity.cpp +++ b/source/BlockEntities/HopperEntity.cpp @@ -1,523 +1,523 @@ - -// HopperEntity.cpp - -// Implements the cHopperEntity representing a hopper block entity - -#include "Globals.h" -#include "HopperEntity.h" -#include "../Chunk.h" -#include "../Player.h" -#include "ChestEntity.h" -#include "DropSpenserEntity.h" -#include "FurnaceEntity.h" - - - - - -cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) : - super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, NULL), - m_LastMoveItemsInTick(0), - m_LastMoveItemsOutTick(0) -{ -} - - - - - -cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), - m_LastMoveItemsInTick(0), - m_LastMoveItemsOutTick(0) -{ -} - - - - - -/** Returns the block coords of the block receiving the output items, based on the meta -Returns false if unattached -*/ -bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ) -{ - a_OutputX = m_PosX; - a_OutputY = m_PosY; - a_OutputZ = m_PosZ; - switch (a_BlockMeta) - { - case E_META_HOPPER_FACING_XM: a_OutputX--; return true; - case E_META_HOPPER_FACING_XP: a_OutputX++; return true; - case E_META_HOPPER_FACING_YM: a_OutputY--; return true; - case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true; - case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true; - default: - { - // Not attached - return false; - } - } -} - - - - - -bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); - - bool res = false; - res = MoveItemsIn (a_Chunk, CurrentTick) || res; - res = MovePickupsIn(a_Chunk, CurrentTick) || res; - res = MoveItemsOut (a_Chunk, CurrentTick) || res; - return res; -} - - - - - -void cHopperEntity::SaveToJson(Json::Value & a_Value) -{ - // TODO - LOGWARNING("%s: Not implemented yet", __FUNCTION__); -} - - - - - -void cHopperEntity::SendTo(cClientHandle & a_Client) -{ - // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance - // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked - - UNUSED(a_Client); -} - - - - - -void cHopperEntity::UsedBy(cPlayer * a_Player) -{ - // If the window is not created, open it anew: - cWindow * Window = GetWindow(); - if (Window == NULL) - { - OpenNewWindow(); - Window = GetWindow(); - } - - // Open the window for the player: - if (Window != NULL) - { - if (a_Player->GetWindow() != Window) - { - a_Player->OpenWindow(Window); - } - } - - // This is rather a hack - // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now - // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. - // The few false positives aren't much to worry about - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(m_PosX, m_PosY, m_PosZ, ChunkX, ChunkZ); - m_World->MarkChunkDirty(ChunkX, ChunkZ); -} - - - - - -/// Opens a new window UI for this hopper -void cHopperEntity::OpenNewWindow(void) -{ - OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this)); -} - - - - - -/// Moves items from the container above it into this hopper. Returns true if the contents have changed. -bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) -{ - if (m_PosY >= cChunkDef::Height) - { - // This hopper is at the top of the world, no more blocks above - return false; - } - - if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER) - { - // Too early after the previous transfer - return false; - } - - // Try moving an item in: - bool res = false; - switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ)) - { - case E_BLOCK_CHEST: res = MoveItemsFromChest(a_Chunk); break; - case E_BLOCK_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break; - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: res = MoveItemsFromGrid(((cDropSpenserEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break; - case E_BLOCK_HOPPER: res = MoveItemsFromGrid(((cHopperEntity *) a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break; - case E_BLOCK_LIT_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break; - } - - // If the item has been moved, reset the last tick: - if (res) - { - m_LastMoveItemsInTick = a_CurrentTick; - } - - return res; -} - - - - - -/// Moves pickups from above this hopper into it. Returns true if the contents have changed. -bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) -{ - // TODO - return false; -} - - - - - -/// Moves items out from this hopper into the destination. Returns true if the contents have changed. -bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) -{ - if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER) - { - // Too early after the previous transfer - return false; - } - - int bx, by, bz; - NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - if (!GetOutputBlockPos(Meta, bx, by, bz)) - { - // Not attached to another container - return false; - } - if (by < 0) - { - // Cannot output below the zero-th block level - return false; - } - - // Convert coords to relative: - int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width; - int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz); - if (DestChunk == NULL) - { - // The destination chunk has been unloaded, don't tick - return false; - } - - // Call proper moving function, based on the blocktype present at the coords: - bool res = false; - switch (DestChunk->GetBlock(rx, by, rz)) - { - case E_BLOCK_CHEST: res = MoveItemsToChest(*DestChunk, bx, by, bz); break; - case E_BLOCK_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break; - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: res = MoveItemsToGrid(((cDropSpenserEntity *)DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break; - case E_BLOCK_HOPPER: res = MoveItemsToGrid(((cHopperEntity *) DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break; - case E_BLOCK_LIT_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break; - } - - // If the item has been moved, reset the last tick: - if (res) - { - m_LastMoveItemsOutTick = a_CurrentTick; - } - - return res; -} - - - - - -/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. -bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) -{ - if (MoveItemsFromGrid(((cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents())) - { - // Moved the item from the chest directly above the hopper - return true; - } - - // Check if the chest is a double-chest, if so, try to move from there: - static const struct - { - int x, z; - } - Coords [] = - { - {1, 0}, - {-1, 0}, - {0, 1}, - {0, -1}, - } ; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - int x = m_RelX + Coords[i].x; - int z = m_RelZ + Coords[i].z; - cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); - if ( - (Neighbor == NULL) || - (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) - ) - { - continue; - } - if (MoveItemsFromGrid(((cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))->GetContents())) - { - return true; - } - return false; - } - - // The chest was single and nothing could be moved - return false; -} - - - - - -/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. -bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) -{ - cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); - ASSERT(Furnace != NULL); - - // Try move from the output slot: - if (MoveItemsFromSlot(Furnace->GetOutputSlot(), true)) - { - cItem NewOutput(Furnace->GetOutputSlot()); - Furnace->SetOutputSlot(NewOutput.AddCount(-1)); - return true; - } - - // No output moved, check if we can move an empty bucket out of the fuel slot: - if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET) - { - if (MoveItemsFromSlot(Furnace->GetFuelSlot(), true)) - { - Furnace->SetFuelSlot(cItem()); - return true; - } - } - - // Nothing can be moved - return false; -} - - - - - -/// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed. -bool cHopperEntity::MoveItemsFromGrid(cItemGrid & a_Grid) -{ - int NumSlots = a_Grid.GetNumSlots(); - - // First try adding items of types already in the hopper: - for (int i = 0; i < NumSlots; i++) - { - if (a_Grid.IsSlotEmpty(i)) - { - continue; - } - if (MoveItemsFromSlot(a_Grid.GetSlot(i), false)) - { - a_Grid.ChangeSlotCount(i, -1); - return true; - } - } - - // No already existing stack can be topped up, try again with allowing new stacks: - for (int i = 0; i < NumSlots; i++) - { - if (a_Grid.IsSlotEmpty(i)) - { - continue; - } - if (MoveItemsFromSlot(a_Grid.GetSlot(i), true)) - { - a_Grid.ChangeSlotCount(i, -1); - return true; - } - } - return false; -} - - - - - -/// Moves one of the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. -bool cHopperEntity::MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks) -{ - cItem One(a_ItemStack.CopyOne()); - if (m_Contents.AddItem(One, a_AllowNewStacks) > 0) - { - return true; - } - return false; -} - - - - - -/// Moves items to the chest at the specified coords. Returns true if contents have changed -bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Try the chest directly connected to the hopper: - if (MoveItemsToGrid(((cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents())) - { - return true; - } - - // Check if the chest is a double-chest, if so, try to move into the other half: - static const struct - { - int x, z; - } - Coords [] = - { - {1, 0}, - {-1, 0}, - {0, 1}, - {0, -1}, - } ; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - int x = m_RelX + Coords[i].x; - int z = m_RelZ + Coords[i].z; - cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); - if ( - (Neighbor == NULL) || - (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) - ) - { - continue; - } - if (MoveItemsToGrid(((cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents())) - { - return true; - } - return false; - } - - // The chest was single and nothing could be moved - return false; -} - - - - - -/// Moves items to the furnace at the specified coords. Returns true if contents have changed -bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta) -{ - cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); - if (a_HopperMeta == E_META_HOPPER_FACING_YM) - { - // Feed the input slot of the furnace - return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsInput); - } - else - { - // Feed the fuel slot of the furnace - return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsFuel); - } - return false; -} - - - - - -/// Moves items to the specified ItemGrid. Returns true if contents have changed -bool cHopperEntity::MoveItemsToGrid(cItemGrid & a_ItemGrid) -{ - // Iterate through our slots, try to move from each one: - for (int i = 0; i < ContentsWidth * ContentsHeight; i++) - { - const cItem & SrcItem = m_Contents.GetSlot(i); - if (SrcItem.IsEmpty()) - { - continue; - } - - cItem ToAdd = SrcItem.CopyOne(); - if (a_ItemGrid.AddItem(ToAdd) > 0) - { - m_Contents.ChangeSlotCount(i, -1); - return true; - } - } - return false; -} - - - - - -/// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed. -bool cHopperEntity::MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum) -{ - if (a_ItemGrid.IsSlotEmpty(a_DestSlotNum)) - { - // The slot is empty, move the first non-empty slot from our contents: - for (int i = 0; i < ContentsWidth * ContentsHeight; i++) - { - if (!m_Contents.IsSlotEmpty(i)) - { - a_ItemGrid.SetSlot(a_DestSlotNum, m_Contents.GetSlot(i).CopyOne()); - m_Contents.ChangeSlotCount(i, -1); - return true; - } - } - return false; - } - else - { - // The slot is taken, try to top it up: - const cItem & DestSlot = a_ItemGrid.GetSlot(a_DestSlotNum); - if (DestSlot.IsFullStack()) - { - return false; - } - for (int i = 0; i < ContentsWidth * ContentsHeight; i++) - { - if (m_Contents.GetSlot(i).IsStackableWith(DestSlot)) - { - a_ItemGrid.ChangeSlotCount(a_DestSlotNum, 1); - m_Contents.ChangeSlotCount(i, -1); - return true; - } - } - return false; - } -} - - - - + +// HopperEntity.cpp + +// Implements the cHopperEntity representing a hopper block entity + +#include "Globals.h" +#include "HopperEntity.h" +#include "../Chunk.h" +#include "../Player.h" +#include "ChestEntity.h" +#include "DropSpenserEntity.h" +#include "FurnaceEntity.h" + + + + + +cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) : + super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, NULL), + m_LastMoveItemsInTick(0), + m_LastMoveItemsOutTick(0) +{ +} + + + + + +cHopperEntity::cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + super(E_BLOCK_HOPPER, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_LastMoveItemsInTick(0), + m_LastMoveItemsOutTick(0) +{ +} + + + + + +/** Returns the block coords of the block receiving the output items, based on the meta +Returns false if unattached +*/ +bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ) +{ + a_OutputX = m_PosX; + a_OutputY = m_PosY; + a_OutputZ = m_PosZ; + switch (a_BlockMeta) + { + case E_META_HOPPER_FACING_XM: a_OutputX--; return true; + case E_META_HOPPER_FACING_XP: a_OutputX++; return true; + case E_META_HOPPER_FACING_YM: a_OutputY--; return true; + case E_META_HOPPER_FACING_ZM: a_OutputZ--; return true; + case E_META_HOPPER_FACING_ZP: a_OutputZ++; return true; + default: + { + // Not attached + return false; + } + } +} + + + + + +bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); + + bool res = false; + res = MoveItemsIn (a_Chunk, CurrentTick) || res; + res = MovePickupsIn(a_Chunk, CurrentTick) || res; + res = MoveItemsOut (a_Chunk, CurrentTick) || res; + return res; +} + + + + + +void cHopperEntity::SaveToJson(Json::Value & a_Value) +{ + // TODO + LOGWARNING("%s: Not implemented yet", __FUNCTION__); +} + + + + + +void cHopperEntity::SendTo(cClientHandle & a_Client) +{ + // The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance + // All the actual handling is in the cWindow UI code that gets called when the hopper is rclked + + UNUSED(a_Client); +} + + + + + +void cHopperEntity::UsedBy(cPlayer * a_Player) +{ + // If the window is not created, open it anew: + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenNewWindow(); + Window = GetWindow(); + } + + // Open the window for the player: + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } + + // This is rather a hack + // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now + // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. + // The few false positives aren't much to worry about + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(m_PosX, m_PosY, m_PosZ, ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkZ); +} + + + + + +/// Opens a new window UI for this hopper +void cHopperEntity::OpenNewWindow(void) +{ + OpenWindow(new cHopperWindow(m_PosX, m_PosY, m_PosZ, this)); +} + + + + + +/// Moves items from the container above it into this hopper. Returns true if the contents have changed. +bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + if (m_PosY >= cChunkDef::Height) + { + // This hopper is at the top of the world, no more blocks above + return false; + } + + if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER) + { + // Too early after the previous transfer + return false; + } + + // Try moving an item in: + bool res = false; + switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ)) + { + case E_BLOCK_CHEST: res = MoveItemsFromChest(a_Chunk); break; + case E_BLOCK_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break; + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: res = MoveItemsFromGrid(((cDropSpenserEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break; + case E_BLOCK_HOPPER: res = MoveItemsFromGrid(((cHopperEntity *) a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()); break; + case E_BLOCK_LIT_FURNACE: res = MoveItemsFromFurnace(a_Chunk); break; + } + + // If the item has been moved, reset the last tick: + if (res) + { + m_LastMoveItemsInTick = a_CurrentTick; + } + + return res; +} + + + + + +/// Moves pickups from above this hopper into it. Returns true if the contents have changed. +bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + // TODO + return false; +} + + + + + +/// Moves items out from this hopper into the destination. Returns true if the contents have changed. +bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) +{ + if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER) + { + // Too early after the previous transfer + return false; + } + + int bx, by, bz; + NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); + if (!GetOutputBlockPos(Meta, bx, by, bz)) + { + // Not attached to another container + return false; + } + if (by < 0) + { + // Cannot output below the zero-th block level + return false; + } + + // Convert coords to relative: + int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width; + int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz); + if (DestChunk == NULL) + { + // The destination chunk has been unloaded, don't tick + return false; + } + + // Call proper moving function, based on the blocktype present at the coords: + bool res = false; + switch (DestChunk->GetBlock(rx, by, rz)) + { + case E_BLOCK_CHEST: res = MoveItemsToChest(*DestChunk, bx, by, bz); break; + case E_BLOCK_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break; + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: res = MoveItemsToGrid(((cDropSpenserEntity *)DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break; + case E_BLOCK_HOPPER: res = MoveItemsToGrid(((cHopperEntity *) DestChunk->GetBlockEntity(bx, by, bz))->GetContents()); break; + case E_BLOCK_LIT_FURNACE: res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); break; + } + + // If the item has been moved, reset the last tick: + if (res) + { + m_LastMoveItemsOutTick = a_CurrentTick; + } + + return res; +} + + + + + +/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) +{ + if (MoveItemsFromGrid(((cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents())) + { + // Moved the item from the chest directly above the hopper + return true; + } + + // Check if the chest is a double-chest, if so, try to move from there: + static const struct + { + int x, z; + } + Coords [] = + { + {1, 0}, + {-1, 0}, + {0, 1}, + {0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + int x = m_RelX + Coords[i].x; + int z = m_RelZ + Coords[i].z; + cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); + if ( + (Neighbor == NULL) || + (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) + ) + { + continue; + } + if (MoveItemsFromGrid(((cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))->GetContents())) + { + return true; + } + return false; + } + + // The chest was single and nothing could be moved + return false; +} + + + + + +/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) +{ + cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); + ASSERT(Furnace != NULL); + + // Try move from the output slot: + if (MoveItemsFromSlot(Furnace->GetOutputSlot(), true)) + { + cItem NewOutput(Furnace->GetOutputSlot()); + Furnace->SetOutputSlot(NewOutput.AddCount(-1)); + return true; + } + + // No output moved, check if we can move an empty bucket out of the fuel slot: + if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET) + { + if (MoveItemsFromSlot(Furnace->GetFuelSlot(), true)) + { + Furnace->SetFuelSlot(cItem()); + return true; + } + } + + // Nothing can be moved + return false; +} + + + + + +/// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed. +bool cHopperEntity::MoveItemsFromGrid(cItemGrid & a_Grid) +{ + int NumSlots = a_Grid.GetNumSlots(); + + // First try adding items of types already in the hopper: + for (int i = 0; i < NumSlots; i++) + { + if (a_Grid.IsSlotEmpty(i)) + { + continue; + } + if (MoveItemsFromSlot(a_Grid.GetSlot(i), false)) + { + a_Grid.ChangeSlotCount(i, -1); + return true; + } + } + + // No already existing stack can be topped up, try again with allowing new stacks: + for (int i = 0; i < NumSlots; i++) + { + if (a_Grid.IsSlotEmpty(i)) + { + continue; + } + if (MoveItemsFromSlot(a_Grid.GetSlot(i), true)) + { + a_Grid.ChangeSlotCount(i, -1); + return true; + } + } + return false; +} + + + + + +/// Moves one of the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. +bool cHopperEntity::MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks) +{ + cItem One(a_ItemStack.CopyOne()); + if (m_Contents.AddItem(One, a_AllowNewStacks) > 0) + { + return true; + } + return false; +} + + + + + +/// Moves items to the chest at the specified coords. Returns true if contents have changed +bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Try the chest directly connected to the hopper: + if (MoveItemsToGrid(((cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents())) + { + return true; + } + + // Check if the chest is a double-chest, if so, try to move into the other half: + static const struct + { + int x, z; + } + Coords [] = + { + {1, 0}, + {-1, 0}, + {0, 1}, + {0, -1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + int x = m_RelX + Coords[i].x; + int z = m_RelZ + Coords[i].z; + cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); + if ( + (Neighbor == NULL) || + (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) + ) + { + continue; + } + if (MoveItemsToGrid(((cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents())) + { + return true; + } + return false; + } + + // The chest was single and nothing could be moved + return false; +} + + + + + +/// Moves items to the furnace at the specified coords. Returns true if contents have changed +bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta) +{ + cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (a_HopperMeta == E_META_HOPPER_FACING_YM) + { + // Feed the input slot of the furnace + return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsInput); + } + else + { + // Feed the fuel slot of the furnace + return MoveItemsToSlot(Furnace->GetContents(), cFurnaceEntity::fsFuel); + } + return false; +} + + + + + +/// Moves items to the specified ItemGrid. Returns true if contents have changed +bool cHopperEntity::MoveItemsToGrid(cItemGrid & a_ItemGrid) +{ + // Iterate through our slots, try to move from each one: + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + const cItem & SrcItem = m_Contents.GetSlot(i); + if (SrcItem.IsEmpty()) + { + continue; + } + + cItem ToAdd = SrcItem.CopyOne(); + if (a_ItemGrid.AddItem(ToAdd) > 0) + { + m_Contents.ChangeSlotCount(i, -1); + return true; + } + } + return false; +} + + + + + +/// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed. +bool cHopperEntity::MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum) +{ + if (a_ItemGrid.IsSlotEmpty(a_DestSlotNum)) + { + // The slot is empty, move the first non-empty slot from our contents: + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (!m_Contents.IsSlotEmpty(i)) + { + a_ItemGrid.SetSlot(a_DestSlotNum, m_Contents.GetSlot(i).CopyOne()); + m_Contents.ChangeSlotCount(i, -1); + return true; + } + } + return false; + } + else + { + // The slot is taken, try to top it up: + const cItem & DestSlot = a_ItemGrid.GetSlot(a_DestSlotNum); + if (DestSlot.IsFullStack()) + { + return false; + } + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (m_Contents.GetSlot(i).IsStackableWith(DestSlot)) + { + a_ItemGrid.ChangeSlotCount(a_DestSlotNum, 1); + m_Contents.ChangeSlotCount(i, -1); + return true; + } + } + return false; + } +} + + + + diff --git a/source/BlockEntities/HopperEntity.h b/source/BlockEntities/HopperEntity.h index 7f5eb23d1..8fc17b631 100644 --- a/source/BlockEntities/HopperEntity.h +++ b/source/BlockEntities/HopperEntity.h @@ -1,102 +1,102 @@ - -// HopperEntity.h - -// Declares the cHopperEntity representing a hopper block entity - - - - - -#pragma once - -#include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" - - - - - -class cHopperEntity : // tolua_export - public cBlockEntityWindowOwner, - // tolua_begin - public cBlockEntityWithItems -{ - typedef cBlockEntityWithItems super; - -public: - enum { - ContentsHeight = 1, - ContentsWidth = 5, - TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper - } ; - - /// Constructor used while generating a chunk; sets m_World to NULL - cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ); - - // tolua_end - - /// Constructor used for normal operation - cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - - // tolua_begin - - /** Returns the block coords of the block receiving the output items, based on the meta - Returns false if unattached - */ - bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ); - - // tolua_end - - static const char * GetClassStatic(void) { return "cHopperEntity"; } - -protected: - - Int64 m_LastMoveItemsInTick; - Int64 m_LastMoveItemsOutTick; - - // cBlockEntity overrides: - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void SaveToJson(Json::Value & a_Value) override; - virtual void SendTo(cClientHandle & a_Client) override; - virtual void UsedBy(cPlayer * a_Player) override; - - /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. - void OpenNewWindow(void); - - /// Moves items from the container above it into this hopper. Returns true if the contents have changed. - bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick); - - /// Moves pickups from above this hopper into it. Returns true if the contents have changed. - bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick); - - /// Moves items out from this hopper into the destination. Returns true if the contents have changed. - bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick); - - /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. - bool MoveItemsFromChest(cChunk & a_Chunk); - - /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. - bool MoveItemsFromFurnace(cChunk & a_Chunk); - - /// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed. - bool MoveItemsFromGrid(cItemGrid & a_Grid); - - /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. - bool MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks); - - /// Moves items to the chest at the specified coords. Returns true if contents have changed - bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Moves items to the furnace at the specified coords. Returns true if contents have changed - bool MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta); - - /// Moves items to the specified ItemGrid. Returns true if contents have changed - bool MoveItemsToGrid(cItemGrid & a_ItemGrid); - - /// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed. - bool MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum); -} ; - - - - + +// HopperEntity.h + +// Declares the cHopperEntity representing a hopper block entity + + + + + +#pragma once + +#include "BlockEntityWithItems.h" +#include "../UI/WindowOwner.h" + + + + + +class cHopperEntity : // tolua_export + public cBlockEntityWindowOwner, + // tolua_begin + public cBlockEntityWithItems +{ + typedef cBlockEntityWithItems super; + +public: + enum { + ContentsHeight = 1, + ContentsWidth = 5, + TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper + } ; + + /// Constructor used while generating a chunk; sets m_World to NULL + cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ); + + // tolua_end + + /// Constructor used for normal operation + cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + // tolua_begin + + /** Returns the block coords of the block receiving the output items, based on the meta + Returns false if unattached + */ + bool GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, int & a_OutputY, int & a_OutputZ); + + // tolua_end + + static const char * GetClassStatic(void) { return "cHopperEntity"; } + +protected: + + Int64 m_LastMoveItemsInTick; + Int64 m_LastMoveItemsOutTick; + + // cBlockEntity overrides: + virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void SaveToJson(Json::Value & a_Value) override; + virtual void SendTo(cClientHandle & a_Client) override; + virtual void UsedBy(cPlayer * a_Player) override; + + /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. + void OpenNewWindow(void); + + /// Moves items from the container above it into this hopper. Returns true if the contents have changed. + bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves pickups from above this hopper into it. Returns true if the contents have changed. + bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves items out from this hopper into the destination. Returns true if the contents have changed. + bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick); + + /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. + bool MoveItemsFromChest(cChunk & a_Chunk); + + /// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed. + bool MoveItemsFromFurnace(cChunk & a_Chunk); + + /// Moves items from the specified ItemGrid into this hopper. Returns true if contents have changed. + bool MoveItemsFromGrid(cItemGrid & a_Grid); + + /// Moves one piece from the specified itemstack into this hopper. Returns true if contents have changed. Doesn't change the itemstack. + bool MoveItemsFromSlot(const cItem & a_ItemStack, bool a_AllowNewStacks); + + /// Moves items to the chest at the specified coords. Returns true if contents have changed + bool MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Moves items to the furnace at the specified coords. Returns true if contents have changed + bool MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta); + + /// Moves items to the specified ItemGrid. Returns true if contents have changed + bool MoveItemsToGrid(cItemGrid & a_ItemGrid); + + /// Moves one piece to the specified ItemGrid's slot. Returns true if contents have changed. + bool MoveItemsToSlot(cItemGrid & a_ItemGrid, int a_DestSlotNum); +} ; + + + + diff --git a/source/BlockEntities/JukeboxEntity.cpp b/source/BlockEntities/JukeboxEntity.cpp index 617b7bd00..ec6d13282 100644 --- a/source/BlockEntities/JukeboxEntity.cpp +++ b/source/BlockEntities/JukeboxEntity.cpp @@ -1,123 +1,123 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "JukeboxEntity.h" -#include "../World.h" -#include - - - - - -cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) - : cBlockEntity(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World) - , m_Record( 0 ) -{ -} - - - - - -cJukeboxEntity::~cJukeboxEntity() -{ - if (m_Record >= 2256 && m_Record <= 2267) - { - EjectRecord(); - m_Record = 0; - } -} - - - - - -void cJukeboxEntity::UsedBy(cPlayer * a_Player) -{ - if (m_Record == 0) - { - const cItem & HeldItem = a_Player->GetEquippedItem(); - if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267) - { - m_Record = HeldItem.m_ItemType; - a_Player->GetInventory().RemoveOneEquippedItem(); - PlayRecord(); - } - } - else if (m_Record >= 2256 && m_Record <= 2267) - { - EjectRecord(); - m_Record = 0; - } -} - - - - - -void cJukeboxEntity::PlayRecord( void ) -{ - m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, m_Record); -} - - - - - -void cJukeboxEntity::EjectRecord( void ) -{ - cItems Drops; - Drops.push_back(cItem(m_Record, 1, 0)); - m_World->SpawnItemPickups(Drops, m_PosX, m_PosY+1, m_PosZ); - m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 0); -} - - - - - -int cJukeboxEntity::GetRecord( void ) -{ - return m_Record; -} - - - - - -void cJukeboxEntity::SetRecord( int a_Record ) -{ - m_Record = a_Record; -} - - - - - -bool cJukeboxEntity::LoadFromJson( const Json::Value & a_Value ) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - m_Record = a_Value.get("Record", 0).asInt(); - - return true; -} - - - - - -void cJukeboxEntity::SaveToJson( Json::Value & a_Value ) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - a_Value["Record"] = m_Record; -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "JukeboxEntity.h" +#include "../World.h" +#include + + + + + +cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) + : cBlockEntity(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World) + , m_Record( 0 ) +{ +} + + + + + +cJukeboxEntity::~cJukeboxEntity() +{ + if (m_Record >= 2256 && m_Record <= 2267) + { + EjectRecord(); + m_Record = 0; + } +} + + + + + +void cJukeboxEntity::UsedBy(cPlayer * a_Player) +{ + if (m_Record == 0) + { + const cItem & HeldItem = a_Player->GetEquippedItem(); + if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267) + { + m_Record = HeldItem.m_ItemType; + a_Player->GetInventory().RemoveOneEquippedItem(); + PlayRecord(); + } + } + else if (m_Record >= 2256 && m_Record <= 2267) + { + EjectRecord(); + m_Record = 0; + } +} + + + + + +void cJukeboxEntity::PlayRecord( void ) +{ + m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, m_Record); +} + + + + + +void cJukeboxEntity::EjectRecord( void ) +{ + cItems Drops; + Drops.push_back(cItem(m_Record, 1, 0)); + m_World->SpawnItemPickups(Drops, m_PosX, m_PosY+1, m_PosZ); + m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 0); +} + + + + + +int cJukeboxEntity::GetRecord( void ) +{ + return m_Record; +} + + + + + +void cJukeboxEntity::SetRecord( int a_Record ) +{ + m_Record = a_Record; +} + + + + + +bool cJukeboxEntity::LoadFromJson( const Json::Value & a_Value ) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Record = a_Value.get("Record", 0).asInt(); + + return true; +} + + + + + +void cJukeboxEntity::SaveToJson( Json::Value & a_Value ) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["Record"] = m_Record; +} + + + + diff --git a/source/BlockEntities/JukeboxEntity.h b/source/BlockEntities/JukeboxEntity.h index 063453607..74d40ecef 100644 --- a/source/BlockEntities/JukeboxEntity.h +++ b/source/BlockEntities/JukeboxEntity.h @@ -1,43 +1,43 @@ - -#pragma once - -#include "BlockEntity.h" -#include "../Player.h" - - - - - -namespace Json -{ - class Value; -} - - - - - -class cJukeboxEntity : - public cBlockEntity -{ -public: - cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - virtual ~cJukeboxEntity(); - - bool LoadFromJson( const Json::Value& a_Value ); - virtual void SaveToJson( Json::Value& a_Value ) override; - - int GetRecord( void ); - void SetRecord( int a_Record ); - void PlayRecord( void ); - void EjectRecord( void ); - virtual void UsedBy( cPlayer * a_Player ) override; - virtual void SendTo(cClientHandle & a_Client) override { }; - -private: - int m_Record; -}; - - - - + +#pragma once + +#include "BlockEntity.h" +#include "../Player.h" + + + + + +namespace Json +{ + class Value; +} + + + + + +class cJukeboxEntity : + public cBlockEntity +{ +public: + cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + virtual ~cJukeboxEntity(); + + bool LoadFromJson( const Json::Value& a_Value ); + virtual void SaveToJson( Json::Value& a_Value ) override; + + int GetRecord( void ); + void SetRecord( int a_Record ); + void PlayRecord( void ); + void EjectRecord( void ); + virtual void UsedBy( cPlayer * a_Player ) override; + virtual void SendTo(cClientHandle & a_Client) override { }; + +private: + int m_Record; +}; + + + + diff --git a/source/BlockEntities/NoteEntity.cpp b/source/BlockEntities/NoteEntity.cpp index 36da13692..6dc0e20a1 100644 --- a/source/BlockEntities/NoteEntity.cpp +++ b/source/BlockEntities/NoteEntity.cpp @@ -1,159 +1,159 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "NoteEntity.h" -#include "../World.h" -#include - - -cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) - : cBlockEntity(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World) - , m_Pitch( 0 ) -{ -} - - - - - -cNoteEntity::~cNoteEntity() -{ -} - - - - - -void cNoteEntity::UsedBy( cPlayer * a_Player ) -{ - IncrementPitch(); - MakeSound(); -} - - - - - -void cNoteEntity::MakeSound( void ) -{ - char instrument; - AString sampleName; - - switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ)) - { - case E_BLOCK_PLANKS: - case E_BLOCK_LOG: - case E_BLOCK_NOTE_BLOCK: - { - // TODO: add other wood-based blocks if needed - instrument = E_INST_DOUBLE_BASS; - sampleName = "note.db"; - break; - } - - case E_BLOCK_SAND: - case E_BLOCK_GRAVEL: - case E_BLOCK_SOULSAND: - { - instrument = E_INST_SNARE_DRUM; - sampleName = "note.snare"; - break; - } - - case E_BLOCK_GLASS: - case E_BLOCK_GLASS_PANE: - case E_BLOCK_GLOWSTONE: - { - instrument = E_INST_CLICKS; - sampleName = "note.hat"; - break; - } - - case E_BLOCK_STONE: - case E_BLOCK_STONE_BRICKS: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_OBSIDIAN: - case E_BLOCK_NETHERRACK: - case E_BLOCK_BRICK: - case E_BLOCK_NETHER_BRICK: - { - // TODO: add other stone-based blocks if needed - instrument = E_INST_BASS_DRUM; - sampleName = "note.bassattack"; - break; - } - - default: - { - instrument = E_INST_HARP_PIANO; - sampleName = "note.harp"; - break; - } - } - - m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK); - - // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all - float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f); - m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch); -} - - - - - -char cNoteEntity::GetPitch( void ) -{ - return m_Pitch; -} - - - - - -void cNoteEntity::SetPitch( char a_Pitch ) -{ - m_Pitch = a_Pitch % 25; -} - - - - - -void cNoteEntity::IncrementPitch( void ) -{ - SetPitch( m_Pitch + 1 ); -} - - - - - -bool cNoteEntity::LoadFromJson( const Json::Value & a_Value ) -{ - - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - m_Pitch = (char)a_Value.get("p", 0).asInt(); - - return true; -} - - - - - -void cNoteEntity::SaveToJson( Json::Value & a_Value ) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - a_Value["p"] = m_Pitch; -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "NoteEntity.h" +#include "../World.h" +#include + + +cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) + : cBlockEntity(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World) + , m_Pitch( 0 ) +{ +} + + + + + +cNoteEntity::~cNoteEntity() +{ +} + + + + + +void cNoteEntity::UsedBy( cPlayer * a_Player ) +{ + IncrementPitch(); + MakeSound(); +} + + + + + +void cNoteEntity::MakeSound( void ) +{ + char instrument; + AString sampleName; + + switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ)) + { + case E_BLOCK_PLANKS: + case E_BLOCK_LOG: + case E_BLOCK_NOTE_BLOCK: + { + // TODO: add other wood-based blocks if needed + instrument = E_INST_DOUBLE_BASS; + sampleName = "note.db"; + break; + } + + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + case E_BLOCK_SOULSAND: + { + instrument = E_INST_SNARE_DRUM; + sampleName = "note.snare"; + break; + } + + case E_BLOCK_GLASS: + case E_BLOCK_GLASS_PANE: + case E_BLOCK_GLOWSTONE: + { + instrument = E_INST_CLICKS; + sampleName = "note.hat"; + break; + } + + case E_BLOCK_STONE: + case E_BLOCK_STONE_BRICKS: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_OBSIDIAN: + case E_BLOCK_NETHERRACK: + case E_BLOCK_BRICK: + case E_BLOCK_NETHER_BRICK: + { + // TODO: add other stone-based blocks if needed + instrument = E_INST_BASS_DRUM; + sampleName = "note.bassattack"; + break; + } + + default: + { + instrument = E_INST_HARP_PIANO; + sampleName = "note.harp"; + break; + } + } + + m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK); + + // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all + float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f); + m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch); +} + + + + + +char cNoteEntity::GetPitch( void ) +{ + return m_Pitch; +} + + + + + +void cNoteEntity::SetPitch( char a_Pitch ) +{ + m_Pitch = a_Pitch % 25; +} + + + + + +void cNoteEntity::IncrementPitch( void ) +{ + SetPitch( m_Pitch + 1 ); +} + + + + + +bool cNoteEntity::LoadFromJson( const Json::Value & a_Value ) +{ + + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Pitch = (char)a_Value.get("p", 0).asInt(); + + return true; +} + + + + + +void cNoteEntity::SaveToJson( Json::Value & a_Value ) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["p"] = m_Pitch; +} + + + + diff --git a/source/BlockEntities/NoteEntity.h b/source/BlockEntities/NoteEntity.h index 29ce4ef8f..385591df6 100644 --- a/source/BlockEntities/NoteEntity.h +++ b/source/BlockEntities/NoteEntity.h @@ -1,52 +1,52 @@ - -#pragma once - -#include "BlockEntity.h" - - -namespace Json -{ - class Value; -} - - - - - -enum ENUM_NOTE_INSTRUMENTS -{ - E_INST_HARP_PIANO = 0, - E_INST_DOUBLE_BASS = 1, - E_INST_SNARE_DRUM = 2, - E_INST_CLICKS = 3, - E_INST_BASS_DRUM = 4 -}; - - - - - -class cNoteEntity : - public cBlockEntity -{ -public: - cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); - virtual ~cNoteEntity(); - - bool LoadFromJson( const Json::Value& a_Value ); - virtual void SaveToJson( Json::Value& a_Value ) override; - - char GetPitch( void ); - void SetPitch( char a_Pitch ); - void IncrementPitch( void ); - void MakeSound( void ); - virtual void UsedBy( cPlayer * a_Player ) override; - virtual void SendTo(cClientHandle & a_Client) override { }; - -private: - unsigned char m_Pitch; -}; - - - - + +#pragma once + +#include "BlockEntity.h" + + +namespace Json +{ + class Value; +} + + + + + +enum ENUM_NOTE_INSTRUMENTS +{ + E_INST_HARP_PIANO = 0, + E_INST_DOUBLE_BASS = 1, + E_INST_SNARE_DRUM = 2, + E_INST_CLICKS = 3, + E_INST_BASS_DRUM = 4 +}; + + + + + +class cNoteEntity : + public cBlockEntity +{ +public: + cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); + virtual ~cNoteEntity(); + + bool LoadFromJson( const Json::Value& a_Value ); + virtual void SaveToJson( Json::Value& a_Value ) override; + + char GetPitch( void ); + void SetPitch( char a_Pitch ); + void IncrementPitch( void ); + void MakeSound( void ); + virtual void UsedBy( cPlayer * a_Player ) override; + virtual void SendTo(cClientHandle & a_Client) override { }; + +private: + unsigned char m_Pitch; +}; + + + + diff --git a/source/Blocks/BlockBed.cpp b/source/Blocks/BlockBed.cpp index 513874252..2ae6980ac 100644 --- a/source/Blocks/BlockBed.cpp +++ b/source/Blocks/BlockBed.cpp @@ -1,85 +1,85 @@ -#include "Globals.h" -#include "BlockBed.h" - - - - - -void cBlockBedHandler::OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - if (a_BlockMeta < 8) - { - Vector3i Direction = MetaDataToDirection(a_BlockMeta); - a_World->SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8); - } -} - - - - - -void cBlockBedHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ ); - Vector3i Direction = MetaDataToDirection( OldMeta & 0x7 ); - if (OldMeta & 0x8) - { - // Was pillow - if (a_World->GetBlock(ThisPos - Direction) == E_BLOCK_BED) - { - a_World->FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0); - } - } - else - { - // Was foot end - if (a_World->GetBlock(ThisPos + Direction) == E_BLOCK_BED) - { - a_World->FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0); - } - } -} - - - - - -void cBlockBedHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - if (a_World->GetDimension() != 0) - { - a_World->DoExplosiontAt(5, a_BlockX, a_BlockY, a_BlockZ); - } else { - if (a_World->GetTimeOfDay() > 13000) - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta & 0x8) - { - // Is pillow - a_World->BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ); - } - else - { - // Is foot end - Vector3i Direction = MetaDataToDirection( Meta & 0x7 ); - if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping - { - a_World->BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z); - } - } - } else { - a_Player->SendMessage("You can only sleep at night"); - } - } -} - - - - +#include "Globals.h" +#include "BlockBed.h" + + + + + +void cBlockBedHandler::OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + if (a_BlockMeta < 8) + { + Vector3i Direction = MetaDataToDirection(a_BlockMeta); + a_World->SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8); + } +} + + + + + +void cBlockBedHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ ); + Vector3i Direction = MetaDataToDirection( OldMeta & 0x7 ); + if (OldMeta & 0x8) + { + // Was pillow + if (a_World->GetBlock(ThisPos - Direction) == E_BLOCK_BED) + { + a_World->FastSetBlock(ThisPos - Direction, E_BLOCK_AIR, 0); + } + } + else + { + // Was foot end + if (a_World->GetBlock(ThisPos + Direction) == E_BLOCK_BED) + { + a_World->FastSetBlock(ThisPos + Direction, E_BLOCK_AIR, 0); + } + } +} + + + + + +void cBlockBedHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + if (a_World->GetDimension() != 0) + { + a_World->DoExplosiontAt(5, a_BlockX, a_BlockY, a_BlockZ); + } else { + if (a_World->GetTimeOfDay() > 13000) + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (Meta & 0x8) + { + // Is pillow + a_World->BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + else + { + // Is foot end + Vector3i Direction = MetaDataToDirection( Meta & 0x7 ); + if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) == E_BLOCK_BED) // Must always use pillow location for sleeping + { + a_World->BroadcastUseBed(*a_Player, a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z); + } + } + } else { + a_Player->SendMessage("You can only sleep at night"); + } + } +} + + + + diff --git a/source/Blocks/BlockBed.h b/source/Blocks/BlockBed.h index bf8906750..8286ceb11 100644 --- a/source/Blocks/BlockBed.h +++ b/source/Blocks/BlockBed.h @@ -1,73 +1,73 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" -#include "../Player.h" - - - - - -class cBlockBedHandler : - public cBlockHandler -{ -public: - cBlockBedHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to zero - a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0)); - } - - - virtual bool DoesAllowBlockOnTop() override - { - return false; - } - - - // Bed specific helper functions - static NIBBLETYPE RotationToMetaData(double a_Rotation) - { - a_Rotation += 180 + (180 / 4); // So its not aligned with axis - if (a_Rotation > 360) a_Rotation -= 360; - - a_Rotation = (a_Rotation / 360) * 4; - - return ((char)a_Rotation + 2) % 4; - } - - - static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData) - { - switch (a_MetaData) - { - case 0: return Vector3i(0, 0, 1); - case 1: return Vector3i(-1, 0, 0); - case 2: return Vector3i(0, 0, -1); - case 3: return Vector3i(1, 0, 0); - } - return Vector3i(); - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Player.h" + + + + + +class cBlockBedHandler : + public cBlockHandler +{ +public: + cBlockBedHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to zero + a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0)); + } + + + virtual bool DoesAllowBlockOnTop() override + { + return false; + } + + + // Bed specific helper functions + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 180 + (180 / 4); // So its not aligned with axis + if (a_Rotation > 360) a_Rotation -= 360; + + a_Rotation = (a_Rotation / 360) * 4; + + return ((char)a_Rotation + 2) % 4; + } + + + static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData) + { + switch (a_MetaData) + { + case 0: return Vector3i(0, 0, 1); + case 1: return Vector3i(-1, 0, 0); + case 2: return Vector3i(0, 0, -1); + case 3: return Vector3i(1, 0, 0); + } + return Vector3i(); + } +} ; + + + + diff --git a/source/Blocks/BlockBrewingStand.h b/source/Blocks/BlockBrewingStand.h index 34a2b6c56..57642bcb6 100644 --- a/source/Blocks/BlockBrewingStand.h +++ b/source/Blocks/BlockBrewingStand.h @@ -1,32 +1,32 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockBrewingStandHandler : - public cBlockHandler -{ -public: - cBlockBrewingStandHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_BREWING_STAND, 1, 0)); - } - - virtual bool IsUseable() override - { - return true; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockBrewingStandHandler : + public cBlockHandler +{ +public: + cBlockBrewingStandHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_BREWING_STAND, 1, 0)); + } + + virtual bool IsUseable() override + { + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockCactus.h b/source/Blocks/BlockCactus.h index 516f024d8..1d123bc0a 100644 --- a/source/Blocks/BlockCactus.h +++ b/source/Blocks/BlockCactus.h @@ -1,88 +1,88 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockCactusHandler : - public cBlockHandler -{ -public: - cBlockCactusHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(m_BlockType, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if (a_RelY <= 0) - { - return false; - } - BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); - if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS)) - { - // Cactus can only be placed on sand and itself - return false; - } - - // Check surroundings. Cacti may ONLY be surrounded by air - static const struct - { - int x, z; - } Coords[] = - { - {-1, 0}, - { 1, 0}, - { 0, -1}, - { 0, 1}, - } ; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if ( - a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && - (BlockType != E_BLOCK_AIR) - ) - { - return false; - } - } // for i - Coords[] - - return true; - } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - a_World->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1); - } - - - virtual const char * GetStepSound(void) override - { - return "step.cloth"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockCactusHandler : + public cBlockHandler +{ +public: + cBlockCactusHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); + if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS)) + { + // Cactus can only be placed on sand and itself + return false; + } + + // Check surroundings. Cacti may ONLY be surrounded by air + static const struct + { + int x, z; + } Coords[] = + { + {-1, 0}, + { 1, 0}, + { 0, -1}, + { 0, 1}, + } ; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if ( + a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && + (BlockType != E_BLOCK_AIR) + ) + { + return false; + } + } // for i - Coords[] + + return true; + } + + + virtual bool CanBePlacedOnSide(void) override + { + return false; + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, 1); + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/source/Blocks/BlockCauldron.h b/source/Blocks/BlockCauldron.h index 571235441..b0e00f869 100644 --- a/source/Blocks/BlockCauldron.h +++ b/source/Blocks/BlockCauldron.h @@ -1,59 +1,59 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockCauldronHandler : - public cBlockHandler -{ -public: - cBlockCauldronHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0)); - } - - void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) - { - char Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); - switch( a_Player->GetEquippedItem().m_ItemType ) - { - case E_ITEM_WATER_BUCKET: - { - a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 ); - a_Player->GetInventory().RemoveOneEquippedItem(); - cItem NewItem(E_ITEM_BUCKET, 1); - a_Player->GetInventory().AddItem(NewItem); - break; - } - case E_ITEM_GLASS_BOTTLE: - { - if( Meta > 0 ) - { - a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta); - a_Player->GetInventory().RemoveOneEquippedItem(); - cItem NewItem(E_ITEM_POTIONS, 1, 0); - a_Player->GetInventory().AddItem(NewItem); - } - break; - } - } - } - - virtual bool IsUseable() override - { - return true; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockCauldronHandler : + public cBlockHandler +{ +public: + cBlockCauldronHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_CAULDRON, 1, 0)); + } + + void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + char Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); + switch( a_Player->GetEquippedItem().m_ItemType ) + { + case E_ITEM_WATER_BUCKET: + { + a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 ); + a_Player->GetInventory().RemoveOneEquippedItem(); + cItem NewItem(E_ITEM_BUCKET, 1); + a_Player->GetInventory().AddItem(NewItem); + break; + } + case E_ITEM_GLASS_BOTTLE: + { + if( Meta > 0 ) + { + a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta); + a_Player->GetInventory().RemoveOneEquippedItem(); + cItem NewItem(E_ITEM_POTIONS, 1, 0); + a_Player->GetInventory().AddItem(NewItem); + } + break; + } + } + } + + virtual bool IsUseable() override + { + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockChest.h b/source/Blocks/BlockChest.h index 5a64a16bb..1975e11b2 100644 --- a/source/Blocks/BlockChest.h +++ b/source/Blocks/BlockChest.h @@ -1,222 +1,222 @@ - -#pragma once - -#include "BlockEntity.h" -#include "../World.h" -#include "../Player.h" - - - - - -class cBlockChestHandler : - public cBlockEntityHandler -{ -public: - cBlockChestHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - - // Is there a doublechest already next to this block? - if (!CanBeAt(a_World, a_BlockX, a_BlockY, a_BlockZ)) - { - // Yup, cannot form a triple-chest, refuse: - return false; - } - - // Check if this forms a doublechest, if so, need to adjust the meta: - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) - { - return false; - } - double rot = a_Player->GetRotation(); - if ( - (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) - ) - { - a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; - return true; - } - if ( - (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) - ) - { - a_BlockMeta = (rot < 0) ? 4 : 5; - return true; - } - - // Single chest, get meta from rotation only - a_BlockMeta = RotationToMetaData(rot); - return true; - } - - - virtual void OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - // Check if this forms a doublechest, if so, need to adjust the meta: - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) - { - return; - } - - double rot = a_Player->GetRotation(); - // Choose meta from player rotation, choose only between 2 or 3 - NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; - if ( - CheckAndAdjustNeighbor(a_World, Area, 0, 1, NewMeta) || - CheckAndAdjustNeighbor(a_World, Area, 2, 1, NewMeta) - ) - { - // Forming a double chest in the X direction - return; - } - // Choose meta from player rotation, choose only between 4 or 5 - NewMeta = (rot < 0) ? 4 : 5; - if ( - CheckAndAdjustNeighbor(a_World, Area, 1, 0, NewMeta) || - CheckAndAdjustNeighbor(a_World, Area, 2, 2, NewMeta) - ) - { - // Forming a double chest in the Z direction - return; - } - - // Single chest, no further processing needed - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } - - - virtual bool CanBeAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2)) - { - // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow. - return false; - } - - int NumChestNeighbors = 0; - if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST) - { - if ( - (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) - ) - { - // Already a doublechest neighbor, disallow: - return false; - } - NumChestNeighbors += 1; - } - if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST) - { - if ( - (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) - ) - { - // Already a doublechest neighbor, disallow: - return false; - } - NumChestNeighbors += 1; - } - if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) - { - if ( - (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) - ) - { - // Already a doublechest neighbor, disallow: - return false; - } - NumChestNeighbors += 1; - } - if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST) - { - if ( - (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) - ) - { - // Already a doublechest neighbor, disallow: - return false; - } - NumChestNeighbors += 1; - } - return (NumChestNeighbors < 2); - } - - - /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only - static NIBBLETYPE RotationToMetaData(double a_Rotation) - { - a_Rotation += 90 + 45; // So its not aligned with axis - - if (a_Rotation > 360.f) - { - a_Rotation -= 360.f; - } - if ((a_Rotation >= 0.f) && (a_Rotation < 90.f)) - { - return 0x4; - } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) - { - return 0x5; - } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) - { - return 0x2; - } - else - { - return 0x3; - } - } - - - /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true. - bool CheckAndAdjustNeighbor(cWorld * a_World, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta) - { - if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST) - { - return false; - } - a_World->SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta); - return true; - } -} ; - - - - + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" +#include "../Player.h" + + + + + +class cBlockChestHandler : + public cBlockEntityHandler +{ +public: + cBlockChestHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // Is there a doublechest already next to this block? + if (!CanBeAt(a_World, a_BlockX, a_BlockY, a_BlockZ)) + { + // Yup, cannot form a triple-chest, refuse: + return false; + } + + // Check if this forms a doublechest, if so, need to adjust the meta: + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) + { + return false; + } + double rot = a_Player->GetRotation(); + if ( + (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + ) + { + a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; + return true; + } + if ( + (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + ) + { + a_BlockMeta = (rot < 0) ? 4 : 5; + return true; + } + + // Single chest, get meta from rotation only + a_BlockMeta = RotationToMetaData(rot); + return true; + } + + + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override + { + // Check if this forms a doublechest, if so, need to adjust the meta: + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) + { + return; + } + + double rot = a_Player->GetRotation(); + // Choose meta from player rotation, choose only between 2 or 3 + NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; + if ( + CheckAndAdjustNeighbor(a_World, Area, 0, 1, NewMeta) || + CheckAndAdjustNeighbor(a_World, Area, 2, 1, NewMeta) + ) + { + // Forming a double chest in the X direction + return; + } + // Choose meta from player rotation, choose only between 4 or 5 + NewMeta = (rot < 0) ? 4 : 5; + if ( + CheckAndAdjustNeighbor(a_World, Area, 1, 0, NewMeta) || + CheckAndAdjustNeighbor(a_World, Area, 2, 2, NewMeta) + ) + { + // Forming a double chest in the Z direction + return; + } + + // Single chest, no further processing needed + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + virtual bool CanBeAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2)) + { + // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow. + return false; + } + + int NumChestNeighbors = 0; + if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST) + { + if ( + (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) || + (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) + ) + { + // Already a doublechest neighbor, disallow: + return false; + } + NumChestNeighbors += 1; + } + return (NumChestNeighbors < 2); + } + + + /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + + if (a_Rotation > 360.f) + { + a_Rotation -= 360.f; + } + if ((a_Rotation >= 0.f) && (a_Rotation < 90.f)) + { + return 0x4; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x5; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } + + + /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true. + bool CheckAndAdjustNeighbor(cWorld * a_World, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta) + { + if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST) + { + return false; + } + a_World->SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta); + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockCloth.h b/source/Blocks/BlockCloth.h index a95f12500..a136d3b9d 100644 --- a/source/Blocks/BlockCloth.h +++ b/source/Blocks/BlockCloth.h @@ -1,34 +1,34 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockClothHandler : - public cBlockHandler -{ -public: - cBlockClothHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.cloth"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockClothHandler : + public cBlockHandler +{ +public: + cBlockClothHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_WOOL, 1, a_BlockMeta)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/source/Blocks/BlockCobWeb.h b/source/Blocks/BlockCobWeb.h index 86bb6e773..982bfaa30 100644 --- a/source/Blocks/BlockCobWeb.h +++ b/source/Blocks/BlockCobWeb.h @@ -1,30 +1,30 @@ - -// BlockCobWeb.h - -// Declares the cBlockCobWebHandler object representing the BlockHandler for cobwebs - -#pragma once - - - - - -class cBlockCobWebHandler : - public cBlockHandler -{ -public: - cBlockCobWebHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override - { - a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0)); - } -} ; - - - - + +// BlockCobWeb.h + +// Declares the cBlockCobWebHandler object representing the BlockHandler for cobwebs + +#pragma once + + + + + +class cBlockCobWebHandler : + public cBlockHandler +{ +public: + cBlockCobWebHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override + { + a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0)); + } +} ; + + + + diff --git a/source/Blocks/BlockCrops.h b/source/Blocks/BlockCrops.h index 7658d2633..4bc76fd50 100644 --- a/source/Blocks/BlockCrops.h +++ b/source/Blocks/BlockCrops.h @@ -1,108 +1,108 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -/// Common class that takes care of carrots, potatoes and wheat -class cBlockCropsHandler : - public cBlockHandler -{ -public: - cBlockCropsHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool DoesAllowBlockOnTop() override - { - return false; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override - { - MTRand rand; - - if (a_Meta == 0x7) - { - // Is fully grown, drop the entire produce: - switch (m_BlockType) - { - case E_BLOCK_CROPS: - { - a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0)); - a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 - break; - } - case E_BLOCK_CARROTS: - { - a_Pickups.push_back(cItem(E_ITEM_CARROT, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 - break; - } - case E_BLOCK_POTATOES: - { - a_Pickups.push_back(cItem(E_ITEM_POTATO, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 - if (rand.randInt(20) == 0) - { - // With a 5% chance, drop a poisonous potato as well - a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0)); - } - break; - } - default: - { - ASSERT(!"Unhandled block type"); - break; - } - } // switch (m_BlockType) - } - else - { - // Drop 1 item of whatever is growing - switch (m_BlockType) - { - case E_BLOCK_CROPS: a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); break; - case E_BLOCK_CARROTS: a_Pickups.push_back(cItem(E_ITEM_CARROT, 1, 0)); break; - case E_BLOCK_POTATOES: a_Pickups.push_back(cItem(E_ITEM_POTATO, 1, 0)); break; - default: - { - ASSERT(!"Unhandled block type"); - break; - } - } - } - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta < 7) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta); - } - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +/// Common class that takes care of carrots, potatoes and wheat +class cBlockCropsHandler : + public cBlockHandler +{ +public: + cBlockCropsHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool DoesAllowBlockOnTop() override + { + return false; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override + { + MTRand rand; + + if (a_Meta == 0x7) + { + // Is fully grown, drop the entire produce: + switch (m_BlockType) + { + case E_BLOCK_CROPS: + { + a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0)); + a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + break; + } + case E_BLOCK_CARROTS: + { + a_Pickups.push_back(cItem(E_ITEM_CARROT, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + break; + } + case E_BLOCK_POTATOES: + { + a_Pickups.push_back(cItem(E_ITEM_POTATO, 1 + (int)(rand.randInt(2) + rand.randInt(2)) / 2, 0)); // [1 .. 3] with high preference of 2 + if (rand.randInt(20) == 0) + { + // With a 5% chance, drop a poisonous potato as well + a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0)); + } + break; + } + default: + { + ASSERT(!"Unhandled block type"); + break; + } + } // switch (m_BlockType) + } + else + { + // Drop 1 item of whatever is growing + switch (m_BlockType) + { + case E_BLOCK_CROPS: a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); break; + case E_BLOCK_CARROTS: a_Pickups.push_back(cItem(E_ITEM_CARROT, 1, 0)); break; + case E_BLOCK_POTATOES: a_Pickups.push_back(cItem(E_ITEM_POTATO, 1, 0)); break; + default: + { + ASSERT(!"Unhandled block type"); + break; + } + } + } + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (Meta < 7) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CROPS, ++Meta); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/source/Blocks/BlockDeadBush.h b/source/Blocks/BlockDeadBush.h index be0e77e55..379e8e5df 100644 --- a/source/Blocks/BlockDeadBush.h +++ b/source/Blocks/BlockDeadBush.h @@ -1,47 +1,47 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockDeadBushHandler : - public cBlockHandler -{ -public: - cBlockDeadBushHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Don't drop anything - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SAND); - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual bool CanBePlacedOnSide() override - { - return false; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockDeadBushHandler : + public cBlockHandler +{ +public: + cBlockDeadBushHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Don't drop anything + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SAND); + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + virtual bool CanBePlacedOnSide() override + { + return false; + } +} ; + + + + diff --git a/source/Blocks/BlockDirt.h b/source/Blocks/BlockDirt.h index 6cec5d99e..b2bc4756c 100644 --- a/source/Blocks/BlockDirt.h +++ b/source/Blocks/BlockDirt.h @@ -1,88 +1,88 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -/// Handler used for both dirt and grass -class cBlockDirtHandler : - public cBlockHandler -{ -public: - cBlockDirtHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); - } - - - virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - if (m_BlockType != E_BLOCK_GRASS) - { - return; - } - - // Grass becomes dirt if there is something on top of it: - if (a_BlockY < cChunkDef::Height - 1) - { - BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); - if (!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above]) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); - return; - } - } - - // Grass spreads to adjacent blocks: - MTRand rand; - for (int i = 0; i < 2; i++) // Pick two blocks to grow to - { - int OfsX = rand.randInt(2) - 1; // [-1 .. 1] - int OfsY = rand.randInt(4) - 3; // [-3 .. 1] - int OfsZ = rand.randInt(2) - 1; // [-1 .. 1] - - BLOCKTYPE DestBlock; - NIBBLETYPE DestMeta; - if ((a_BlockY + OfsY < 0) || (a_BlockY + OfsY >= cChunkDef::Height - 1)) - { - // Y Coord out of range - continue; - } - bool IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, DestBlock, DestMeta); - if (!IsValid || (DestBlock != E_BLOCK_DIRT)) - { - continue; - } - - BLOCKTYPE AboveDest; - NIBBLETYPE AboveMeta; - IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta); - ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid? - if (g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest]) - { - a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0); - } - } // for i - repeat twice - } - - - virtual const char * GetStepSound(void) override - { - return "step.gravel"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +/// Handler used for both dirt and grass +class cBlockDirtHandler : + public cBlockHandler +{ +public: + cBlockDirtHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + if (m_BlockType != E_BLOCK_GRASS) + { + return; + } + + // Grass becomes dirt if there is something on top of it: + if (a_BlockY < cChunkDef::Height - 1) + { + BLOCKTYPE Above = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); + if (!g_BlockTransparent[Above] && !g_BlockOneHitDig[Above]) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); + return; + } + } + + // Grass spreads to adjacent blocks: + MTRand rand; + for (int i = 0; i < 2; i++) // Pick two blocks to grow to + { + int OfsX = rand.randInt(2) - 1; // [-1 .. 1] + int OfsY = rand.randInt(4) - 3; // [-3 .. 1] + int OfsZ = rand.randInt(2) - 1; // [-1 .. 1] + + BLOCKTYPE DestBlock; + NIBBLETYPE DestMeta; + if ((a_BlockY + OfsY < 0) || (a_BlockY + OfsY >= cChunkDef::Height - 1)) + { + // Y Coord out of range + continue; + } + bool IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, DestBlock, DestMeta); + if (!IsValid || (DestBlock != E_BLOCK_DIRT)) + { + continue; + } + + BLOCKTYPE AboveDest; + NIBBLETYPE AboveMeta; + IsValid = a_World->GetBlockTypeMeta(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, AboveDest, AboveMeta); + ASSERT(IsValid); // WTF - how did we get the DestBlock if AboveBlock is not valid? + if (g_BlockOneHitDig[AboveDest] || g_BlockTransparent[AboveDest]) + { + a_World->FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ, E_BLOCK_GRASS, 0); + } + } // for i - repeat twice + } + + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } +} ; + + + + diff --git a/source/Blocks/BlockDoor.cpp b/source/Blocks/BlockDoor.cpp index 49ce56e07..b2fe273c5 100644 --- a/source/Blocks/BlockDoor.cpp +++ b/source/Blocks/BlockDoor.cpp @@ -1,88 +1,88 @@ - -#include "Globals.h" -#include "BlockDoor.h" -#include "../Item.h" -#include "../World.h" -#include "../Doors.h" -#include "../Player.h" - - - - - -cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - if (OldMeta & 8) - { - // Was upper part of door - if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) - { - a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - } - } - else - { - // Was lower part - if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) - { - a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0); - } - } -} - - - - - -void cBlockDoorHandler::OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - cDoors::ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cBlockDoorHandler::OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - NIBBLETYPE a_TopBlockMeta = 8; - if ( - (a_BlockMeta == 0) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType) || - (a_BlockMeta == 1) && (a_World->GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType) || - (a_BlockMeta == 2) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType) || - (a_BlockMeta == 3) && (a_World->GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType) - ) - { - a_TopBlockMeta = 9; - } - a_World->SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta); -} - - - - - -const char * cBlockDoorHandler::GetStepSound(void) -{ - return (m_BlockType == E_BLOCK_WOODEN_DOOR) ? "step.wood" : "step.stone"; -} - - - - + +#include "Globals.h" +#include "BlockDoor.h" +#include "../Item.h" +#include "../World.h" +#include "../Doors.h" +#include "../Player.h" + + + + + +cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockDoorHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + NIBBLETYPE OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if (OldMeta & 8) + { + // Was upper part of door + if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + } + } + else + { + // Was lower part + if (cDoors::IsDoor(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0); + } + } +} + + + + + +void cBlockDoorHandler::OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + cDoors::ChangeDoor(a_World, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cBlockDoorHandler::OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + NIBBLETYPE a_TopBlockMeta = 8; + if ( + (a_BlockMeta == 0) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType) || + (a_BlockMeta == 1) && (a_World->GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType) || + (a_BlockMeta == 2) && (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType) || + (a_BlockMeta == 3) && (a_World->GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType) + ) + { + a_TopBlockMeta = 9; + } + a_World->SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta); +} + + + + + +const char * cBlockDoorHandler::GetStepSound(void) +{ + return (m_BlockType == E_BLOCK_WOODEN_DOOR) ? "step.wood" : "step.stone"; +} + + + + diff --git a/source/Blocks/BlockDoor.h b/source/Blocks/BlockDoor.h index a988c2d4b..19701ffc9 100644 --- a/source/Blocks/BlockDoor.h +++ b/source/Blocks/BlockDoor.h @@ -1,105 +1,105 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" -#include "../Doors.h" -#include "../Player.h" - - - - - -class cBlockDoorHandler : - public cBlockHandler -{ -public: - cBlockDoorHandler(BLOCKTYPE a_BlockType); - - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - virtual const char * GetStepSound(void) override; - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - // If clicking a bottom face, place the door one block lower: - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - a_BlockY--; - } - - if ( - !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) || - !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) - ) - { - return false; - } - - a_BlockType = m_BlockType; - a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation()); - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0)); - } - - - virtual void OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override; - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); - } - - - bool CanReplaceBlock(BLOCKTYPE a_BlockType) - { - switch (a_BlockType) - { - case E_BLOCK_AIR: - case E_BLOCK_TALL_GRASS: - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - case E_BLOCK_SNOW: - case E_BLOCK_FIRE: - { - return true; - } - } - return false; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Doors.h" +#include "../Player.h" + + + + + +class cBlockDoorHandler : + public cBlockHandler +{ +public: + cBlockDoorHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + virtual const char * GetStepSound(void) override; + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // If clicking a bottom face, place the door one block lower: + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + a_BlockY--; + } + + if ( + !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) || + !CanReplaceBlock(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) + ) + { + return false; + } + + a_BlockType = m_BlockType; + a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation()); + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem((m_BlockType == E_BLOCK_WOODEN_DOOR) ? E_ITEM_WOODEN_DOOR : E_ITEM_IRON_DOOR, 1, 0)); + } + + + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override; + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool CanBePlacedOnSide(void) override + { + return false; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + bool CanReplaceBlock(BLOCKTYPE a_BlockType) + { + switch (a_BlockType) + { + case E_BLOCK_AIR: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_SNOW: + case E_BLOCK_FIRE: + { + return true; + } + } + return false; + } +} ; + + + + diff --git a/source/Blocks/BlockDropSpenser.h b/source/Blocks/BlockDropSpenser.h index bcc75758d..cfb607a7b 100644 --- a/source/Blocks/BlockDropSpenser.h +++ b/source/Blocks/BlockDropSpenser.h @@ -1,39 +1,39 @@ - -// BlockDropSpenser.h - -// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks - -#pragma once - - - - - -class cBlockDropSpenserHandler : - public cBlockEntityHandler -{ -public: - cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) : - cBlockEntityHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - - // FIXME: Do not use cPiston class for dispenser placement! - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); - return true; - } -} ; - - - - + +// BlockDropSpenser.h + +// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks + +#pragma once + + + + + +class cBlockDropSpenserHandler : + public cBlockEntityHandler +{ +public: + cBlockDropSpenserHandler(BLOCKTYPE a_BlockType) : + cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // FIXME: Do not use cPiston class for dispenser placement! + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockEnderchest.h b/source/Blocks/BlockEnderchest.h index b40deb1f7..0ce813f1c 100644 --- a/source/Blocks/BlockEnderchest.h +++ b/source/Blocks/BlockEnderchest.h @@ -1,28 +1,28 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockEnderchestHandler : - public cBlockHandler -{ -public: - cBlockEnderchestHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - //todo: Drop Ender Chest if using silk touch pickaxe - a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0)); - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockEnderchestHandler : + public cBlockHandler +{ +public: + cBlockEnderchestHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + //todo: Drop Ender Chest if using silk touch pickaxe + a_Pickups.push_back(cItem(E_BLOCK_OBSIDIAN, 8, 0)); + } +} ; + + + + diff --git a/source/Blocks/BlockEntity.h b/source/Blocks/BlockEntity.h index 8b82b43a6..9c6b23665 100644 --- a/source/Blocks/BlockEntity.h +++ b/source/Blocks/BlockEntity.h @@ -1,31 +1,31 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockEntityHandler : public cBlockHandler -{ -public: - cBlockEntityHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void OnUse(cWorld * a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override - { - a_World->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); - } - - virtual bool IsUseable() override - { - return true; - } -}; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockEntityHandler : public cBlockHandler +{ +public: + cBlockEntityHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void OnUse(cWorld * a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + a_World->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + + virtual bool IsUseable() override + { + return true; + } +}; + + + + diff --git a/source/Blocks/BlockFarmland.h b/source/Blocks/BlockFarmland.h index fb2d91a86..6cab1fa38 100644 --- a/source/Blocks/BlockFarmland.h +++ b/source/Blocks/BlockFarmland.h @@ -1,99 +1,99 @@ - -// BlockFarmland.h - -// Declares the cBlcokFarmlandHandler representing the block handler for farmland - - - - - -#pragma once - -#include "BlockHandler.h" -#include "../BlockArea.h" - - - - - -class cBlockFarmlandHandler : - public cBlockHandler -{ - typedef cBlockHandler super; - -public: - cBlockFarmlandHandler(void) : - super(E_BLOCK_FARMLAND) - { - } - - - virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - // TODO: Rain hydrates farmland, too. Check world weather, don't search for water if raining. - // NOTE: The desert biomes do not get precipitation, so another check needs to be made. - - // Search for water in a close proximity: - // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles - cBlockArea Area; - if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4)) - { - // Too close to the world edge, cannot check surroudnings; don't tick at all - return; - } - bool Found = false; - int NumBlocks = Area.GetBlockCount(); - BLOCKTYPE * BlockTypes = Area.GetBlockTypes(); - for (int i = 0; i < NumBlocks; i++) - { - if ( - (BlockTypes[i] == E_BLOCK_WATER) || - (BlockTypes[i] == E_BLOCK_STATIONARY_WATER) - ) - { - Found = true; - break; - } - } - - NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - if (Found) - { - // Water was found, hydrate the block until hydration reaches 7: - if (BlockMeta < 7) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ++BlockMeta); - } - return; - } - - // Water wasn't found, de-hydrate block: - if (BlockMeta > 0) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, --BlockMeta); - return; - } - - // Farmland too dry. If nothing is growing on top, turn back to dirt: - switch (a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) - { - case E_BLOCK_CROPS: - case E_BLOCK_MELON_STEM: - case E_BLOCK_PUMPKIN_STEM: - { - // Produce on top, don't revert - break; - } - default: - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); - break; - } - } - } -} ; - - - - + +// BlockFarmland.h + +// Declares the cBlcokFarmlandHandler representing the block handler for farmland + + + + + +#pragma once + +#include "BlockHandler.h" +#include "../BlockArea.h" + + + + + +class cBlockFarmlandHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockFarmlandHandler(void) : + super(E_BLOCK_FARMLAND) + { + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + // TODO: Rain hydrates farmland, too. Check world weather, don't search for water if raining. + // NOTE: The desert biomes do not get precipitation, so another check needs to be made. + + // Search for water in a close proximity: + // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles + cBlockArea Area; + if (!Area.Read(a_World, a_BlockX - 4, a_BlockX + 4, a_BlockY, a_BlockY + 1, a_BlockZ - 4, a_BlockZ + 4)) + { + // Too close to the world edge, cannot check surroudnings; don't tick at all + return; + } + bool Found = false; + int NumBlocks = Area.GetBlockCount(); + BLOCKTYPE * BlockTypes = Area.GetBlockTypes(); + for (int i = 0; i < NumBlocks; i++) + { + if ( + (BlockTypes[i] == E_BLOCK_WATER) || + (BlockTypes[i] == E_BLOCK_STATIONARY_WATER) + ) + { + Found = true; + break; + } + } + + NIBBLETYPE BlockMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if (Found) + { + // Water was found, hydrate the block until hydration reaches 7: + if (BlockMeta < 7) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ++BlockMeta); + } + return; + } + + // Water wasn't found, de-hydrate block: + if (BlockMeta > 0) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, --BlockMeta); + return; + } + + // Farmland too dry. If nothing is growing on top, turn back to dirt: + switch (a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) + { + case E_BLOCK_CROPS: + case E_BLOCK_MELON_STEM: + case E_BLOCK_PUMPKIN_STEM: + { + // Produce on top, don't revert + break; + } + default: + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DIRT, 0); + break; + } + } + } +} ; + + + + diff --git a/source/Blocks/BlockFenceGate.h b/source/Blocks/BlockFenceGate.h index a84ce8303..d6f8aa85f 100644 --- a/source/Blocks/BlockFenceGate.h +++ b/source/Blocks/BlockFenceGate.h @@ -1,60 +1,60 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../Doors.h" - - - - - -class cBlockFenceGateHandler : - public cBlockHandler -{ -public: - cBlockFenceGateHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation() + 270); - return true; - } - - - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override - { - NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - NIBBLETYPE NewMetaData = cDoors::RotationToMetaData(a_Player->GetRotation() + 270); - OldMetaData ^= 4; // Toggle the gate - if ((OldMetaData & 1) == (NewMetaData & 1)) - { - // Standing in front of the gate - apply new direction - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3)); - } - else - { - // Standing aside - use last direction - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData); - } - } - - - virtual bool IsUseable(void) override - { - return true; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../Doors.h" + + + + + +class cBlockFenceGateHandler : + public cBlockHandler +{ +public: + cBlockFenceGateHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = cDoors::RotationToMetaData(a_Player->GetRotation() + 270); + return true; + } + + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + NIBBLETYPE NewMetaData = cDoors::RotationToMetaData(a_Player->GetRotation() + 270); + OldMetaData ^= 4; // Toggle the gate + if ((OldMetaData & 1) == (NewMetaData & 1)) + { + // Standing in front of the gate - apply new direction + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3)); + } + else + { + // Standing aside - use last direction + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData); + } + } + + + virtual bool IsUseable(void) override + { + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockFire.h b/source/Blocks/BlockFire.h index 7da66f982..d3ba499b1 100644 --- a/source/Blocks/BlockFire.h +++ b/source/Blocks/BlockFire.h @@ -1,42 +1,42 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFireHandler : - public cBlockHandler -{ -public: - cBlockFireHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // No pickups from this block - } - - virtual bool IsClickedThrough(void) override - { - return true; - } - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFireHandler : + public cBlockHandler +{ +public: + cBlockFireHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // No pickups from this block + } + + virtual bool IsClickedThrough(void) override + { + return true; + } + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockFlower.h b/source/Blocks/BlockFlower.h index b46273c51..202609538 100644 --- a/source/Blocks/BlockFlower.h +++ b/source/Blocks/BlockFlower.h @@ -1,53 +1,53 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFlowerHandler : - public cBlockHandler -{ -public: - cBlockFlowerHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(m_BlockType, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFlowerHandler : + public cBlockHandler +{ +public: + cBlockFlowerHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + virtual bool CanBePlacedOnSide(void) override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/source/Blocks/BlockFlowerPot.h b/source/Blocks/BlockFlowerPot.h index 12cd594de..b0faf5218 100644 --- a/source/Blocks/BlockFlowerPot.h +++ b/source/Blocks/BlockFlowerPot.h @@ -1,105 +1,105 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFlowerPotHandler : - public cBlockHandler -{ -public: - cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0)); - if (a_BlockMeta == 0) - { - return; - } - cItem Plant; - switch (a_BlockMeta) - { - case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break; - case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break; - case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break; - case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break; - case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break; - case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break; - case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break; - case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break; - case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break; - case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break; - case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break; - default: return; - } - a_Pickups.push_back(Plant); - } - - - void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) - { - NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); - if (Meta != 0) - { - // Already filled - return; - } - - switch (a_Player->GetEquippedItem().m_ItemType) - { - case E_BLOCK_RED_ROSE: Meta = 1; break; - case E_BLOCK_YELLOW_FLOWER: Meta = 2; break; - case E_BLOCK_SAPLING: - { - switch (a_Player->GetEquippedItem().m_ItemDamage) - { - case E_META_SAPLING_APPLE: Meta = 3; break; - case E_META_SAPLING_CONIFER: Meta = 4; break; - case E_META_SAPLING_BIRCH: Meta = 5; break; - case E_META_SAPLING_JUNGLE: Meta = 6; break; - } - break; - } - case E_BLOCK_RED_MUSHROOM: Meta = 7; break; - case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break; - case E_BLOCK_CACTUS: Meta = 9; break; - case E_BLOCK_DEAD_BUSH: Meta = 10; break; - case E_BLOCK_TALL_GRASS: - { - if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN) - { - Meta = 11; - } - else - { - return; - } - break; - } - } - - if (a_Player->GetGameMode() != gmCreative) - { - a_Player->GetInventory().RemoveOneEquippedItem(); - } - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); - } - - - virtual bool IsUseable(void) override - { - return true; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFlowerPotHandler : + public cBlockHandler +{ +public: + cBlockFlowerPotHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_FLOWER_POT, 1, 0)); + if (a_BlockMeta == 0) + { + return; + } + cItem Plant; + switch (a_BlockMeta) + { + case 1: Plant = cItem(E_BLOCK_RED_ROSE, 1, 0); break; + case 2: Plant = cItem(E_BLOCK_YELLOW_FLOWER, 1, 0); break; + case 3: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_APPLE); break; + case 4: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_CONIFER); break; + case 5: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_BIRCH); break; + case 6: Plant = cItem(E_BLOCK_SAPLING, 1, E_META_SAPLING_JUNGLE); break; + case 7: Plant = cItem(E_BLOCK_RED_MUSHROOM, 1, 0); break; + case 8: Plant = cItem(E_BLOCK_BROWN_MUSHROOM, 1, 0); break; + case 9: Plant = cItem(E_BLOCK_CACTUS, 1, 0); break; + case 10: Plant = cItem(E_BLOCK_DEAD_BUSH, 1, 0); break; + case 11: Plant = cItem(E_BLOCK_TALL_GRASS, 1, E_META_TALL_GRASS_FERN); break; + default: return; + } + a_Pickups.push_back(Plant); + } + + + void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + NIBBLETYPE Meta = a_World->GetBlockMeta( a_BlockX, a_BlockY, a_BlockZ ); + if (Meta != 0) + { + // Already filled + return; + } + + switch (a_Player->GetEquippedItem().m_ItemType) + { + case E_BLOCK_RED_ROSE: Meta = 1; break; + case E_BLOCK_YELLOW_FLOWER: Meta = 2; break; + case E_BLOCK_SAPLING: + { + switch (a_Player->GetEquippedItem().m_ItemDamage) + { + case E_META_SAPLING_APPLE: Meta = 3; break; + case E_META_SAPLING_CONIFER: Meta = 4; break; + case E_META_SAPLING_BIRCH: Meta = 5; break; + case E_META_SAPLING_JUNGLE: Meta = 6; break; + } + break; + } + case E_BLOCK_RED_MUSHROOM: Meta = 7; break; + case E_BLOCK_BROWN_MUSHROOM: Meta = 8; break; + case E_BLOCK_CACTUS: Meta = 9; break; + case E_BLOCK_DEAD_BUSH: Meta = 10; break; + case E_BLOCK_TALL_GRASS: + { + if (a_Player->GetEquippedItem().m_ItemDamage == E_META_TALL_GRASS_FERN) + { + Meta = 11; + } + else + { + return; + } + break; + } + } + + if (a_Player->GetGameMode() != gmCreative) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); + } + + + virtual bool IsUseable(void) override + { + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockFluid.h b/source/Blocks/BlockFluid.h index b184a5b33..696bfb3ce 100644 --- a/source/Blocks/BlockFluid.h +++ b/source/Blocks/BlockFluid.h @@ -1,50 +1,50 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockFluidHandler : - public cBlockHandler -{ - typedef cBlockHandler super; - -public: - cBlockFluidHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - - } - - - virtual bool DoesIgnoreBuildCollision(void) override - { - return true; - } - - - virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override - { - switch (m_BlockType) - { - case E_BLOCK_STATIONARY_LAVA: - { - a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_LAVA, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); - break; - } - case E_BLOCK_STATIONARY_WATER: - { - a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_WATER, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); - break; - } - } - super::Check(a_RelX, a_RelY, a_RelZ, a_Chunk); - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockFluidHandler : + public cBlockHandler +{ + typedef cBlockHandler super; + +public: + cBlockFluidHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override + { + switch (m_BlockType) + { + case E_BLOCK_STATIONARY_LAVA: + { + a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_LAVA, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + break; + } + case E_BLOCK_STATIONARY_WATER: + { + a_Chunk.FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_WATER, a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + break; + } + } + super::Check(a_RelX, a_RelY, a_RelZ, a_Chunk); + } +} ; + + + + diff --git a/source/Blocks/BlockFurnace.h b/source/Blocks/BlockFurnace.h index 7231c4d65..b358a1d71 100644 --- a/source/Blocks/BlockFurnace.h +++ b/source/Blocks/BlockFurnace.h @@ -1,47 +1,47 @@ - -#pragma once - -#include "BlockEntity.h" -#include "../World.h" -#include "../Piston.h" -#include "../Player.h" - - - - - -class cBlockFurnaceHandler : - public cBlockEntityHandler -{ -public: - cBlockFurnaceHandler(BLOCKTYPE a_BlockType) : - cBlockEntityHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_FURNACE, 1, 0)); - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - - // FIXME: Do not use cPiston class for furnace placement! - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); - - return true; - } -} ; - - - - + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" +#include "../Piston.h" +#include "../Player.h" + + + + + +class cBlockFurnaceHandler : + public cBlockEntityHandler +{ +public: + cBlockFurnaceHandler(BLOCKTYPE a_BlockType) : + cBlockEntityHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_FURNACE, 1, 0)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // FIXME: Do not use cPiston class for furnace placement! + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0); + + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockGlass.h b/source/Blocks/BlockGlass.h index d5147af96..f6958bbb6 100644 --- a/source/Blocks/BlockGlass.h +++ b/source/Blocks/BlockGlass.h @@ -1,26 +1,26 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockGlassHandler : - public cBlockHandler -{ -public: - cBlockGlassHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGlassHandler : + public cBlockHandler +{ +public: + cBlockGlassHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + } +} ; + + + + diff --git a/source/Blocks/BlockGlowstone.h b/source/Blocks/BlockGlowstone.h index 5ff9394b7..5f0d95dee 100644 --- a/source/Blocks/BlockGlowstone.h +++ b/source/Blocks/BlockGlowstone.h @@ -1,30 +1,30 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockGlowstoneHandler : - public cBlockHandler -{ -public: - cBlockGlowstoneHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - // TODO: More drops? - a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0)); - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGlowstoneHandler : + public cBlockHandler +{ +public: + cBlockGlowstoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + // TODO: More drops? + a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0)); + } +} ; + + + + diff --git a/source/Blocks/BlockGravel.h b/source/Blocks/BlockGravel.h index 6324fe06f..e1c9ff390 100644 --- a/source/Blocks/BlockGravel.h +++ b/source/Blocks/BlockGravel.h @@ -1,27 +1,27 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockGravelHandler : - public cBlockHandler -{ -public: - cBlockGravelHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual const char * GetStepSound(void) override - { - return "step.gravel"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockGravelHandler : + public cBlockHandler +{ +public: + cBlockGravelHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual const char * GetStepSound(void) override + { + return "step.gravel"; + } +} ; + + + + diff --git a/source/Blocks/BlockHandler.cpp b/source/Blocks/BlockHandler.cpp index a853c6f68..550a6795c 100644 --- a/source/Blocks/BlockHandler.cpp +++ b/source/Blocks/BlockHandler.cpp @@ -1,453 +1,453 @@ - -#include "Globals.h" -#include "BlockHandler.h" -#include "../Item.h" -#include "../World.h" -#include "../Root.h" -#include "../PluginManager.h" -#include "BlockSand.h" -#include "BlockGravel.h" -#include "BlockDoor.h" -#include "BlockFire.h" -#include "BlockRedstone.h" -#include "BlockRedstoneTorch.h" -#include "BlockRedstoneRepeater.h" -#include "BlockPiston.h" -#include "BlockWorkbench.h" -#include "BlockEntity.h" -#include "BlockVine.h" -#include "BlockTallGrass.h" -#include "BlockSnow.h" -#include "BlockCloth.h" -#include "BlockSlab.h" -#include "BlockDirt.h" -#include "BlockTorch.h" -#include "BlockWood.h" -#include "BlockLeaves.h" -#include "BlockSapling.h" -#include "BlockFluid.h" -#include "BlockChest.h" -#include "BlockFurnace.h" -#include "BlockDropSpenser.h" -#include "BlockStairs.h" -#include "BlockLadder.h" -#include "BlockLever.h" -#include "BlockSign.h" -#include "BlockCrops.h" -#include "BlockSugarcane.h" -#include "BlockFlower.h" -#include "BlockMushroom.h" -#include "BlockCactus.h" -#include "BlockStems.h" -#include "BlockGlowstone.h" -#include "BlockStone.h" -#include "BlockMelon.h" -#include "BlockIce.h" -#include "BlockOre.h" -#include "BlockNote.h" -#include "BlockBed.h" -#include "BlockFarmland.h" -#include "BlockMycelium.h" -#include "BlockRail.h" -#include "BlockGlass.h" -#include "BlockEnderchest.h" -#include "BlockFenceGate.h" -#include "BlockFlowerPot.h" -#include "BlockCauldron.h" -#include "BlockBrewingStand.h" -#include "BlockCobWeb.h" -#include "BlockDeadBush.h" -#include "BlockHopper.h" - - - - - -bool cBlockHandler::m_HandlerInitialized = false; -cBlockHandler * cBlockHandler::m_BlockHandler[256]; - - - - - -cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType) -{ - if (!m_HandlerInitialized) - { - // We have to initialize - memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); - m_HandlerInitialized = true; - } - if (m_BlockHandler[a_BlockType] != NULL) - { - return m_BlockHandler[a_BlockType]; - } - - return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType); -} - - - - - -cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) -{ - switch(a_BlockType) - { - // Block handlers, alphabetically sorted: - case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType); - case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType); - case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); - case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType); - case E_BLOCK_CARROTS: return new cBlockCropsHandler (a_BlockType); - case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType); - case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType); - case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType); - case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType); - case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType); - case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType); - case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_DIRT: return new cBlockDirtHandler (a_BlockType); - case E_BLOCK_DISPENSER: return new cBlockDropSpenserHandler (a_BlockType); - case E_BLOCK_DOUBLE_STONE_SLAB: return new cBlockSlabHandler (a_BlockType); - case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType); - case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType); - case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType); - case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler; - case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType); - case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType); - case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType); - case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType); - case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType); - case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType); - case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType); - case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType); - case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType); - case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType); - case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType); - case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType); - case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType); - case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType); - case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_LAVA: return new cBlockFluidHandler (a_BlockType); - case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType); - case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType); - case E_BLOCK_LOG: return new cBlockWoodHandler (a_BlockType); - case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType); - case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType); - case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType); - case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType); - case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType); - case E_BLOCK_PLANKS: return new cBlockWoodHandler (a_BlockType); - case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType); - case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType); - case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType); - case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType); - case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType); - case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType); - case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType); - case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType); - case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); - case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType); - case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType); - case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType); - case E_BLOCK_SIGN_POST: return new cBlockSignHandler (a_BlockType); - case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType); - case E_BLOCK_SPRUCE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_STATIONARY_LAVA: return new cBlockFluidHandler (a_BlockType); - case E_BLOCK_STATIONARY_WATER: return new cBlockFluidHandler (a_BlockType); - case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType); - case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType); - case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType); - case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType); - case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType); - case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType); - case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); - case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); - case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); - case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType); - case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType); - case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType); - case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType); - case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType); - case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType); - - default: return new cBlockHandler(a_BlockType); - } -} - - - - - -void cBlockHandler::Deinit() -{ - for (int i = 0; i < 256; i++) - { - delete m_BlockHandler[i]; - } - memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case - m_HandlerInitialized = false; -} - - - - - -cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType) -{ - m_BlockType = a_BlockType; -} - - - - - -bool cBlockHandler::GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta -) -{ - // By default, all blocks can be placed and the meta is copied over from the item's damage value: - a_BlockType = m_BlockType; - a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); - return true; -} - - - - - -void cBlockHandler::OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ -} - - - - - -void cBlockHandler::OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ -} - - - - - -void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ -} - - - - - -void cBlockHandler::OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - // Notify the neighbors - NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); - NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); - NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); -} - - - - - -void cBlockHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Notify the neighbors - NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); - NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); - NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); - NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); -} - - - - - -void cBlockHandler::NeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)) - { - GetBlockHandler(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ); - } -} - - - - - -void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ -} - - - - - -void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ -} - - - - - -void cBlockHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ -} - - - - - -void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) -{ - // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this. - a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta)); -} - - - - - -void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cItems Pickups; - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - ConvertToPickups(Pickups, Meta); - - // Allow plugins to modify the pickups: - cRoot::Get()->GetPluginManager()->CallHookBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups); - - if (!Pickups.empty()) - { - a_World->SpawnItemPickups(Pickups, a_BlockX, a_BlockY, a_BlockZ); - } -} - - - - - -const char * cBlockHandler::GetStepSound() -{ - return "step.stone"; -} - - - - - -bool cBlockHandler::CanBeAt(int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk) -{ - return true; -} - - - - - -bool cBlockHandler::IsUseable() -{ - return false; -} - - - - - -bool cBlockHandler::IsClickedThrough(void) -{ - return false; -} - - - - - -bool cBlockHandler::DoesIgnoreBuildCollision(void) -{ - return (m_BlockType == E_BLOCK_AIR); -} - - - - - -bool cBlockHandler::DoesAllowBlockOnTop(void) -{ - return true; -} - - - - - -bool cBlockHandler::CanBePlacedOnSide(void) -{ - return true; -} - - - - - -bool cBlockHandler::DoesDropOnUnsuitable(void) -{ - return true; -} - - - - - -void cBlockHandler::Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) -{ - if (!CanBeAt(a_RelX, a_RelY, a_RelZ, a_Chunk)) - { - if (DoesDropOnUnsuitable()) - { - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); - } - - a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); - } - else - { - // Wake up the simulators for this block: - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); - } -} - - - - + +#include "Globals.h" +#include "BlockHandler.h" +#include "../Item.h" +#include "../World.h" +#include "../Root.h" +#include "../PluginManager.h" +#include "BlockSand.h" +#include "BlockGravel.h" +#include "BlockDoor.h" +#include "BlockFire.h" +#include "BlockRedstone.h" +#include "BlockRedstoneTorch.h" +#include "BlockRedstoneRepeater.h" +#include "BlockPiston.h" +#include "BlockWorkbench.h" +#include "BlockEntity.h" +#include "BlockVine.h" +#include "BlockTallGrass.h" +#include "BlockSnow.h" +#include "BlockCloth.h" +#include "BlockSlab.h" +#include "BlockDirt.h" +#include "BlockTorch.h" +#include "BlockWood.h" +#include "BlockLeaves.h" +#include "BlockSapling.h" +#include "BlockFluid.h" +#include "BlockChest.h" +#include "BlockFurnace.h" +#include "BlockDropSpenser.h" +#include "BlockStairs.h" +#include "BlockLadder.h" +#include "BlockLever.h" +#include "BlockSign.h" +#include "BlockCrops.h" +#include "BlockSugarcane.h" +#include "BlockFlower.h" +#include "BlockMushroom.h" +#include "BlockCactus.h" +#include "BlockStems.h" +#include "BlockGlowstone.h" +#include "BlockStone.h" +#include "BlockMelon.h" +#include "BlockIce.h" +#include "BlockOre.h" +#include "BlockNote.h" +#include "BlockBed.h" +#include "BlockFarmland.h" +#include "BlockMycelium.h" +#include "BlockRail.h" +#include "BlockGlass.h" +#include "BlockEnderchest.h" +#include "BlockFenceGate.h" +#include "BlockFlowerPot.h" +#include "BlockCauldron.h" +#include "BlockBrewingStand.h" +#include "BlockCobWeb.h" +#include "BlockDeadBush.h" +#include "BlockHopper.h" + + + + + +bool cBlockHandler::m_HandlerInitialized = false; +cBlockHandler * cBlockHandler::m_BlockHandler[256]; + + + + + +cBlockHandler * cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockType) +{ + if (!m_HandlerInitialized) + { + // We have to initialize + memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); + m_HandlerInitialized = true; + } + if (m_BlockHandler[a_BlockType] != NULL) + { + return m_BlockHandler[a_BlockType]; + } + + return m_BlockHandler[a_BlockType] = CreateBlockHandler(a_BlockType); +} + + + + + +cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) +{ + switch(a_BlockType) + { + // Block handlers, alphabetically sorted: + case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType); + case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType); + case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_BROWN_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); + case E_BLOCK_CACTUS: return new cBlockCactusHandler (a_BlockType); + case E_BLOCK_CARROTS: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType); + case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType); + case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType); + case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_COBWEB: return new cBlockCobWebHandler (a_BlockType); + case E_BLOCK_CROPS: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_DEAD_BUSH: return new cBlockDeadBushHandler (a_BlockType); + case E_BLOCK_DETECTOR_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_DIAMOND_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_DIRT: return new cBlockDirtHandler (a_BlockType); + case E_BLOCK_DISPENSER: return new cBlockDropSpenserHandler (a_BlockType); + case E_BLOCK_DOUBLE_STONE_SLAB: return new cBlockSlabHandler (a_BlockType); + case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType); + case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType); + case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType); + case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler; + case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType); + case E_BLOCK_FIRE: return new cBlockFireHandler (a_BlockType); + case E_BLOCK_FLOWER_POT: return new cBlockFlowerPotHandler (a_BlockType); + case E_BLOCK_FURNACE: return new cBlockFurnaceHandler (a_BlockType); + case E_BLOCK_GLOWSTONE: return new cBlockGlowstoneHandler (a_BlockType); + case E_BLOCK_GOLD_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_GLASS: return new cBlockGlassHandler (a_BlockType); + case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType); + case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType); + case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType); + case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType); + case E_BLOCK_IRON_DOOR: return new cBlockDoorHandler (a_BlockType); + case E_BLOCK_IRON_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_JUKEBOX: return new cBlockEntityHandler (a_BlockType); + case E_BLOCK_JUNGLE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_LADDER: return new cBlockLadderHandler (a_BlockType); + case E_BLOCK_LEVER: return new cBlockLeverHandler (a_BlockType); + case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_LAVA: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType); + case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType); + case E_BLOCK_LOG: return new cBlockWoodHandler (a_BlockType); + case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType); + case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType); + case E_BLOCK_MYCELIUM: return new cBlockMyceliumHandler (a_BlockType); + case E_BLOCK_NETHER_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_NOTE_BLOCK: return new cBlockNoteHandler (a_BlockType); + case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType); + case E_BLOCK_PLANKS: return new cBlockWoodHandler (a_BlockType); + case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType); + case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_POTATOES: return new cBlockCropsHandler (a_BlockType); + case E_BLOCK_POWERED_RAIL: return new cBlockRailHandler (a_BlockType); + case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType); + case E_BLOCK_REDSTONE_REPEATER_ON: return new cBlockRedstoneRepeaterHandler(a_BlockType); + case E_BLOCK_REDSTONE_TORCH_OFF: return new cBlockRedstoneTorchHandler (a_BlockType); + case E_BLOCK_REDSTONE_TORCH_ON: return new cBlockRedstoneTorchHandler (a_BlockType); + case E_BLOCK_REDSTONE_WIRE: return new cBlockRedstoneHandler (a_BlockType); + case E_BLOCK_RED_MUSHROOM: return new cBlockMushroomHandler (a_BlockType); + case E_BLOCK_RED_ROSE: return new cBlockFlowerHandler (a_BlockType); + case E_BLOCK_SAND: return new cBlockSandHandler (a_BlockType); + case E_BLOCK_SANDSTONE_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_SAPLING: return new cBlockSaplingHandler (a_BlockType); + case E_BLOCK_SIGN_POST: return new cBlockSignHandler (a_BlockType); + case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType); + case E_BLOCK_SPRUCE_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_STATIONARY_LAVA: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_STATIONARY_WATER: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_STICKY_PISTON: return new cBlockPistonHandler (a_BlockType); + case E_BLOCK_STONE: return new cBlockStoneHandler (a_BlockType); + case E_BLOCK_STONE_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_STONE_SLAB: return new cBlockSlabHandler (a_BlockType); + case E_BLOCK_SUGARCANE: return new cBlockSugarcaneHandler (a_BlockType); + case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType); + case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType); + case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); + case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); + case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); + case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType); + case E_BLOCK_WOODEN_SLAB: return new cBlockSlabHandler (a_BlockType); + case E_BLOCK_WOODEN_STAIRS: return new cBlockStairsHandler (a_BlockType); + case E_BLOCK_WOOL: return new cBlockClothHandler (a_BlockType); + case E_BLOCK_WORKBENCH: return new cBlockWorkbenchHandler (a_BlockType); + case E_BLOCK_YELLOW_FLOWER: return new cBlockFlowerHandler (a_BlockType); + + default: return new cBlockHandler(a_BlockType); + } +} + + + + + +void cBlockHandler::Deinit() +{ + for (int i = 0; i < 256; i++) + { + delete m_BlockHandler[i]; + } + memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); // Don't leave any dangling pointers around, just in case + m_HandlerInitialized = false; +} + + + + + +cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType) +{ + m_BlockType = a_BlockType; +} + + + + + +bool cBlockHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + // By default, all blocks can be placed and the meta is copied over from the item's damage value: + a_BlockType = m_BlockType; + a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); + return true; +} + + + + + +void cBlockHandler::OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnPlacedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ +} + + + + + +void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnPlaced(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // Notify the neighbors + NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); +} + + + + + +void cBlockHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Notify the neighbors + NeighborChanged(a_World, a_BlockX - 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX + 1, a_BlockY, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY - 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY + 1, a_BlockZ); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ - 1); + NeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ + 1); +} + + + + + +void cBlockHandler::NeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if ((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height)) + { + GetBlockHandler(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnNeighborChanged(a_World, a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ +} + + + + + +void cBlockHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ +} + + + + + +void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) +{ + // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this. + a_Pickups.push_back(cItem(m_BlockType, 1, a_BlockMeta)); +} + + + + + +void cBlockHandler::DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cItems Pickups; + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + ConvertToPickups(Pickups, Meta); + + // Allow plugins to modify the pickups: + cRoot::Get()->GetPluginManager()->CallHookBlockToPickups(a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups); + + if (!Pickups.empty()) + { + a_World->SpawnItemPickups(Pickups, a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +const char * cBlockHandler::GetStepSound() +{ + return "step.stone"; +} + + + + + +bool cBlockHandler::CanBeAt(int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk) +{ + return true; +} + + + + + +bool cBlockHandler::IsUseable() +{ + return false; +} + + + + + +bool cBlockHandler::IsClickedThrough(void) +{ + return false; +} + + + + + +bool cBlockHandler::DoesIgnoreBuildCollision(void) +{ + return (m_BlockType == E_BLOCK_AIR); +} + + + + + +bool cBlockHandler::DoesAllowBlockOnTop(void) +{ + return true; +} + + + + + +bool cBlockHandler::CanBePlacedOnSide(void) +{ + return true; +} + + + + + +bool cBlockHandler::DoesDropOnUnsuitable(void) +{ + return true; +} + + + + + +void cBlockHandler::Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) +{ + if (!CanBeAt(a_RelX, a_RelY, a_RelZ, a_Chunk)) + { + if (DoesDropOnUnsuitable()) + { + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); + } + + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + } + else + { + // Wake up the simulators for this block: + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); + } +} + + + + diff --git a/source/Blocks/BlockHandler.h b/source/Blocks/BlockHandler.h index cb52be497..228ce174b 100644 --- a/source/Blocks/BlockHandler.h +++ b/source/Blocks/BlockHandler.h @@ -1,158 +1,158 @@ - -#pragma once - -#include "../Defines.h" -#include "../Item.h" -#include "../Chunk.h" - - - - - -// fwd: -class cWorld; -class cPlayer; - - - - - -class cBlockHandler -{ -public: - cBlockHandler(BLOCKTYPE a_BlockType); - - /// Called when the block gets ticked either by a random tick or by a queued tick - virtual void OnUpdate(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /** Called before a block is placed into a world. - The handler should return true to allow placement, false to refuse. - Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. - Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block - */ - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ); - - /// Called by cWorld::SetBlock() after the block has been set - virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced(). - virtual void OnPlacedByPlayer( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ); - - /// Called before the player has destroyed a block - virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called before a block gets destroyed / replaced with air - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position) - virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Notifies all neighbors of the given block about a change - static void NeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called while the player diggs the block. - virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Called if the user right clicks the block and the block is useable - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); - - /// Called when the item is mined to convert it into pickups. Pickups may specify multiple items. - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta); - - /// Handles the dropping of a block based on what ConvertToDrops() returns. This will not destroy the block. a_Digger is the entity causing the drop; it may be NULL - virtual void DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Returns step sound name of block - virtual const char * GetStepSound(void); - - /// Checks if the block can stay at the specified relative coords in the chunk - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk); - - /** Checks if the block can be placed at this point. - Default: CanBeAt(...) - NOTE: This call doesn't actually place the block - */ - // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); - - /// Called when the player tries to place a block on top of this block (Only if he aims directly on this block); return false to disallow - virtual bool DoesAllowBlockOnTop(void); - - /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called - virtual bool IsUseable(void); - - /** Indicates whether the client will click through this block. - For example digging a fire will hit the block below the fire so fire is clicked through - */ - virtual bool IsClickedThrough(void); - - /** Checks if the player can build "inside" this block. - For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision - */ - virtual bool DoesIgnoreBuildCollision(void); - - /// Indicates this block can be placed on the side of other blocks. Default: true - virtual bool CanBePlacedOnSide(void); - - /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true - virtual bool DoesDropOnUnsuitable(void); - - /** Called when one of the neighbors gets set; equivalent to MC block update. - By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()), - and wakes up all simulators on the block. - */ - virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk); - - /// Returns the meta for a block after rotating it counter-clockwise from the specified meta. Default: no change - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) { return a_Meta; } - - /// Returns the meta for a block after rotating it clockwise from the specified meta. Default: no change - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) { return a_Meta; } - - /// Returns the meta for a block after mirroring it around the XY plane. Default: no change - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) { return a_Meta; } - - /// Returns the meta for a block after mirroring it around the XZ plane. Default: no change - virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) { return a_Meta; } - - /// Returns the meta for a block after mirroring it around the YZ plane. Default: no change - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; } - - - /// Get the blockhandler for a specific block id - static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType); - - /// Deletes all initialised block handlers - static void Deinit(); - -protected: - BLOCKTYPE m_BlockType; - - // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead. - static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType); - static cBlockHandler *m_BlockHandler[256]; - static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized -}; - - - - - -// Shortcut to get the blockhandler for a specific block -inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType) -{ - return cBlockHandler::GetBlockHandler(a_BlockType); -} - - - - + +#pragma once + +#include "../Defines.h" +#include "../Item.h" +#include "../Chunk.h" + + + + + +// fwd: +class cWorld; +class cPlayer; + + + + + +class cBlockHandler +{ +public: + cBlockHandler(BLOCKTYPE a_BlockType); + + /// Called when the block gets ticked either by a random tick or by a queued tick + virtual void OnUpdate(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Called before a block is placed into a world. + The handler should return true to allow placement, false to refuse. + Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. + Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block + */ + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ); + + /// Called by cWorld::SetBlock() after the block has been set + virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced(). + virtual void OnPlacedByPlayer( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ); + + /// Called before the player has destroyed a block + virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called before a block gets destroyed / replaced with air + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position) + virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Notifies all neighbors of the given block about a change + static void NeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called while the player diggs the block. + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Called if the user right clicks the block and the block is useable + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); + + /// Called when the item is mined to convert it into pickups. Pickups may specify multiple items. + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta); + + /// Handles the dropping of a block based on what ConvertToDrops() returns. This will not destroy the block. a_Digger is the entity causing the drop; it may be NULL + virtual void DropBlock(cWorld * a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Returns step sound name of block + virtual const char * GetStepSound(void); + + /// Checks if the block can stay at the specified relative coords in the chunk + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk); + + /** Checks if the block can be placed at this point. + Default: CanBeAt(...) + NOTE: This call doesn't actually place the block + */ + // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); + + /// Called when the player tries to place a block on top of this block (Only if he aims directly on this block); return false to disallow + virtual bool DoesAllowBlockOnTop(void); + + /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called + virtual bool IsUseable(void); + + /** Indicates whether the client will click through this block. + For example digging a fire will hit the block below the fire so fire is clicked through + */ + virtual bool IsClickedThrough(void); + + /** Checks if the player can build "inside" this block. + For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision + */ + virtual bool DoesIgnoreBuildCollision(void); + + /// Indicates this block can be placed on the side of other blocks. Default: true + virtual bool CanBePlacedOnSide(void); + + /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true + virtual bool DoesDropOnUnsuitable(void); + + /** Called when one of the neighbors gets set; equivalent to MC block update. + By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()), + and wakes up all simulators on the block. + */ + virtual void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk); + + /// Returns the meta for a block after rotating it counter-clockwise from the specified meta. Default: no change + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after rotating it clockwise from the specified meta. Default: no change + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the XY plane. Default: no change + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the XZ plane. Default: no change + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) { return a_Meta; } + + /// Returns the meta for a block after mirroring it around the YZ plane. Default: no change + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) { return a_Meta; } + + + /// Get the blockhandler for a specific block id + static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockType); + + /// Deletes all initialised block handlers + static void Deinit(); + +protected: + BLOCKTYPE m_BlockType; + + // Creates a new blockhandler for the given block type. For internal use only, use ::GetBlockHandler() instead. + static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockType); + static cBlockHandler *m_BlockHandler[256]; + static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized +}; + + + + + +// Shortcut to get the blockhandler for a specific block +inline cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType) +{ + return cBlockHandler::GetBlockHandler(a_BlockType); +} + + + + diff --git a/source/Blocks/BlockHopper.h b/source/Blocks/BlockHopper.h index 10f865564..3998276d7 100644 --- a/source/Blocks/BlockHopper.h +++ b/source/Blocks/BlockHopper.h @@ -1,46 +1,46 @@ - -// BlockHopper.h - -// Declares the cBlockHopperHandler class representing the handler for the Hopper block - - - - - -class cBlockHopperHandler : - public cBlockEntityHandler -{ -public: - cBlockHopperHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - - // Convert the blockface into meta: - switch (a_BlockFace) - { - case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break; - case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break; - case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break; - case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break; - case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break; - case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break; - default: a_BlockMeta = E_META_HOPPER_UNATTACHED; break; - } - return true; - } -} ; - - - - + +// BlockHopper.h + +// Declares the cBlockHopperHandler class representing the handler for the Hopper block + + + + + +class cBlockHopperHandler : + public cBlockEntityHandler +{ +public: + cBlockHopperHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + + // Convert the blockface into meta: + switch (a_BlockFace) + { + case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break; + case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break; + case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break; + case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break; + case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break; + case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break; + default: a_BlockMeta = E_META_HOPPER_UNATTACHED; break; + } + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockIce.h b/source/Blocks/BlockIce.h index 605a8ae1d..11fe425f3 100644 --- a/source/Blocks/BlockIce.h +++ b/source/Blocks/BlockIce.h @@ -1,37 +1,37 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockIceHandler : - public cBlockHandler -{ -public: - cBlockIceHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // No pickups - } - - - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - // TODO: Ice destroyed with air below it should turn into air instead of water - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_STATIONARY_WATER, 8); - // This is called later than the real destroying of this ice block - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockIceHandler : + public cBlockHandler +{ +public: + cBlockIceHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // No pickups + } + + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + // TODO: Ice destroyed with air below it should turn into air instead of water + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_STATIONARY_WATER, 8); + // This is called later than the real destroying of this ice block + } +} ; + + + + diff --git a/source/Blocks/BlockLadder.h b/source/Blocks/BlockLadder.h index 93473f757..c0aa25f60 100644 --- a/source/Blocks/BlockLadder.h +++ b/source/Blocks/BlockLadder.h @@ -1,115 +1,115 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockLadderHandler : - public cBlockHandler -{ -public: - cBlockLadderHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - if (!LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) - { - a_BlockFace = FindSuitableBlockFace(a_World, a_BlockX, a_BlockY, a_BlockZ); - - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - return false; - } - } - - a_BlockType = m_BlockType; - a_BlockMeta = DirectionToMetaData(a_BlockFace); - return true; - } - - - static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export - { // tolua_export - switch (a_Direction) - { - case 0x2: return 0x2; - case 0x3: return 0x3; - case 0x4: return 0x4; - case 0x5: return 0x5; - default: return 0x2; - } - } // tolua_export - - - static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export - { // tolua_export - switch (a_MetaData) - { - case 0x2: return 0x2; - case 0x3: return 0x3; - case 0x4: return 0x4; - case 0x5: return 0x5; - default: return 0x2; - } - } // tolua_export - - - /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure - static char FindSuitableBlockFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - for (int Face = 2; Face <= 5; Face++) - { - if (LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, Face)) - { - return Face; - } - } - return BLOCK_FACE_BOTTOM; - } - - - static bool LadderCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) - { - if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP)) - { - return false; - } - - AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); - - return g_BlockIsSolid[a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)]; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison - char BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - return LadderCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, BlockFace); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockLadderHandler : + public cBlockHandler +{ +public: + cBlockLadderHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (!LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + a_BlockFace = FindSuitableBlockFace(a_World, a_BlockX, a_BlockY, a_BlockZ); + + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + return false; + } + } + + a_BlockType = m_BlockType; + a_BlockMeta = DirectionToMetaData(a_BlockFace); + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export + { // tolua_export + switch (a_Direction) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: return 0x2; + } + } // tolua_export + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export + { // tolua_export + switch (a_MetaData) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: return 0x2; + } + } // tolua_export + + + /// Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure + static char FindSuitableBlockFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + for (int Face = 2; Face <= 5; Face++) + { + if (LadderCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, Face)) + { + return Face; + } + } + return BLOCK_FACE_BOTTOM; + } + + + static bool LadderCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP)) + { + return false; + } + + AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); + + return g_BlockIsSolid[a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)]; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison + char BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + return LadderCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, BlockFace); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockLeaves.h b/source/Blocks/BlockLeaves.h index 3563babcf..6e015b8fa 100644 --- a/source/Blocks/BlockLeaves.h +++ b/source/Blocks/BlockLeaves.h @@ -1,184 +1,184 @@ -#pragma once -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" -#include "../BlockArea.h" - - - - - -// Leaves can be this many blocks that away (inclusive) from the log not to decay -#define LEAVES_CHECK_DISTANCE 6 - -#define PROCESS_NEIGHBOR(x,y,z) \ - switch (a_Area.GetBlockType(x, y, z)) \ - { \ - case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \ - case E_BLOCK_LOG: return true; \ - } - -bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); - - - - - -class cBlockLeavesHandler : - public cBlockHandler -{ -public: - cBlockLeavesHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - MTRand rand; - - // Only the first 2 bits contain the display information, the others are for growing - if (rand.randInt(5) == 0) - { - a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); - } - if ((a_BlockMeta & 3) == E_META_SAPLING_APPLE) - { - if (rand.rand(100) == 0) - { - a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); - } - } - } - - - void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - cBlockHandler::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ); - - //0.5% chance of dropping an apple - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - //check if Oak (0x1 and 0x2 bit not set) - MTRand rand; - if(!(Meta & 3) && rand.randInt(200) == 100) - { - cItems Drops; - Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); - a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); - } - } - - - virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x7); // Unset 0x8 bit so it gets checked for decay - } - - - virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if ((Meta & 0x04) != 0) - { - // Player-placed leaves, don't decay - return; - } - - if ((Meta & 0x8) != 0) - { - // These leaves have been checked for decay lately and nothing around them changed - return; - } - - // Get the data around the leaves: - cBlockArea Area; - if (!Area.Read( - a_World, - a_BlockX - LEAVES_CHECK_DISTANCE, a_BlockX + LEAVES_CHECK_DISTANCE, - a_BlockY - LEAVES_CHECK_DISTANCE, a_BlockY + LEAVES_CHECK_DISTANCE, - a_BlockZ - LEAVES_CHECK_DISTANCE, a_BlockZ + LEAVES_CHECK_DISTANCE, - cBlockArea::baTypes) - ) - { - // Cannot check leaves, a chunk is missing too close - return; - } - - if (HasNearLog(Area, a_BlockX, a_BlockY, a_BlockZ)) - { - // Wood found, the leaves stay; mark them as checked: - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x8); - return; - } - // Decay the leaves: - DropBlock(a_World, NULL, a_BlockX, a_BlockY, a_BlockZ); - - a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); - - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - - -bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Filter the blocks into a {leaves, log, other (air)} set: - BLOCKTYPE * Types = a_Area.GetBlockTypes(); - for (int i = a_Area.GetBlockCount() - 1; i > 0; i--) - { - switch (Types[i]) - { - case E_BLOCK_LEAVES: - case E_BLOCK_LOG: - { - break; - } - default: - { - Types[i] = E_BLOCK_AIR; - break; - } - } - } // for i - Types[] - - // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block: - // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations - a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE); - for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++) - { - for (int y = a_BlockY - i; y <= a_BlockY + i; y++) - { - for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++) - { - for (int x = a_BlockX - i; x <= a_BlockX + i; x++) - { - if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i) - { - continue; - } - PROCESS_NEIGHBOR(x - 1, y, z); - PROCESS_NEIGHBOR(x + 1, y, z); - PROCESS_NEIGHBOR(x, y, z - 1); - PROCESS_NEIGHBOR(x, y, z + 1); - PROCESS_NEIGHBOR(x, y + 1, z); - PROCESS_NEIGHBOR(x, y - 1, z); - } // for x - } // for z - } // for y - } // for i - BFS iterations - return false; -} - - - - +#pragma once +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" +#include "../BlockArea.h" + + + + + +// Leaves can be this many blocks that away (inclusive) from the log not to decay +#define LEAVES_CHECK_DISTANCE 6 + +#define PROCESS_NEIGHBOR(x,y,z) \ + switch (a_Area.GetBlockType(x, y, z)) \ + { \ + case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \ + case E_BLOCK_LOG: return true; \ + } + +bool HasNearLog(cBlockArea &a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); + + + + + +class cBlockLeavesHandler : + public cBlockHandler +{ +public: + cBlockLeavesHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + MTRand rand; + + // Only the first 2 bits contain the display information, the others are for growing + if (rand.randInt(5) == 0) + { + a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); + } + if ((a_BlockMeta & 3) == E_META_SAPLING_APPLE) + { + if (rand.rand(100) == 0) + { + a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); + } + } + } + + + void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + cBlockHandler::OnDestroyed(a_World, a_BlockX, a_BlockY, a_BlockZ); + + //0.5% chance of dropping an apple + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + //check if Oak (0x1 and 0x2 bit not set) + MTRand rand; + if(!(Meta & 3) && rand.randInt(200) == 100) + { + cItems Drops; + Drops.push_back(cItem(E_ITEM_RED_APPLE, 1, 0)); + a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); + } + } + + + virtual void OnNeighborChanged(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x7); // Unset 0x8 bit so it gets checked for decay + } + + + virtual void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if ((Meta & 0x04) != 0) + { + // Player-placed leaves, don't decay + return; + } + + if ((Meta & 0x8) != 0) + { + // These leaves have been checked for decay lately and nothing around them changed + return; + } + + // Get the data around the leaves: + cBlockArea Area; + if (!Area.Read( + a_World, + a_BlockX - LEAVES_CHECK_DISTANCE, a_BlockX + LEAVES_CHECK_DISTANCE, + a_BlockY - LEAVES_CHECK_DISTANCE, a_BlockY + LEAVES_CHECK_DISTANCE, + a_BlockZ - LEAVES_CHECK_DISTANCE, a_BlockZ + LEAVES_CHECK_DISTANCE, + cBlockArea::baTypes) + ) + { + // Cannot check leaves, a chunk is missing too close + return; + } + + if (HasNearLog(Area, a_BlockX, a_BlockY, a_BlockZ)) + { + // Wood found, the leaves stay; mark them as checked: + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x8); + return; + } + // Decay the leaves: + DropBlock(a_World, NULL, a_BlockX, a_BlockY, a_BlockZ); + + a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + + +bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Filter the blocks into a {leaves, log, other (air)} set: + BLOCKTYPE * Types = a_Area.GetBlockTypes(); + for (int i = a_Area.GetBlockCount() - 1; i > 0; i--) + { + switch (Types[i]) + { + case E_BLOCK_LEAVES: + case E_BLOCK_LOG: + { + break; + } + default: + { + Types[i] = E_BLOCK_AIR; + break; + } + } + } // for i - Types[] + + // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block: + // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log in 4 iterations + a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE); + for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++) + { + for (int y = a_BlockY - i; y <= a_BlockY + i; y++) + { + for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++) + { + for (int x = a_BlockX - i; x <= a_BlockX + i; x++) + { + if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i) + { + continue; + } + PROCESS_NEIGHBOR(x - 1, y, z); + PROCESS_NEIGHBOR(x + 1, y, z); + PROCESS_NEIGHBOR(x, y, z - 1); + PROCESS_NEIGHBOR(x, y, z + 1); + PROCESS_NEIGHBOR(x, y + 1, z); + PROCESS_NEIGHBOR(x, y - 1, z); + } // for x + } // for z + } // for y + } // for i - BFS iterations + return false; +} + + + + diff --git a/source/Blocks/BlockLever.cpp b/source/Blocks/BlockLever.cpp index a9ab1fbbb..6dbff10c1 100644 --- a/source/Blocks/BlockLever.cpp +++ b/source/Blocks/BlockLever.cpp @@ -1,48 +1,48 @@ - -#include "Globals.h" -#include "BlockLever.h" -#include "../Item.h" -#include "../World.h" -#include "../Player.h" -#include "../Simulator/RedstoneSimulator.h" - - - - - -cBlockLeverHandler::cBlockLeverHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockLeverHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off. - NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta); - if (Meta & 0x08) - { - a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); - } - else - { - a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f); - } -} - - - - - -void cBlockLeverHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8); -} - - - - + +#include "Globals.h" +#include "BlockLever.h" +#include "../Item.h" +#include "../World.h" +#include "../Player.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +cBlockLeverHandler::cBlockLeverHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockLeverHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + // Flip the ON bit on/off. Using XOR bitwise operation to turn it on/off. + NIBBLETYPE Meta = ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08) & 0x0f); + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta); + if (Meta & 0x08) + { + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + } + else + { + a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.5f); + } +} + + + + + +void cBlockLeverHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8); +} + + + + diff --git a/source/Blocks/BlockLever.h b/source/Blocks/BlockLever.h index 42a16fd9b..362cf563e 100644 --- a/source/Blocks/BlockLever.h +++ b/source/Blocks/BlockLever.h @@ -1,61 +1,61 @@ -#pragma once - -#include "BlockHandler.h" -#include "../World.h" -#include "../Simulator/RedstoneSimulator.h" - - - - - -class cBlockLeverHandler : - public cBlockHandler -{ -public: - cBlockLeverHandler(BLOCKTYPE a_BlockType); - - virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(E_BLOCK_LEVER, 1, 0)); - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = cRedstoneSimulator::LeverDirectionToMetaData(a_BlockFace); - return true; - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +class cBlockLeverHandler : + public cBlockHandler +{ +public: + cBlockLeverHandler(BLOCKTYPE a_BlockType); + + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_BLOCK_LEVER, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = cRedstoneSimulator::LeverDirectionToMetaData(a_BlockFace); + return true; + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockMelon.h b/source/Blocks/BlockMelon.h index d985ef126..2f7d9a461 100644 --- a/source/Blocks/BlockMelon.h +++ b/source/Blocks/BlockMelon.h @@ -1,35 +1,35 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockMelonHandler : - public cBlockHandler -{ -public: - cBlockMelonHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - MTRand r1; - a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, (char)(3 + r1.randInt(4)), 0)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMelonHandler : + public cBlockHandler +{ +public: + cBlockMelonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + MTRand r1; + a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, (char)(3 + r1.randInt(4)), 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockMushroom.h b/source/Blocks/BlockMushroom.h index 62cc898c0..b3b23e2ba 100644 --- a/source/Blocks/BlockMushroom.h +++ b/source/Blocks/BlockMushroom.h @@ -1,71 +1,71 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockMushroomHandler : - public cBlockHandler -{ -public: - cBlockMushroomHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(m_BlockType, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if (a_RelY <= 0) - { - return false; - } - - // TODO: Cannot be at too much daylight - - switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) - { - case E_BLOCK_GLASS: - case E_BLOCK_CACTUS: - case E_BLOCK_ICE: - case E_BLOCK_LEAVES: - case E_BLOCK_AIR: - { - return false; - } - } - return true; - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMushroomHandler : + public cBlockHandler +{ +public: + cBlockMushroomHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + + // TODO: Cannot be at too much daylight + + switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) + { + case E_BLOCK_GLASS: + case E_BLOCK_CACTUS: + case E_BLOCK_ICE: + case E_BLOCK_LEAVES: + case E_BLOCK_AIR: + { + return false; + } + } + return true; + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + virtual bool CanBePlacedOnSide(void) override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/source/Blocks/BlockMycelium.h b/source/Blocks/BlockMycelium.h index 8e1f577e1..0ed7162ac 100644 --- a/source/Blocks/BlockMycelium.h +++ b/source/Blocks/BlockMycelium.h @@ -1,27 +1,27 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockMyceliumHandler : - public cBlockHandler -{ -public: - cBlockMyceliumHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockMyceliumHandler : + public cBlockHandler +{ +public: + cBlockMyceliumHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0)); + } +} ; + + + + diff --git a/source/Blocks/BlockNote.h b/source/Blocks/BlockNote.h index 5884a1eb3..fef38d845 100644 --- a/source/Blocks/BlockNote.h +++ b/source/Blocks/BlockNote.h @@ -1,13 +1,13 @@ -#pragma once -#include "BlockHandler.h" -#include "BlockEntity.h" - -class cBlockNoteHandler : public cBlockEntityHandler -{ -public: - cBlockNoteHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) - { - } - -}; +#pragma once +#include "BlockHandler.h" +#include "BlockEntity.h" + +class cBlockNoteHandler : public cBlockEntityHandler +{ +public: + cBlockNoteHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + +}; diff --git a/source/Blocks/BlockOre.h b/source/Blocks/BlockOre.h index 1bfd8d17c..9684dbb19 100644 --- a/source/Blocks/BlockOre.h +++ b/source/Blocks/BlockOre.h @@ -1,80 +1,80 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -class cBlockOreHandler : - public cBlockHandler -{ -public: - cBlockOreHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - short ItemType = m_BlockType; - char Count = 1; - short Meta = 0; - - MTRand r1; - switch (m_BlockType) - { - case E_BLOCK_LAPIS_ORE: - { - ItemType = E_ITEM_DYE; - Count = 4 + (char)r1.randInt(4); - Meta = 4; - break; - } - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - { - Count = 4 + (char)r1.randInt(1); - break; - } - default: - { - Count = 1; - break; - } - } - - switch (m_BlockType) - { - case E_BLOCK_DIAMOND_ORE: - { - ItemType = E_ITEM_DIAMOND; - break; - } - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - { - ItemType = E_ITEM_REDSTONE_DUST; - break; - } - case E_BLOCK_EMERALD_ORE: - { - ItemType = E_ITEM_EMERALD; - break; - } - case E_BLOCK_COAL_ORE: - { - ItemType = E_ITEM_COAL; - break; - } - } - a_Pickups.push_back(cItem(ItemType, Count, Meta)); - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockOreHandler : + public cBlockHandler +{ +public: + cBlockOreHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + short ItemType = m_BlockType; + char Count = 1; + short Meta = 0; + + MTRand r1; + switch (m_BlockType) + { + case E_BLOCK_LAPIS_ORE: + { + ItemType = E_ITEM_DYE; + Count = 4 + (char)r1.randInt(4); + Meta = 4; + break; + } + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + Count = 4 + (char)r1.randInt(1); + break; + } + default: + { + Count = 1; + break; + } + } + + switch (m_BlockType) + { + case E_BLOCK_DIAMOND_ORE: + { + ItemType = E_ITEM_DIAMOND; + break; + } + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + ItemType = E_ITEM_REDSTONE_DUST; + break; + } + case E_BLOCK_EMERALD_ORE: + { + ItemType = E_ITEM_EMERALD; + break; + } + case E_BLOCK_COAL_ORE: + { + ItemType = E_ITEM_COAL; + break; + } + } + a_Pickups.push_back(cItem(ItemType, Count, Meta)); + } +} ; + + + + diff --git a/source/Blocks/BlockPiston.cpp b/source/Blocks/BlockPiston.cpp index 29312e97c..d0f90559e 100644 --- a/source/Blocks/BlockPiston.cpp +++ b/source/Blocks/BlockPiston.cpp @@ -1,69 +1,69 @@ - -#include "Globals.h" -#include "BlockPiston.h" -#include "../Item.h" -#include "../World.h" -#include "../Player.h" -#include "../Piston.h" - - - - - -#define AddPistonDir(x, y, z, dir, amount) \ - switch (dir) \ - { \ - case 0: (y) -= (amount); break; \ - case 1: (y) += (amount); break; \ - case 2: (z) -= (amount); break; \ - case 3: (z) += (amount); break; \ - case 4: (x) -= (amount); break; \ - case 5: (x) += (amount); break; \ - } - - - - -cBlockPistonHandler::cBlockPistonHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockPistonHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - char OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - int newX = a_BlockX; - int newY = a_BlockY; - int newZ = a_BlockZ; - AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1); - - if (a_World->GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION) - { - a_World->SetBlock(newX, newY, newZ, E_BLOCK_AIR, 0); - } -} - - - - - -bool cBlockPistonHandler::GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta -) -{ - a_BlockType = m_BlockType; - a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); - return true; -} - - - - + +#include "Globals.h" +#include "BlockPiston.h" +#include "../Item.h" +#include "../World.h" +#include "../Player.h" +#include "../Piston.h" + + + + + +#define AddPistonDir(x, y, z, dir, amount) \ + switch (dir) \ + { \ + case 0: (y) -= (amount); break; \ + case 1: (y) += (amount); break; \ + case 2: (z) -= (amount); break; \ + case 3: (z) += (amount); break; \ + case 4: (x) -= (amount); break; \ + case 5: (x) += (amount); break; \ + } + + + + +cBlockPistonHandler::cBlockPistonHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockPistonHandler::OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + char OldMeta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + int newX = a_BlockX; + int newY = a_BlockY; + int newZ = a_BlockZ; + AddPistonDir(newX, newY, newZ, OldMeta & ~(8), 1); + + if (a_World->GetBlock(newX, newY, newZ) == E_BLOCK_PISTON_EXTENSION) + { + a_World->SetBlock(newX, newY, newZ, E_BLOCK_AIR, 0); + } +} + + + + + +bool cBlockPistonHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + a_BlockType = m_BlockType; + a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch()); + return true; +} + + + + diff --git a/source/Blocks/BlockPiston.h b/source/Blocks/BlockPiston.h index 5d9d5c2b7..ef6a4fa97 100644 --- a/source/Blocks/BlockPiston.h +++ b/source/Blocks/BlockPiston.h @@ -1,28 +1,28 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockPistonHandler : - public cBlockHandler -{ -public: - cBlockPistonHandler(BLOCKTYPE a_BlockType); - - virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override; -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockPistonHandler : + public cBlockHandler +{ +public: + cBlockPistonHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override; +} ; + + + + diff --git a/source/Blocks/BlockRail.h b/source/Blocks/BlockRail.h index 60865abf5..65d120923 100644 --- a/source/Blocks/BlockRail.h +++ b/source/Blocks/BlockRail.h @@ -1,421 +1,421 @@ - -#pragma once - -#include "BlockEntity.h" -#include "../World.h" - - - - - -/// Meta values for the rail -enum ENUM_RAIL_DIRECTIONS -{ - E_RAIL_NORTH_SOUTH = 0, - E_RAIL_EAST_WEST = 1, - E_RAIL_ASCEND_EAST = 2, - E_RAIL_ASCEND_WEST = 3, - E_RAIL_ASCEND_NORTH = 4, - E_RAIL_ASCEND_SOUTH = 5, - E_RAIL_CURVED_SOUTH_EAST = 6, - E_RAIL_CURVED_SOUTH_WEST = 7, - E_RAIL_CURVED_NORTH_WEST = 8, - E_RAIL_CURVED_NORTH_EAST = 9, - - // Some useful synonyms: - E_RAIL_DIR_X = E_RAIL_EAST_WEST, - E_RAIL_DIR_Z = E_RAIL_NORTH_SOUTH, - E_RAIL_ASCEND_XP = E_RAIL_ASCEND_EAST, - E_RAIL_ASCEND_XM = E_RAIL_ASCEND_WEST, - E_RAIL_ASCEND_ZM = E_RAIL_ASCEND_NORTH, - E_RAIL_ASCEND_ZP = E_RAIL_ASCEND_SOUTH, - E_RAIL_CURVED_XPZP = E_RAIL_CURVED_SOUTH_EAST, - E_RAIL_CURVED_XMZP = E_RAIL_CURVED_SOUTH_WEST, - E_RAIL_CURVED_XMZM = E_RAIL_CURVED_NORTH_WEST, - E_RAIL_CURVED_XPZM = E_RAIL_CURVED_NORTH_EAST, -} ; - - - - - -enum ENUM_PURE -{ - E_PURE_UPDOWN = 0, - E_PURE_DOWN = 1, - E_PURE_NONE = 2 -}; - - - - - -class cBlockRailHandler : - public cBlockHandler -{ -public: - cBlockRailHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ); - return true; - } - - - virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ))) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)); - } - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if (a_RelY <= 0) - { - return false; - } - if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]) - { - return false; - } - - NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); - switch (Meta) - { - case E_RAIL_ASCEND_EAST: - case E_RAIL_ASCEND_WEST: - case E_RAIL_ASCEND_NORTH: - case E_RAIL_ASCEND_SOUTH: - { - // Mapping between the meta and the neighbors that need checking - Meta -= E_RAIL_ASCEND_EAST; // Base index at zero - static const struct - { - int x, z; - } Coords[] = - { - { 1, 0}, // east, XP - {-1, 0}, // west, XM - { 0, -1}, // north, ZM - { 0, 1}, // south, ZP - } ; - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta)) - { - // Too close to the edge, cannot simulate - return true; - } - return g_BlockIsSolid[BlockType]; - } - } - return true; - } - - NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - NIBBLETYPE Meta = 0; - char RailsCnt = 0; - bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP - memset(Neighbors, false, sizeof(Neighbors)); - Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN)); - Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)); - Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN)); - Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)); - Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE)); - Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE)); - Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE)); - Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE)); - if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST)) - Neighbors[0] = true; - if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST)) - Neighbors[1] = true; - if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH)) - Neighbors[2] = true; - if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH)) - Neighbors[3] = true; - for (int i = 0; i < 8; i++) - { - if (Neighbors[i]) - { - RailsCnt++; - } - } - if (RailsCnt == 1) - { - if (Neighbors[7]) return E_RAIL_ASCEND_SOUTH; - else if (Neighbors[6]) return E_RAIL_ASCEND_NORTH; - else if (Neighbors[5]) return E_RAIL_ASCEND_WEST; - else if (Neighbors[4]) return E_RAIL_ASCEND_EAST; - else if (Neighbors[0] || Neighbors[1]) return E_RAIL_EAST_WEST; - else if (Neighbors[2] || Neighbors[3]) return E_RAIL_NORTH_SOUTH; - ASSERT(!"Weird neighbor count"); - return Meta; - } - for (int i = 0; i < 4; i++) - { - if (Neighbors[i + 4]) - { - Neighbors[i] = true; - } - } - if (RailsCnt > 1) - { - if (Neighbors[3] && Neighbors[0]) return E_RAIL_CURVED_SOUTH_EAST; - else if (Neighbors[3] && Neighbors[1]) return E_RAIL_CURVED_SOUTH_WEST; - else if (Neighbors[2] && Neighbors[0]) return E_RAIL_CURVED_NORTH_EAST; - else if (Neighbors[2] && Neighbors[1]) return E_RAIL_CURVED_NORTH_WEST; - else if (Neighbors[7] && Neighbors[2]) return E_RAIL_ASCEND_SOUTH; - else if (Neighbors[3] && Neighbors[6]) return E_RAIL_ASCEND_NORTH; - else if (Neighbors[5] && Neighbors[0]) return E_RAIL_ASCEND_WEST; - else if (Neighbors[4] && Neighbors[1]) return E_RAIL_ASCEND_EAST; - else if (Neighbors[0] && Neighbors[1]) return E_RAIL_EAST_WEST; - else if (Neighbors[2] && Neighbors[3]) return E_RAIL_NORTH_SOUTH; - ASSERT(!"Weird neighbor count"); - } - return Meta; - } - - - bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) - { - return false; - } - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - switch (Meta) - { - case E_RAIL_NORTH_SOUTH: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN) - ) - { - return true; - } - break; - } - - case E_RAIL_EAST_WEST: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN) - ) - { - return true; - } - break; - } - - case E_RAIL_ASCEND_EAST: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) - ) - { - return true; - } - break; - } - - case E_RAIL_ASCEND_WEST: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) || - IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST) - ) - { - return true; - } - break; - } - - case E_RAIL_ASCEND_NORTH: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) - ) - { - return true; - } - break; - } - - case E_RAIL_ASCEND_SOUTH: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH) - ) - { - return true; - } - break; - } - - case E_RAIL_CURVED_SOUTH_EAST: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) - ) - { - return true; - } - break; - } - - case E_RAIL_CURVED_SOUTH_WEST: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) - ) - { - return true; - } - break; - } - - case E_RAIL_CURVED_NORTH_WEST: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) - ) - { - return true; - } - break; - } - - case E_RAIL_CURVED_NORTH_EAST: - { - if ( - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || - IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) - ) - { - return true; - } - break; - } - } - return false; - } - - - bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0) - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); - NIBBLETYPE Meta; - if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) - { - if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN)) - { - if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE)) - { - return true; - } - else - { - Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ); - } - } - else - { - Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ); - } - } - else - { - Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - } - - switch (a_BlockFace) - { - case BLOCK_FACE_NORTH: - { - if ( - (Meta == E_RAIL_NORTH_SOUTH) || - (Meta == E_RAIL_ASCEND_NORTH) || - (Meta == E_RAIL_ASCEND_SOUTH) || - (Meta == E_RAIL_CURVED_SOUTH_EAST) || - (Meta == E_RAIL_CURVED_SOUTH_WEST) - ) - { - return false; - } - break; - } - - case BLOCK_FACE_SOUTH: - { - if ( - (Meta == E_RAIL_NORTH_SOUTH) || - (Meta == E_RAIL_ASCEND_NORTH) || - (Meta == E_RAIL_ASCEND_SOUTH) || - (Meta == E_RAIL_CURVED_NORTH_EAST) || - (Meta == E_RAIL_CURVED_NORTH_WEST) - ) - { - return false; - } - break; - } - - case BLOCK_FACE_EAST: - { - if ( - (Meta == E_RAIL_EAST_WEST) || - (Meta == E_RAIL_ASCEND_EAST) || - (Meta == E_RAIL_ASCEND_WEST) || - (Meta == E_RAIL_CURVED_SOUTH_WEST) || - (Meta == E_RAIL_CURVED_NORTH_WEST) - ) - { - return false; - } - break; - } - case BLOCK_FACE_WEST: - { - if ( - (Meta == E_RAIL_EAST_WEST) || - (Meta == E_RAIL_ASCEND_EAST) || - (Meta == E_RAIL_ASCEND_WEST) || - (Meta == E_RAIL_CURVED_SOUTH_EAST) || - (Meta == E_RAIL_CURVED_NORTH_EAST) - ) - { - return false; - } - break; - } - } - return true; - } -} ; - - - - + +#pragma once + +#include "BlockEntity.h" +#include "../World.h" + + + + + +/// Meta values for the rail +enum ENUM_RAIL_DIRECTIONS +{ + E_RAIL_NORTH_SOUTH = 0, + E_RAIL_EAST_WEST = 1, + E_RAIL_ASCEND_EAST = 2, + E_RAIL_ASCEND_WEST = 3, + E_RAIL_ASCEND_NORTH = 4, + E_RAIL_ASCEND_SOUTH = 5, + E_RAIL_CURVED_SOUTH_EAST = 6, + E_RAIL_CURVED_SOUTH_WEST = 7, + E_RAIL_CURVED_NORTH_WEST = 8, + E_RAIL_CURVED_NORTH_EAST = 9, + + // Some useful synonyms: + E_RAIL_DIR_X = E_RAIL_EAST_WEST, + E_RAIL_DIR_Z = E_RAIL_NORTH_SOUTH, + E_RAIL_ASCEND_XP = E_RAIL_ASCEND_EAST, + E_RAIL_ASCEND_XM = E_RAIL_ASCEND_WEST, + E_RAIL_ASCEND_ZM = E_RAIL_ASCEND_NORTH, + E_RAIL_ASCEND_ZP = E_RAIL_ASCEND_SOUTH, + E_RAIL_CURVED_XPZP = E_RAIL_CURVED_SOUTH_EAST, + E_RAIL_CURVED_XMZP = E_RAIL_CURVED_SOUTH_WEST, + E_RAIL_CURVED_XMZM = E_RAIL_CURVED_NORTH_WEST, + E_RAIL_CURVED_XPZM = E_RAIL_CURVED_NORTH_EAST, +} ; + + + + + +enum ENUM_PURE +{ + E_PURE_UPDOWN = 0, + E_PURE_DOWN = 1, + E_PURE_NONE = 2 +}; + + + + + +class cBlockRailHandler : + public cBlockHandler +{ +public: + cBlockRailHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ); + return true; + } + + + virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ))) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]) + { + return false; + } + + NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + switch (Meta) + { + case E_RAIL_ASCEND_EAST: + case E_RAIL_ASCEND_WEST: + case E_RAIL_ASCEND_NORTH: + case E_RAIL_ASCEND_SOUTH: + { + // Mapping between the meta and the neighbors that need checking + Meta -= E_RAIL_ASCEND_EAST; // Base index at zero + static const struct + { + int x, z; + } Coords[] = + { + { 1, 0}, // east, XP + {-1, 0}, // west, XM + { 0, -1}, // north, ZM + { 0, 1}, // south, ZP + } ; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta)) + { + // Too close to the edge, cannot simulate + return true; + } + return g_BlockIsSolid[BlockType]; + } + } + return true; + } + + NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + NIBBLETYPE Meta = 0; + char RailsCnt = 0; + bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP + memset(Neighbors, false, sizeof(Neighbors)); + Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN)); + Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)); + Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN)); + Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)); + Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE)); + Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE)); + Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE)); + Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE)); + if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST)) + Neighbors[0] = true; + if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST)) + Neighbors[1] = true; + if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH)) + Neighbors[2] = true; + if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH)) + Neighbors[3] = true; + for (int i = 0; i < 8; i++) + { + if (Neighbors[i]) + { + RailsCnt++; + } + } + if (RailsCnt == 1) + { + if (Neighbors[7]) return E_RAIL_ASCEND_SOUTH; + else if (Neighbors[6]) return E_RAIL_ASCEND_NORTH; + else if (Neighbors[5]) return E_RAIL_ASCEND_WEST; + else if (Neighbors[4]) return E_RAIL_ASCEND_EAST; + else if (Neighbors[0] || Neighbors[1]) return E_RAIL_EAST_WEST; + else if (Neighbors[2] || Neighbors[3]) return E_RAIL_NORTH_SOUTH; + ASSERT(!"Weird neighbor count"); + return Meta; + } + for (int i = 0; i < 4; i++) + { + if (Neighbors[i + 4]) + { + Neighbors[i] = true; + } + } + if (RailsCnt > 1) + { + if (Neighbors[3] && Neighbors[0]) return E_RAIL_CURVED_SOUTH_EAST; + else if (Neighbors[3] && Neighbors[1]) return E_RAIL_CURVED_SOUTH_WEST; + else if (Neighbors[2] && Neighbors[0]) return E_RAIL_CURVED_NORTH_EAST; + else if (Neighbors[2] && Neighbors[1]) return E_RAIL_CURVED_NORTH_WEST; + else if (Neighbors[7] && Neighbors[2]) return E_RAIL_ASCEND_SOUTH; + else if (Neighbors[3] && Neighbors[6]) return E_RAIL_ASCEND_NORTH; + else if (Neighbors[5] && Neighbors[0]) return E_RAIL_ASCEND_WEST; + else if (Neighbors[4] && Neighbors[1]) return E_RAIL_ASCEND_EAST; + else if (Neighbors[0] && Neighbors[1]) return E_RAIL_EAST_WEST; + else if (Neighbors[2] && Neighbors[3]) return E_RAIL_NORTH_SOUTH; + ASSERT(!"Weird neighbor count"); + } + return Meta; + } + + + bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) + { + return false; + } + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + switch (Meta) + { + case E_RAIL_NORTH_SOUTH: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN) + ) + { + return true; + } + break; + } + + case E_RAIL_EAST_WEST: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN) + ) + { + return true; + } + break; + } + + case E_RAIL_ASCEND_EAST: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_RAIL_ASCEND_WEST: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) || + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_RAIL_ASCEND_NORTH: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) + ) + { + return true; + } + break; + } + + case E_RAIL_ASCEND_SOUTH: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH) + ) + { + return true; + } + break; + } + + case E_RAIL_CURVED_SOUTH_EAST: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) + ) + { + return true; + } + break; + } + + case E_RAIL_CURVED_SOUTH_WEST: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_RAIL_CURVED_NORTH_WEST: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST) + ) + { + return true; + } + break; + } + + case E_RAIL_CURVED_NORTH_EAST: + { + if ( + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) || + IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) + ) + { + return true; + } + break; + } + } + return false; + } + + + bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); + NIBBLETYPE Meta; + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL) + { + if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN)) + { + if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE)) + { + return true; + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ); + } + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ); + } + } + else + { + Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + } + + switch (a_BlockFace) + { + case BLOCK_FACE_NORTH: + { + if ( + (Meta == E_RAIL_NORTH_SOUTH) || + (Meta == E_RAIL_ASCEND_NORTH) || + (Meta == E_RAIL_ASCEND_SOUTH) || + (Meta == E_RAIL_CURVED_SOUTH_EAST) || + (Meta == E_RAIL_CURVED_SOUTH_WEST) + ) + { + return false; + } + break; + } + + case BLOCK_FACE_SOUTH: + { + if ( + (Meta == E_RAIL_NORTH_SOUTH) || + (Meta == E_RAIL_ASCEND_NORTH) || + (Meta == E_RAIL_ASCEND_SOUTH) || + (Meta == E_RAIL_CURVED_NORTH_EAST) || + (Meta == E_RAIL_CURVED_NORTH_WEST) + ) + { + return false; + } + break; + } + + case BLOCK_FACE_EAST: + { + if ( + (Meta == E_RAIL_EAST_WEST) || + (Meta == E_RAIL_ASCEND_EAST) || + (Meta == E_RAIL_ASCEND_WEST) || + (Meta == E_RAIL_CURVED_SOUTH_WEST) || + (Meta == E_RAIL_CURVED_NORTH_WEST) + ) + { + return false; + } + break; + } + case BLOCK_FACE_WEST: + { + if ( + (Meta == E_RAIL_EAST_WEST) || + (Meta == E_RAIL_ASCEND_EAST) || + (Meta == E_RAIL_ASCEND_WEST) || + (Meta == E_RAIL_CURVED_SOUTH_EAST) || + (Meta == E_RAIL_CURVED_NORTH_EAST) + ) + { + return false; + } + break; + } + } + return true; + } +} ; + + + + diff --git a/source/Blocks/BlockRedstone.cpp b/source/Blocks/BlockRedstone.cpp index f433be4ef..35cdc34cf 100644 --- a/source/Blocks/BlockRedstone.cpp +++ b/source/Blocks/BlockRedstone.cpp @@ -1,27 +1,27 @@ - -#include "Globals.h" -#include "BlockRedstone.h" -#include "../Item.h" -#include "../World.h" - - - - - -cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockRedstoneHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Nothing needed yet -} - - - - + +#include "Globals.h" +#include "BlockRedstone.h" +#include "../Item.h" +#include "../World.h" + + + + + +cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockRedstoneHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + diff --git a/source/Blocks/BlockRedstone.h b/source/Blocks/BlockRedstone.h index 3a4649d7e..ae0466937 100644 --- a/source/Blocks/BlockRedstone.h +++ b/source/Blocks/BlockRedstone.h @@ -1,46 +1,46 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockRedstoneHandler : - public cBlockHandler -{ -public: - cBlockRedstoneHandler(BLOCKTYPE a_BlockType); - - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]); - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1)); - } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockRedstoneHandler : + public cBlockHandler +{ +public: + cBlockRedstoneHandler(BLOCKTYPE a_BlockType); + + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]); + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, 1)); + } + + + virtual bool CanBePlacedOnSide(void) override + { + return false; + } +} ; + + + + diff --git a/source/Blocks/BlockRedstoneRepeater.cpp b/source/Blocks/BlockRedstoneRepeater.cpp index 15b736cbc..9857dd223 100644 --- a/source/Blocks/BlockRedstoneRepeater.cpp +++ b/source/Blocks/BlockRedstoneRepeater.cpp @@ -1,47 +1,47 @@ - -#include "Globals.h" -#include "BlockRedstoneRepeater.h" -#include "../Item.h" -#include "../World.h" -#include "../Player.h" -#include "../Simulator/RedstoneSimulator.h" - - - - - -cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) -{ -} - - - - - -void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // Nothing needed yet -} - - - - - -void cBlockRedstoneRepeaterHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) -{ - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f)); -} - - - - - -void cBlockRedstoneRepeaterHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8); -} - - - - + +#include "Globals.h" +#include "BlockRedstoneRepeater.h" +#include "../Item.h" +#include "../World.h" +#include "../Player.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) +{ +} + + + + + +void cBlockRedstoneRepeaterHandler::OnDestroyed(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Nothing needed yet +} + + + + + +void cBlockRedstoneRepeaterHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) +{ + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, ((a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) + 0x04) & 0x0f)); +} + + + + + +void cBlockRedstoneRepeaterHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + OnUse(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NONE, 8, 8, 8); +} + + + + diff --git a/source/Blocks/BlockRedstoneRepeater.h b/source/Blocks/BlockRedstoneRepeater.h index 4a16149fd..f3e250963 100644 --- a/source/Blocks/BlockRedstoneRepeater.h +++ b/source/Blocks/BlockRedstoneRepeater.h @@ -1,60 +1,60 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockRedstoneRepeaterHandler : - public cBlockHandler -{ -public: - cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType); - virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; - - virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Reset meta to 0 - a_Pickups.push_back(cItem(E_ITEM_REDSTONE_REPEATER, 1, 0)); - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); - } - - - virtual bool CanBePlacedOnSide(void) override - { - return false; - } - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockRedstoneRepeaterHandler : + public cBlockHandler +{ +public: + cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockType); + virtual void OnDestroyed(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override; + + virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_ITEM_REDSTONE_REPEATER, 1, 0)); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual bool CanBePlacedOnSide(void) override + { + return false; + } + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockRedstoneTorch.h b/source/Blocks/BlockRedstoneTorch.h index c8256ce64..cb897ba3f 100644 --- a/source/Blocks/BlockRedstoneTorch.h +++ b/source/Blocks/BlockRedstoneTorch.h @@ -1,36 +1,36 @@ - -#pragma once - -#include "BlockRedstone.h" -#include "BlockTorch.h" - - - - - -class cBlockRedstoneTorchHandler : - public cBlockTorchHandler -{ -public: - cBlockRedstoneTorchHandler(BLOCKTYPE a_BlockType) - : cBlockTorchHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Always drop the ON torch, meta 0 - a_Pickups.push_back(cItem(E_BLOCK_REDSTONE_TORCH_ON, 1, 0)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - + +#pragma once + +#include "BlockRedstone.h" +#include "BlockTorch.h" + + + + + +class cBlockRedstoneTorchHandler : + public cBlockTorchHandler +{ +public: + cBlockRedstoneTorchHandler(BLOCKTYPE a_BlockType) + : cBlockTorchHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Always drop the ON torch, meta 0 + a_Pickups.push_back(cItem(E_BLOCK_REDSTONE_TORCH_ON, 1, 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockSand.h b/source/Blocks/BlockSand.h index 878770d22..3fc271483 100644 --- a/source/Blocks/BlockSand.h +++ b/source/Blocks/BlockSand.h @@ -1,28 +1,28 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockSandHandler : - public cBlockHandler -{ -public: - cBlockSandHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual const char * GetStepSound(void) override - { - return "step.sand"; - } - -}; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSandHandler : + public cBlockHandler +{ +public: + cBlockSandHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual const char * GetStepSound(void) override + { + return "step.sand"; + } + +}; + + + + diff --git a/source/Blocks/BlockSapling.h b/source/Blocks/BlockSapling.h index 957331886..17ef4984f 100644 --- a/source/Blocks/BlockSapling.h +++ b/source/Blocks/BlockSapling.h @@ -1,69 +1,69 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockSaplingHandler : - public cBlockHandler -{ -public: - cBlockSaplingHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Only the first 2 bits contain the display information, the others are for growing - a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - if ((Meta & 0x08) != 0) - { - a_World->GrowTree(a_BlockX, a_BlockY, a_BlockZ); - } - else - { - a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08); - } - } - - - virtual bool CanBePlacedOnSide() override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockSaplingHandler : + public cBlockHandler +{ +public: + cBlockSaplingHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Only the first 2 bits contain the display information, the others are for growing + a_Pickups.push_back(cItem(E_BLOCK_SAPLING, 1, a_BlockMeta & 3)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if ((Meta & 0x08) != 0) + { + a_World->GrowTree(a_BlockX, a_BlockY, a_BlockZ); + } + else + { + a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x08); + } + } + + + virtual bool CanBePlacedOnSide() override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/source/Blocks/BlockSign.h b/source/Blocks/BlockSign.h index 4bdccb47e..a89ae0c49 100644 --- a/source/Blocks/BlockSign.h +++ b/source/Blocks/BlockSign.h @@ -1,73 +1,73 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" -#include "../Player.h" - - - - - -class cBlockSignHandler : - public cBlockHandler -{ -public: - cBlockSignHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0)); - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } - - - static char RotationToMetaData(double a_Rotation) - { - a_Rotation += 180 + (180 / 16); // So it's not aligned with axis - if (a_Rotation > 360) - { - a_Rotation -= 360; - } - - a_Rotation = (a_Rotation / 360) * 16; - - return ((char)a_Rotation) % 16; - } - - - static char DirectionToMetaData(char a_Direction) - { - switch (a_Direction) - { - case 0x2: return 0x2; - case 0x3: return 0x3; - case 0x4: return 0x4; - case 0x5: return 0x5; - default: - { - break; - } - } - return 0x2; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" +#include "../Player.h" + + + + + +class cBlockSignHandler : + public cBlockHandler +{ +public: + cBlockSignHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SIGN, 1, 0)); + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + static char RotationToMetaData(double a_Rotation) + { + a_Rotation += 180 + (180 / 16); // So it's not aligned with axis + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + + a_Rotation = (a_Rotation / 360) * 16; + + return ((char)a_Rotation) % 16; + } + + + static char DirectionToMetaData(char a_Direction) + { + switch (a_Direction) + { + case 0x2: return 0x2; + case 0x3: return 0x3; + case 0x4: return 0x4; + case 0x5: return 0x5; + default: + { + break; + } + } + return 0x2; + } +} ; + + + + diff --git a/source/Blocks/BlockSlab.h b/source/Blocks/BlockSlab.h index 2abcc6364..f34f42bae 100644 --- a/source/Blocks/BlockSlab.h +++ b/source/Blocks/BlockSlab.h @@ -1,70 +1,70 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockSlabHandler : - public cBlockHandler -{ -public: - cBlockSlabHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - char Count = ((m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? 2 : 1; - a_Pickups.push_back(cItem(m_BlockType, Count, a_BlockMeta)); - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07); - switch (a_BlockFace) - { - case BLOCK_FACE_TOP: a_BlockMeta = Meta & 0x7; break; // Always bottom half of the slab when placing on top of something - case BLOCK_FACE_BOTTOM: a_BlockMeta = Meta | 0x8; break; // Always top half of the slab when placing on bottom of something - case BLOCK_FACE_EAST: - case BLOCK_FACE_NORTH: - case BLOCK_FACE_SOUTH: - case BLOCK_FACE_WEST: - { - if (a_CursorY > 7) - { - // Cursor at the top half of the face, place a top half of slab - a_BlockMeta = Meta | 0x8; - } - else - { - // Cursor at the bottom half of the face, place a bottom half of slab: - a_BlockMeta = Meta & 0x7; - } - break; - } - } // switch (a_BlockFace) - return true; - } - - - virtual const char * GetStepSound(void) override - { - return ((m_BlockType == E_BLOCK_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSlabHandler : + public cBlockHandler +{ +public: + cBlockSlabHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + char Count = ((m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? 2 : 1; + a_Pickups.push_back(cItem(m_BlockType, Count, a_BlockMeta)); + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07); + switch (a_BlockFace) + { + case BLOCK_FACE_TOP: a_BlockMeta = Meta & 0x7; break; // Always bottom half of the slab when placing on top of something + case BLOCK_FACE_BOTTOM: a_BlockMeta = Meta | 0x8; break; // Always top half of the slab when placing on bottom of something + case BLOCK_FACE_EAST: + case BLOCK_FACE_NORTH: + case BLOCK_FACE_SOUTH: + case BLOCK_FACE_WEST: + { + if (a_CursorY > 7) + { + // Cursor at the top half of the face, place a top half of slab + a_BlockMeta = Meta | 0x8; + } + else + { + // Cursor at the bottom half of the face, place a bottom half of slab: + a_BlockMeta = Meta & 0x7; + } + break; + } + } // switch (a_BlockFace) + return true; + } + + + virtual const char * GetStepSound(void) override + { + return ((m_BlockType == E_BLOCK_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone"; + } +} ; + + + + diff --git a/source/Blocks/BlockSnow.h b/source/Blocks/BlockSnow.h index f150f497c..bdd9f0b87 100644 --- a/source/Blocks/BlockSnow.h +++ b/source/Blocks/BlockSnow.h @@ -1,52 +1,52 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockSnowHandler : - public cBlockHandler -{ -public: - cBlockSnowHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool DoesIgnoreBuildCollision(void) override - { - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_SNOWBALL, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return (a_RelY > 0) && g_BlockIsSnowable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]; - } - - - virtual bool DoesDropOnUnsuitable(void) override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.cloth"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSnowHandler : + public cBlockHandler +{ +public: + cBlockSnowHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SNOWBALL, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return (a_RelY > 0) && g_BlockIsSnowable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]; + } + + + virtual bool DoesDropOnUnsuitable(void) override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.cloth"; + } +} ; + + + + diff --git a/source/Blocks/BlockStairs.h b/source/Blocks/BlockStairs.h index 6fc316e45..485ebda1a 100644 --- a/source/Blocks/BlockStairs.h +++ b/source/Blocks/BlockStairs.h @@ -1,153 +1,153 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockStairsHandler : - public cBlockHandler -{ -public: - cBlockStairsHandler(BLOCKTYPE a_BlockType) : - cBlockHandler(a_BlockType) - { - - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = m_BlockType; - a_BlockMeta = RotationToMetaData(a_Player->GetRotation()); - switch (a_BlockFace) - { - case BLOCK_FACE_TOP: break; - case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block - case BLOCK_FACE_EAST: - case BLOCK_FACE_NORTH: - case BLOCK_FACE_SOUTH: - case BLOCK_FACE_WEST: - { - // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block - if (a_CursorY > 8) - { - a_BlockMeta |= 0x4; - } - break; - } - } - return true; - } - - // TODO: step sound - - - static NIBBLETYPE RotationToMetaData(double a_Rotation) - { - a_Rotation += 90 + 45; // So its not aligned with axis - NIBBLETYPE result = 0x0; - if (a_Rotation > 360) - { - a_Rotation -= 360; - } - if ((a_Rotation >= 0) && (a_Rotation < 90)) - { - return 0x0; - } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) - { - return 0x1; - } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) - { - return 0x2; - } - else - { - return 0x3; - } - } - - - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x03; // East -> North - case 0x01: return TopBits | 0x02; // West -> South - case 0x02: return TopBits | 0x00; // South -> East - case 0x03: return TopBits | 0x01; // North -> West - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x02; // East -> South - case 0x01: return TopBits | 0x03; // West -> North - case 0x02: return TopBits | 0x01; // South -> West - case 0x03: return TopBits | 0x00; // North -> East - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x00; // East -> East - case 0x01: return TopBits | 0x01; // West -> West - case 0x02: return TopBits | 0x03; // South -> North - case 0x03: return TopBits | 0x02; // North -> South - } - // Not reachable, but to avoid a compiler warning: - return 0; - } - - - virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override - { - // Toggle bit 3: - return (a_Meta & 0x0b) | ((~a_Meta) & 0x04); - } - - - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override - { - // Bits 3 and 4 stay, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x0c); - switch (a_Meta & 0x03) - { - case 0x00: return TopBits | 0x01; // East -> West - case 0x01: return TopBits | 0x00; // West -> East - case 0x02: return TopBits | 0x02; // South -> South - case 0x03: return TopBits | 0x03; // North -> North - } - // Not reachable, but to avoid a compiler warning: - return 0; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockStairsHandler : + public cBlockHandler +{ +public: + cBlockStairsHandler(BLOCKTYPE a_BlockType) : + cBlockHandler(a_BlockType) + { + + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = m_BlockType; + a_BlockMeta = RotationToMetaData(a_Player->GetRotation()); + switch (a_BlockFace) + { + case BLOCK_FACE_TOP: break; + case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block + case BLOCK_FACE_EAST: + case BLOCK_FACE_NORTH: + case BLOCK_FACE_SOUTH: + case BLOCK_FACE_WEST: + { + // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block + if (a_CursorY > 8) + { + a_BlockMeta |= 0x4; + } + break; + } + } + return true; + } + + // TODO: step sound + + + static NIBBLETYPE RotationToMetaData(double a_Rotation) + { + a_Rotation += 90 + 45; // So its not aligned with axis + NIBBLETYPE result = 0x0; + if (a_Rotation > 360) + { + a_Rotation -= 360; + } + if ((a_Rotation >= 0) && (a_Rotation < 90)) + { + return 0x0; + } + else if ((a_Rotation >= 180) && (a_Rotation < 270)) + { + return 0x1; + } + else if ((a_Rotation >= 90) && (a_Rotation < 180)) + { + return 0x2; + } + else + { + return 0x3; + } + } + + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x03; // East -> North + case 0x01: return TopBits | 0x02; // West -> South + case 0x02: return TopBits | 0x00; // South -> East + case 0x03: return TopBits | 0x01; // North -> West + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x02; // East -> South + case 0x01: return TopBits | 0x03; // West -> North + case 0x02: return TopBits | 0x01; // South -> West + case 0x03: return TopBits | 0x00; // North -> East + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x00; // East -> East + case 0x01: return TopBits | 0x01; // West -> West + case 0x02: return TopBits | 0x03; // South -> North + case 0x03: return TopBits | 0x02; // North -> South + } + // Not reachable, but to avoid a compiler warning: + return 0; + } + + + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override + { + // Toggle bit 3: + return (a_Meta & 0x0b) | ((~a_Meta) & 0x04); + } + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bits 3 and 4 stay, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x0c); + switch (a_Meta & 0x03) + { + case 0x00: return TopBits | 0x01; // East -> West + case 0x01: return TopBits | 0x00; // West -> East + case 0x02: return TopBits | 0x02; // South -> South + case 0x03: return TopBits | 0x03; // North -> North + } + // Not reachable, but to avoid a compiler warning: + return 0; + } +} ; + + + + diff --git a/source/Blocks/BlockStems.h b/source/Blocks/BlockStems.h index 44c4d9cc9..ce02d9cb8 100644 --- a/source/Blocks/BlockStems.h +++ b/source/Blocks/BlockStems.h @@ -1,58 +1,58 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -class cBlockStemsHandler : - public cBlockHandler -{ -public: - cBlockStemsHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - int ItemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS; - a_Pickups.push_back(cItem(ItemType, 1, 0)); - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta >= 7) - { - // Grow the produce: - a_World->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, m_BlockType); - } - else - { - // Grow the stem: - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta + 1); - } - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockStemsHandler : + public cBlockHandler +{ +public: + cBlockStemsHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + int ItemType = (m_BlockType == E_BLOCK_MELON_STEM) ? E_ITEM_MELON_SEEDS : E_ITEM_PUMPKIN_SEEDS; + a_Pickups.push_back(cItem(ItemType, 1, 0)); + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (Meta >= 7) + { + // Grow the produce: + a_World->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, m_BlockType); + } + else + { + // Grow the stem: + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta + 1); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockStone.h b/source/Blocks/BlockStone.h index 89bef5969..af4c6509a 100644 --- a/source/Blocks/BlockStone.h +++ b/source/Blocks/BlockStone.h @@ -1,29 +1,29 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" - - - - - -class cBlockStoneHandler : - public cBlockHandler -{ -public: - cBlockStoneHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_BLOCK_COBBLESTONE, 1, 0)); - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../MersenneTwister.h" +#include "../World.h" + + + + + +class cBlockStoneHandler : + public cBlockHandler +{ +public: + cBlockStoneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_BLOCK_COBBLESTONE, 1, 0)); + } +} ; + + + + diff --git a/source/Blocks/BlockSugarcane.h b/source/Blocks/BlockSugarcane.h index a9503ba37..9d66d6be6 100644 --- a/source/Blocks/BlockSugarcane.h +++ b/source/Blocks/BlockSugarcane.h @@ -1,96 +1,96 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockSugarcaneHandler : - public cBlockHandler -{ -public: - cBlockSugarcaneHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - a_Pickups.push_back(cItem(E_ITEM_SUGARCANE, 1, 0)); - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - if (a_RelY <= 0) - { - return false; - } - switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_FARMLAND: - case E_BLOCK_SAND: - { - static const struct - { - int x, z; - } Coords[] = - { - {-1, 0}, - { 1, 0}, - { 0, -1}, - { 0, 1}, - } ; - a_RelY -= 1; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) - { - // Too close to the edge, cannot simulate - return true; - } - if (IsBlockWater(BlockType)) - { - return true; - } - } // for i - Coords[] - // Not directly neighboring a water block - return false; - } - case E_BLOCK_SUGARCANE: - { - return true; - } - } - return false; - } - - - void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override - { - a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1); - } - - - virtual bool CanBePlacedOnSide() override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSugarcaneHandler : + public cBlockHandler +{ +public: + cBlockSugarcaneHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_SUGARCANE, 1, 0)); + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + if (a_RelY <= 0) + { + return false; + } + switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + case E_BLOCK_SAND: + { + static const struct + { + int x, z; + } Coords[] = + { + {-1, 0}, + { 1, 0}, + { 0, -1}, + { 0, 1}, + } ; + a_RelY -= 1; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) + { + // Too close to the edge, cannot simulate + return true; + } + if (IsBlockWater(BlockType)) + { + return true; + } + } // for i - Coords[] + // Not directly neighboring a water block + return false; + } + case E_BLOCK_SUGARCANE: + { + return true; + } + } + return false; + } + + + void OnUpdate(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override + { + a_World->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, 1); + } + + + virtual bool CanBePlacedOnSide() override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/source/Blocks/BlockTallGrass.h b/source/Blocks/BlockTallGrass.h index eea629bf5..cd27ab7e6 100644 --- a/source/Blocks/BlockTallGrass.h +++ b/source/Blocks/BlockTallGrass.h @@ -1,51 +1,51 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockTallGrassHandler : - public cBlockHandler -{ -public: - cBlockTallGrassHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool DoesIgnoreBuildCollision(void) override - { - return true; - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Drop seeds, sometimes - MTRand r1; - if (r1.randInt(10) == 5) - { - a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); - } - } - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockTallGrassHandler : + public cBlockHandler +{ +public: + cBlockTallGrassHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Drop seeds, sometimes + MTRand r1; + if (r1.randInt(10) == 5) + { + a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); + } + } + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } +} ; + + + + diff --git a/source/Blocks/BlockTorch.h b/source/Blocks/BlockTorch.h index 9951b8ea4..00e0a585c 100644 --- a/source/Blocks/BlockTorch.h +++ b/source/Blocks/BlockTorch.h @@ -1,259 +1,259 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../World.h" - - - - - -class cBlockTorchHandler : - public cBlockHandler -{ -public: - cBlockTorchHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - // Find proper placement. Use the player-supplied one as the default, but fix if not okay: - if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) - { - a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); - - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - return false; - } - } - a_BlockType = m_BlockType; - a_BlockMeta = DirectionToMetaData(a_BlockFace); - return true; - } - - - static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export - { // tolua_export - switch (a_Direction) - { - case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0; - case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR; - case BLOCK_FACE_EAST: return E_META_TORCH_EAST; - case BLOCK_FACE_WEST: return E_META_TORCH_WEST; - case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH; - case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH; - default: - { - ASSERT(!"Unhandled torch direction!"); - break; - } - }; - return 0x0; - } // tolua_export - - - static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export - { // tolua_export - switch (a_MetaData) - { - case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground - case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP; - case E_META_TORCH_EAST: return BLOCK_FACE_EAST; - case E_META_TORCH_WEST: return BLOCK_FACE_WEST; - case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH; - case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH; - default: - { - ASSERT(!"Unhandled torch metadata"); - break; - } - } - return 0; - } // tolua_export - - - static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos) - { - switch (a_TorchMeta) - { - case 0x0: - case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0))); - case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1))); - case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1))); - case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))); - case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0))); - default: - { - ASSERT(!"Unhandled torch meta!"); - break; - } - } - return false; - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_Direction) - { - switch (a_BlockType) - { - case E_BLOCK_GLASS: - case E_BLOCK_FENCE: - case E_BLOCK_NETHER_BRICK_FENCE: - { - return (a_Direction == 0x1); // allow only direction "standing on floor" - } - - default: - { - return g_BlockIsSolid[a_BlockType]; - } - } - } - - - static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) - { - // TODO: If placing a torch from below, check all 4 XZ neighbors, place it on that neighbor instead - // How to propagate that change up? - // Simon: The easiest way is to calculate the position two times, shouldn´t cost much cpu power :) - - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - return false; - } - - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); - - return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace); - } - - - /// Finds a suitable Face for the Torch. Returns BLOCK_FACE_BOTTOM on failure - static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) - { - for (int i = 1; i <= 5; i++) - { - if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, i)) - { - return i; - } - } - return BLOCK_FACE_BOTTOM; - } - - - /* - virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override - { - if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) - { - return true; - } - - return (FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ) != BLOCK_FACE_BOTTOM); - } - */ - - - virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override - { - // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison - char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - return TorchCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, Face); - } - - - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override - { - // Always drop meta = 0 - a_Pickups.push_back(cItem(m_BlockType, 1, 0)); - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } - - - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x04; // East -> North - case 0x02: return TopBits | 0x03; // West -> South - case 0x03: return TopBits | 0x01; // South -> East - case 0x04: return TopBits | 0x02; // North -> West - default: return a_Meta; // Floor -> Floor - } - } - - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x03; // East -> South - case 0x02: return TopBits | 0x04; // West -> North - case 0x03: return TopBits | 0x02; // South -> West - case 0x04: return TopBits | 0x01; // North -> East - default: return a_Meta; // Floor -> Floor - } - } - - - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x03: return TopBits | 0x04; // South -> North - case 0x04: return TopBits | 0x03; // North -> South - default: return a_Meta; // Keep the rest - } - } - - - // Mirroring around the XZ plane doesn't make sense for floor torches, - // the others stay the same, so let's keep all the metas the same. - // The base class does tht for us, no need to override MetaMirrorXZ() - - - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override - { - // Bit 4 stays, the rest is swapped around according to a table: - NIBBLETYPE TopBits = (a_Meta & 0x08); - switch (a_Meta & 0x07) - { - case 0x01: return TopBits | 0x02; // East -> West - case 0x02: return TopBits | 0x01; // West -> East - default: return a_Meta; // Keep the rest - } - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../World.h" + + + + + +class cBlockTorchHandler : + public cBlockHandler +{ +public: + cBlockTorchHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // Find proper placement. Use the player-supplied one as the default, but fix if not okay: + if (!TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + a_BlockFace = FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ); + + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + return false; + } + } + a_BlockType = m_BlockType; + a_BlockMeta = DirectionToMetaData(a_BlockFace); + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_Direction) // tolua_export + { // tolua_export + switch (a_Direction) + { + case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0; + case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR; + case BLOCK_FACE_EAST: return E_META_TORCH_EAST; + case BLOCK_FACE_WEST: return E_META_TORCH_WEST; + case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH; + case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH; + default: + { + ASSERT(!"Unhandled torch direction!"); + break; + } + }; + return 0x0; + } // tolua_export + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) // tolua_export + { // tolua_export + switch (a_MetaData) + { + case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground + case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP; + case E_META_TORCH_EAST: return BLOCK_FACE_EAST; + case E_META_TORCH_WEST: return BLOCK_FACE_WEST; + case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH; + case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH; + default: + { + ASSERT(!"Unhandled torch metadata"); + break; + } + } + return 0; + } // tolua_export + + + static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos) + { + switch (a_TorchMeta) + { + case 0x0: + case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0))); + case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1))); + case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1))); + case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))); + case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0))); + default: + { + ASSERT(!"Unhandled torch meta!"); + break; + } + } + return false; + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_Direction) + { + switch (a_BlockType) + { + case E_BLOCK_GLASS: + case E_BLOCK_FENCE: + case E_BLOCK_NETHER_BRICK_FENCE: + { + return (a_Direction == 0x1); // allow only direction "standing on floor" + } + + default: + { + return g_BlockIsSolid[a_BlockType]; + } + } + } + + + static bool TorchCanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + // TODO: If placing a torch from below, check all 4 XZ neighbors, place it on that neighbor instead + // How to propagate that change up? + // Simon: The easiest way is to calculate the position two times, shouldn´t cost much cpu power :) + + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + return false; + } + + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); + + return CanBePlacedOn(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ), a_BlockFace); + } + + + /// Finds a suitable Face for the Torch. Returns BLOCK_FACE_BOTTOM on failure + static char FindSuitableFace(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + for (int i = 1; i <= 5; i++) + { + if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, i)) + { + return i; + } + } + return BLOCK_FACE_BOTTOM; + } + + + /* + virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + if (TorchCanBePlacedAt(a_World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + return true; + } + + return (FindSuitableFace(a_World, a_BlockX, a_BlockY, a_BlockZ) != BLOCK_FACE_BOTTOM); + } + */ + + + virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + // TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison + char Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + return TorchCanBePlacedAt(a_Chunk.GetWorld(), BlockX, a_RelY, BlockZ, Face); + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Always drop meta = 0 + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } + + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x04; // East -> North + case 0x02: return TopBits | 0x03; // West -> South + case 0x03: return TopBits | 0x01; // South -> East + case 0x04: return TopBits | 0x02; // North -> West + default: return a_Meta; // Floor -> Floor + } + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x03; // East -> South + case 0x02: return TopBits | 0x04; // West -> North + case 0x03: return TopBits | 0x02; // South -> West + case 0x04: return TopBits | 0x01; // North -> East + default: return a_Meta; // Floor -> Floor + } + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x03: return TopBits | 0x04; // South -> North + case 0x04: return TopBits | 0x03; // North -> South + default: return a_Meta; // Keep the rest + } + } + + + // Mirroring around the XZ plane doesn't make sense for floor torches, + // the others stay the same, so let's keep all the metas the same. + // The base class does tht for us, no need to override MetaMirrorXZ() + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bit 4 stays, the rest is swapped around according to a table: + NIBBLETYPE TopBits = (a_Meta & 0x08); + switch (a_Meta & 0x07) + { + case 0x01: return TopBits | 0x02; // East -> West + case 0x02: return TopBits | 0x01; // West -> East + default: return a_Meta; // Keep the rest + } + } +} ; + + + + diff --git a/source/Blocks/BlockVine.h b/source/Blocks/BlockVine.h index 2a88c9b68..0bc935272 100644 --- a/source/Blocks/BlockVine.h +++ b/source/Blocks/BlockVine.h @@ -1,200 +1,200 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockVineHandler : - public cBlockHandler -{ -public: - cBlockVineHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - // TODO: Disallow placement where the vine doesn't attach to something properly - BLOCKTYPE BlockType = 0; - NIBBLETYPE BlockMeta; - a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - if (BlockType == m_BlockType) - { - a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace); - } - else - { - a_BlockMeta = DirectionToMetaData(a_BlockFace); - } - a_BlockType = m_BlockType; - return true; - } - - - static NIBBLETYPE DirectionToMetaData(char a_BlockFace) - { - switch (a_BlockFace) - { - case BLOCK_FACE_NORTH: return 0x1; - case BLOCK_FACE_SOUTH: return 0x4; - case BLOCK_FACE_WEST: return 0x8; - case BLOCK_FACE_EAST: return 0x2; - default: return 0x0; - } - } - - - static char MetaDataToDirection(NIBBLETYPE a_MetaData) - { - switch(a_MetaData) - { - case 0x1: return BLOCK_FACE_NORTH; - case 0x4: return BLOCK_FACE_SOUTH; - case 0x8: return BLOCK_FACE_WEST; - case 0x2: return BLOCK_FACE_EAST; - default: return BLOCK_FACE_TOP; - } - } - - - /// Returns true if the specified block type is good for vines to attach to - static bool IsBlockAttachable(BLOCKTYPE a_BlockType) - { - return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType]; - } - - - /// Returns the meta that has the maximum allowable sides of the vine, given the surroundings - NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) - { - static const struct - { - int x, z; - int Bit; - } Coords[] = - { - { 0, 1, 1}, // south, ZP - {-1, 0, 2}, // west, XM - { 0, -1, 4}, // north, ZM - { 1, 0, 8}, // east, XP - } ; - int res = 0; - for (int i = 0; i < ARRAYCOUNT(Coords); i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if ( - a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && - IsBlockAttachable(BlockType) - ) - { - res |= Coords[i].Bit; - } - } - return res; - } - - - void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override - { - NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); - NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ); - - // Check if vine above us, add its meta to MaxMeta - if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType)) - { - MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ); - } - - NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal - if (Common != CurMeta) - { - // There is a neighbor missing, need to update the meta or even destroy the block - bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ)); - if ((Common == 0) && !HasTop) - { - // The vine just lost all its support, destroy the block: - if (DoesDropOnUnsuitable()) - { - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); - } - a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); - return; - } - a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common); - } - else - { - // Wake up the simulators for this block: - int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; - int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; - a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); - } - } - - - virtual bool DoesIgnoreBuildCollision(void) override - { - return true; - } - - - virtual bool DoesAllowBlockOnTop(void) override - { - return false; - } - - - virtual const char * GetStepSound(void) override - { - return "step.grass"; - } - - - virtual bool DoesDropOnUnsuitable(void) override - { - return false; - } - - - virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override - { - return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right - } - - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override - { - return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left - } - - - virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override - { - // Bits 2 and 4 stay, bits 1 and 3 swap - return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2)); - } - - - virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override - { - // Bits 1 and 3 stay, bits 2 and 4 swap - return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2)); - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockVineHandler : + public cBlockHandler +{ +public: + cBlockVineHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + // TODO: Disallow placement where the vine doesn't attach to something properly + BLOCKTYPE BlockType = 0; + NIBBLETYPE BlockMeta; + a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + if (BlockType == m_BlockType) + { + a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace); + } + else + { + a_BlockMeta = DirectionToMetaData(a_BlockFace); + } + a_BlockType = m_BlockType; + return true; + } + + + static NIBBLETYPE DirectionToMetaData(char a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_NORTH: return 0x1; + case BLOCK_FACE_SOUTH: return 0x4; + case BLOCK_FACE_WEST: return 0x8; + case BLOCK_FACE_EAST: return 0x2; + default: return 0x0; + } + } + + + static char MetaDataToDirection(NIBBLETYPE a_MetaData) + { + switch(a_MetaData) + { + case 0x1: return BLOCK_FACE_NORTH; + case 0x4: return BLOCK_FACE_SOUTH; + case 0x8: return BLOCK_FACE_WEST; + case 0x2: return BLOCK_FACE_EAST; + default: return BLOCK_FACE_TOP; + } + } + + + /// Returns true if the specified block type is good for vines to attach to + static bool IsBlockAttachable(BLOCKTYPE a_BlockType) + { + return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType]; + } + + + /// Returns the meta that has the maximum allowable sides of the vine, given the surroundings + NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) + { + static const struct + { + int x, z; + int Bit; + } Coords[] = + { + { 0, 1, 1}, // south, ZP + {-1, 0, 2}, // west, XM + { 0, -1, 4}, // north, ZM + { 1, 0, 8}, // east, XP + } ; + int res = 0; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if ( + a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && + IsBlockAttachable(BlockType) + ) + { + res |= Coords[i].Bit; + } + } + return res; + } + + + void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override + { + NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ); + + // Check if vine above us, add its meta to MaxMeta + if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType)) + { + MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ); + } + + NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal + if (Common != CurMeta) + { + // There is a neighbor missing, need to update the meta or even destroy the block + bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ)); + if ((Common == 0) && !HasTop) + { + // The vine just lost all its support, destroy the block: + if (DoesDropOnUnsuitable()) + { + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ); + } + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0); + return; + } + a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common); + } + else + { + // Wake up the simulators for this block: + int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; + int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; + a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk); + } + } + + + virtual bool DoesIgnoreBuildCollision(void) override + { + return true; + } + + + virtual bool DoesAllowBlockOnTop(void) override + { + return false; + } + + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } + + + virtual bool DoesDropOnUnsuitable(void) override + { + return false; + } + + + virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override + { + return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right + } + + + virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override + { + return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left + } + + + virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override + { + // Bits 2 and 4 stay, bits 1 and 3 swap + return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2)); + } + + + virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override + { + // Bits 1 and 3 stay, bits 2 and 4 swap + return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2)); + } +} ; + + + + diff --git a/source/Blocks/BlockWood.h b/source/Blocks/BlockWood.h index 907fb587c..4e2246506 100644 --- a/source/Blocks/BlockWood.h +++ b/source/Blocks/BlockWood.h @@ -1,27 +1,27 @@ - -#pragma once - -#include "BlockHandler.h" - - - - - -class cBlockWoodHandler : public cBlockHandler -{ -public: - cBlockWoodHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockWoodHandler : public cBlockHandler +{ +public: + cBlockWoodHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/Blocks/BlockWorkbench.h b/source/Blocks/BlockWorkbench.h index ccd1ded9a..60aa1791b 100644 --- a/source/Blocks/BlockWorkbench.h +++ b/source/Blocks/BlockWorkbench.h @@ -1,43 +1,43 @@ - -#pragma once - -#include "BlockHandler.h" -#include "../UI/Window.h" -#include "../Player.h" - - - - - -class cBlockWorkbenchHandler: - public cBlockHandler -{ -public: - cBlockWorkbenchHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) - { - } - - - virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override - { - cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ); - a_Player->OpenWindow(Window); - } - - - virtual bool IsUseable(void) override - { - return true; - } - - - virtual const char * GetStepSound(void) override - { - return "step.wood"; - } -} ; - - - - + +#pragma once + +#include "BlockHandler.h" +#include "../UI/Window.h" +#include "../Player.h" + + + + + +class cBlockWorkbenchHandler: + public cBlockHandler +{ +public: + cBlockWorkbenchHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override + { + cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ); + a_Player->OpenWindow(Window); + } + + + virtual bool IsUseable(void) override + { + return true; + } + + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +} ; + + + + diff --git a/source/ByteBuffer.cpp b/source/ByteBuffer.cpp index 6d630068a..c82951271 100644 --- a/source/ByteBuffer.cpp +++ b/source/ByteBuffer.cpp @@ -1,661 +1,661 @@ - -// ByteBuffer.cpp - -// Implements the cByteBuffer class representing a ringbuffer of bytes - -#include "Globals.h" - -#include "ByteBuffer.h" -#include "Endianness.h" -#include "OSSupport/IsThread.h" - - - - - -#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false; -#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false; - - - - - -#ifdef _DEBUG - -/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously -class cSingleThreadAccessChecker -{ -public: - cSingleThreadAccessChecker(unsigned long * a_ThreadID) : - m_ThreadID(a_ThreadID) - { - ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID())); - } - - ~cSingleThreadAccessChecker() - { - *m_ThreadID = 0; - } - -protected: - unsigned long * m_ThreadID; -} ; - -#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast(&m_ThreadID)) - -#else - #define CHECK_THREAD -#endif - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cByteBuffer: - -cByteBuffer::cByteBuffer(int a_BufferSize) : - m_Buffer(new char[a_BufferSize + 1]), - m_BufferSize(a_BufferSize + 1), - #ifdef _DEBUG - m_ThreadID(0), - #endif // _DEBUG - m_DataStart(0), - m_WritePos(0), - m_ReadPos(0) -{ - // Allocating one byte more than the buffer size requested, so that we can distinguish between - // completely-full and completely-empty states -} - - - - - -cByteBuffer::~cByteBuffer() -{ - CheckValid(); - delete[] m_Buffer; -} - - - - - -bool cByteBuffer::Write(const char * a_Bytes, int a_Count) -{ - CHECK_THREAD; - CheckValid(); - - // Store the current free space for a check after writing: - int CurFreeSpace = GetFreeSpace(); - int CurReadableSpace = GetReadableSpace(); - int WrittenBytes = 0; - - if (GetFreeSpace() < a_Count) - { - return false; - } - int TillEnd = m_BufferSize - m_WritePos; - if (TillEnd <= a_Count) - { - // Need to wrap around the ringbuffer end - if (TillEnd > 0) - { - memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd); - a_Bytes += TillEnd; - a_Count -= TillEnd; - WrittenBytes = TillEnd; - } - m_WritePos = 0; - } - - // We're guaranteed that we'll fit in a single write op - if (a_Count > 0) - { - memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count); - m_WritePos += a_Count; - WrittenBytes += a_Count; - } - - ASSERT(GetFreeSpace() == CurFreeSpace - WrittenBytes); - ASSERT(GetReadableSpace() == CurReadableSpace + WrittenBytes); - return true; -} - - - - - -int cByteBuffer::GetFreeSpace(void) const -{ - CHECK_THREAD; - CheckValid(); - if (m_WritePos >= m_DataStart) - { - // Wrap around the buffer end: - return m_BufferSize - m_WritePos + m_DataStart - 1; - } - // Single free space partition: - return m_DataStart - m_WritePos - 1; -} - - - - - -/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() -int cByteBuffer::GetUsedSpace(void) const -{ - CHECK_THREAD; - CheckValid(); - return m_BufferSize - GetFreeSpace(); -} - - - - - -/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) -int cByteBuffer::GetReadableSpace(void) const -{ - CHECK_THREAD; - CheckValid(); - if (m_ReadPos > m_WritePos) - { - // Wrap around the buffer end: - return m_BufferSize - m_ReadPos + m_WritePos; - } - // Single readable space partition: - return m_WritePos - m_ReadPos ; -} - - - - - -bool cByteBuffer::CanReadBytes(int a_Count) const -{ - CHECK_THREAD; - CheckValid(); - return (a_Count <= GetReadableSpace()); -} - - - - - -bool cByteBuffer::CanWriteBytes(int a_Count) const -{ - CHECK_THREAD; - CheckValid(); - return (a_Count <= GetFreeSpace()); -} - - - - - -bool cByteBuffer::ReadChar(char & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(1); - ReadBuf(&a_Value, 1); - return true; -} - - - - - -bool cByteBuffer::ReadByte(unsigned char & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(1); - ReadBuf(&a_Value, 1); - return true; -} - - - - - -bool cByteBuffer::ReadBEShort(short & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(2); - ReadBuf(&a_Value, 2); - a_Value = ntohs(a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBEInt(int & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(4); - ReadBuf(&a_Value, 4); - a_Value = ntohl(a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBEInt64(Int64 & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(8); - ReadBuf(&a_Value, 8); - a_Value = NetworkToHostLong8(&a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBEFloat(float & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(4); - ReadBuf(&a_Value, 4); - a_Value = NetworkToHostFloat4(&a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBEDouble(double & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(8); - ReadBuf(&a_Value, 8); - a_Value = NetworkToHostDouble8(&a_Value); - return true; -} - - - - - -bool cByteBuffer::ReadBool(bool & a_Value) -{ - CHECK_THREAD; - CheckValid(); - NEEDBYTES(1); - char Value = 0; - ReadBuf(&Value, 1); - a_Value = (Value != 0); - return true; -} - - - - - -bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) -{ - CHECK_THREAD; - CheckValid(); - short Length; - if (!ReadBEShort(Length)) - { - return false; - } - if (Length < 0) - { - ASSERT(!"Negative string length? Are you sure?"); - return true; - } - return ReadUTF16String(a_Value, Length); -} - - - - - -bool cByteBuffer::WriteChar(char a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(1); - return WriteBuf(&a_Value, 1); -} - - - - - -bool cByteBuffer::WriteByte(unsigned char a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(1); - return WriteBuf(&a_Value, 1); -} - - - - - -bool cByteBuffer::WriteBEShort(short a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(2); - short Converted = htons(a_Value); - return WriteBuf(&Converted, 2); -} - - - - - -bool cByteBuffer::WriteBEInt(int a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(4); - int Converted = HostToNetwork4(&a_Value); - return WriteBuf(&Converted, 4); -} - - - - - -bool cByteBuffer::WriteBEInt64(Int64 a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(8); - Int64 Converted = HostToNetwork8(&a_Value); - return WriteBuf(&Converted, 8); -} - - - - - -bool cByteBuffer::WriteBEFloat(float a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(4); - int Converted = HostToNetwork4(&a_Value); - return WriteBuf(&Converted, 4); -} - - - - - -bool cByteBuffer::WriteBEDouble(double a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(8); - Int64 Converted = HostToNetwork8(&a_Value); - return WriteBuf(&Converted, 8); -} - - - - - - -bool cByteBuffer::WriteBool(bool a_Value) -{ - CHECK_THREAD; - CheckValid(); - return WriteChar(a_Value ? 1 : 0); -} - - - - - -bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value) -{ - CHECK_THREAD; - CheckValid(); - PUTBYTES(2); - AString UTF16BE; - UTF8ToRawBEUTF16(a_Value.data(), a_Value.size(), UTF16BE); - WriteBEShort((short)(UTF16BE.size() / 2)); - PUTBYTES(UTF16BE.size()); - WriteBuf(UTF16BE.data(), UTF16BE.size()); - return true; -} - - - - - -bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count) -{ - CHECK_THREAD; - CheckValid(); - ASSERT(a_Count >= 0); - NEEDBYTES(a_Count); - char * Dst = (char *)a_Buffer; // So that we can do byte math - int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; - ASSERT(BytesToEndOfBuffer >= 0); // Sanity check - if (BytesToEndOfBuffer <= a_Count) - { - // Reading across the ringbuffer end, read the first part and adjust parameters: - if (BytesToEndOfBuffer > 0) - { - memcpy(Dst, m_Buffer + m_ReadPos, BytesToEndOfBuffer); - Dst += BytesToEndOfBuffer; - a_Count -= BytesToEndOfBuffer; - } - m_ReadPos = 0; - } - - // Read the rest of the bytes in a single read (guaranteed to fit): - if (a_Count > 0) - { - memcpy(Dst, m_Buffer + m_ReadPos, a_Count); - m_ReadPos += a_Count; - } - return true; -} - - - - - -bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count) -{ - CHECK_THREAD; - CheckValid(); - ASSERT(a_Count >= 0); - PUTBYTES(a_Count); - char * Src = (char *)a_Buffer; // So that we can do byte math - int BytesToEndOfBuffer = m_BufferSize - m_WritePos; - if (BytesToEndOfBuffer <= a_Count) - { - // Reading across the ringbuffer end, read the first part and adjust parameters: - memcpy(m_Buffer + m_WritePos, Src, BytesToEndOfBuffer); - Src += BytesToEndOfBuffer; - a_Count -= BytesToEndOfBuffer; - m_WritePos = 0; - } - - // Read the rest of the bytes in a single read (guaranteed to fit): - if (a_Count > 0) - { - memcpy(m_Buffer + m_WritePos, Src, a_Count); - m_WritePos += a_Count; - } - return true; -} - - - - - -bool cByteBuffer::ReadString(AString & a_String, int a_Count) -{ - CHECK_THREAD; - CheckValid(); - ASSERT(a_Count >= 0); - NEEDBYTES(a_Count); - a_String.clear(); - a_String.reserve(a_Count); - int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; - ASSERT(BytesToEndOfBuffer >= 0); // Sanity check - if (BytesToEndOfBuffer <= a_Count) - { - // Reading across the ringbuffer end, read the first part and adjust parameters: - if (BytesToEndOfBuffer > 0) - { - a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer); - a_Count -= BytesToEndOfBuffer; - } - m_ReadPos = 0; - } - - // Read the rest of the bytes in a single read (guaranteed to fit): - if (a_Count > 0) - { - a_String.append(m_Buffer + m_ReadPos, a_Count); - m_ReadPos += a_Count; - } - return true; -} - - - - - -bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars) -{ - // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String - CHECK_THREAD; - CheckValid(); - ASSERT(a_NumChars >= 0); - AString RawData; - if (!ReadString(RawData, a_NumChars * 2)) - { - return false; - } - RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String); - return true; -} - - - - - -bool cByteBuffer::SkipRead(int a_Count) -{ - CHECK_THREAD; - CheckValid(); - ASSERT(a_Count >= 0); - if (!CanReadBytes(a_Count)) - { - return false; - } - AdvanceReadPos(a_Count); - return true; -} - - - - - -void cByteBuffer::ReadAll(AString & a_Data) -{ - CHECK_THREAD; - CheckValid(); - ReadString(a_Data, GetReadableSpace()); -} - - - - - -void cByteBuffer::CommitRead(void) -{ - CHECK_THREAD; - CheckValid(); - m_DataStart = m_ReadPos; -} - - - - - -void cByteBuffer::ResetRead(void) -{ - CHECK_THREAD; - CheckValid(); - m_ReadPos = m_DataStart; -} - - - - - -void cByteBuffer::ReadAgain(AString & a_Out) -{ - // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed) - // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party - CHECK_THREAD; - CheckValid(); - int DataStart = m_DataStart; - if (m_ReadPos < m_DataStart) - { - // Across the ringbuffer end, read the first part and adjust next part's start: - a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart); - DataStart = 0; - } - a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart); -} - - - - - -void cByteBuffer::AdvanceReadPos(int a_Count) -{ - CHECK_THREAD; - CheckValid(); - m_ReadPos += a_Count; - if (m_ReadPos > m_BufferSize) - { - m_ReadPos -= m_BufferSize; - } -} - - - - - -void cByteBuffer::CheckValid(void) const -{ - ASSERT(m_ReadPos >= 0); - ASSERT(m_ReadPos < m_BufferSize); - ASSERT(m_WritePos >= 0); - ASSERT(m_WritePos < m_BufferSize); -} - - - - + +// ByteBuffer.cpp + +// Implements the cByteBuffer class representing a ringbuffer of bytes + +#include "Globals.h" + +#include "ByteBuffer.h" +#include "Endianness.h" +#include "OSSupport/IsThread.h" + + + + + +#define NEEDBYTES(Num) if (!CanReadBytes(Num)) return false; +#define PUTBYTES(Num) if (!CanWriteBytes(Num)) return false; + + + + + +#ifdef _DEBUG + +/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously +class cSingleThreadAccessChecker +{ +public: + cSingleThreadAccessChecker(unsigned long * a_ThreadID) : + m_ThreadID(a_ThreadID) + { + ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID())); + } + + ~cSingleThreadAccessChecker() + { + *m_ThreadID = 0; + } + +protected: + unsigned long * m_ThreadID; +} ; + +#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast(&m_ThreadID)) + +#else + #define CHECK_THREAD +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cByteBuffer: + +cByteBuffer::cByteBuffer(int a_BufferSize) : + m_Buffer(new char[a_BufferSize + 1]), + m_BufferSize(a_BufferSize + 1), + #ifdef _DEBUG + m_ThreadID(0), + #endif // _DEBUG + m_DataStart(0), + m_WritePos(0), + m_ReadPos(0) +{ + // Allocating one byte more than the buffer size requested, so that we can distinguish between + // completely-full and completely-empty states +} + + + + + +cByteBuffer::~cByteBuffer() +{ + CheckValid(); + delete[] m_Buffer; +} + + + + + +bool cByteBuffer::Write(const char * a_Bytes, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + + // Store the current free space for a check after writing: + int CurFreeSpace = GetFreeSpace(); + int CurReadableSpace = GetReadableSpace(); + int WrittenBytes = 0; + + if (GetFreeSpace() < a_Count) + { + return false; + } + int TillEnd = m_BufferSize - m_WritePos; + if (TillEnd <= a_Count) + { + // Need to wrap around the ringbuffer end + if (TillEnd > 0) + { + memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd); + a_Bytes += TillEnd; + a_Count -= TillEnd; + WrittenBytes = TillEnd; + } + m_WritePos = 0; + } + + // We're guaranteed that we'll fit in a single write op + if (a_Count > 0) + { + memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count); + m_WritePos += a_Count; + WrittenBytes += a_Count; + } + + ASSERT(GetFreeSpace() == CurFreeSpace - WrittenBytes); + ASSERT(GetReadableSpace() == CurReadableSpace + WrittenBytes); + return true; +} + + + + + +int cByteBuffer::GetFreeSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + if (m_WritePos >= m_DataStart) + { + // Wrap around the buffer end: + return m_BufferSize - m_WritePos + m_DataStart - 1; + } + // Single free space partition: + return m_DataStart - m_WritePos - 1; +} + + + + + +/// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() +int cByteBuffer::GetUsedSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + return m_BufferSize - GetFreeSpace(); +} + + + + + +/// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) +int cByteBuffer::GetReadableSpace(void) const +{ + CHECK_THREAD; + CheckValid(); + if (m_ReadPos > m_WritePos) + { + // Wrap around the buffer end: + return m_BufferSize - m_ReadPos + m_WritePos; + } + // Single readable space partition: + return m_WritePos - m_ReadPos ; +} + + + + + +bool cByteBuffer::CanReadBytes(int a_Count) const +{ + CHECK_THREAD; + CheckValid(); + return (a_Count <= GetReadableSpace()); +} + + + + + +bool cByteBuffer::CanWriteBytes(int a_Count) const +{ + CHECK_THREAD; + CheckValid(); + return (a_Count <= GetFreeSpace()); +} + + + + + +bool cByteBuffer::ReadChar(char & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + ReadBuf(&a_Value, 1); + return true; +} + + + + + +bool cByteBuffer::ReadByte(unsigned char & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + ReadBuf(&a_Value, 1); + return true; +} + + + + + +bool cByteBuffer::ReadBEShort(short & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(2); + ReadBuf(&a_Value, 2); + a_Value = ntohs(a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEInt(int & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + a_Value = ntohl(a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEInt64(Int64 & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(8); + ReadBuf(&a_Value, 8); + a_Value = NetworkToHostLong8(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEFloat(float & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + a_Value = NetworkToHostFloat4(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBEDouble(double & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(8); + ReadBuf(&a_Value, 8); + a_Value = NetworkToHostDouble8(&a_Value); + return true; +} + + + + + +bool cByteBuffer::ReadBool(bool & a_Value) +{ + CHECK_THREAD; + CheckValid(); + NEEDBYTES(1); + char Value = 0; + ReadBuf(&Value, 1); + a_Value = (Value != 0); + return true; +} + + + + + +bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + short Length; + if (!ReadBEShort(Length)) + { + return false; + } + if (Length < 0) + { + ASSERT(!"Negative string length? Are you sure?"); + return true; + } + return ReadUTF16String(a_Value, Length); +} + + + + + +bool cByteBuffer::WriteChar(char a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(1); + return WriteBuf(&a_Value, 1); +} + + + + + +bool cByteBuffer::WriteByte(unsigned char a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(1); + return WriteBuf(&a_Value, 1); +} + + + + + +bool cByteBuffer::WriteBEShort(short a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(2); + short Converted = htons(a_Value); + return WriteBuf(&Converted, 2); +} + + + + + +bool cByteBuffer::WriteBEInt(int a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(4); + int Converted = HostToNetwork4(&a_Value); + return WriteBuf(&Converted, 4); +} + + + + + +bool cByteBuffer::WriteBEInt64(Int64 a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(8); + Int64 Converted = HostToNetwork8(&a_Value); + return WriteBuf(&Converted, 8); +} + + + + + +bool cByteBuffer::WriteBEFloat(float a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(4); + int Converted = HostToNetwork4(&a_Value); + return WriteBuf(&Converted, 4); +} + + + + + +bool cByteBuffer::WriteBEDouble(double a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(8); + Int64 Converted = HostToNetwork8(&a_Value); + return WriteBuf(&Converted, 8); +} + + + + + + +bool cByteBuffer::WriteBool(bool a_Value) +{ + CHECK_THREAD; + CheckValid(); + return WriteChar(a_Value ? 1 : 0); +} + + + + + +bool cByteBuffer::WriteBEUTF16String16(const AString & a_Value) +{ + CHECK_THREAD; + CheckValid(); + PUTBYTES(2); + AString UTF16BE; + UTF8ToRawBEUTF16(a_Value.data(), a_Value.size(), UTF16BE); + WriteBEShort((short)(UTF16BE.size() / 2)); + PUTBYTES(UTF16BE.size()); + WriteBuf(UTF16BE.data(), UTF16BE.size()); + return true; +} + + + + + +bool cByteBuffer::ReadBuf(void * a_Buffer, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + NEEDBYTES(a_Count); + char * Dst = (char *)a_Buffer; // So that we can do byte math + int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; + ASSERT(BytesToEndOfBuffer >= 0); // Sanity check + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + if (BytesToEndOfBuffer > 0) + { + memcpy(Dst, m_Buffer + m_ReadPos, BytesToEndOfBuffer); + Dst += BytesToEndOfBuffer; + a_Count -= BytesToEndOfBuffer; + } + m_ReadPos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + memcpy(Dst, m_Buffer + m_ReadPos, a_Count); + m_ReadPos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::WriteBuf(const void * a_Buffer, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + PUTBYTES(a_Count); + char * Src = (char *)a_Buffer; // So that we can do byte math + int BytesToEndOfBuffer = m_BufferSize - m_WritePos; + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + memcpy(m_Buffer + m_WritePos, Src, BytesToEndOfBuffer); + Src += BytesToEndOfBuffer; + a_Count -= BytesToEndOfBuffer; + m_WritePos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + memcpy(m_Buffer + m_WritePos, Src, a_Count); + m_WritePos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::ReadString(AString & a_String, int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + NEEDBYTES(a_Count); + a_String.clear(); + a_String.reserve(a_Count); + int BytesToEndOfBuffer = m_BufferSize - m_ReadPos; + ASSERT(BytesToEndOfBuffer >= 0); // Sanity check + if (BytesToEndOfBuffer <= a_Count) + { + // Reading across the ringbuffer end, read the first part and adjust parameters: + if (BytesToEndOfBuffer > 0) + { + a_String.assign(m_Buffer + m_ReadPos, BytesToEndOfBuffer); + a_Count -= BytesToEndOfBuffer; + } + m_ReadPos = 0; + } + + // Read the rest of the bytes in a single read (guaranteed to fit): + if (a_Count > 0) + { + a_String.append(m_Buffer + m_ReadPos, a_Count); + m_ReadPos += a_Count; + } + return true; +} + + + + + +bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars) +{ + // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String + CHECK_THREAD; + CheckValid(); + ASSERT(a_NumChars >= 0); + AString RawData; + if (!ReadString(RawData, a_NumChars * 2)) + { + return false; + } + RawBEToUTF8((short *)(RawData.data()), a_NumChars, a_String); + return true; +} + + + + + +bool cByteBuffer::SkipRead(int a_Count) +{ + CHECK_THREAD; + CheckValid(); + ASSERT(a_Count >= 0); + if (!CanReadBytes(a_Count)) + { + return false; + } + AdvanceReadPos(a_Count); + return true; +} + + + + + +void cByteBuffer::ReadAll(AString & a_Data) +{ + CHECK_THREAD; + CheckValid(); + ReadString(a_Data, GetReadableSpace()); +} + + + + + +void cByteBuffer::CommitRead(void) +{ + CHECK_THREAD; + CheckValid(); + m_DataStart = m_ReadPos; +} + + + + + +void cByteBuffer::ResetRead(void) +{ + CHECK_THREAD; + CheckValid(); + m_ReadPos = m_DataStart; +} + + + + + +void cByteBuffer::ReadAgain(AString & a_Out) +{ + // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed) + // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party + CHECK_THREAD; + CheckValid(); + int DataStart = m_DataStart; + if (m_ReadPos < m_DataStart) + { + // Across the ringbuffer end, read the first part and adjust next part's start: + a_Out.append(m_Buffer + m_DataStart, m_BufferSize - m_DataStart); + DataStart = 0; + } + a_Out.append(m_Buffer + DataStart, m_ReadPos - DataStart); +} + + + + + +void cByteBuffer::AdvanceReadPos(int a_Count) +{ + CHECK_THREAD; + CheckValid(); + m_ReadPos += a_Count; + if (m_ReadPos > m_BufferSize) + { + m_ReadPos -= m_BufferSize; + } +} + + + + + +void cByteBuffer::CheckValid(void) const +{ + ASSERT(m_ReadPos >= 0); + ASSERT(m_ReadPos < m_BufferSize); + ASSERT(m_WritePos >= 0); + ASSERT(m_WritePos < m_BufferSize); +} + + + + diff --git a/source/ByteBuffer.h b/source/ByteBuffer.h index ccc1ddfa1..650eda5b0 100644 --- a/source/ByteBuffer.h +++ b/source/ByteBuffer.h @@ -1,121 +1,121 @@ - -// ByteStream.h - -// Interfaces to the cByteBuffer class representing a ringbuffer of bytes - - - - - -#pragma once - - - - - -/** An object that can store incoming bytes and lets its clients read the bytes sequentially -The bytes are stored in a ringbuffer of constant size; if more than that size -is requested, the write operation fails. -The bytes stored can be retrieved using various ReadXXX functions; these assume that the needed -number of bytes are present in the buffer (ASSERT; for performance reasons). -The reading doesn't actually remove the bytes, it only moves the internal read ptr. -To remove the bytes, call CommitRead(). -To re-start reading from the beginning, call ResetRead(). -This class doesn't implement thread safety, the clients of this class need to provide -their own synchronization. -*/ -class cByteBuffer -{ -public: - cByteBuffer(int a_BufferSize); - ~cByteBuffer(); - - /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not - bool Write(const char * a_Bytes, int a_Count); - - /// Returns the number of bytes that can be successfully written to the ringbuffer - int GetFreeSpace(void) const; - - /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() - int GetUsedSpace(void) const; - - /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) - int GetReadableSpace(void) const; - - /// Returns true if the specified amount of bytes are available for reading - bool CanReadBytes(int a_Count) const; - - /// Returns true if the specified amount of bytes are available for writing - bool CanWriteBytes(int a_Count) const; - - // Read the specified datatype and advance the read pointer; return true if successfully read: - bool ReadChar (char & a_Value); - bool ReadByte (unsigned char & a_Value); - bool ReadBEShort (short & a_Value); - bool ReadBEInt (int & a_Value); - bool ReadBEInt64 (Int64 & a_Value); - bool ReadBEFloat (float & a_Value); - bool ReadBEDouble (double & a_Value); - bool ReadBool (bool & a_Value); - bool ReadBEUTF16String16(AString & a_Value); - - // Write the specified datatype; return true if successfully written - bool WriteChar (char a_Value); - bool WriteByte (unsigned char a_Value); - bool WriteBEShort (short a_Value); - bool WriteBEInt (int a_Value); - bool WriteBEInt64 (Int64 a_Value); - bool WriteBEFloat (float a_Value); - bool WriteBEDouble (double a_Value); - bool WriteBool (bool a_Value); - bool WriteBEUTF16String16(const AString & a_Value); - - /// Reads a_Count bytes into a_Buffer; returns true if successful - bool ReadBuf(void * a_Buffer, int a_Count); - - /// Writes a_Count bytes into a_Buffer; returns true if successful - bool WriteBuf(const void * a_Buffer, int a_Count); - - /// Reads a_Count bytes into a_String; returns true if successful - bool ReadString(AString & a_String, int a_Count); - - /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String - bool ReadUTF16String(AString & a_String, int a_NumChars); - - /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer - bool SkipRead(int a_Count); - - /// Reads all available data into a_Data - void ReadAll(AString & a_Data); - - /// Removes the bytes that have been read from the ringbuffer - void CommitRead(void); - - /// Restarts next reading operation at the start of the ringbuffer - void ResetRead(void); - - /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication - void ReadAgain(AString & a_Out); - - /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs - void CheckValid(void) const; - -protected: - char * m_Buffer; - int m_BufferSize; // Total size of the ringbuffer - - #ifdef _DEBUG - unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker - #endif // _DEBUG - - int m_DataStart; // Where the data starts in the ringbuffer - int m_WritePos; // Where the data ends in the ringbuffer - int m_ReadPos; // Where the next read will start in the ringbuffer - - /// Advances the m_ReadPos by a_Count bytes - void AdvanceReadPos(int a_Count); -} ; - - - - + +// ByteStream.h + +// Interfaces to the cByteBuffer class representing a ringbuffer of bytes + + + + + +#pragma once + + + + + +/** An object that can store incoming bytes and lets its clients read the bytes sequentially +The bytes are stored in a ringbuffer of constant size; if more than that size +is requested, the write operation fails. +The bytes stored can be retrieved using various ReadXXX functions; these assume that the needed +number of bytes are present in the buffer (ASSERT; for performance reasons). +The reading doesn't actually remove the bytes, it only moves the internal read ptr. +To remove the bytes, call CommitRead(). +To re-start reading from the beginning, call ResetRead(). +This class doesn't implement thread safety, the clients of this class need to provide +their own synchronization. +*/ +class cByteBuffer +{ +public: + cByteBuffer(int a_BufferSize); + ~cByteBuffer(); + + /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not + bool Write(const char * a_Bytes, int a_Count); + + /// Returns the number of bytes that can be successfully written to the ringbuffer + int GetFreeSpace(void) const; + + /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() + int GetUsedSpace(void) const; + + /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) + int GetReadableSpace(void) const; + + /// Returns true if the specified amount of bytes are available for reading + bool CanReadBytes(int a_Count) const; + + /// Returns true if the specified amount of bytes are available for writing + bool CanWriteBytes(int a_Count) const; + + // Read the specified datatype and advance the read pointer; return true if successfully read: + bool ReadChar (char & a_Value); + bool ReadByte (unsigned char & a_Value); + bool ReadBEShort (short & a_Value); + bool ReadBEInt (int & a_Value); + bool ReadBEInt64 (Int64 & a_Value); + bool ReadBEFloat (float & a_Value); + bool ReadBEDouble (double & a_Value); + bool ReadBool (bool & a_Value); + bool ReadBEUTF16String16(AString & a_Value); + + // Write the specified datatype; return true if successfully written + bool WriteChar (char a_Value); + bool WriteByte (unsigned char a_Value); + bool WriteBEShort (short a_Value); + bool WriteBEInt (int a_Value); + bool WriteBEInt64 (Int64 a_Value); + bool WriteBEFloat (float a_Value); + bool WriteBEDouble (double a_Value); + bool WriteBool (bool a_Value); + bool WriteBEUTF16String16(const AString & a_Value); + + /// Reads a_Count bytes into a_Buffer; returns true if successful + bool ReadBuf(void * a_Buffer, int a_Count); + + /// Writes a_Count bytes into a_Buffer; returns true if successful + bool WriteBuf(const void * a_Buffer, int a_Count); + + /// Reads a_Count bytes into a_String; returns true if successful + bool ReadString(AString & a_String, int a_Count); + + /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String + bool ReadUTF16String(AString & a_String, int a_NumChars); + + /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer + bool SkipRead(int a_Count); + + /// Reads all available data into a_Data + void ReadAll(AString & a_Data); + + /// Removes the bytes that have been read from the ringbuffer + void CommitRead(void); + + /// Restarts next reading operation at the start of the ringbuffer + void ResetRead(void); + + /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication + void ReadAgain(AString & a_Out); + + /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs + void CheckValid(void) const; + +protected: + char * m_Buffer; + int m_BufferSize; // Total size of the ringbuffer + + #ifdef _DEBUG + unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker + #endif // _DEBUG + + int m_DataStart; // Where the data starts in the ringbuffer + int m_WritePos; // Where the data ends in the ringbuffer + int m_ReadPos; // Where the next read will start in the ringbuffer + + /// Advances the m_ReadPos by a_Count bytes + void AdvanceReadPos(int a_Count); +} ; + + + + diff --git a/source/CommandOutput.cpp b/source/CommandOutput.cpp index 49102b38c..c221682a1 100644 --- a/source/CommandOutput.cpp +++ b/source/CommandOutput.cpp @@ -1,71 +1,71 @@ - -// CommandOutput.cpp - -// Implements the various classes that process command output - -#include "Globals.h" -#include "CommandOutput.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCommandOutputCallback: - -void cCommandOutputCallback::Out(const char * a_Fmt, ...) -{ - AString Output; - va_list args; - va_start(args, a_Fmt); - AppendVPrintf(Output, a_Fmt, args); - va_end(args); - Output.append("\n"); - Out(Output); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cLogCommandOutputCallback: - -void cLogCommandOutputCallback::Out(const AString & a_Text) -{ - m_Buffer.append(a_Text); -} - - - - - -void cLogCommandOutputCallback::Finished(void) -{ - // Log each line separately: - size_t len = m_Buffer.length(); - size_t last = 0; - for (size_t i = 0; i < len; i++) - { - switch (m_Buffer[i]) - { - case '\n': - { - LOG(m_Buffer.substr(last, i - last).c_str()); - last = i + 1; - break; - } - } - } // for i - m_Buffer[] - if (last < len) - { - LOG(m_Buffer.substr(last).c_str()); - } - - // Clear the buffer for the next command output: - m_Buffer.clear(); -} - - - - + +// CommandOutput.cpp + +// Implements the various classes that process command output + +#include "Globals.h" +#include "CommandOutput.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCommandOutputCallback: + +void cCommandOutputCallback::Out(const char * a_Fmt, ...) +{ + AString Output; + va_list args; + va_start(args, a_Fmt); + AppendVPrintf(Output, a_Fmt, args); + va_end(args); + Output.append("\n"); + Out(Output); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLogCommandOutputCallback: + +void cLogCommandOutputCallback::Out(const AString & a_Text) +{ + m_Buffer.append(a_Text); +} + + + + + +void cLogCommandOutputCallback::Finished(void) +{ + // Log each line separately: + size_t len = m_Buffer.length(); + size_t last = 0; + for (size_t i = 0; i < len; i++) + { + switch (m_Buffer[i]) + { + case '\n': + { + LOG(m_Buffer.substr(last, i - last).c_str()); + last = i + 1; + break; + } + } + } // for i - m_Buffer[] + if (last < len) + { + LOG(m_Buffer.substr(last).c_str()); + } + + // Clear the buffer for the next command output: + m_Buffer.clear(); +} + + + + diff --git a/source/CommandOutput.h b/source/CommandOutput.h index 68217dbb4..bdf675238 100644 --- a/source/CommandOutput.h +++ b/source/CommandOutput.h @@ -1,82 +1,82 @@ - -// CommandOutput.h - -// Declares various classes that process command output - - - - - -/** Interface for a callback that receives command output -The Out() function is called for any output the command has produced. -Descendants override that function to provide specific processing of the output. -*/ -class cCommandOutputCallback -{ -public: - virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses - - /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n" - void Out(const char * a_Fmt, ...); - - /// Called when the command wants to output anything; may be called multiple times - virtual void Out(const AString & a_Text) = 0; - - /// Called when the command processing has been finished - virtual void Finished(void) {}; -} ; - - - - - -/// Class that discards all command output -class cNullCommandOutputCallback : - public cCommandOutputCallback -{ - // cCommandOutputCallback overrides: - virtual void Out(const AString & a_Text) override - { - // Do nothing - } -} ; - - - - - - -/// Sends all command output to a log, line by line, when the command finishes processing -class cLogCommandOutputCallback : - public cCommandOutputCallback -{ -public: - // cCommandOutputCallback overrides: - virtual void Out(const AString & a_Text) override; - virtual void Finished(void) override; - -protected: - /// Output is stored here until the command finishes processing - AString m_Buffer; -} ; - - - - - -/// Sends all command output to a log, line by line; deletes self when command finishes processing -class cLogCommandDeleteSelfOutputCallback : - public cLogCommandOutputCallback -{ - typedef cLogCommandOutputCallback super; - - virtual void Finished(void) override - { - super::Finished(); - delete this; - } -} ; - - - - + +// CommandOutput.h + +// Declares various classes that process command output + + + + + +/** Interface for a callback that receives command output +The Out() function is called for any output the command has produced. +Descendants override that function to provide specific processing of the output. +*/ +class cCommandOutputCallback +{ +public: + virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses + + /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n" + void Out(const char * a_Fmt, ...); + + /// Called when the command wants to output anything; may be called multiple times + virtual void Out(const AString & a_Text) = 0; + + /// Called when the command processing has been finished + virtual void Finished(void) {}; +} ; + + + + + +/// Class that discards all command output +class cNullCommandOutputCallback : + public cCommandOutputCallback +{ + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override + { + // Do nothing + } +} ; + + + + + + +/// Sends all command output to a log, line by line, when the command finishes processing +class cLogCommandOutputCallback : + public cCommandOutputCallback +{ +public: + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override; + virtual void Finished(void) override; + +protected: + /// Output is stored here until the command finishes processing + AString m_Buffer; +} ; + + + + + +/// Sends all command output to a log, line by line; deletes self when command finishes processing +class cLogCommandDeleteSelfOutputCallback : + public cLogCommandOutputCallback +{ + typedef cLogCommandOutputCallback super; + + virtual void Finished(void) override + { + super::Finished(); + delete this; + } +} ; + + + + diff --git a/source/Enchantments.cpp b/source/Enchantments.cpp index 08a24d8f3..0caf4eb11 100644 --- a/source/Enchantments.cpp +++ b/source/Enchantments.cpp @@ -1,298 +1,298 @@ - -// Enchantments.cpp - -// Implements the cEnchantments class representing a storage for item enchantments and stored-enchantments - -#include "Globals.h" -#include "Enchantments.h" -#include "WorldStorage/FastNBT.h" - - - - - -cEnchantments::cEnchantments(void) -{ - // Nothing needed yet, but the constructor needs to be declared and impemented in order to be usable -} - - - - - -cEnchantments::cEnchantments(const AString & a_StringSpec) -{ - AddFromString(a_StringSpec); -} - - - - - -void cEnchantments::AddFromString(const AString & a_StringSpec) -{ - // Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it - - // Split the StringSpec into separate declarations, each in the form "id=lvl": - AStringVector Decls = StringSplit(a_StringSpec, ";"); - for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr) - { - // Split each declaration into the id and lvl part: - if (itr->empty()) - { - // The decl is empty (may happen if there's an extra semicolon at the end), ignore silently - continue; - } - AStringVector Split = StringSplitAndTrim(*itr, "="); - if (Split.size() != 2) - { - // Malformed decl - LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str()); - continue; - } - int id = atoi(Split[0].c_str()); - if ((id == 0) && (Split[0] != "0")) - { - id = StringToEnchantmentID(Split[0]); - } - int lvl = atoi(Split[1].c_str()); - if ( - ((id <= 0) && (Split[0] != "0")) || - ((lvl == 0) && (Split[1] != "0")) - ) - { - // Numbers failed to parse - LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.", - __FUNCTION__, Split[0].c_str(), Split[1].c_str() - ); - continue; - } - SetLevel(id, lvl); - } // for itr - Decls[] -} - - - - - -AString cEnchantments::ToString(void) const -{ - // Serialize all the enchantments into a string - AString res; - for (cEnchantments::cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) - { - AppendPrintf(res, "%d=%d;", itr->first, itr->second); - } // for itr - m_Enchantments[] - return res; -} - - - - - -int cEnchantments::GetLevel(int a_EnchantmentID) const -{ - // Return the level for the specified enchantment; 0 if not stored - cMap::const_iterator itr = m_Enchantments.find(a_EnchantmentID); - if (itr != m_Enchantments.end()) - { - return itr->second; - } - - // Not stored, return zero - return 0; -} - - - - - -void cEnchantments::SetLevel(int a_EnchantmentID, int a_Level) -{ - // Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 - if (a_Level == 0) - { - // Delete enchantment, if present: - cMap::iterator itr = m_Enchantments.find(a_EnchantmentID); - if (itr != m_Enchantments.end()) - { - m_Enchantments.erase(itr); - } - } - else - { - // Add / overwrite enchantment - m_Enchantments[a_EnchantmentID] = a_Level; - } -} - - - - - - -void cEnchantments::Clear(void) -{ - m_Enchantments.clear(); -} - - - - - -bool cEnchantments::IsEmpty(void) const -{ - return m_Enchantments.empty(); -} - - - - - -int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName) -{ - struct - { - int m_Value; - const char * m_Name; - } EnchantmentNames[] = - { - { enchProtection, "Protection"}, - { enchFireProtection, "FireProtection"}, - { enchFeatherFalling, "FeatherFalling"}, - { enchBlastProtection, "BlastProtection"}, - { enchProjectileProtection, "ProjectileProtection"}, - { enchRespiration, "Respiration"}, - { enchAquaAffinity, "AquaAffinity"}, - { enchThorns, "Thorns"}, - { enchSharpness, "Sharpness"}, - { enchSmite, "Smite"}, - { enchBaneOfArthropods, "BaneOfArthropods"}, - { enchKnockback, "Knockback"}, - { enchFireAspect, "FireAspect"}, - { enchLooting, "Looting"}, - { enchEfficiency, "Efficiency"}, - { enchSilkTouch, "SilkTouch"}, - { enchUnbreaking, "Unbreaking"}, - { enchFortune, "Fortune"}, - { enchPower, "Power"}, - { enchPunch, "Punch"}, - { enchFlame, "Flame"}, - { enchInfinity, "Infinity"}, - } ; - for (int i = 0; i < ARRAYCOUNT(EnchantmentNames); i++) - { - if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0) - { - return EnchantmentNames[i].m_Value; - } - } // for i - EnchantmentNames[] - return -1; -} - - - - - -bool cEnchantments::operator ==(const cEnchantments & a_Other) const -{ - return m_Enchantments == a_Other.m_Enchantments; -} - - - - - -bool cEnchantments::operator !=(const cEnchantments & a_Other) const -{ - return m_Enchantments != a_Other.m_Enchantments; -} - - - - - -void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const -{ - // Write the enchantments into the specified NBT writer - // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments") - - a_Writer.BeginList(a_ListTagName, TAG_Compound); - for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) - { - a_Writer.BeginCompound(""); - a_Writer.AddShort("id", itr->first); - a_Writer.AddShort("lvl", itr->second); - a_Writer.EndCompound(); - } // for itr - m_Enchantments[] - a_Writer.EndList(); -} - - - - - -void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx) -{ - // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments) - - // Verify that the tag is a list: - if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List) - { - LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed", - __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx) - ); - ASSERT(!"Bad EnchListTag type"); - return; - } - - // Verify that the list is of Compounds: - if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound) - { - LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed", - __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx) - ); - ASSERT(!"Bad EnchListTag children type"); - return; - } - - Clear(); - - // Iterate over all the compound children, parse an enchantment from each: - for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag)) - { - // tag is the compound inside the "ench" list tag - ASSERT(a_NBT.GetType(tag) == TAG_Compound); - - // Search for the id and lvl tags' values: - int id = -1, lvl = -1; - for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) - { - if (a_NBT.GetType(ch) != TAG_Short) - { - continue; - } - if (a_NBT.GetName(ch) == "id") - { - id = a_NBT.GetShort(ch); - } - else if (a_NBT.GetName(ch) == "lvl") - { - lvl = a_NBT.GetShort(ch); - } - } // for ch - children of the compound tag - - if ((id == -1) || (lvl <= 0)) - { - // Failed to parse either the id or the lvl, skip this compound - continue; - } - - // Store the enchantment: - m_Enchantments[id] = lvl; - } // for tag - children of the ench list tag -} - - - - + +// Enchantments.cpp + +// Implements the cEnchantments class representing a storage for item enchantments and stored-enchantments + +#include "Globals.h" +#include "Enchantments.h" +#include "WorldStorage/FastNBT.h" + + + + + +cEnchantments::cEnchantments(void) +{ + // Nothing needed yet, but the constructor needs to be declared and impemented in order to be usable +} + + + + + +cEnchantments::cEnchantments(const AString & a_StringSpec) +{ + AddFromString(a_StringSpec); +} + + + + + +void cEnchantments::AddFromString(const AString & a_StringSpec) +{ + // Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it + + // Split the StringSpec into separate declarations, each in the form "id=lvl": + AStringVector Decls = StringSplit(a_StringSpec, ";"); + for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr) + { + // Split each declaration into the id and lvl part: + if (itr->empty()) + { + // The decl is empty (may happen if there's an extra semicolon at the end), ignore silently + continue; + } + AStringVector Split = StringSplitAndTrim(*itr, "="); + if (Split.size() != 2) + { + // Malformed decl + LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str()); + continue; + } + int id = atoi(Split[0].c_str()); + if ((id == 0) && (Split[0] != "0")) + { + id = StringToEnchantmentID(Split[0]); + } + int lvl = atoi(Split[1].c_str()); + if ( + ((id <= 0) && (Split[0] != "0")) || + ((lvl == 0) && (Split[1] != "0")) + ) + { + // Numbers failed to parse + LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.", + __FUNCTION__, Split[0].c_str(), Split[1].c_str() + ); + continue; + } + SetLevel(id, lvl); + } // for itr - Decls[] +} + + + + + +AString cEnchantments::ToString(void) const +{ + // Serialize all the enchantments into a string + AString res; + for (cEnchantments::cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) + { + AppendPrintf(res, "%d=%d;", itr->first, itr->second); + } // for itr - m_Enchantments[] + return res; +} + + + + + +int cEnchantments::GetLevel(int a_EnchantmentID) const +{ + // Return the level for the specified enchantment; 0 if not stored + cMap::const_iterator itr = m_Enchantments.find(a_EnchantmentID); + if (itr != m_Enchantments.end()) + { + return itr->second; + } + + // Not stored, return zero + return 0; +} + + + + + +void cEnchantments::SetLevel(int a_EnchantmentID, int a_Level) +{ + // Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 + if (a_Level == 0) + { + // Delete enchantment, if present: + cMap::iterator itr = m_Enchantments.find(a_EnchantmentID); + if (itr != m_Enchantments.end()) + { + m_Enchantments.erase(itr); + } + } + else + { + // Add / overwrite enchantment + m_Enchantments[a_EnchantmentID] = a_Level; + } +} + + + + + + +void cEnchantments::Clear(void) +{ + m_Enchantments.clear(); +} + + + + + +bool cEnchantments::IsEmpty(void) const +{ + return m_Enchantments.empty(); +} + + + + + +int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName) +{ + struct + { + int m_Value; + const char * m_Name; + } EnchantmentNames[] = + { + { enchProtection, "Protection"}, + { enchFireProtection, "FireProtection"}, + { enchFeatherFalling, "FeatherFalling"}, + { enchBlastProtection, "BlastProtection"}, + { enchProjectileProtection, "ProjectileProtection"}, + { enchRespiration, "Respiration"}, + { enchAquaAffinity, "AquaAffinity"}, + { enchThorns, "Thorns"}, + { enchSharpness, "Sharpness"}, + { enchSmite, "Smite"}, + { enchBaneOfArthropods, "BaneOfArthropods"}, + { enchKnockback, "Knockback"}, + { enchFireAspect, "FireAspect"}, + { enchLooting, "Looting"}, + { enchEfficiency, "Efficiency"}, + { enchSilkTouch, "SilkTouch"}, + { enchUnbreaking, "Unbreaking"}, + { enchFortune, "Fortune"}, + { enchPower, "Power"}, + { enchPunch, "Punch"}, + { enchFlame, "Flame"}, + { enchInfinity, "Infinity"}, + } ; + for (int i = 0; i < ARRAYCOUNT(EnchantmentNames); i++) + { + if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0) + { + return EnchantmentNames[i].m_Value; + } + } // for i - EnchantmentNames[] + return -1; +} + + + + + +bool cEnchantments::operator ==(const cEnchantments & a_Other) const +{ + return m_Enchantments == a_Other.m_Enchantments; +} + + + + + +bool cEnchantments::operator !=(const cEnchantments & a_Other) const +{ + return m_Enchantments != a_Other.m_Enchantments; +} + + + + + +void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const +{ + // Write the enchantments into the specified NBT writer + // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments") + + a_Writer.BeginList(a_ListTagName, TAG_Compound); + for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr) + { + a_Writer.BeginCompound(""); + a_Writer.AddShort("id", itr->first); + a_Writer.AddShort("lvl", itr->second); + a_Writer.EndCompound(); + } // for itr - m_Enchantments[] + a_Writer.EndList(); +} + + + + + +void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx) +{ + // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments) + + // Verify that the tag is a list: + if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List) + { + LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed", + __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx) + ); + ASSERT(!"Bad EnchListTag type"); + return; + } + + // Verify that the list is of Compounds: + if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound) + { + LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed", + __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx) + ); + ASSERT(!"Bad EnchListTag children type"); + return; + } + + Clear(); + + // Iterate over all the compound children, parse an enchantment from each: + for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag)) + { + // tag is the compound inside the "ench" list tag + ASSERT(a_NBT.GetType(tag) == TAG_Compound); + + // Search for the id and lvl tags' values: + int id = -1, lvl = -1; + for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) + { + if (a_NBT.GetType(ch) != TAG_Short) + { + continue; + } + if (a_NBT.GetName(ch) == "id") + { + id = a_NBT.GetShort(ch); + } + else if (a_NBT.GetName(ch) == "lvl") + { + lvl = a_NBT.GetShort(ch); + } + } // for ch - children of the compound tag + + if ((id == -1) || (lvl <= 0)) + { + // Failed to parse either the id or the lvl, skip this compound + continue; + } + + // Store the enchantment: + m_Enchantments[id] = lvl; + } // for tag - children of the ench list tag +} + + + + diff --git a/source/Enchantments.h b/source/Enchantments.h index c20da654b..cda743daf 100644 --- a/source/Enchantments.h +++ b/source/Enchantments.h @@ -1,114 +1,114 @@ - -// Enchantments.h - -// Declares the cEnchantments class representing a storage for item enchantments and stored-enchantments - - - - - -#pragma once - - - - - -// fwd: WorldStorage/FastNBT.h -class cFastNBTWriter; -class cParsedNBT; - - - - - -// tolua_begin - -/** Class that stores item enchantments or stored-enchantments -The enchantments may be serialized to a stringspec and read back from such stringspec. -The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end, -mapping each enchantment's id onto its level. ID may be either a number or the enchantment name. -Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments. -Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs. -*/ -class cEnchantments -{ -public: - /// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs ) - enum - { - enchProtection = 0, - enchFireProtection = 1, - enchFeatherFalling = 2, - enchBlastProtection = 3, - enchProjectileProtection = 4, - enchRespiration = 5, - enchAquaAffinity = 6, - enchThorns = 7, - enchSharpness = 16, - enchSmite = 17, - enchBaneOfArthropods = 18, - enchKnockback = 19, - enchFireAspect = 20, - enchLooting = 21, - enchEfficiency = 32, - enchSilkTouch = 33, - enchUnbreaking = 34, - enchFortune = 35, - enchPower = 48, - enchPunch = 49, - enchFlame = 50, - enchInfinity = 51, - } ; - - /// Creates an empty enchantments container - cEnchantments(void); - - /// Creates an enchantments container filled with enchantments parsed from stringspec - cEnchantments(const AString & a_StringSpec); - - /// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it - void AddFromString(const AString & a_StringSpec); - - /// Serializes all the enchantments into a string - AString ToString(void) const; - - /// Returns the level for the specified enchantment; 0 if not stored - int GetLevel(int a_EnchantmentID) const; - - /// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 - void SetLevel(int a_EnchantmentID, int a_Level); - - /// Removes all enchantments - void Clear(void); - - /// Returns true if there are no enchantments - bool IsEmpty(void) const; - - /// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive - static int StringToEnchantmentID(const AString & a_EnchantmentName); - - /// Returns true if a_Other contains exactly the same enchantments and levels - bool operator ==(const cEnchantments & a_Other) const; - - // tolua_end - - /// Returns true if a_Other doesn't contain exactly the same enchantments and levels - bool operator !=(const cEnchantments & a_Other) const; - - /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") - void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const; - - /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) - void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx); - -protected: - /// Maps enchantment ID -> enchantment level - typedef std::map cMap; - - /// Currently stored enchantments - cMap m_Enchantments; -} ; // tolua_export - - - - + +// Enchantments.h + +// Declares the cEnchantments class representing a storage for item enchantments and stored-enchantments + + + + + +#pragma once + + + + + +// fwd: WorldStorage/FastNBT.h +class cFastNBTWriter; +class cParsedNBT; + + + + + +// tolua_begin + +/** Class that stores item enchantments or stored-enchantments +The enchantments may be serialized to a stringspec and read back from such stringspec. +The format for the stringspec is "id=lvl;id=lvl;id=lvl...", with an optional semicolon at the end, +mapping each enchantment's id onto its level. ID may be either a number or the enchantment name. +Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments. +Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs. +*/ +class cEnchantments +{ +public: + /// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs ) + enum + { + enchProtection = 0, + enchFireProtection = 1, + enchFeatherFalling = 2, + enchBlastProtection = 3, + enchProjectileProtection = 4, + enchRespiration = 5, + enchAquaAffinity = 6, + enchThorns = 7, + enchSharpness = 16, + enchSmite = 17, + enchBaneOfArthropods = 18, + enchKnockback = 19, + enchFireAspect = 20, + enchLooting = 21, + enchEfficiency = 32, + enchSilkTouch = 33, + enchUnbreaking = 34, + enchFortune = 35, + enchPower = 48, + enchPunch = 49, + enchFlame = 50, + enchInfinity = 51, + } ; + + /// Creates an empty enchantments container + cEnchantments(void); + + /// Creates an enchantments container filled with enchantments parsed from stringspec + cEnchantments(const AString & a_StringSpec); + + /// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it + void AddFromString(const AString & a_StringSpec); + + /// Serializes all the enchantments into a string + AString ToString(void) const; + + /// Returns the level for the specified enchantment; 0 if not stored + int GetLevel(int a_EnchantmentID) const; + + /// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 + void SetLevel(int a_EnchantmentID, int a_Level); + + /// Removes all enchantments + void Clear(void); + + /// Returns true if there are no enchantments + bool IsEmpty(void) const; + + /// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive + static int StringToEnchantmentID(const AString & a_EnchantmentName); + + /// Returns true if a_Other contains exactly the same enchantments and levels + bool operator ==(const cEnchantments & a_Other) const; + + // tolua_end + + /// Returns true if a_Other doesn't contain exactly the same enchantments and levels + bool operator !=(const cEnchantments & a_Other) const; + + /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") + void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const; + + /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) + void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx); + +protected: + /// Maps enchantment ID -> enchantment level + typedef std::map cMap; + + /// Currently stored enchantments + cMap m_Enchantments; +} ; // tolua_export + + + + diff --git a/source/FallingBlock.cpp b/source/FallingBlock.cpp index c3f980303..5dc99c771 100644 --- a/source/FallingBlock.cpp +++ b/source/FallingBlock.cpp @@ -1,103 +1,103 @@ -#include "Globals.h" - -#include "FallingBlock.h" -#include "World.h" -#include "ClientHandle.h" -#include "Simulator/SandSimulator.h" -#include "Chunk.h" - - - - - -cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : - super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98), - m_BlockType(a_BlockType), - m_BlockMeta(a_BlockMeta), - m_OriginalPosition(a_BlockPosition) -{ -} - - - - - -void cFallingBlock::Initialize(cWorld * a_World) -{ - super::Initialize(a_World); - a_World->BroadcastSpawnEntity(*this); -} - - - - - -void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle) -{ - a_ClientHandle.SendSpawnFallingBlock(*this); -} - - - - - -void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) -{ - float MilliDt = a_Dt * 0.001f; - AddSpeedY(MilliDt * -9.8f); - AddPosY(GetSpeedY() * MilliDt); - - // GetWorld()->BroadcastTeleportEntity(*this); // Test position - - int BlockX = m_OriginalPosition.x; - int BlockY = (int)(GetPosY() - 0.5); - int BlockZ = m_OriginalPosition.z; - - if (BlockY < 0) - { - // Fallen out of this world, just continue falling until out of sight, then destroy: - if (BlockY < 100) - { - Destroy(true); - } - return; - } - - if (BlockY >= cChunkDef::Height) - { - // Above the world, just wait for it to fall back down - return; - } - - int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width); - BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx); - NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx); - if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta)) - { - // Fallen onto a block that breaks this into pickups (e. g. half-slab) - // Must finish the fall with coords one below the block: - cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta); - Destroy(true); - return; - } - else if (!cSandSimulator::CanContinueFallThrough(BlockBelow)) - { - // Fallen onto a solid block - /* - LOGD( - "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.", - BlockX, BlockY, BlockZ, - BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width, - ItemTypeToString(BlockBelow).c_str() - ); - */ - - cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta); - Destroy(true); - return; - } -} - - - - +#include "Globals.h" + +#include "FallingBlock.h" +#include "World.h" +#include "ClientHandle.h" +#include "Simulator/SandSimulator.h" +#include "Chunk.h" + + + + + +cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : + super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta), + m_OriginalPosition(a_BlockPosition) +{ +} + + + + + +void cFallingBlock::Initialize(cWorld * a_World) +{ + super::Initialize(a_World); + a_World->BroadcastSpawnEntity(*this); +} + + + + + +void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnFallingBlock(*this); +} + + + + + +void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) +{ + float MilliDt = a_Dt * 0.001f; + AddSpeedY(MilliDt * -9.8f); + AddPosY(GetSpeedY() * MilliDt); + + // GetWorld()->BroadcastTeleportEntity(*this); // Test position + + int BlockX = m_OriginalPosition.x; + int BlockY = (int)(GetPosY() - 0.5); + int BlockZ = m_OriginalPosition.z; + + if (BlockY < 0) + { + // Fallen out of this world, just continue falling until out of sight, then destroy: + if (BlockY < 100) + { + Destroy(true); + } + return; + } + + if (BlockY >= cChunkDef::Height) + { + // Above the world, just wait for it to fall back down + return; + } + + int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width); + BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx); + NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx); + if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta)) + { + // Fallen onto a block that breaks this into pickups (e. g. half-slab) + // Must finish the fall with coords one below the block: + cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta); + Destroy(true); + return; + } + else if (!cSandSimulator::CanContinueFallThrough(BlockBelow)) + { + // Fallen onto a solid block + /* + LOGD( + "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.", + BlockX, BlockY, BlockZ, + BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width, + ItemTypeToString(BlockBelow).c_str() + ); + */ + + cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta); + Destroy(true); + return; + } +} + + + + diff --git a/source/FallingBlock.h b/source/FallingBlock.h index b7d448327..887ae6268 100644 --- a/source/FallingBlock.h +++ b/source/FallingBlock.h @@ -1,45 +1,45 @@ - -#pragma once - -#include "Entity.h" -#include "Defines.h" - - - - -class cPlayer; -class cItem; - - - - - - -class cFallingBlock : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cFallingBlock); - - /// Creates a new falling block. a_BlockPosition is expected in world coords - cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - BLOCKTYPE GetBlockType(void) const { return m_BlockType; } - NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; } - - // cEntity overrides: - virtual void Initialize(cWorld * a_World) override; - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - -private: - BLOCKTYPE m_BlockType; - NIBBLETYPE m_BlockMeta; - Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords -} ; - - - - + +#pragma once + +#include "Entity.h" +#include "Defines.h" + + + + +class cPlayer; +class cItem; + + + + + + +class cFallingBlock : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cFallingBlock); + + /// Creates a new falling block. a_BlockPosition is expected in world coords + cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + BLOCKTYPE GetBlockType(void) const { return m_BlockType; } + NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; } + + // cEntity overrides: + virtual void Initialize(cWorld * a_World) override; + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +private: + BLOCKTYPE m_BlockType; + NIBBLETYPE m_BlockMeta; + Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords +} ; + + + + diff --git a/source/FastRandom.cpp b/source/FastRandom.cpp index b778fb4bb..887e4426d 100644 --- a/source/FastRandom.cpp +++ b/source/FastRandom.cpp @@ -1,174 +1,174 @@ - -// FastRandom.cpp - -// Implements the cFastRandom class representing a fast random number generator - -#include "Globals.h" -#include -#include "FastRandom.h" - - - - - -#if 0 && defined(_DEBUG) -// Self-test -// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs, -// and if it performs well in terms of distribution (checked by avg, expected to be in the range midpoint -class cFastRandomTest -{ -public: - cFastRandomTest(void) - { - TestInts(); - TestFloats(); - } - - - void TestInts(void) - { - printf("Testing ints...\n"); - cFastRandom rnd; - int sum = 0; - const int BUCKETS = 8; - int Counts[BUCKETS]; - memset(Counts, 0, sizeof(Counts)); - const int ITER = 10000; - for (int i = 0; i < ITER; i++) - { - int v = rnd.NextInt(1000); - ASSERT(v >= 0); - ASSERT(v < 1000); - Counts[v % BUCKETS]++; - sum += v; - } - double avg = (double)sum / ITER; - printf("avg: %f\n", avg); - for (int i = 0; i < BUCKETS; i++) - { - printf(" bucket %d: %d\n", i, Counts[i]); - } - } - - - void TestFloats(void) - { - printf("Testing floats...\n"); - cFastRandom rnd; - float sum = 0; - const int BUCKETS = 8; - int Counts[BUCKETS]; - memset(Counts, 0, sizeof(Counts)); - const int ITER = 10000; - for (int i = 0; i < ITER; i++) - { - float v = rnd.NextFloat(1000); - ASSERT(v >= 0); - ASSERT(v <= 1000); - Counts[((int)v) % BUCKETS]++; - sum += v; - } - sum = sum / ITER; - printf("avg: %f\n", sum); - for (int i = 0; i < BUCKETS; i++) - { - printf(" bucket %d: %d\n", i, Counts[i]); - } - } -} g_Test; - -#endif - - - - - - -int cFastRandom::m_SeedCounter = 0; - - - - - -cFastRandom::cFastRandom(void) : - m_Seed(m_SeedCounter++) -{ -} - - - - - -int cFastRandom::NextInt(int a_Range) -{ - ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges - ASSERT(a_Range > 0); - - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - return ((n / 11) % a_Range); -} - - - - - -int cFastRandom::NextInt(int a_Range, int a_Salt) -{ - ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges - ASSERT(a_Range > 0); - - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - return ((n / 11) % a_Range); -} - - - - - -float cFastRandom::NextFloat(float a_Range) -{ - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - - // Convert the integer into float with the specified range: - return (((float)n / (float)0x7fffffff) * a_Range); -} - - - - - -float cFastRandom::NextFloat(float a_Range, int a_Salt) -{ - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - - // Convert the integer into float with the specified range: - return (((float)n / (float)0x7fffffff) * a_Range); -} - - - - + +// FastRandom.cpp + +// Implements the cFastRandom class representing a fast random number generator + +#include "Globals.h" +#include +#include "FastRandom.h" + + + + + +#if 0 && defined(_DEBUG) +// Self-test +// Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs, +// and if it performs well in terms of distribution (checked by avg, expected to be in the range midpoint +class cFastRandomTest +{ +public: + cFastRandomTest(void) + { + TestInts(); + TestFloats(); + } + + + void TestInts(void) + { + printf("Testing ints...\n"); + cFastRandom rnd; + int sum = 0; + const int BUCKETS = 8; + int Counts[BUCKETS]; + memset(Counts, 0, sizeof(Counts)); + const int ITER = 10000; + for (int i = 0; i < ITER; i++) + { + int v = rnd.NextInt(1000); + ASSERT(v >= 0); + ASSERT(v < 1000); + Counts[v % BUCKETS]++; + sum += v; + } + double avg = (double)sum / ITER; + printf("avg: %f\n", avg); + for (int i = 0; i < BUCKETS; i++) + { + printf(" bucket %d: %d\n", i, Counts[i]); + } + } + + + void TestFloats(void) + { + printf("Testing floats...\n"); + cFastRandom rnd; + float sum = 0; + const int BUCKETS = 8; + int Counts[BUCKETS]; + memset(Counts, 0, sizeof(Counts)); + const int ITER = 10000; + for (int i = 0; i < ITER; i++) + { + float v = rnd.NextFloat(1000); + ASSERT(v >= 0); + ASSERT(v <= 1000); + Counts[((int)v) % BUCKETS]++; + sum += v; + } + sum = sum / ITER; + printf("avg: %f\n", sum); + for (int i = 0; i < BUCKETS; i++) + { + printf(" bucket %d: %d\n", i, Counts[i]); + } + } +} g_Test; + +#endif + + + + + + +int cFastRandom::m_SeedCounter = 0; + + + + + +cFastRandom::cFastRandom(void) : + m_Seed(m_SeedCounter++) +{ +} + + + + + +int cFastRandom::NextInt(int a_Range) +{ + ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges + ASSERT(a_Range > 0); + + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + return ((n / 11) % a_Range); +} + + + + + +int cFastRandom::NextInt(int a_Range, int a_Salt) +{ + ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges + ASSERT(a_Range > 0); + + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + return ((n / 11) % a_Range); +} + + + + + +float cFastRandom::NextFloat(float a_Range) +{ + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + + // Convert the integer into float with the specified range: + return (((float)n / (float)0x7fffffff) * a_Range); +} + + + + + +float cFastRandom::NextFloat(float a_Range, int a_Salt) +{ + // Make the m_Counter operations as minimal as possible, to emulate atomicity + int Counter = m_Counter++; + + // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: + int n = (int)a_Range + m_Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; + n = (n << 13) ^ n; + n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + + // Convert the integer into float with the specified range: + return (((float)n / (float)0x7fffffff) * a_Range); +} + + + + diff --git a/source/FastRandom.h b/source/FastRandom.h index 011c0c8da..bf70822cf 100644 --- a/source/FastRandom.h +++ b/source/FastRandom.h @@ -1,57 +1,57 @@ - -// FastRandom.h - -// Declares the cFastRandom class representing a fast random number generator - -/* -The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator. -It is fast to instantiate, fast to query next, and partially multi-thread-safe. -It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may -yield duplicate numbers in that case. - -Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is -taken from a global counter and the random is calculated using a counter that is incremented on each use (hence -the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter, -and another that takes an additional "salt" parameter; this salt is used as an additional input to the random, -in order to avoid multi-thread duplication. If two threads both use the class at the same time with different -salts, the values they get will be different. -*/ - - - - - -#pragma once - - - - - -class cFastRandom -{ -public: - cFastRandom(void); - - /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M - int NextInt(int a_Range); - - /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness - int NextInt(int a_Range, int a_Salt); - - /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M - float NextFloat(float a_Range); - - /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness - float NextFloat(float a_Range, int a_Salt); - -protected: - int m_Seed; - int m_Counter; - - /// Counter that is used to initialize the seed, incremented for each object created - static int m_SeedCounter; -} ; - - - - + +// FastRandom.h + +// Declares the cFastRandom class representing a fast random number generator + +/* +The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator. +It is fast to instantiate, fast to query next, and partially multi-thread-safe. +It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may +yield duplicate numbers in that case. + +Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is +taken from a global counter and the random is calculated using a counter that is incremented on each use (hence +the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter, +and another that takes an additional "salt" parameter; this salt is used as an additional input to the random, +in order to avoid multi-thread duplication. If two threads both use the class at the same time with different +salts, the values they get will be different. +*/ + + + + + +#pragma once + + + + + +class cFastRandom +{ +public: + cFastRandom(void); + + /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M + int NextInt(int a_Range); + + /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness + int NextInt(int a_Range, int a_Salt); + + /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M + float NextFloat(float a_Range); + + /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness + float NextFloat(float a_Range, int a_Salt); + +protected: + int m_Seed; + int m_Counter; + + /// Counter that is used to initialize the seed, incremented for each object created + static int m_SeedCounter; +} ; + + + + diff --git a/source/Generating/Caves.cpp b/source/Generating/Caves.cpp index 2bfef8054..4221ea187 100644 --- a/source/Generating/Caves.cpp +++ b/source/Generating/Caves.cpp @@ -1,970 +1,970 @@ - -// Caves.cpp - -// Implements the various cave structure generators: -// - cStructGenWormNestCaves -// - cStructGenDualRidgeCaves -// - cStructGenMarbleCaves -// - cStructGenNetherCaves - -/* -WormNestCave generator: -Caves are generated in "nests" - groups of tunnels generated from a single point. -For each chunk, all the nests that could intersect it are generated. -For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...) -Then each tunnel is randomized by inserting points in between its ends. -Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other. -When the tunnels are ready, they are simply carved into the chunk, one by one. -To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it. - -MarbleCaves generator: -For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept. -Problem with this is the amount of CPU work needed for each chunk. -Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground. - -DualRidgeCaves generator: -Instead of evaluating a single noise function, two different noise functions are multiplied. This produces -regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be -reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best. -*/ - -#include "Globals.h" -#include "Caves.h" - - - - - -/// How many nests in each direction are generated for a given chunk. Must be an even number -#define NEIGHBORHOOD_SIZE 8 - - - - - -const int MIN_RADIUS = 3; -const int MAX_RADIUS = 8; - - - - - -struct cCaveDefPoint -{ - int m_BlockX; - int m_BlockY; - int m_BlockZ; - int m_Radius; - - cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) : - m_BlockX(a_BlockX), - m_BlockY(a_BlockY), - m_BlockZ(a_BlockZ), - m_Radius(a_Radius) - { - } -} ; - -typedef std::vector cCaveDefPoints; - - - - - -/// A single non-branching tunnel of a WormNestCave -class cCaveTunnel -{ - // The bounding box, including the radii around defpoints: - int m_MinBlockX, m_MaxBlockX; - int m_MinBlockY, m_MaxBlockY; - int m_MinBlockZ, m_MaxBlockZ; - - /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise - void Randomize(cNoise & a_Noise); - - /// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short) - bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst); - - /// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true - void Smooth(void); - - /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block - void FinishLinear(void); - - /// Calculates the bounding box of the points present - void CalcBoundingBox(void); - -public: - cCaveDefPoints m_Points; - - cCaveTunnel( - int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, - int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, - cNoise & a_Noise - ); - - /// Carves the tunnel into the chunk specified - void ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap - ); - - #ifdef _DEBUG - AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; - #endif // _DEBUG -} ; - -typedef std::vector cCaveTunnels; - - - - - -/// A collection of connected tunnels, possibly branching. -class cStructGenWormNestCaves::cCaveSystem -{ -public: - // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk() - int m_BlockX; - int m_BlockZ; - - cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise); - ~cCaveSystem(); - - /// Carves the cave system into the chunk specified - void ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap - ); - - #ifdef _DEBUG - AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; - #endif // _DEBUG - -protected: - int m_Size; - cCaveTunnels m_Tunnels; - - void Clear(void); - - /// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments] - void GenerateTunnelsFromPoint( - int a_OriginX, int a_OriginY, int a_OriginZ, - cNoise & a_Noise, int a_Segments - ); - - /// Returns a radius based on the location provided. - int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ); -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cCaveTunnel: - -cCaveTunnel::cCaveTunnel( - int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, - int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, - cNoise & a_Noise -) -{ - m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius)); - m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius)); - - if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0)) - { - // Don't bother detailing this cave, it's under the world anyway - return; - } - - Randomize(a_Noise); - Smooth(); - - // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data: - CalcBoundingBox(); - - FinishLinear(); -} - - - - - -void cCaveTunnel::Randomize(cNoise & a_Noise) -{ - // Repeat 4 times: - for (int i = 0; i < 4; i++) - { - // For each already present point, insert a point in between it and its predecessor, shifted randomly. - int PrevX = m_Points.front().m_BlockX; - int PrevY = m_Points.front().m_BlockY; - int PrevZ = m_Points.front().m_BlockZ; - int PrevR = m_Points.front().m_Radius; - cCaveDefPoints Pts; - Pts.reserve(m_Points.size() * 2 + 1); - Pts.push_back(m_Points.front()); - for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) - { - int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11; - int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX); - len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY); - len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ); - len = 3 * (int)sqrt((double)len) / 4; - int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1)); - Random /= 4; - int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2); - Random /= 256; - int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4); - Random /= 256; - int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2); - Pts.push_back(cCaveDefPoint(x, y, z, Rad)); - Pts.push_back(*itr); - PrevX = itr->m_BlockX; - PrevY = itr->m_BlockY; - PrevZ = itr->m_BlockZ; - PrevR = itr->m_Radius; - } - std::swap(Pts, m_Points); - } -} - - - - - -bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst) -{ - // Smoothing: for each line segment, add points on its 1/4 lengths - bool res = false; - int Num = a_Src.size() - 2; // this many intermediary points - a_Dst.clear(); - a_Dst.reserve(Num * 2 + 2); - cCaveDefPoints::const_iterator itr = a_Src.begin() + 1; - a_Dst.push_back(a_Src.front()); - int PrevX = a_Src.front().m_BlockX; - int PrevY = a_Src.front().m_BlockY; - int PrevZ = a_Src.front().m_BlockZ; - int PrevR = a_Src.front().m_Radius; - for (int i = 0; i <= Num; ++i, ++itr) - { - int dx = itr->m_BlockX - PrevX; - int dy = itr->m_BlockY - PrevY; - int dz = itr->m_BlockZ - PrevZ; - if (abs(dx) + abs(dz) + abs(dy) < 6) - { - // Too short a segment to smooth-subdivide into quarters - PrevX = itr->m_BlockX; - PrevY = itr->m_BlockY; - PrevZ = itr->m_BlockZ; - PrevR = itr->m_Radius; - continue; - } - int dr = itr->m_Radius - PrevR; - int Rad1 = std::max(PrevR + 1 * dr / 4, 1); - int Rad2 = std::max(PrevR + 3 * dr / 4, 1); - a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1)); - a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2)); - PrevX = itr->m_BlockX; - PrevY = itr->m_BlockY; - PrevZ = itr->m_BlockZ; - PrevR = itr->m_Radius; - res = true; - } - a_Dst.push_back(a_Src.back()); - return res && (a_Src.size() < a_Dst.size()); -} - - - - - -void cCaveTunnel::Smooth(void) -{ - cCaveDefPoints Pts; - while (true) - { - if (!RefineDefPoints(m_Points, Pts)) - { - std::swap(Pts, m_Points); - return; - } - if (!RefineDefPoints(Pts, m_Points)) - { - return; - } - } -} - - - - - -void cCaveTunnel::FinishLinear(void) -{ - // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints - cCaveDefPoints Pts; - std::swap(Pts, m_Points); - - m_Points.reserve(Pts.size() * 3); - int PrevX = Pts.front().m_BlockX; - int PrevY = Pts.front().m_BlockY; - int PrevZ = Pts.front().m_BlockZ; - for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) - { - int x1 = itr->m_BlockX; - int y1 = itr->m_BlockY; - int z1 = itr->m_BlockZ; - int dx = abs(x1 - PrevX); - int dy = abs(y1 - PrevY); - int dz = abs(z1 - PrevZ); - int sx = (PrevX < x1) ? 1 : -1; - int sy = (PrevY < y1) ? 1 : -1; - int sz = (PrevZ < z1) ? 1 : -1; - int err = dx - dz; - int R = itr->m_Radius; - - if (dx >= std::max(dy, dz)) // x dominant - { - int yd = dy - dx / 2; - int zd = dz - dx / 2; - - while (true) - { - m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); - - if (PrevX == x1) - { - break; - } - - if (yd >= 0) // move along y - { - PrevY += sy; - yd -= dx; - } - - if (zd >= 0) // move along z - { - PrevZ += sz; - zd -= dx; - } - - // move along x - PrevX += 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) - { - m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); - - if (PrevY == y1) - { - break; - } - - if (xd >= 0) // move along x - { - PrevX += sx; - xd -= dy; - } - - if (zd >= 0) // move along z - { - PrevZ += sz; - zd -= dy; - } - - // move along y - PrevY += 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) - { - m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); - - if (PrevZ == z1) - { - break; - } - - if (xd >= 0) // move along x - { - PrevX += sx; - xd -= dz; - } - - if (yd >= 0) // move along y - { - PrevY += sy; - yd -= dz; - } - - // move along z - PrevZ += sz; - xd += dx; - yd += dy; - } - } // if (which dimension is dominant) - } // for itr -} - - - - - -void cCaveTunnel::CalcBoundingBox(void) -{ - m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX; - m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY; - m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ; - for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) - { - m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius); - m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius); - m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius); - m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius); - m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius); - m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius); - } // for itr - m_Points[] -} - - - - - -void cCaveTunnel::ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap -) -{ - int BaseX = a_ChunkX * cChunkDef::Width; - int BaseZ = a_ChunkZ * cChunkDef::Width; - if ( - (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) || - (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) - ) - { - // Tunnel does not intersect the chunk at all, bail out - return; - } - - int BlockStartX = a_ChunkX * cChunkDef::Width; - int BlockStartZ = a_ChunkZ * cChunkDef::Width; - int BlockEndX = BlockStartX + cChunkDef::Width; - int BlockEndZ = BlockStartZ + cChunkDef::Width; - for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) - { - if ( - (itr->m_BlockX + itr->m_Radius < BlockStartX) || - (itr->m_BlockX - itr->m_Radius > BlockEndX) || - (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || - (itr->m_BlockZ - itr->m_Radius > BlockEndZ) - ) - { - // Cannot intersect, bail out early - continue; - } - - // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom: - int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc - int DifY = itr->m_BlockY; - int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc - int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1); - int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height)); - int SqRad = itr->m_Radius * itr->m_Radius; - for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) - { - for (int y = Bottom; y <= Top; y++) - { - int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z); - if (4 * SqDist <= SqRad) - { - switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) - { - // Only carve out these specific block types - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_STONE: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_GRAVEL: - case E_BLOCK_SAND: - case E_BLOCK_SANDSTONE: - case E_BLOCK_NETHERRACK: - case E_BLOCK_COAL_ORE: - case E_BLOCK_IRON_ORE: - case E_BLOCK_GOLD_ORE: - case E_BLOCK_DIAMOND_ORE: - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - { - cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); - break; - } - default: break; - } - } - } // for y - } // for x, z - } // for itr - m_Points[] - - /* - #ifdef _DEBUG - // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave: - for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) - { - int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc - int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc - if ( - (DifX >= 0) && (DifX < cChunkDef::Width) && - (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) && - (DifZ >= 0) && (DifZ < cChunkDef::Width) - ) - { - cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE); - } - } // for itr - m_Points[] - #endif // _DEBUG - //*/ -} - - - - - -#ifdef _DEBUG -AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const -{ - AString SVG; - SVG.reserve(m_Points.size() * 20 + 200); - AppendPrintf(SVG, "m_BlockX, a_OffsetZ + itr->m_BlockZ); - Prefix = 'L'; - } - SVG.append("\"/>\n"); - return SVG; -} -#endif // _DEBUG - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenWormNestCaves::cCaveSystem: - -cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) : - m_BlockX(a_BlockX), - m_BlockZ(a_BlockZ), - m_Size(a_Size) -{ - int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3; - for (int i = 0; i < Num; i++) - { - int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset; - int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset; - int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20; - - // Generate three branches from the origin point: - // The tunnels generated depend on X, Y, Z and Branches, - // for the same set of numbers it generates the same offsets! - // That's why we add a +1 to X in the third line - GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3); - GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2); - GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3); - } -} - - - - - -cStructGenWormNestCaves::cCaveSystem::~cCaveSystem() -{ - Clear(); -} - - - - - - -void cStructGenWormNestCaves::cCaveSystem::ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap -) -{ - for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) - { - (*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap); - } // for itr - m_Tunnels[] -} - - - - - -#ifdef _DEBUG -AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const -{ - AString SVG; - SVG.reserve(512 * 1024); - for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) - { - SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ)); - } // for itr - m_Tunnels[] - - // Base point highlight: - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ - ); - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 - ); - - // A gray line from the base point to the first point of the ravine, for identification: - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, - a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX, - a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ - ); - - // Offset guides: - if (a_OffsetX > 0) - { - AppendPrintf(SVG, "\n", - a_OffsetX, a_OffsetX - ); - } - if (a_OffsetZ > 0) - { - AppendPrintf(SVG, "\n", - a_OffsetZ, a_OffsetZ - ); - } - - return SVG; -} -#endif // _DEBUG - - - - - -void cStructGenWormNestCaves::cCaveSystem::Clear(void) -{ - for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) - { - delete *itr; - } - m_Tunnels.clear(); -} - - - - - -void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint( - int a_OriginX, int a_OriginY, int a_OriginZ, - cNoise & a_Noise, int a_NumSegments -) -{ - int DoubleSize = m_Size * 2; - int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX); - for (int i = a_NumSegments - 1; i >= 0; --i) - { - int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2; - int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4; - int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2; - int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX); - m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise)); - GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i); - a_OriginX = EndX; - a_OriginY = EndY; - a_OriginZ = EndZ; - Radius = EndR; - } // for i - a_NumSegments -} - - - - - -int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ) -{ - // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large - int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11; - /* - // Not good enough: - // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value. - // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise - int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff); - // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384. - // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center - int res = 3 + abs((sum / 24) - 16); - */ - - // Algorithm of choice: random value in the range of zero to random value - heavily towards zero - int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1); - return res; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenWormNestCaves: - -cStructGenWormNestCaves::~cStructGenWormNestCaves() -{ - ClearCache(); -} - - - - - -void cStructGenWormNestCaves::ClearCache(void) -{ - for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - m_Cache[] - m_Cache.clear(); -} - - - - - -void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - cCaveSystems Caves; - GetCavesForChunk(ChunkX, ChunkZ, Caves); - for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr) - { - (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); - } // for itr - Caves[] -} - - - - - -void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves) -{ - int BaseX = a_ChunkX * cChunkDef::Width / m_Grid; - int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid; - if (BaseX < 0) - { - --BaseX; - } - if (BaseZ < 0) - { - --BaseZ; - } - BaseX -= NEIGHBORHOOD_SIZE / 2; - BaseZ -= NEIGHBORHOOD_SIZE / 2; - - // Walk the cache, move each cave system that we want into a_Caves: - int StartX = BaseX * m_Grid; - int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid; - int StartZ = BaseZ * m_Grid; - int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid; - for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) - { - if ( - ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && - ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) - ) - { - // want - a_Caves.push_back(*itr); - itr = m_Cache.erase(itr); - } - else - { - // don't want - ++itr; - } - } // for itr - m_Cache[] - - for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) - { - int RealX = (BaseX + x) * m_Grid; - for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) - { - int RealZ = (BaseZ + z) * m_Grid; - bool Found = false; - for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) - { - if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) - { - Found = true; - break; - } - } - if (!Found) - { - a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise)); - } - } - } - - // Copy a_Caves into m_Cache to the beginning: - cCaveSystems CavesCopy(a_Caves); - m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end()); - - // Trim the cache if it's too long: - if (m_Cache.size() > 100) - { - cCaveSystems::iterator itr = m_Cache.begin(); - std::advance(itr, 100); - for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } - itr = m_Cache.begin(); - std::advance(itr, 100); - m_Cache.erase(itr, m_Cache.end()); - } - - /* - // Uncomment this block for debugging the caves' shapes in 2D using an SVG export - #ifdef _DEBUG - AString SVG; - SVG.append("\n\n"); - SVG.reserve(2 * 1024 * 1024); - for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) - { - int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid); - Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid); - SVG.append((*itr)->ExportAsSVG(Color, 512, 512)); - } - SVG.append("\n"); - - AString fnam; - Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); - cFile File(fnam, cFile::fmWrite); - File.Write(SVG.c_str(), SVG.size()); - #endif // _DEBUG - //*/ -} - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenMarbleCaves: - -static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise ) -{ - static const float PI_2 = 1.57079633f; - float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4; - - oct1 = oct1 * oct1 * oct1; - if (oct1 < 0.f) oct1 = PI_2; - if (oct1 > PI_2) oct1 = PI_2; - - return oct1; -} - - - - - -void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc) -{ - cNoise Noise(m_Seed); - for (int z = 0; z < cChunkDef::Width; z++) - { - const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z); - for (int x = 0; x < cChunkDef::Width; x++) - { - const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x); - - int Top = a_ChunkDesc.GetHeight(x, z); - for (int y = 1; y < Top; ++y ) - { - if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE) - { - continue; - } - - const float yy = (float)y; - const float WaveNoise = 1; - if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); - } - } // for y - } // for x - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenDualRidgeCaves: - -void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc) -{ - for (int z = 0; z < cChunkDef::Width; z++) - { - const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10; - for (int x = 0; x < cChunkDef::Width; x++) - { - const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10; - - int Top = a_ChunkDesc.GetHeight(x, z); - for (int y = 1; y <= Top; ++y) - { - const float yy = (float)y / 10; - const float WaveNoise = 1; - float n1 = m_Noise1.CubicNoise3D(xx, yy, zz); - float n2 = m_Noise2.CubicNoise3D(xx, yy, zz); - float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; - float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; - if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); - } - } // for y - } // for x - } // for z -} - - - - + +// Caves.cpp + +// Implements the various cave structure generators: +// - cStructGenWormNestCaves +// - cStructGenDualRidgeCaves +// - cStructGenMarbleCaves +// - cStructGenNetherCaves + +/* +WormNestCave generator: +Caves are generated in "nests" - groups of tunnels generated from a single point. +For each chunk, all the nests that could intersect it are generated. +For each nest, first the schematic structure is generated (tunnel from ... to ..., branch, tunnel2 from ... to ...) +Then each tunnel is randomized by inserting points in between its ends. +Finally each tunnel is smoothed and Bresenham-3D-ed so that it is a collection of spheres with their centers next to each other. +When the tunnels are ready, they are simply carved into the chunk, one by one. +To optimize, each tunnel keeps track of its bounding box, so that it can be skipped for chunks that don't intersect it. + +MarbleCaves generator: +For each voxel a 3D noise function is evaluated, if the value crosses a boundary, the voxel is dug out, otherwise it is kept. +Problem with this is the amount of CPU work needed for each chunk. +Also the overall shape of the generated holes is unsatisfactory - there are whole "sheets" of holes in the ground. + +DualRidgeCaves generator: +Instead of evaluating a single noise function, two different noise functions are multiplied. This produces +regular tunnels instead of sheets. However due to the sheer amount of CPU work needed, the noise functions need to be +reduced in complexity in order for this generator to be useful, so the caves' shapes are "bubbly" at best. +*/ + +#include "Globals.h" +#include "Caves.h" + + + + + +/// How many nests in each direction are generated for a given chunk. Must be an even number +#define NEIGHBORHOOD_SIZE 8 + + + + + +const int MIN_RADIUS = 3; +const int MAX_RADIUS = 8; + + + + + +struct cCaveDefPoint +{ + int m_BlockX; + int m_BlockY; + int m_BlockZ; + int m_Radius; + + cCaveDefPoint(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Radius) : + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ), + m_Radius(a_Radius) + { + } +} ; + +typedef std::vector cCaveDefPoints; + + + + + +/// A single non-branching tunnel of a WormNestCave +class cCaveTunnel +{ + // The bounding box, including the radii around defpoints: + int m_MinBlockX, m_MaxBlockX; + int m_MinBlockY, m_MaxBlockY; + int m_MinBlockZ, m_MaxBlockZ; + + /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise + void Randomize(cNoise & a_Noise); + + /// Refines (adds and smooths) defpoints from a_Src into a_Dst; returns false if no refinement possible (segments too short) + bool RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst); + + /// Does rounds of smoothing, two passes of RefineDefPoints(), as long as they return true + void Smooth(void); + + /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block + void FinishLinear(void); + + /// Calculates the bounding box of the points present + void CalcBoundingBox(void); + +public: + cCaveDefPoints m_Points; + + cCaveTunnel( + int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, + int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, + cNoise & a_Noise + ); + + /// Carves the tunnel into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; + #endif // _DEBUG +} ; + +typedef std::vector cCaveTunnels; + + + + + +/// A collection of connected tunnels, possibly branching. +class cStructGenWormNestCaves::cCaveSystem +{ +public: + // The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk() + int m_BlockX; + int m_BlockZ; + + cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise); + ~cCaveSystem(); + + /// Carves the cave system into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const; + #endif // _DEBUG + +protected: + int m_Size; + cCaveTunnels m_Tunnels; + + void Clear(void); + + /// Generates a_Segment successive tunnels, with possible branches. Generates the same output for the same [x, y, z, a_Segments] + void GenerateTunnelsFromPoint( + int a_OriginX, int a_OriginY, int a_OriginZ, + cNoise & a_Noise, int a_Segments + ); + + /// Returns a radius based on the location provided. + int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ); +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCaveTunnel: + +cCaveTunnel::cCaveTunnel( + int a_BlockStartX, int a_BlockStartY, int a_BlockStartZ, int a_StartRadius, + int a_BlockEndX, int a_BlockEndY, int a_BlockEndZ, int a_EndRadius, + cNoise & a_Noise +) +{ + m_Points.push_back(cCaveDefPoint(a_BlockStartX, a_BlockStartY, a_BlockStartZ, a_StartRadius)); + m_Points.push_back(cCaveDefPoint(a_BlockEndX, a_BlockEndY, a_BlockEndZ, a_EndRadius)); + + if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0)) + { + // Don't bother detailing this cave, it's under the world anyway + return; + } + + Randomize(a_Noise); + Smooth(); + + // We know that the linear finishing won't affect the bounding box, so let's calculate it now, as we have less data: + CalcBoundingBox(); + + FinishLinear(); +} + + + + + +void cCaveTunnel::Randomize(cNoise & a_Noise) +{ + // Repeat 4 times: + for (int i = 0; i < 4; i++) + { + // For each already present point, insert a point in between it and its predecessor, shifted randomly. + int PrevX = m_Points.front().m_BlockX; + int PrevY = m_Points.front().m_BlockY; + int PrevZ = m_Points.front().m_BlockZ; + int PrevR = m_Points.front().m_Radius; + cCaveDefPoints Pts; + Pts.reserve(m_Points.size() * 2 + 1); + Pts.push_back(m_Points.front()); + for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) + { + int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11; + int len = (PrevX - itr->m_BlockX) * (PrevX - itr->m_BlockX); + len += (PrevY - itr->m_BlockY) * (PrevY - itr->m_BlockY); + len += (PrevZ - itr->m_BlockZ) * (PrevZ - itr->m_BlockZ); + len = 3 * (int)sqrt((double)len) / 4; + int Rad = std::min(MAX_RADIUS, std::max(MIN_RADIUS, (PrevR + itr->m_Radius) / 2 + (Random % 3) - 1)); + Random /= 4; + int x = (itr->m_BlockX + PrevX) / 2 + (Random % (len + 1) - len / 2); + Random /= 256; + int y = (itr->m_BlockY + PrevY) / 2 + (Random % (len / 2 + 1) - len / 4); + Random /= 256; + int z = (itr->m_BlockZ + PrevZ) / 2 + (Random % (len + 1) - len / 2); + Pts.push_back(cCaveDefPoint(x, y, z, Rad)); + Pts.push_back(*itr); + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + } + std::swap(Pts, m_Points); + } +} + + + + + +bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints & a_Dst) +{ + // Smoothing: for each line segment, add points on its 1/4 lengths + bool res = false; + int Num = a_Src.size() - 2; // this many intermediary points + a_Dst.clear(); + a_Dst.reserve(Num * 2 + 2); + cCaveDefPoints::const_iterator itr = a_Src.begin() + 1; + a_Dst.push_back(a_Src.front()); + int PrevX = a_Src.front().m_BlockX; + int PrevY = a_Src.front().m_BlockY; + int PrevZ = a_Src.front().m_BlockZ; + int PrevR = a_Src.front().m_Radius; + for (int i = 0; i <= Num; ++i, ++itr) + { + int dx = itr->m_BlockX - PrevX; + int dy = itr->m_BlockY - PrevY; + int dz = itr->m_BlockZ - PrevZ; + if (abs(dx) + abs(dz) + abs(dy) < 6) + { + // Too short a segment to smooth-subdivide into quarters + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + continue; + } + int dr = itr->m_Radius - PrevR; + int Rad1 = std::max(PrevR + 1 * dr / 4, 1); + int Rad2 = std::max(PrevR + 3 * dr / 4, 1); + a_Dst.push_back(cCaveDefPoint(PrevX + 1 * dx / 4, PrevY + 1 * dy / 4, PrevZ + 1 * dz / 4, Rad1)); + a_Dst.push_back(cCaveDefPoint(PrevX + 3 * dx / 4, PrevY + 3 * dy / 4, PrevZ + 3 * dz / 4, Rad2)); + PrevX = itr->m_BlockX; + PrevY = itr->m_BlockY; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + res = true; + } + a_Dst.push_back(a_Src.back()); + return res && (a_Src.size() < a_Dst.size()); +} + + + + + +void cCaveTunnel::Smooth(void) +{ + cCaveDefPoints Pts; + while (true) + { + if (!RefineDefPoints(m_Points, Pts)) + { + std::swap(Pts, m_Points); + return; + } + if (!RefineDefPoints(Pts, m_Points)) + { + return; + } + } +} + + + + + +void cCaveTunnel::FinishLinear(void) +{ + // For each segment, use Bresenham's 3D line algorithm to draw a "line" of defpoints + cCaveDefPoints Pts; + std::swap(Pts, m_Points); + + m_Points.reserve(Pts.size() * 3); + int PrevX = Pts.front().m_BlockX; + int PrevY = Pts.front().m_BlockY; + int PrevZ = Pts.front().m_BlockZ; + for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) + { + int x1 = itr->m_BlockX; + int y1 = itr->m_BlockY; + int z1 = itr->m_BlockZ; + int dx = abs(x1 - PrevX); + int dy = abs(y1 - PrevY); + int dz = abs(z1 - PrevZ); + int sx = (PrevX < x1) ? 1 : -1; + int sy = (PrevY < y1) ? 1 : -1; + int sz = (PrevZ < z1) ? 1 : -1; + int err = dx - dz; + int R = itr->m_Radius; + + if (dx >= std::max(dy, dz)) // x dominant + { + int yd = dy - dx / 2; + int zd = dz - dx / 2; + + while (true) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevX == x1) + { + break; + } + + if (yd >= 0) // move along y + { + PrevY += sy; + yd -= dx; + } + + if (zd >= 0) // move along z + { + PrevZ += sz; + zd -= dx; + } + + // move along x + PrevX += 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) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevY == y1) + { + break; + } + + if (xd >= 0) // move along x + { + PrevX += sx; + xd -= dy; + } + + if (zd >= 0) // move along z + { + PrevZ += sz; + zd -= dy; + } + + // move along y + PrevY += 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) + { + m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R)); + + if (PrevZ == z1) + { + break; + } + + if (xd >= 0) // move along x + { + PrevX += sx; + xd -= dz; + } + + if (yd >= 0) // move along y + { + PrevY += sy; + yd -= dz; + } + + // move along z + PrevZ += sz; + xd += dx; + yd += dy; + } + } // if (which dimension is dominant) + } // for itr +} + + + + + +void cCaveTunnel::CalcBoundingBox(void) +{ + m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX; + m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY; + m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ; + for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr) + { + m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius); + m_MaxBlockX = std::max(m_MaxBlockX, itr->m_BlockX + itr->m_Radius); + m_MinBlockY = std::min(m_MinBlockY, itr->m_BlockY - itr->m_Radius); + m_MaxBlockY = std::max(m_MaxBlockY, itr->m_BlockY + itr->m_Radius); + m_MinBlockZ = std::min(m_MinBlockZ, itr->m_BlockZ - itr->m_Radius); + m_MaxBlockZ = std::max(m_MaxBlockZ, itr->m_BlockZ + itr->m_Radius); + } // for itr - m_Points[] +} + + + + + +void cCaveTunnel::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + int BaseX = a_ChunkX * cChunkDef::Width; + int BaseZ = a_ChunkZ * cChunkDef::Width; + if ( + (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) || + (BaseX > m_MaxBlockX) || (BaseX + cChunkDef::Width < m_MinBlockX) + ) + { + // Tunnel does not intersect the chunk at all, bail out + return; + } + + int BlockStartX = a_ChunkX * cChunkDef::Width; + int BlockStartZ = a_ChunkZ * cChunkDef::Width; + int BlockEndX = BlockStartX + cChunkDef::Width; + int BlockEndZ = BlockStartZ + cChunkDef::Width; + for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + if ( + (itr->m_BlockX + itr->m_Radius < BlockStartX) || + (itr->m_BlockX - itr->m_Radius > BlockEndX) || + (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || + (itr->m_BlockZ - itr->m_Radius > BlockEndZ) + ) + { + // Cannot intersect, bail out early + continue; + } + + // Carve out a sphere around the xyz point, m_Radius in diameter; skip 3/7 off the top and bottom: + int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc + int DifY = itr->m_BlockY; + int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc + int Bottom = std::max(itr->m_BlockY - 3 * itr->m_Radius / 7, 1); + int Top = std::min(itr->m_BlockY + 3 * itr->m_Radius / 7, (int)(cChunkDef::Height)); + int SqRad = itr->m_Radius * itr->m_Radius; + for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = Bottom; y <= Top; y++) + { + int SqDist = (DifX - x) * (DifX - x) + (DifY - y) * (DifY - y) + (DifZ - z) * (DifZ - z); + if (4 * SqDist <= SqRad) + { + switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) + { + // Only carve out these specific block types + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_GRAVEL: + case E_BLOCK_SAND: + case E_BLOCK_SANDSTONE: + case E_BLOCK_NETHERRACK: + case E_BLOCK_COAL_ORE: + case E_BLOCK_IRON_ORE: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); + break; + } + default: break; + } + } + } // for y + } // for x, z + } // for itr - m_Points[] + + /* + #ifdef _DEBUG + // For debugging purposes, outline the shape of the cave using glowstone, *after* carving the entire cave: + for (cCaveDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + int DifX = itr->m_BlockX - BlockStartX; // substitution for faster calc + int DifZ = itr->m_BlockZ - BlockStartZ; // substitution for faster calc + if ( + (DifX >= 0) && (DifX < cChunkDef::Width) && + (itr->m_BlockY > 0) && (itr->m_BlockY < cChunkDef::Height) && + (DifZ >= 0) && (DifZ < cChunkDef::Width) + ) + { + cChunkDef::SetBlock(a_BlockTypes, DifX, itr->m_BlockY, DifZ, E_BLOCK_GLOWSTONE); + } + } // for itr - m_Points[] + #endif // _DEBUG + //*/ +} + + + + + +#ifdef _DEBUG +AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + SVG.reserve(m_Points.size() * 20 + 200); + AppendPrintf(SVG, "m_BlockX, a_OffsetZ + itr->m_BlockZ); + Prefix = 'L'; + } + SVG.append("\"/>\n"); + return SVG; +} +#endif // _DEBUG + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenWormNestCaves::cCaveSystem: + +cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_Size(a_Size) +{ + int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3; + for (int i = 0; i < Num; i++) + { + int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset; + int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset; + int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20; + + // Generate three branches from the origin point: + // The tunnels generated depend on X, Y, Z and Branches, + // for the same set of numbers it generates the same offsets! + // That's why we add a +1 to X in the third line + GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 3); + GenerateTunnelsFromPoint(OriginX, OriginY, OriginZ, a_Noise, 2); + GenerateTunnelsFromPoint(OriginX + 1, OriginY, OriginZ, a_Noise, 3); + } +} + + + + + +cStructGenWormNestCaves::cCaveSystem::~cCaveSystem() +{ + Clear(); +} + + + + + + +void cStructGenWormNestCaves::cCaveSystem::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap); + } // for itr - m_Tunnels[] +} + + + + + +#ifdef _DEBUG +AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + SVG.reserve(512 * 1024); + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ)); + } // for itr - m_Tunnels[] + + // Base point highlight: + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ + ); + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 + ); + + // A gray line from the base point to the first point of the ravine, for identification: + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, + a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX, + a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ + ); + + // Offset guides: + if (a_OffsetX > 0) + { + AppendPrintf(SVG, "\n", + a_OffsetX, a_OffsetX + ); + } + if (a_OffsetZ > 0) + { + AppendPrintf(SVG, "\n", + a_OffsetZ, a_OffsetZ + ); + } + + return SVG; +} +#endif // _DEBUG + + + + + +void cStructGenWormNestCaves::cCaveSystem::Clear(void) +{ + for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr) + { + delete *itr; + } + m_Tunnels.clear(); +} + + + + + +void cStructGenWormNestCaves::cCaveSystem::GenerateTunnelsFromPoint( + int a_OriginX, int a_OriginY, int a_OriginZ, + cNoise & a_Noise, int a_NumSegments +) +{ + int DoubleSize = m_Size * 2; + int Radius = GetRadius(a_Noise, a_OriginX + a_OriginY, a_OriginY + a_OriginZ, a_OriginZ + a_OriginX); + for (int i = a_NumSegments - 1; i >= 0; --i) + { + int EndX = a_OriginX + (((a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ + 11 * a_NumSegments) / 7) % DoubleSize) - m_Size) / 2; + int EndY = a_OriginY + (((a_Noise.IntNoise3DInt(a_OriginY, 13 * a_NumSegments, a_OriginZ + a_OriginX) / 7) % DoubleSize) - m_Size) / 4; + int EndZ = a_OriginZ + (((a_Noise.IntNoise3DInt(a_OriginZ + 17 * a_NumSegments, a_OriginX, a_OriginY) / 7) % DoubleSize) - m_Size) / 2; + int EndR = GetRadius(a_Noise, a_OriginX + 7 * i, a_OriginY + 11 * i, a_OriginZ + a_OriginX); + m_Tunnels.push_back(new cCaveTunnel(a_OriginX, a_OriginY, a_OriginZ, Radius, EndX, EndY, EndZ, EndR, a_Noise)); + GenerateTunnelsFromPoint(EndX, EndY, EndZ, a_Noise, i); + a_OriginX = EndX; + a_OriginY = EndY; + a_OriginZ = EndZ; + Radius = EndR; + } // for i - a_NumSegments +} + + + + + +int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ) +{ + // Instead of a flat distribution noise function, we need to shape it, so that most caves are smallish and only a few select are large + int rnd = a_Noise.IntNoise3DInt(a_OriginX, a_OriginY, a_OriginZ) / 11; + /* + // Not good enough: + // The algorithm of choice: emulate gauss-distribution noise by adding 3 flat noises, then fold it in half using absolute value. + // To save on processing, use one random value and extract 3 bytes to be separately added as the gaussian noise + int sum = (rnd & 0xff) + ((rnd >> 8) & 0xff) + ((rnd >> 16) & 0xff); + // sum is now a gaussian-distribution noise within [0 .. 767], with center at 384. + // We want mapping 384 -> 3, 0 -> 19, 768 -> 19, so divide by 24 to get [0 .. 31] with center at 16, then use abs() to fold around the center + int res = 3 + abs((sum / 24) - 16); + */ + + // Algorithm of choice: random value in the range of zero to random value - heavily towards zero + int res = MIN_RADIUS + (rnd >> 8) % ((rnd % (MAX_RADIUS - MIN_RADIUS)) + 1); + return res; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenWormNestCaves: + +cStructGenWormNestCaves::~cStructGenWormNestCaves() +{ + ClearCache(); +} + + + + + +void cStructGenWormNestCaves::ClearCache(void) +{ + for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenWormNestCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cCaveSystems Caves; + GetCavesForChunk(ChunkX, ChunkZ, Caves); + for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr) + { + (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); + } // for itr - Caves[] +} + + + + + +void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_Grid; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= NEIGHBORHOOD_SIZE / 2; + BaseZ -= NEIGHBORHOOD_SIZE / 2; + + // Walk the cache, move each cave system that we want into a_Caves: + int StartX = BaseX * m_Grid; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid; + int StartZ = BaseZ * m_Grid; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid; + for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_Caves.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_Grid; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_Grid; + bool Found = false; + for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } + if (!Found) + { + a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise)); + } + } + } + + // Copy a_Caves into m_Cache to the beginning: + cCaveSystems CavesCopy(a_Caves); + m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cCaveSystems::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } + + /* + // Uncomment this block for debugging the caves' shapes in 2D using an SVG export + #ifdef _DEBUG + AString SVG; + SVG.append("\n\n"); + SVG.reserve(2 * 1024 * 1024); + for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr) + { + int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid); + Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid); + SVG.append((*itr)->ExportAsSVG(Color, 512, 512)); + } + SVG.append("\n"); + + AString fnam; + Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); + cFile File(fnam, cFile::fmWrite); + File.Write(SVG.c_str(), SVG.size()); + #endif // _DEBUG + //*/ +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMarbleCaves: + +static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise ) +{ + static const float PI_2 = 1.57079633f; + float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4; + + oct1 = oct1 * oct1 * oct1; + if (oct1 < 0.f) oct1 = PI_2; + if (oct1 > PI_2) oct1 = PI_2; + + return oct1; +} + + + + + +void cStructGenMarbleCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + cNoise Noise(m_Seed); + for (int z = 0; z < cChunkDef::Width; z++) + { + const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z); + for (int x = 0; x < cChunkDef::Width; x++) + { + const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x); + + int Top = a_ChunkDesc.GetHeight(x, z); + for (int y = 1; y < Top; ++y ) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_STONE) + { + continue; + } + + const float yy = (float)y; + const float WaveNoise = 1; + if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + } // for y + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenDualRidgeCaves: + +void cStructGenDualRidgeCaves::GenStructures(cChunkDesc & a_ChunkDesc) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + const float zz = (float)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z) / 10; + for (int x = 0; x < cChunkDef::Width; x++) + { + const float xx = (float)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + x) / 10; + + int Top = a_ChunkDesc.GetHeight(x, z); + for (int y = 1; y <= Top; ++y) + { + const float yy = (float)y / 10; + const float WaveNoise = 1; + float n1 = m_Noise1.CubicNoise3D(xx, yy, zz); + float n2 = m_Noise2.CubicNoise3D(xx, yy, zz); + float n3 = m_Noise1.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; + float n4 = m_Noise2.CubicNoise3D(xx * 4, yy * 4, zz * 4) / 4; + if ((abs(n1 + n3) * abs(n2 + n4)) > m_Threshold) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + } // for y + } // for x + } // for z +} + + + + diff --git a/source/Generating/Caves.h b/source/Generating/Caves.h index bb69550a8..70cf6fe8c 100644 --- a/source/Generating/Caves.h +++ b/source/Generating/Caves.h @@ -1,102 +1,102 @@ - -// Caves.h - -// Interfaces to the various cave structure generators: -// - cStructGenWormNestCaves -// - cStructGenMarbleCaves -// - cStructGenNetherCaves - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cStructGenMarbleCaves : - public cStructureGen -{ -public: - cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {} - -protected: - - int m_Seed; - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cStructGenDualRidgeCaves : - public cStructureGen -{ -public: - cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) : - m_Noise1(a_Seed), - m_Noise2(2 * a_Seed + 19999), - m_Seed(a_Seed), - m_Threshold(a_Threshold) - { - } - -protected: - cNoise m_Noise1; - cNoise m_Noise2; - int m_Seed; - float m_Threshold; - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cStructGenWormNestCaves : - public cStructureGen -{ -public: - cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) : - m_Noise(a_Seed), - m_Size(a_Size), - m_Grid(a_Grid), - m_MaxOffset(a_MaxOffset) - { - } - - ~cStructGenWormNestCaves(); - -protected: - class cCaveSystem; // fwd: Caves.cpp - typedef std::list cCaveSystems; - - cNoise m_Noise; - int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel - int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to - int m_Grid; // average spacing of the nests - cCaveSystems m_Cache; - - /// Clears everything from the cache - void ClearCache(void); - - /// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function. - void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves); - - // cStructGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - + +// Caves.h + +// Interfaces to the various cave structure generators: +// - cStructGenWormNestCaves +// - cStructGenMarbleCaves +// - cStructGenNetherCaves + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenMarbleCaves : + public cStructureGen +{ +public: + cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {} + +protected: + + int m_Seed; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenDualRidgeCaves : + public cStructureGen +{ +public: + cStructGenDualRidgeCaves(int a_Seed, float a_Threshold) : + m_Noise1(a_Seed), + m_Noise2(2 * a_Seed + 19999), + m_Seed(a_Seed), + m_Threshold(a_Threshold) + { + } + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + int m_Seed; + float m_Threshold; + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenWormNestCaves : + public cStructureGen +{ +public: + cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) : + m_Noise(a_Seed), + m_Size(a_Size), + m_Grid(a_Grid), + m_MaxOffset(a_MaxOffset) + { + } + + ~cStructGenWormNestCaves(); + +protected: + class cCaveSystem; // fwd: Caves.cpp + typedef std::list cCaveSystems; + + cNoise m_Noise; + int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel + int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to + int m_Grid; // average spacing of the nests + cCaveSystems m_Cache; + + /// Clears everything from the cache + void ClearCache(void); + + /// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function. + void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves); + + // cStructGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/source/Generating/ChunkDesc.cpp b/source/Generating/ChunkDesc.cpp index 5c59055f7..dc6c74a3c 100644 --- a/source/Generating/ChunkDesc.cpp +++ b/source/Generating/ChunkDesc.cpp @@ -1,578 +1,578 @@ - -// ChunkDesc.cpp - -// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING. - -#include "Globals.h" -#include "ChunkDesc.h" -#include "../BlockArea.h" -#include "../Cuboid.h" -#include "../Noise.h" - - - - - -cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) : - m_ChunkX(a_ChunkX), - m_ChunkZ(a_ChunkZ), - m_bUseDefaultBiomes(true), - m_bUseDefaultHeight(true), - m_bUseDefaultComposition(true), - m_bUseDefaultStructures(true), - m_bUseDefaultFinish(true) -{ - m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width); - /* - memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes)); - memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles)); - */ - memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap)); - memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap)); -} - - - - - -cChunkDesc::~cChunkDesc() -{ - // Nothing needed yet -} - - - - - -void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ) -{ - m_ChunkX = a_ChunkX; - m_ChunkZ = a_ChunkZ; -} - - - - - -void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta); -} - - - - - -void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); -} - - - - - -void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); -} - - - - - -void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) -{ - cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType); -} - - - - - -BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ) -{ - return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ); -} - - - - - -NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ) -{ - return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ); -} - - - - - -void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta) -{ - m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta); -} - - - - - -void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID) -{ - cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID); -} - - - - -EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ) -{ - return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); -} - - - - - -void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height) -{ - cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height); -} - - - - - -int cChunkDesc::GetHeight(int a_RelX, int a_RelZ) -{ - return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ); -} - - - - - -void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes) -{ - m_bUseDefaultBiomes = a_bUseDefaultBiomes; -} - - - - - -bool cChunkDesc::IsUsingDefaultBiomes(void) const -{ - return m_bUseDefaultBiomes; -} - - - - - -void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight) -{ - m_bUseDefaultHeight = a_bUseDefaultHeight; -} - - - - - -bool cChunkDesc::IsUsingDefaultHeight(void) const -{ - return m_bUseDefaultHeight; -} - - - - - -void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition) -{ - m_bUseDefaultComposition = a_bUseDefaultComposition; -} - - - - - -bool cChunkDesc::IsUsingDefaultComposition(void) const -{ - return m_bUseDefaultComposition; -} - - - - - -void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures) -{ - m_bUseDefaultStructures = a_bUseDefaultStructures; -} - - - - - -bool cChunkDesc::IsUsingDefaultStructures(void) const -{ - return m_bUseDefaultStructures; -} - - - - - -void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish) -{ - m_bUseDefaultFinish = a_bUseDefaultFinish; -} - - - - - -bool cChunkDesc::IsUsingDefaultFinish(void) const -{ - return m_bUseDefaultFinish; -} - - - - -void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy) -{ - m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy); -} - - - - - -void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ) -{ - // Normalize the coords: - if (a_MinRelX > a_MaxRelX) - { - std::swap(a_MinRelX, a_MaxRelX); - } - if (a_MinRelY > a_MaxRelY) - { - std::swap(a_MinRelY, a_MaxRelY); - } - if (a_MinRelZ > a_MaxRelZ) - { - std::swap(a_MinRelZ, a_MaxRelZ); - } - - // Include the Max coords: - a_MaxRelX += 1; - a_MaxRelY += 1; - a_MaxRelZ += 1; - - // Check coords validity: - if (a_MinRelX < 0) - { - LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__); - a_MinRelX = 0; - } - else if (a_MinRelX >= cChunkDef::Width) - { - LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__); - a_MinRelX = cChunkDef::Width - 1; - } - if (a_MaxRelX < 0) - { - LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__); - a_MaxRelX = 0; - } - else if (a_MinRelX >= cChunkDef::Width) - { - LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__); - a_MaxRelX = cChunkDef::Width - 1; - } - - if (a_MinRelY < 0) - { - LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__); - a_MinRelY = 0; - } - else if (a_MinRelY >= cChunkDef::Height) - { - LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MinRelY = cChunkDef::Height - 1; - } - if (a_MaxRelY < 0) - { - LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__); - a_MaxRelY = 0; - } - else if (a_MinRelY >= cChunkDef::Height) - { - LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__); - a_MaxRelY = cChunkDef::Height - 1; - } - - if (a_MinRelZ < 0) - { - LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__); - a_MinRelZ = 0; - } - else if (a_MinRelZ >= cChunkDef::Width) - { - LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); - a_MinRelZ = cChunkDef::Width - 1; - } - if (a_MaxRelZ < 0) - { - LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__); - a_MaxRelZ = 0; - } - else if (a_MinRelZ >= cChunkDef::Width) - { - LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); - a_MaxRelZ = cChunkDef::Width - 1; - } - - // Prepare the block area: - int SizeX = a_MaxRelX - a_MinRelX; - int SizeY = a_MaxRelY - a_MinRelY; - int SizeZ = a_MaxRelZ - a_MinRelZ; - a_Dest.Clear(); - a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX; - a_Dest.m_OriginY = a_MinRelY; - a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ; - a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas); - - for (int y = 0; y < SizeY; y++) - { - int CDY = a_MinRelY + y; - for (int z = 0; z < SizeZ; z++) - { - int CDZ = a_MinRelZ + z; - for (int x = 0; x < SizeX; x++) - { - int CDX = a_MinRelX + x; - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta); - a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta); - } // for x - } // for z - } // for y -} - - - - - -HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const -{ - HEIGHTTYPE MaxHeight = m_HeightMap[0]; - for (int i = 1; i < ARRAYCOUNT(m_HeightMap); i++) - { - if (m_HeightMap[i] > MaxHeight) - { - MaxHeight = m_HeightMap[i]; - } - } - return MaxHeight; -} - - - - - -void cChunkDesc::FillRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - int MinX = std::max(a_MinX, 0); - int MinY = std::max(a_MinY, 0); - int MinZ = std::max(a_MinZ, 0); - int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); - int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); - int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); - - for (int y = MinY; y <= MaxY; y++) - { - for (int z = MinZ; z <= MaxZ; z++) - { - for (int x = MinX; x <= MaxX; x++) - { - SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); - } - } // for z - } // for y -} - - - - - -void cChunkDesc::ReplaceRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, - BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta -) -{ - int MinX = std::max(a_MinX, 0); - int MinY = std::max(a_MinY, 0); - int MinZ = std::max(a_MinZ, 0); - int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); - int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); - int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); - - for (int y = MinY; y <= MaxY; y++) - { - for (int z = MinZ; z <= MaxZ; z++) - { - for (int x = MinX; x <= MaxX; x++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - GetBlockTypeMeta(x, y, z, BlockType, BlockMeta); - if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta)) - { - SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); - } - } - } // for z - } // for y -} - - - - - -void cChunkDesc::FloorRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta -) -{ - int MinX = std::max(a_MinX, 0); - int MinY = std::max(a_MinY, 0); - int MinZ = std::max(a_MinZ, 0); - int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); - int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); - int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); - - for (int y = MinY; y <= MaxY; y++) - { - for (int z = MinZ; z <= MaxZ; z++) - { - for (int x = MinX; x <= MaxX; x++) - { - switch (GetBlockType(x, y, z)) - { - case E_BLOCK_AIR: - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - { - SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); - break; - } - } // switch (GetBlockType) - } // for x - } // for z - } // for y -} - - - - - -void cChunkDesc::RandomFillRelCuboid( - int a_MinX, int a_MaxX, - int a_MinY, int a_MaxY, - int a_MinZ, int a_MaxZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - int a_RandomSeed, int a_ChanceOutOf10k -) -{ - cNoise Noise(a_RandomSeed); - int MinX = std::max(a_MinX, 0); - int MinY = std::max(a_MinY, 0); - int MinZ = std::max(a_MinZ, 0); - int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); - int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); - int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); - - for (int y = MinY; y <= MaxY; y++) - { - for (int z = MinZ; z <= MaxZ; z++) - { - for (int x = MinX; x <= MaxX; x++) - { - int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000; - if (rnd <= a_ChanceOutOf10k) - { - SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); - } - } - } // for z - } // for y -} - - - - - -void cChunkDesc::AddBlockEntity(cBlockEntity * a_BlockEntity) -{ - m_BlockEntities.push_back(a_BlockEntity); -} - - - - - -void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas) -{ - const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas(); - for (int i = 0; i < ARRAYCOUNT(a_DestMetas); i++) - { - a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4); - } -} - - - - - -#ifdef _DEBUG - -void cChunkDesc::VerifyHeightmap(void) -{ - for (int x = 0; x < cChunkDef::Width; x++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = cChunkDef::Height - 1; y > 0; y--) - { - BLOCKTYPE BlockType = GetBlockType(x, y, z); - if (BlockType != E_BLOCK_AIR) - { - int Height = GetHeight(x, z); - ASSERT(Height == y); - break; - } - } // for y - } // for z - } // for x -} - -#endif // _DEBUG - - - - - + +// ChunkDesc.cpp + +// Implements the cChunkDesc class representing the chunk description used while generating a chunk. This class is also exported to Lua for HOOK_CHUNK_GENERATING. + +#include "Globals.h" +#include "ChunkDesc.h" +#include "../BlockArea.h" +#include "../Cuboid.h" +#include "../Noise.h" + + + + + +cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) : + m_ChunkX(a_ChunkX), + m_ChunkZ(a_ChunkZ), + m_bUseDefaultBiomes(true), + m_bUseDefaultHeight(true), + m_bUseDefaultComposition(true), + m_bUseDefaultStructures(true), + m_bUseDefaultFinish(true) +{ + m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width); + /* + memset(m_BlockTypes, 0, sizeof(cChunkDef::BlockTypes)); + memset(m_BlockMeta, 0, sizeof(cChunkDef::BlockNibbles)); + */ + memset(m_BiomeMap, 0, sizeof(cChunkDef::BiomeMap)); + memset(m_HeightMap, 0, sizeof(cChunkDef::HeightMap)); +} + + + + + +cChunkDesc::~cChunkDesc() +{ + // Nothing needed yet +} + + + + + +void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkX = a_ChunkX; + m_ChunkZ = a_ChunkZ; +} + + + + + +void cChunkDesc::FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.Fill(cBlockArea::baTypes | cBlockArea::baMetas, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.SetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + m_BlockArea.GetRelBlockTypeMeta(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType) +{ + cChunkDef::SetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ, a_BlockType); +} + + + + + +BLOCKTYPE cChunkDesc::GetBlockType(int a_RelX, int a_RelY, int a_RelZ) +{ + return cChunkDef::GetBlock(m_BlockArea.GetBlockTypes(), a_RelX, a_RelY, a_RelZ); +} + + + + + +NIBBLETYPE cChunkDesc::GetBlockMeta(int a_RelX, int a_RelY, int a_RelZ) +{ + return m_BlockArea.GetRelBlockMeta(a_RelX, a_RelY, a_RelZ); +} + + + + + +void cChunkDesc::SetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta) +{ + m_BlockArea.SetRelBlockMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta); +} + + + + + +void cChunkDesc::SetBiome(int a_RelX, int a_RelZ, int a_BiomeID) +{ + cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, (EMCSBiome)a_BiomeID); +} + + + + +EMCSBiome cChunkDesc::GetBiome(int a_RelX, int a_RelZ) +{ + return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); +} + + + + + +void cChunkDesc::SetHeight(int a_RelX, int a_RelZ, int a_Height) +{ + cChunkDef::SetHeight(m_HeightMap, a_RelX, a_RelZ, a_Height); +} + + + + + +int cChunkDesc::GetHeight(int a_RelX, int a_RelZ) +{ + return cChunkDef::GetHeight(m_HeightMap, a_RelX, a_RelZ); +} + + + + + +void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes) +{ + m_bUseDefaultBiomes = a_bUseDefaultBiomes; +} + + + + + +bool cChunkDesc::IsUsingDefaultBiomes(void) const +{ + return m_bUseDefaultBiomes; +} + + + + + +void cChunkDesc::SetUseDefaultHeight(bool a_bUseDefaultHeight) +{ + m_bUseDefaultHeight = a_bUseDefaultHeight; +} + + + + + +bool cChunkDesc::IsUsingDefaultHeight(void) const +{ + return m_bUseDefaultHeight; +} + + + + + +void cChunkDesc::SetUseDefaultComposition(bool a_bUseDefaultComposition) +{ + m_bUseDefaultComposition = a_bUseDefaultComposition; +} + + + + + +bool cChunkDesc::IsUsingDefaultComposition(void) const +{ + return m_bUseDefaultComposition; +} + + + + + +void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures) +{ + m_bUseDefaultStructures = a_bUseDefaultStructures; +} + + + + + +bool cChunkDesc::IsUsingDefaultStructures(void) const +{ + return m_bUseDefaultStructures; +} + + + + + +void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish) +{ + m_bUseDefaultFinish = a_bUseDefaultFinish; +} + + + + + +bool cChunkDesc::IsUsingDefaultFinish(void) const +{ + return m_bUseDefaultFinish; +} + + + + +void cChunkDesc::WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy) +{ + m_BlockArea.Merge(a_BlockArea, a_RelX, a_RelY, a_RelZ, a_MergeStrategy); +} + + + + + +void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ) +{ + // Normalize the coords: + if (a_MinRelX > a_MaxRelX) + { + std::swap(a_MinRelX, a_MaxRelX); + } + if (a_MinRelY > a_MaxRelY) + { + std::swap(a_MinRelY, a_MaxRelY); + } + if (a_MinRelZ > a_MaxRelZ) + { + std::swap(a_MinRelZ, a_MaxRelZ); + } + + // Include the Max coords: + a_MaxRelX += 1; + a_MaxRelY += 1; + a_MaxRelZ += 1; + + // Check coords validity: + if (a_MinRelX < 0) + { + LOGWARNING("%s: MinRelX less than zero, adjusting to zero", __FUNCTION__); + a_MinRelX = 0; + } + else if (a_MinRelX >= cChunkDef::Width) + { + LOGWARNING("%s: MinRelX more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MinRelX = cChunkDef::Width - 1; + } + if (a_MaxRelX < 0) + { + LOGWARNING("%s: MaxRelX less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelX = 0; + } + else if (a_MinRelX >= cChunkDef::Width) + { + LOGWARNING("%s: MaxRelX more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MaxRelX = cChunkDef::Width - 1; + } + + if (a_MinRelY < 0) + { + LOGWARNING("%s: MinRelY less than zero, adjusting to zero", __FUNCTION__); + a_MinRelY = 0; + } + else if (a_MinRelY >= cChunkDef::Height) + { + LOGWARNING("%s: MinRelY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MinRelY = cChunkDef::Height - 1; + } + if (a_MaxRelY < 0) + { + LOGWARNING("%s: MaxRelY less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelY = 0; + } + else if (a_MinRelY >= cChunkDef::Height) + { + LOGWARNING("%s: MaxRelY more than chunk height, adjusting to chunk height", __FUNCTION__); + a_MaxRelY = cChunkDef::Height - 1; + } + + if (a_MinRelZ < 0) + { + LOGWARNING("%s: MinRelZ less than zero, adjusting to zero", __FUNCTION__); + a_MinRelZ = 0; + } + else if (a_MinRelZ >= cChunkDef::Width) + { + LOGWARNING("%s: MinRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MinRelZ = cChunkDef::Width - 1; + } + if (a_MaxRelZ < 0) + { + LOGWARNING("%s: MaxRelZ less than zero, adjusting to zero", __FUNCTION__); + a_MaxRelZ = 0; + } + else if (a_MinRelZ >= cChunkDef::Width) + { + LOGWARNING("%s: MaxRelZ more than chunk width, adjusting to chunk width", __FUNCTION__); + a_MaxRelZ = cChunkDef::Width - 1; + } + + // Prepare the block area: + int SizeX = a_MaxRelX - a_MinRelX; + int SizeY = a_MaxRelY - a_MinRelY; + int SizeZ = a_MaxRelZ - a_MinRelZ; + a_Dest.Clear(); + a_Dest.m_OriginX = m_ChunkX * cChunkDef::Width + a_MinRelX; + a_Dest.m_OriginY = a_MinRelY; + a_Dest.m_OriginZ = m_ChunkZ * cChunkDef::Width + a_MinRelZ; + a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas); + + for (int y = 0; y < SizeY; y++) + { + int CDY = a_MinRelY + y; + for (int z = 0; z < SizeZ; z++) + { + int CDZ = a_MinRelZ + z; + for (int x = 0; x < SizeX; x++) + { + int CDX = a_MinRelX + x; + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(CDX, CDY, CDZ, BlockType, BlockMeta); + a_Dest.SetRelBlockTypeMeta(x, y, z, BlockType, BlockMeta); + } // for x + } // for z + } // for y +} + + + + + +HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const +{ + HEIGHTTYPE MaxHeight = m_HeightMap[0]; + for (int i = 1; i < ARRAYCOUNT(m_HeightMap); i++) + { + if (m_HeightMap[i] > MaxHeight) + { + MaxHeight = m_HeightMap[i]; + } + } + return MaxHeight; +} + + + + + +void cChunkDesc::FillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); + } + } // for z + } // for y +} + + + + + +void cChunkDesc::ReplaceRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(x, y, z, BlockType, BlockMeta); + if ((BlockType == a_SrcType) && (BlockMeta == a_SrcMeta)) + { + SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); + } + } + } // for z + } // for y +} + + + + + +void cChunkDesc::FloorRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta +) +{ + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + switch (GetBlockType(x, y, z)) + { + case E_BLOCK_AIR: + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + SetBlockTypeMeta(x, y, z, a_DstType, a_DstMeta); + break; + } + } // switch (GetBlockType) + } // for x + } // for z + } // for y +} + + + + + +void cChunkDesc::RandomFillRelCuboid( + int a_MinX, int a_MaxX, + int a_MinY, int a_MaxY, + int a_MinZ, int a_MaxZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + int a_RandomSeed, int a_ChanceOutOf10k +) +{ + cNoise Noise(a_RandomSeed); + int MinX = std::max(a_MinX, 0); + int MinY = std::max(a_MinY, 0); + int MinZ = std::max(a_MinZ, 0); + int MaxX = std::min(a_MaxX, cChunkDef::Width - 1); + int MaxY = std::min(a_MaxY, cChunkDef::Height - 1); + int MaxZ = std::min(a_MaxZ, cChunkDef::Width - 1); + + for (int y = MinY; y <= MaxY; y++) + { + for (int z = MinZ; z <= MaxZ; z++) + { + for (int x = MinX; x <= MaxX; x++) + { + int rnd = (Noise.IntNoise3DInt(x, y, z) / 7) % 10000; + if (rnd <= a_ChanceOutOf10k) + { + SetBlockTypeMeta(x, y, z, a_BlockType, a_BlockMeta); + } + } + } // for z + } // for y +} + + + + + +void cChunkDesc::AddBlockEntity(cBlockEntity * a_BlockEntity) +{ + m_BlockEntities.push_back(a_BlockEntity); +} + + + + + +void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas) +{ + const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas(); + for (int i = 0; i < ARRAYCOUNT(a_DestMetas); i++) + { + a_DestMetas[i] = AreaMetas[2 * i] | (AreaMetas[2 * i + 1] << 4); + } +} + + + + + +#ifdef _DEBUG + +void cChunkDesc::VerifyHeightmap(void) +{ + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = cChunkDef::Height - 1; y > 0; y--) + { + BLOCKTYPE BlockType = GetBlockType(x, y, z); + if (BlockType != E_BLOCK_AIR) + { + int Height = GetHeight(x, z); + ASSERT(Height == y); + break; + } + } // for y + } // for z + } // for x +} + +#endif // _DEBUG + + + + + diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp index c038712aa..fb31ec7b2 100644 --- a/source/Generating/ComposableGenerator.cpp +++ b/source/Generating/ComposableGenerator.cpp @@ -1,524 +1,524 @@ - -// ComposableGenerator.cpp - -// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks - -#include "Globals.h" - -#include "ComposableGenerator.h" -#include "../World.h" -#include "../../iniFile/iniFile.h" -#include "../Root.h" - -// Individual composed algorithms: -#include "BioGen.h" -#include "HeiGen.h" -#include "CompoGen.h" -#include "StructGen.h" -#include "FinishGen.h" - -#include "Caves.h" -#include "DistortedHeightmap.h" -#include "EndGen.h" -#include "MineShafts.h" -#include "Noise3DGenerator.h" -#include "Ravines.h" - - - - - - - - - - -cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : - super(a_ChunkGenerator), - m_BiomeGen(NULL), - m_HeightGen(NULL), - m_CompositionGen(NULL), - m_UnderlyingBiomeGen(NULL), - m_UnderlyingHeightGen(NULL), - m_UnderlyingCompositionGen(NULL) -{ -} - - - - - -cComposableGenerator::~cComposableGenerator() -{ - // Delete the generating composition: - for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) - { - delete *itr; - } - m_FinishGens.clear(); - for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) - { - delete *itr; - } - m_StructureGens.clear(); - - delete m_CompositionGen; - m_CompositionGen = NULL; - delete m_HeightGen; - m_HeightGen = NULL; - delete m_BiomeGen; - m_BiomeGen = NULL; - delete m_UnderlyingCompositionGen; - m_UnderlyingCompositionGen = NULL; - delete m_UnderlyingHeightGen; - m_UnderlyingHeightGen = NULL; - delete m_UnderlyingBiomeGen; - m_UnderlyingBiomeGen = NULL; -} - - - - - -void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) -{ - super::Initialize(a_World, a_IniFile); - - InitBiomeGen(a_IniFile); - InitHeightGen(a_IniFile); - InitCompositionGen(a_IniFile); - InitStructureGens(a_IniFile); - InitFinishGens(a_IniFile); -} - - - - - -void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading - { - m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); - } -} - - - - - -void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) -{ - if (a_ChunkDesc.IsUsingDefaultBiomes()) - { - m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap()); - } - - if (a_ChunkDesc.IsUsingDefaultHeight()) - { - m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap()); - } - - if (a_ChunkDesc.IsUsingDefaultComposition()) - { - m_CompositionGen->ComposeTerrain(a_ChunkDesc); - } - - if (a_ChunkDesc.IsUsingDefaultStructures()) - { - for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) - { - (*itr)->GenStructures(a_ChunkDesc); - } // for itr - m_StructureGens[] - } - - if (a_ChunkDesc.IsUsingDefaultFinish()) - { - for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) - { - (*itr)->GenFinish(a_ChunkDesc); - } // for itr - m_FinishGens[] - } -} - - - - - -void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) -{ - AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", ""); - if (BiomeGenName.empty()) - { - LOGWARN("[Generator]::BiomeGen value not found in world.ini, using \"MultiStepMap\"."); - BiomeGenName = "MultiStepMap"; - } - - int Seed = m_ChunkGenerator.GetSeed(); - bool CacheOffByDefault = false; - if (NoCaseCompare(BiomeGenName, "constant") == 0) - { - m_BiomeGen = new cBioGenConstant; - CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :) - } - else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0) - { - m_BiomeGen = new cBioGenCheckerboard; - CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data - } - else if (NoCaseCompare(BiomeGenName, "voronoi") == 0) - { - m_BiomeGen = new cBioGenVoronoi(Seed); - } - else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0) - { - m_BiomeGen = new cBioGenDistortedVoronoi(Seed); - } - else - { - if (NoCaseCompare(BiomeGenName, "multistepmap") != 0) - { - LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str()); - } - m_BiomeGen = new cBioGenMultiStepMap(Seed); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cBioGenMultiStepMap..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 5000; x++) - { - cChunkDef::BiomeMap Biomes; - m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - - // Add a cache, if requested: - int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64); - if (CacheSize > 0) - { - if (CacheSize < 4) - { - LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", - CacheSize, 4 - ); - CacheSize = 4; - } - LOGINFO("Using a cache for biomegen of size %d.", CacheSize); - m_UnderlyingBiomeGen = m_BiomeGen; - m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize); - } - m_BiomeGen->Initialize(a_IniFile); -} - - - - - -void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) -{ - AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); - if (HeightGenName.empty()) - { - LOGWARN("[Generator]::HeightGen value not found in world.ini, using \"Biomal\"."); - HeightGenName = "Biomal"; - } - - int Seed = m_ChunkGenerator.GetSeed(); - bool CacheOffByDefault = false; - if (NoCaseCompare(HeightGenName, "flat") == 0) - { - int Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", 5); - m_HeightGen = new cHeiGenFlat(Height); - CacheOffByDefault = true; // We're generating faster than a cache would retrieve data - } - else if (NoCaseCompare(HeightGenName, "classic") == 0) - { - // These used to be in terrain.ini, but now they are in world.ini (so that multiple worlds can have different values): - float HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); - float HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); - float HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); - float HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); - float HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); - float HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); - m_HeightGen = new cHeiGenClassic(Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3); - } - else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) - { - m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen); - ((cDistortedHeightmap *)m_HeightGen)->Initialize(a_IniFile); - } - else if (NoCaseCompare(HeightGenName, "End") == 0) - { - m_HeightGen = new cEndGen(Seed); - ((cEndGen *)m_HeightGen)->Initialize(a_IniFile); - } - else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) - { - m_HeightGen = new cNoise3DComposable(Seed); - ((cNoise3DComposable *)m_HeightGen)->Initialize(a_IniFile); - } - else // "biomal" or - { - if (NoCaseCompare(HeightGenName, "biomal") != 0) - { - LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); - } - m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cHeiGenBiomal..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 500; x++) - { - cChunkDef::HeightMap Heights; - m_HeightGen->GenHeightMap(x * 5, x * 5, Heights); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - - // Add a cache, if requested: - int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); - if (CacheSize > 0) - { - if (CacheSize < 4) - { - LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", - CacheSize, 4 - ); - CacheSize = 4; - } - LOGINFO("Using a cache for Heightgen of size %d.", CacheSize); - m_UnderlyingHeightGen = m_HeightGen; - m_HeightGen = new cHeiGenCache(m_UnderlyingHeightGen, CacheSize); - } -} - - - - - -void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) -{ - AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", ""); - if (CompoGenName.empty()) - { - LOGWARN("[Generator]::CompositionGen value not found in world.ini, using \"Biomal\"."); - CompoGenName = "Biomal"; - } - if (NoCaseCompare(CompoGenName, "sameblock") == 0) - { - int Block = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "SameBlockType", "stone"); - bool Bedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); - m_CompositionGen = new cCompoGenSameBlock((BLOCKTYPE)Block, Bedrocked); - } - else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) - { - m_CompositionGen = new cCompoGenDebugBiomes; - } - else if (NoCaseCompare(CompoGenName, "classic") == 0) - { - int SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", 60); - int BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", 2); - int BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", 4); - BLOCKTYPE BlockTop = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockTop", "grass"); - BLOCKTYPE BlockMiddle = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt"); - BLOCKTYPE BlockBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBottom", "stone"); - BLOCKTYPE BlockBeach = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeach", "sand"); - BLOCKTYPE BlockBeachBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone"); - BLOCKTYPE BlockSea = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater"); - m_CompositionGen = new cCompoGenClassic( - SeaLevel, BeachHeight, BeachDepth, BlockTop, BlockMiddle, BlockBottom, BlockBeach, - BlockBeachBottom, BlockSea - ); - } - else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) - { - m_CompositionGen = new cDistortedHeightmap(m_ChunkGenerator.GetSeed(), *m_BiomeGen); - ((cDistortedHeightmap *)m_CompositionGen)->Initialize(a_IniFile); - } - else if (NoCaseCompare(CompoGenName, "end") == 0) - { - m_CompositionGen = new cEndGen(m_ChunkGenerator.GetSeed()); - ((cEndGen *)m_CompositionGen)->Initialize(a_IniFile); - } - else if (NoCaseCompare(CompoGenName, "nether") == 0) - { - m_CompositionGen = new cCompoGenNether(m_ChunkGenerator.GetSeed()); - } - else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) - { - m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); - ((cNoise3DComposable *)m_CompositionGen)->Initialize(a_IniFile); - } - else - { - if (NoCaseCompare(CompoGenName, "biomal") != 0) - { - LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str()); - } - int SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", 62); - int Seed = m_ChunkGenerator.GetSeed(); - m_CompositionGen = new cCompoGenBiomal(Seed, SeaLevel); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cCompoGenBiomal..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 500; x++) - { - cChunkDesc Desc(200 + x * 8, 200 + x * 8); - m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap()); - m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap()); - m_CompositionGen->ComposeTerrain(Desc); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - - int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64); - if (CompoGenCacheSize > 1) - { - m_UnderlyingCompositionGen = m_CompositionGen; - m_CompositionGen = new cCompoGenCache(m_UnderlyingCompositionGen, 32); - } -} - - - - - -void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile) -{ - AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees"); - - int Seed = m_ChunkGenerator.GetSeed(); - AStringVector Str = StringSplitAndTrim(Structures, ","); - for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) - { - if (NoCaseCompare(*itr, "DualRidgeCaves") == 0) - { - float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3); - m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold)); - } - else if (NoCaseCompare(*itr, "DirectOverhangs") == 0) - { - m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed)); - } - else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0) - { - m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed)); - } - else if (NoCaseCompare(*itr, "LavaLakes") == 0) - { - int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10); - m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability)); - } - else if (NoCaseCompare(*itr, "MarbleCaves") == 0) - { - m_StructureGens.push_back(new cStructGenMarbleCaves(Seed)); - } - else if (NoCaseCompare(*itr, "MineShafts") == 0) - { - int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512); - int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160); - int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600); - int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200); - int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200); - m_StructureGens.push_back(new cStructGenMineShafts( - Seed, GridSize, MaxSystemSize, - ChanceCorridor, ChanceCrossing, ChanceStaircase - )); - } - else if (NoCaseCompare(*itr, "OreNests") == 0) - { - m_StructureGens.push_back(new cStructGenOreNests(Seed)); - } - else if (NoCaseCompare(*itr, "Ravines") == 0) - { - m_StructureGens.push_back(new cStructGenRavines(Seed, 128)); - } - else if (NoCaseCompare(*itr, "Trees") == 0) - { - m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen)); - } - else if (NoCaseCompare(*itr, "WaterLakes") == 0) - { - int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25); - m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability)); - } - else if (NoCaseCompare(*itr, "WormNestCaves") == 0) - { - m_StructureGens.push_back(new cStructGenWormNestCaves(Seed)); - } - else - { - LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str()); - } - } // for itr - Str[] -} - - - - - -void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) -{ - int Seed = m_ChunkGenerator.GetSeed(); - AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator"); - - AStringVector Str = StringSplitAndTrim(Structures, ","); - for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) - { - // Finishers, alpha-sorted: - if (NoCaseCompare(*itr, "BottomLava") == 0) - { - int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10; - int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel); - m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel)); - } - else if (NoCaseCompare(*itr, "DeadBushes") == 0) - { - m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND)); - } - else if (NoCaseCompare(*itr, "Ice") == 0) - { - m_FinishGens.push_back(new cFinishGenIce); - } - else if (NoCaseCompare(*itr, "LavaSprings") == 0) - { - m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World)); - } - else if (NoCaseCompare(*itr, "Lilypads") == 0) - { - m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER)); - } - else if (NoCaseCompare(*itr, "PreSimulator") == 0) - { - m_FinishGens.push_back(new cFinishGenPreSimulator); - } - else if (NoCaseCompare(*itr, "Snow") == 0) - { - m_FinishGens.push_back(new cFinishGenSnow); - } - else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0) - { - m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed)); - } - else if (NoCaseCompare(*itr, "WaterSprings") == 0) - { - m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World)); - } - } // for itr - Str[] -} - - - - + +// ComposableGenerator.cpp + +// Implements the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks + +#include "Globals.h" + +#include "ComposableGenerator.h" +#include "../World.h" +#include "../../iniFile/iniFile.h" +#include "../Root.h" + +// Individual composed algorithms: +#include "BioGen.h" +#include "HeiGen.h" +#include "CompoGen.h" +#include "StructGen.h" +#include "FinishGen.h" + +#include "Caves.h" +#include "DistortedHeightmap.h" +#include "EndGen.h" +#include "MineShafts.h" +#include "Noise3DGenerator.h" +#include "Ravines.h" + + + + + + + + + + +cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) : + super(a_ChunkGenerator), + m_BiomeGen(NULL), + m_HeightGen(NULL), + m_CompositionGen(NULL), + m_UnderlyingBiomeGen(NULL), + m_UnderlyingHeightGen(NULL), + m_UnderlyingCompositionGen(NULL) +{ +} + + + + + +cComposableGenerator::~cComposableGenerator() +{ + // Delete the generating composition: + for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) + { + delete *itr; + } + m_FinishGens.clear(); + for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) + { + delete *itr; + } + m_StructureGens.clear(); + + delete m_CompositionGen; + m_CompositionGen = NULL; + delete m_HeightGen; + m_HeightGen = NULL; + delete m_BiomeGen; + m_BiomeGen = NULL; + delete m_UnderlyingCompositionGen; + m_UnderlyingCompositionGen = NULL; + delete m_UnderlyingHeightGen; + m_UnderlyingHeightGen = NULL; + delete m_UnderlyingBiomeGen; + m_UnderlyingBiomeGen = NULL; +} + + + + + +void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +{ + super::Initialize(a_World, a_IniFile); + + InitBiomeGen(a_IniFile); + InitHeightGen(a_IniFile); + InitCompositionGen(a_IniFile); + InitStructureGens(a_IniFile); + InitFinishGens(a_IniFile); +} + + + + + +void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + if (m_BiomeGen != NULL) // Quick fix for generator deinitializing before the world storage finishes loading + { + m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap); + } +} + + + + + +void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) +{ + if (a_ChunkDesc.IsUsingDefaultBiomes()) + { + m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap()); + } + + if (a_ChunkDesc.IsUsingDefaultHeight()) + { + m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap()); + } + + if (a_ChunkDesc.IsUsingDefaultComposition()) + { + m_CompositionGen->ComposeTerrain(a_ChunkDesc); + } + + if (a_ChunkDesc.IsUsingDefaultStructures()) + { + for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr) + { + (*itr)->GenStructures(a_ChunkDesc); + } // for itr - m_StructureGens[] + } + + if (a_ChunkDesc.IsUsingDefaultFinish()) + { + for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr) + { + (*itr)->GenFinish(a_ChunkDesc); + } // for itr - m_FinishGens[] + } +} + + + + + +void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile) +{ + AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", ""); + if (BiomeGenName.empty()) + { + LOGWARN("[Generator]::BiomeGen value not found in world.ini, using \"MultiStepMap\"."); + BiomeGenName = "MultiStepMap"; + } + + int Seed = m_ChunkGenerator.GetSeed(); + bool CacheOffByDefault = false; + if (NoCaseCompare(BiomeGenName, "constant") == 0) + { + m_BiomeGen = new cBioGenConstant; + CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :) + } + else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0) + { + m_BiomeGen = new cBioGenCheckerboard; + CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data + } + else if (NoCaseCompare(BiomeGenName, "voronoi") == 0) + { + m_BiomeGen = new cBioGenVoronoi(Seed); + } + else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0) + { + m_BiomeGen = new cBioGenDistortedVoronoi(Seed); + } + else + { + if (NoCaseCompare(BiomeGenName, "multistepmap") != 0) + { + LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str()); + } + m_BiomeGen = new cBioGenMultiStepMap(Seed); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cBioGenMultiStepMap..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 5000; x++) + { + cChunkDef::BiomeMap Biomes; + m_BiomeGen->GenBiomes(x * 5, x * 5, Biomes); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + // Add a cache, if requested: + int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64); + if (CacheSize > 0) + { + if (CacheSize < 4) + { + LOGWARNING("Biomegen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", + CacheSize, 4 + ); + CacheSize = 4; + } + LOGINFO("Using a cache for biomegen of size %d.", CacheSize); + m_UnderlyingBiomeGen = m_BiomeGen; + m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize); + } + m_BiomeGen->Initialize(a_IniFile); +} + + + + + +void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile) +{ + AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", ""); + if (HeightGenName.empty()) + { + LOGWARN("[Generator]::HeightGen value not found in world.ini, using \"Biomal\"."); + HeightGenName = "Biomal"; + } + + int Seed = m_ChunkGenerator.GetSeed(); + bool CacheOffByDefault = false; + if (NoCaseCompare(HeightGenName, "flat") == 0) + { + int Height = a_IniFile.GetValueSetI("Generator", "FlatHeight", 5); + m_HeightGen = new cHeiGenFlat(Height); + CacheOffByDefault = true; // We're generating faster than a cache would retrieve data + } + else if (NoCaseCompare(HeightGenName, "classic") == 0) + { + // These used to be in terrain.ini, but now they are in world.ini (so that multiple worlds can have different values): + float HeightFreq1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq1", 0.1); + float HeightFreq2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq2", 1.0); + float HeightFreq3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightFreq3", 2.0); + float HeightAmp1 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp1", 1.0); + float HeightAmp2 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp2", 0.5); + float HeightAmp3 = (float)a_IniFile.GetValueSetF("Generator", "ClassicHeightAmp3", 0.5); + m_HeightGen = new cHeiGenClassic(Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3); + } + else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0) + { + m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen); + ((cDistortedHeightmap *)m_HeightGen)->Initialize(a_IniFile); + } + else if (NoCaseCompare(HeightGenName, "End") == 0) + { + m_HeightGen = new cEndGen(Seed); + ((cEndGen *)m_HeightGen)->Initialize(a_IniFile); + } + else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) + { + m_HeightGen = new cNoise3DComposable(Seed); + ((cNoise3DComposable *)m_HeightGen)->Initialize(a_IniFile); + } + else // "biomal" or + { + if (NoCaseCompare(HeightGenName, "biomal") != 0) + { + LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str()); + } + m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cHeiGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDef::HeightMap Heights; + m_HeightGen->GenHeightMap(x * 5, x * 5, Heights); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + // Add a cache, if requested: + int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64); + if (CacheSize > 0) + { + if (CacheSize < 4) + { + LOGWARNING("Heightgen cache size set too low, would hurt performance instead of helping. Increasing from %d to %d", + CacheSize, 4 + ); + CacheSize = 4; + } + LOGINFO("Using a cache for Heightgen of size %d.", CacheSize); + m_UnderlyingHeightGen = m_HeightGen; + m_HeightGen = new cHeiGenCache(m_UnderlyingHeightGen, CacheSize); + } +} + + + + + +void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile) +{ + AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", ""); + if (CompoGenName.empty()) + { + LOGWARN("[Generator]::CompositionGen value not found in world.ini, using \"Biomal\"."); + CompoGenName = "Biomal"; + } + if (NoCaseCompare(CompoGenName, "sameblock") == 0) + { + int Block = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "SameBlockType", "stone"); + bool Bedrocked = (a_IniFile.GetValueSetI("Generator", "SameBlockBedrocked", 1) != 0); + m_CompositionGen = new cCompoGenSameBlock((BLOCKTYPE)Block, Bedrocked); + } + else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) + { + m_CompositionGen = new cCompoGenDebugBiomes; + } + else if (NoCaseCompare(CompoGenName, "classic") == 0) + { + int SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", 60); + int BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", 2); + int BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", 4); + BLOCKTYPE BlockTop = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockTop", "grass"); + BLOCKTYPE BlockMiddle = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockMiddle", "dirt"); + BLOCKTYPE BlockBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBottom", "stone"); + BLOCKTYPE BlockBeach = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeach", "sand"); + BLOCKTYPE BlockBeachBottom = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockBeachBottom", "sandstone"); + BLOCKTYPE BlockSea = m_ChunkGenerator.GetIniBlock(a_IniFile, "Generator", "ClassicBlockSea", "stationarywater"); + m_CompositionGen = new cCompoGenClassic( + SeaLevel, BeachHeight, BeachDepth, BlockTop, BlockMiddle, BlockBottom, BlockBeach, + BlockBeachBottom, BlockSea + ); + } + else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) + { + m_CompositionGen = new cDistortedHeightmap(m_ChunkGenerator.GetSeed(), *m_BiomeGen); + ((cDistortedHeightmap *)m_CompositionGen)->Initialize(a_IniFile); + } + else if (NoCaseCompare(CompoGenName, "end") == 0) + { + m_CompositionGen = new cEndGen(m_ChunkGenerator.GetSeed()); + ((cEndGen *)m_CompositionGen)->Initialize(a_IniFile); + } + else if (NoCaseCompare(CompoGenName, "nether") == 0) + { + m_CompositionGen = new cCompoGenNether(m_ChunkGenerator.GetSeed()); + } + else if (NoCaseCompare(CompoGenName, "Noise3D") == 0) + { + m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed()); + ((cNoise3DComposable *)m_CompositionGen)->Initialize(a_IniFile); + } + else + { + if (NoCaseCompare(CompoGenName, "biomal") != 0) + { + LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str()); + } + int SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", 62); + int Seed = m_ChunkGenerator.GetSeed(); + m_CompositionGen = new cCompoGenBiomal(Seed, SeaLevel); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cCompoGenBiomal..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 500; x++) + { + cChunkDesc Desc(200 + x * 8, 200 + x * 8); + m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap()); + m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap()); + m_CompositionGen->ComposeTerrain(Desc); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + + int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64); + if (CompoGenCacheSize > 1) + { + m_UnderlyingCompositionGen = m_CompositionGen; + m_CompositionGen = new cCompoGenCache(m_UnderlyingCompositionGen, 32); + } +} + + + + + +void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile) +{ + AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees"); + + int Seed = m_ChunkGenerator.GetSeed(); + AStringVector Str = StringSplitAndTrim(Structures, ","); + for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) + { + if (NoCaseCompare(*itr, "DualRidgeCaves") == 0) + { + float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3); + m_StructureGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold)); + } + else if (NoCaseCompare(*itr, "DirectOverhangs") == 0) + { + m_StructureGens.push_back(new cStructGenDirectOverhangs(Seed)); + } + else if (NoCaseCompare(*itr, "DistortedMembraneOverhangs") == 0) + { + m_StructureGens.push_back(new cStructGenDistortedMembraneOverhangs(Seed)); + } + else if (NoCaseCompare(*itr, "LavaLakes") == 0) + { + int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10); + m_StructureGens.push_back(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, *m_HeightGen, Probability)); + } + else if (NoCaseCompare(*itr, "MarbleCaves") == 0) + { + m_StructureGens.push_back(new cStructGenMarbleCaves(Seed)); + } + else if (NoCaseCompare(*itr, "MineShafts") == 0) + { + int GridSize = a_IniFile.GetValueSetI("Generator", "MineShaftsGridSize", 512); + int MaxSystemSize = a_IniFile.GetValueSetI("Generator", "MineShaftsMaxSystemSize", 160); + int ChanceCorridor = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCorridor", 600); + int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200); + int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200); + m_StructureGens.push_back(new cStructGenMineShafts( + Seed, GridSize, MaxSystemSize, + ChanceCorridor, ChanceCrossing, ChanceStaircase + )); + } + else if (NoCaseCompare(*itr, "OreNests") == 0) + { + m_StructureGens.push_back(new cStructGenOreNests(Seed)); + } + else if (NoCaseCompare(*itr, "Ravines") == 0) + { + m_StructureGens.push_back(new cStructGenRavines(Seed, 128)); + } + else if (NoCaseCompare(*itr, "Trees") == 0) + { + m_StructureGens.push_back(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen)); + } + else if (NoCaseCompare(*itr, "WaterLakes") == 0) + { + int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25); + m_StructureGens.push_back(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, *m_HeightGen, Probability)); + } + else if (NoCaseCompare(*itr, "WormNestCaves") == 0) + { + m_StructureGens.push_back(new cStructGenWormNestCaves(Seed)); + } + else + { + LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str()); + } + } // for itr - Str[] +} + + + + + +void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) +{ + int Seed = m_ChunkGenerator.GetSeed(); + AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator"); + + AStringVector Str = StringSplitAndTrim(Structures, ","); + for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) + { + // Finishers, alpha-sorted: + if (NoCaseCompare(*itr, "BottomLava") == 0) + { + int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10; + int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel); + m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel)); + } + else if (NoCaseCompare(*itr, "DeadBushes") == 0) + { + m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, biDesert, 2, E_BLOCK_SAND, E_BLOCK_SAND)); + } + else if (NoCaseCompare(*itr, "Ice") == 0) + { + m_FinishGens.push_back(new cFinishGenIce); + } + else if (NoCaseCompare(*itr, "LavaSprings") == 0) + { + m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World)); + } + else if (NoCaseCompare(*itr, "Lilypads") == 0) + { + m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER)); + } + else if (NoCaseCompare(*itr, "PreSimulator") == 0) + { + m_FinishGens.push_back(new cFinishGenPreSimulator); + } + else if (NoCaseCompare(*itr, "Snow") == 0) + { + m_FinishGens.push_back(new cFinishGenSnow); + } + else if (NoCaseCompare(*itr, "SprinkleFoliage") == 0) + { + m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed)); + } + else if (NoCaseCompare(*itr, "WaterSprings") == 0) + { + m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World)); + } + } // for itr - Str[] +} + + + + diff --git a/source/Generating/ComposableGenerator.h b/source/Generating/ComposableGenerator.h index c32e7181f..1d5c98e5d 100644 --- a/source/Generating/ComposableGenerator.h +++ b/source/Generating/ComposableGenerator.h @@ -1,175 +1,175 @@ - -// ComposableGenerator.h - -// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks - -/* -Generating works by composing several algorithms: -Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage -Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others, -based on user's preferences in the world.ini. -See http://forum.mc-server.org/showthread.php?tid=409 for details. -*/ - - - - - -#pragma once - -#include "ChunkGenerator.h" -#include "ChunkDesc.h" - - - - - -// fwd: Noise3DGenerator.h -class cNoise3DComposable; - -// fwd: DistortedHeightmap.h -class cDistortedHeightmap; - - - - - -/** The interface that a biome generator must implement -A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output. -The output array is sequenced in the same way as the MapChunk packet's biome data. -*/ -class cBiomeGen -{ -public: - virtual ~cBiomeGen() {} // Force a virtual destructor in descendants - - /// Generates biomes for the given chunk - virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; - - /// Reads parameters from the ini file, prepares generator for use. - virtual void Initialize(cIniFile & a_IniFile) {} -} ; - - - - - -/** The interface that a terrain height generator must implement -A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk. -The output array is sequenced in the same way as the BiomeGen's biome data. -The generator may request biome information from the underlying BiomeGen, it may even request information for -other chunks than the one it's currently generating (possibly neighbors - for averaging) -*/ -class cTerrainHeightGen -{ -public: - virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants - - /// Generates heightmap for the given chunk - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; -} ; - - - - - -/** The interface that a terrain composition generator must implement -Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with -the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose, -but it may request information for other chunks than the one it's currently generating from them. -*/ -class cTerrainCompositionGen -{ -public: - virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants - - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0; -} ; - - - - - -/** The interface that a structure generator must implement -Structures are generated after the terrain composition took place. It should modify the blocktype data to account -for whatever structures the generator is generating. -Note that ores are considered structures too, at least from the interface point of view. -Also note that a worldgenerator may contain multiple structure generators, one for each type of structure -*/ -class cStructureGen -{ -public: - virtual ~cStructureGen() {} // Force a virtual destructor in descendants - - virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0; -} ; - -typedef std::list cStructureGenList; - - - - - -/** The interface that a finisher must implement -Finisher implements small additions after all structures have been generated. -*/ -class cFinishGen -{ -public: - virtual ~cFinishGen() {} // Force a virtual destructor in descendants - - virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0; -} ; - -typedef std::list cFinishGenList; - - - - - -class cComposableGenerator : - public cChunkGenerator::cGenerator -{ - typedef cChunkGenerator::cGenerator super; - -public: - cComposableGenerator(cChunkGenerator & a_ChunkGenerator); - virtual ~cComposableGenerator(); - - virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; - virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; - -protected: - // The generation composition: - cBiomeGen * m_BiomeGen; - cTerrainHeightGen * m_HeightGen; - cTerrainCompositionGen * m_CompositionGen; - cStructureGenList m_StructureGens; - cFinishGenList m_FinishGens; - - // Generators underlying the caches: - cBiomeGen * m_UnderlyingBiomeGen; - cTerrainHeightGen * m_UnderlyingHeightGen; - cTerrainCompositionGen * m_UnderlyingCompositionGen; - - - /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly - void InitBiomeGen(cIniFile & a_IniFile); - - /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly - void InitHeightGen(cIniFile & a_IniFile); - - /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly - void InitCompositionGen(cIniFile & a_IniFile); - - /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly - void InitStructureGens(cIniFile & a_IniFile); - - /// Reads the finishers from the ini and initializes m_FinishGens accordingly - void InitFinishGens(cIniFile & a_IniFile); -} ; - - - - + +// ComposableGenerator.h + +// Declares the cComposableGenerator class representing the chunk generator that takes the composition approach to generating chunks + +/* +Generating works by composing several algorithms: +Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage +Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others, +based on user's preferences in the world.ini. +See http://forum.mc-server.org/showthread.php?tid=409 for details. +*/ + + + + + +#pragma once + +#include "ChunkGenerator.h" +#include "ChunkDesc.h" + + + + + +// fwd: Noise3DGenerator.h +class cNoise3DComposable; + +// fwd: DistortedHeightmap.h +class cDistortedHeightmap; + + + + + +/** The interface that a biome generator must implement +A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output. +The output array is sequenced in the same way as the MapChunk packet's biome data. +*/ +class cBiomeGen +{ +public: + virtual ~cBiomeGen() {} // Force a virtual destructor in descendants + + /// Generates biomes for the given chunk + virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0; + + /// Reads parameters from the ini file, prepares generator for use. + virtual void Initialize(cIniFile & a_IniFile) {} +} ; + + + + + +/** The interface that a terrain height generator must implement +A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk. +The output array is sequenced in the same way as the BiomeGen's biome data. +The generator may request biome information from the underlying BiomeGen, it may even request information for +other chunks than the one it's currently generating (possibly neighbors - for averaging) +*/ +class cTerrainHeightGen +{ +public: + virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants + + /// Generates heightmap for the given chunk + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; +} ; + + + + + +/** The interface that a terrain composition generator must implement +Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with +the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose, +but it may request information for other chunks than the one it's currently generating from them. +*/ +class cTerrainCompositionGen +{ +public: + virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants + + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0; +} ; + + + + + +/** The interface that a structure generator must implement +Structures are generated after the terrain composition took place. It should modify the blocktype data to account +for whatever structures the generator is generating. +Note that ores are considered structures too, at least from the interface point of view. +Also note that a worldgenerator may contain multiple structure generators, one for each type of structure +*/ +class cStructureGen +{ +public: + virtual ~cStructureGen() {} // Force a virtual destructor in descendants + + virtual void GenStructures(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::list cStructureGenList; + + + + + +/** The interface that a finisher must implement +Finisher implements small additions after all structures have been generated. +*/ +class cFinishGen +{ +public: + virtual ~cFinishGen() {} // Force a virtual destructor in descendants + + virtual void GenFinish(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::list cFinishGenList; + + + + + +class cComposableGenerator : + public cChunkGenerator::cGenerator +{ + typedef cChunkGenerator::cGenerator super; + +public: + cComposableGenerator(cChunkGenerator & a_ChunkGenerator); + virtual ~cComposableGenerator(); + + virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; + virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; + +protected: + // The generation composition: + cBiomeGen * m_BiomeGen; + cTerrainHeightGen * m_HeightGen; + cTerrainCompositionGen * m_CompositionGen; + cStructureGenList m_StructureGens; + cFinishGenList m_FinishGens; + + // Generators underlying the caches: + cBiomeGen * m_UnderlyingBiomeGen; + cTerrainHeightGen * m_UnderlyingHeightGen; + cTerrainCompositionGen * m_UnderlyingCompositionGen; + + + /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly + void InitBiomeGen(cIniFile & a_IniFile); + + /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly + void InitHeightGen(cIniFile & a_IniFile); + + /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly + void InitCompositionGen(cIniFile & a_IniFile); + + /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly + void InitStructureGens(cIniFile & a_IniFile); + + /// Reads the finishers from the ini and initializes m_FinishGens accordingly + void InitFinishGens(cIniFile & a_IniFile); +} ; + + + + diff --git a/source/Generating/DistortedHeightmap.cpp b/source/Generating/DistortedHeightmap.cpp index 5efc27895..6ac4d61d5 100644 --- a/source/Generating/DistortedHeightmap.cpp +++ b/source/Generating/DistortedHeightmap.cpp @@ -1,419 +1,419 @@ - -// DistortedHeightmap.cpp - -// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs - -#include "Globals.h" - -#include "DistortedHeightmap.h" -#include "../OSSupport/File.h" -#include "../../iniFile/iniFile.h" -#include "../LinearUpscale.h" - - - - - -/** This table assigns a relative maximum overhang size in each direction to biomes. -Both numbers indicate a number which will multiply the noise value for each coord; -this means that you can have different-sized overhangs in each direction. -Usually you'd want to keep both numbers the same. -The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible -due to the way that noise is calculated. -*/ -const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] = -{ - /* Biome | AmpX | AmpZ */ - /* biOcean */ { 1.5f, 1.5f}, - /* biPlains */ { 0.5f, 0.5f}, - /* biDesert */ { 0.5f, 0.5f}, - /* biExtremeHills */ {16.0f, 16.0f}, - /* biForest */ { 3.0f, 3.0f}, - /* biTaiga */ { 1.5f, 1.5f}, - - /* biSwampland */ { 0.0f, 0.0f}, - /* biRiver */ { 0.0f, 0.0f}, - /* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing - /* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing - /* biFrozenOcean */ { 0.0f, 0.0f}, - /* biFrozenRiver */ { 0.0f, 0.0f}, - /* biIcePlains */ { 0.0f, 0.0f}, - /* biIceMountains */ { 8.0f, 8.0f}, - /* biMushroomIsland */ { 4.0f, 4.0f}, - /* biMushroomShore */ { 0.0f, 0.0f}, - /* biBeach */ { 0.0f, 0.0f}, - /* biDesertHills */ { 5.0f, 5.0f}, - /* biForestHills */ { 6.0f, 6.0f}, - /* biTaigaHills */ { 8.0f, 8.0f}, - /* biExtremeHillsEdge */ { 7.0f, 7.0f}, - /* biJungle */ { 0.0f, 0.0f}, - /* biJungleHills */ { 8.0f, 8.0f}, -} ; - - - - - -cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : - m_NoiseDistortX(a_Seed + 1000), - m_NoiseDistortZ(a_Seed + 2000), - m_OceanFloorSelect(a_Seed + 3000), - m_BiomeGen(a_BiomeGen), - m_UnderlyingHeiGen(a_Seed, a_BiomeGen), - m_HeightGen(&m_UnderlyingHeiGen, 64) -{ - m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); - m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); - m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); - - m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); - m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); - m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); -} - - - - - -void cDistortedHeightmap::Initialize(cIniFile & a_IniFile) -{ - // Read the params from the INI file: - m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62); - m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10); - m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10); - m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10); -} - - - - - -void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ) -{ - if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ)) - { - return; - } - m_CurChunkX = a_ChunkX; - m_CurChunkZ = a_ChunkZ; - - - m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights); - UpdateDistortAmps(); - GenerateHeightArray(); -} - - - - - -void cDistortedHeightmap::GenerateHeightArray(void) -{ - // Generate distortion noise: - NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z]; - NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z]; - NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; - NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX; - NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX; - NOISE_DATATYPE StartY = 0; - NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY; - NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ; - NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ; - - m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); - m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); - - // The distorted heightmap, before linear upscaling - NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z]; - - // Distort the heightmap using the distortion: - for (int z = 0; z < DIM_Z; z++) - { - int AmpIdx = z * DIM_X; - for (int y = 0; y < DIM_Y; y++) - { - int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X; - for (int x = 0; x < DIM_X; x++) - { - NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x]; - NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x]; - DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X); - DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z); - // Adding 0.5 helps alleviate the interpolation artifacts - DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5; - } - } - } - - // Upscale the distorted heightmap into full dimensions: - LinearUpscale3DArray( - DistHei, DIM_X, DIM_Y, DIM_Z, - m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z - ); - - // DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ)); -} - - - - - -void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - PrepareState(a_ChunkX, a_ChunkZ); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int NoiseArrayIdx = x + 17 * 257 * z; - cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1); - for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--) - { - int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; - if (y < HeightMapHeight) - { - cChunkDef::SetHeight(a_HeightMap, x, z, y); - break; - } - } // for y - } // for x - } // for z -} - - - - - -void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - // Frequencies for the ocean floor selecting noise: - NOISE_DATATYPE FrequencyX = 3; - NOISE_DATATYPE FrequencyZ = 3; - - // Prepare the internal state for generating this chunk: - PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); - - // Compose: - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int NoiseArrayIdx = x + 17 * 257 * z; - int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; - bool HasHadWater = false; - for (int y = LastAir - 1; y > 0; y--) - { - int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; - - if (y >= HeightMapHeight) - { - // "air" part - LastAir = y; - if (y < m_SeaLevel) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); - HasHadWater = true; - } - continue; - } - // "ground" part: - if (y < LastAir - 4) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); - continue; - } - if (HasHadWater) - { - // Decide between clay, sand and dirt - NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX; - NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ; - NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); - if (Val < -0.95) - { - // Clay: - switch (LastAir - y) - { - case 0: - case 1: - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY); - break; - } - case 2: - case 3: - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); - break; - } - case 4: - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE); - break; - } - } // switch (floor depth) - } - else if (Val < 0) - { - a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); - } - else - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT); - } - } - else - { - switch (a_ChunkDesc.GetBiome(x, z)) - { - case biOcean: - case biPlains: - case biExtremeHills: - case biForest: - case biTaiga: - case biSwampland: - case biRiver: - case biFrozenOcean: - case biFrozenRiver: - case biIcePlains: - case biIceMountains: - case biForestHills: - case biTaigaHills: - case biExtremeHillsEdge: - case biJungle: - case biJungleHills: - { - a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); - break; - } - case biDesertHills: - case biDesert: - case biBeach: - { - a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); - break; - } - case biMushroomIsland: - case biMushroomShore: - { - a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT); - break; - } - } - } - } // for y - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - } // for x - } // for z -} - - - - - -int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z) -{ - int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16); - int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16); - int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width); - int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width); - - // If we're withing the same chunk, return the pre-cached heightmap: - if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ)) - { - return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ); - } - - // Ask the cache: - HEIGHTTYPE res = 0; - if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res)) - { - // The height was in the cache - return res; - } - - // The height is not in the cache, generate full heightmap and get it there: - cChunkDef::HeightMap Heightmap; - m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap); - return cChunkDef::GetHeight(Heightmap, RelX, RelZ); -} - - - - - -void cDistortedHeightmap::UpdateDistortAmps(void) -{ - BiomeNeighbors Biomes; - for (int z = -1; z <= 1; z++) - { - for (int x = -1; x <= 1; x++) - { - m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]); - } // for x - } // for z - - for (int z = 0; z < DIM_Z; z++) - { - for (int x = 0; x < DIM_Z; x++) - { - GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]); - } - } -} - - - - - -void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ) -{ - // Sum up how many biomes of each type there are in the neighborhood: - int BiomeCounts[biNumBiomes]; - memset(BiomeCounts, 0, sizeof(BiomeCounts)); - int Sum = 0; - for (int z = -8; z <= 8; z++) - { - int FinalZ = a_RelZ + z + cChunkDef::Width; - int IdxZ = FinalZ / cChunkDef::Width; - int ModZ = FinalZ % cChunkDef::Width; - int WeightZ = 9 - abs(z); - for (int x = -8; x <= 8; x++) - { - int FinalX = a_RelX + x + cChunkDef::Width; - int IdxX = FinalX / cChunkDef::Width; - int ModX = FinalX % cChunkDef::Width; - EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ); - if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts))) - { - continue; - } - int WeightX = 9 - abs(x); - BiomeCounts[Biome] += WeightX + WeightZ; - Sum += WeightX + WeightZ; - } // for x - } // for z - - if (Sum <= 0) - { - // No known biome around? Weird. Return a bogus value: - ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around"); - a_DistortAmpX = 16; - a_DistortAmpZ = 16; - } - - // For each biome type that has a nonzero count, calc its amps and add it: - NOISE_DATATYPE AmpX = 0; - NOISE_DATATYPE AmpZ = 0; - for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++) - { - AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX; - AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ; - } - a_DistortAmpX = AmpX / Sum; - a_DistortAmpZ = AmpZ / Sum; -} - - - - + +// DistortedHeightmap.cpp + +// Implements the cDistortedHeightmap class representing the height and composition generator capable of overhangs + +#include "Globals.h" + +#include "DistortedHeightmap.h" +#include "../OSSupport/File.h" +#include "../../iniFile/iniFile.h" +#include "../LinearUpscale.h" + + + + + +/** This table assigns a relative maximum overhang size in each direction to biomes. +Both numbers indicate a number which will multiply the noise value for each coord; +this means that you can have different-sized overhangs in each direction. +Usually you'd want to keep both numbers the same. +The numbers are "relative", not absolute maximum; overhangs of a slightly larger size are possible +due to the way that noise is calculated. +*/ +const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[biNumBiomes] = +{ + /* Biome | AmpX | AmpZ */ + /* biOcean */ { 1.5f, 1.5f}, + /* biPlains */ { 0.5f, 0.5f}, + /* biDesert */ { 0.5f, 0.5f}, + /* biExtremeHills */ {16.0f, 16.0f}, + /* biForest */ { 3.0f, 3.0f}, + /* biTaiga */ { 1.5f, 1.5f}, + + /* biSwampland */ { 0.0f, 0.0f}, + /* biRiver */ { 0.0f, 0.0f}, + /* biNether */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing + /* biSky */ { 0.0f, 0.0f}, // Unused, but must be here due to indexing + /* biFrozenOcean */ { 0.0f, 0.0f}, + /* biFrozenRiver */ { 0.0f, 0.0f}, + /* biIcePlains */ { 0.0f, 0.0f}, + /* biIceMountains */ { 8.0f, 8.0f}, + /* biMushroomIsland */ { 4.0f, 4.0f}, + /* biMushroomShore */ { 0.0f, 0.0f}, + /* biBeach */ { 0.0f, 0.0f}, + /* biDesertHills */ { 5.0f, 5.0f}, + /* biForestHills */ { 6.0f, 6.0f}, + /* biTaigaHills */ { 8.0f, 8.0f}, + /* biExtremeHillsEdge */ { 7.0f, 7.0f}, + /* biJungle */ { 0.0f, 0.0f}, + /* biJungleHills */ { 8.0f, 8.0f}, +} ; + + + + + +cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen) : + m_NoiseDistortX(a_Seed + 1000), + m_NoiseDistortZ(a_Seed + 2000), + m_OceanFloorSelect(a_Seed + 3000), + m_BiomeGen(a_BiomeGen), + m_UnderlyingHeiGen(a_Seed, a_BiomeGen), + m_HeightGen(&m_UnderlyingHeiGen, 64) +{ + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); + m_NoiseDistortX.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); + + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5); + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1); + m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2); +} + + + + + +void cDistortedHeightmap::Initialize(cIniFile & a_IniFile) +{ + // Read the params from the INI file: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10); +} + + + + + +void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ) +{ + if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ)) + { + return; + } + m_CurChunkX = a_ChunkX; + m_CurChunkZ = a_ChunkZ; + + + m_HeightGen.GenHeightMap(a_ChunkX, a_ChunkZ, m_CurChunkHeights); + UpdateDistortAmps(); + GenerateHeightArray(); +} + + + + + +void cDistortedHeightmap::GenerateHeightArray(void) +{ + // Generate distortion noise: + NOISE_DATATYPE DistortNoiseX[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE DistortNoiseZ[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_CurChunkX + 1) * cChunkDef::Width - 1)) / m_FrequencyX; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)(257)) / m_FrequencyY; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_CurChunkZ + 1) * cChunkDef::Width - 1)) / m_FrequencyZ; + + m_NoiseDistortX.Generate3D(DistortNoiseX, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); + m_NoiseDistortZ.Generate3D(DistortNoiseZ, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, Workspace); + + // The distorted heightmap, before linear upscaling + NOISE_DATATYPE DistHei[DIM_X * DIM_Y * DIM_Z]; + + // Distort the heightmap using the distortion: + for (int z = 0; z < DIM_Z; z++) + { + int AmpIdx = z * DIM_X; + for (int y = 0; y < DIM_Y; y++) + { + int NoiseArrayIdx = z * DIM_X * DIM_Y + y * DIM_X; + for (int x = 0; x < DIM_X; x++) + { + NOISE_DATATYPE DistX = DistortNoiseX[NoiseArrayIdx + x] * m_DistortAmpX[AmpIdx + x]; + NOISE_DATATYPE DistZ = DistortNoiseZ[NoiseArrayIdx + x] * m_DistortAmpZ[AmpIdx + x]; + DistX += (NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x * INTERPOL_X); + DistZ += (NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z * INTERPOL_Z); + // Adding 0.5 helps alleviate the interpolation artifacts + DistHei[NoiseArrayIdx + x] = (NOISE_DATATYPE)GetHeightmapAt(DistX, DistZ) + (NOISE_DATATYPE)0.5; + } + } + } + + // Upscale the distorted heightmap into full dimensions: + LinearUpscale3DArray( + DistHei, DIM_X, DIM_Y, DIM_Z, + m_DistortedHeightmap, INTERPOL_X, INTERPOL_Y, INTERPOL_Z + ); + + // DEBUG: Debug3DNoise(m_DistortedHeightmap, 17, 257, 17, Printf("DistortedHeightmap_%d_%d", m_CurChunkX, m_CurChunkZ)); +} + + + + + +void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + PrepareState(a_ChunkX, a_ChunkZ); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int NoiseArrayIdx = x + 17 * 257 * z; + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--) + { + int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; + if (y < HeightMapHeight) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + // Frequencies for the ocean floor selecting noise: + NOISE_DATATYPE FrequencyX = 3; + NOISE_DATATYPE FrequencyZ = 3; + + // Prepare the internal state for generating this chunk: + PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + // Compose: + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int NoiseArrayIdx = x + 17 * 257 * z; + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir - 1; y > 0; y--) + { + int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y]; + + if (y >= HeightMapHeight) + { + // "air" part + LastAir = y; + if (y < m_SeaLevel) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + HasHadWater = true; + } + continue; + } + // "ground" part: + if (y < LastAir - 4) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); + continue; + } + if (HasHadWater) + { + // Decide between clay, sand and dirt + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + x)) / FrequencyX; + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + z)) / FrequencyZ; + NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY); + if (Val < -0.95) + { + // Clay: + switch (LastAir - y) + { + case 0: + case 1: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_CLAY); + break; + } + case 2: + case 3: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + break; + } + case 4: + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SANDSTONE); + break; + } + } // switch (floor depth) + } + else if (Val < 0) + { + a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_DIRT); + } + } + else + { + switch (a_ChunkDesc.GetBiome(x, z)) + { + case biOcean: + case biPlains: + case biExtremeHills: + case biForest: + case biTaiga: + case biSwampland: + case biRiver: + case biFrozenOcean: + case biFrozenRiver: + case biIcePlains: + case biIceMountains: + case biForestHills: + case biTaigaHills: + case biExtremeHillsEdge: + case biJungle: + case biJungleHills: + { + a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + break; + } + case biDesertHills: + case biDesert: + case biBeach: + { + a_ChunkDesc.SetBlockType(x, y, z, (y < LastAir - 3) ? E_BLOCK_SANDSTONE : E_BLOCK_SAND); + break; + } + case biMushroomIsland: + case biMushroomShore: + { + a_ChunkDesc.SetBlockType(x, y, z, (y == LastAir - 1) ? E_BLOCK_MYCELIUM : E_BLOCK_DIRT); + break; + } + } + } + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +int cDistortedHeightmap::GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z) +{ + int ChunkX = (int)floor(a_X / (NOISE_DATATYPE)16); + int ChunkZ = (int)floor(a_Z / (NOISE_DATATYPE)16); + int RelX = (int)(a_X - (NOISE_DATATYPE)ChunkX * cChunkDef::Width); + int RelZ = (int)(a_Z - (NOISE_DATATYPE)ChunkZ * cChunkDef::Width); + + // If we're withing the same chunk, return the pre-cached heightmap: + if ((ChunkX == m_CurChunkX) && (ChunkZ == m_CurChunkZ)) + { + return cChunkDef::GetHeight(m_CurChunkHeights, RelX, RelZ); + } + + // Ask the cache: + HEIGHTTYPE res = 0; + if (m_HeightGen.GetHeightAt(ChunkX, ChunkZ, RelX, RelZ, res)) + { + // The height was in the cache + return res; + } + + // The height is not in the cache, generate full heightmap and get it there: + cChunkDef::HeightMap Heightmap; + m_HeightGen.GenHeightMap(ChunkX, ChunkZ, Heightmap); + return cChunkDef::GetHeight(Heightmap, RelX, RelZ); +} + + + + + +void cDistortedHeightmap::UpdateDistortAmps(void) +{ + BiomeNeighbors Biomes; + for (int z = -1; z <= 1; z++) + { + for (int x = -1; x <= 1; x++) + { + m_BiomeGen.GenBiomes(m_CurChunkX + x, m_CurChunkZ + z, Biomes[x + 1][z + 1]); + } // for x + } // for z + + for (int z = 0; z < DIM_Z; z++) + { + for (int x = 0; x < DIM_Z; x++) + { + GetDistortAmpsAt(Biomes, x * INTERPOL_X, z * INTERPOL_Z, m_DistortAmpX[x + DIM_X * z], m_DistortAmpZ[x + DIM_X * z]); + } + } +} + + + + + +void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ) +{ + // Sum up how many biomes of each type there are in the neighborhood: + int BiomeCounts[biNumBiomes]; + memset(BiomeCounts, 0, sizeof(BiomeCounts)); + int Sum = 0; + for (int z = -8; z <= 8; z++) + { + int FinalZ = a_RelZ + z + cChunkDef::Width; + int IdxZ = FinalZ / cChunkDef::Width; + int ModZ = FinalZ % cChunkDef::Width; + int WeightZ = 9 - abs(z); + for (int x = -8; x <= 8; x++) + { + int FinalX = a_RelX + x + cChunkDef::Width; + int IdxX = FinalX / cChunkDef::Width; + int ModX = FinalX % cChunkDef::Width; + EMCSBiome Biome = cChunkDef::GetBiome(a_Neighbors[IdxX][IdxZ], ModX, ModZ); + if ((Biome < 0) || (Biome >= ARRAYCOUNT(BiomeCounts))) + { + continue; + } + int WeightX = 9 - abs(x); + BiomeCounts[Biome] += WeightX + WeightZ; + Sum += WeightX + WeightZ; + } // for x + } // for z + + if (Sum <= 0) + { + // No known biome around? Weird. Return a bogus value: + ASSERT(!"cHeiGenBiomal: Biome sum failed, no known biome around"); + a_DistortAmpX = 16; + a_DistortAmpZ = 16; + } + + // For each biome type that has a nonzero count, calc its amps and add it: + NOISE_DATATYPE AmpX = 0; + NOISE_DATATYPE AmpZ = 0; + for (int i = 0; i < ARRAYCOUNT(BiomeCounts); i++) + { + AmpX += BiomeCounts[i] * m_GenParam[i].m_DistortAmpX; + AmpZ += BiomeCounts[i] * m_GenParam[i].m_DistortAmpZ; + } + a_DistortAmpX = AmpX / Sum; + a_DistortAmpZ = AmpZ / Sum; +} + + + + diff --git a/source/Generating/DistortedHeightmap.h b/source/Generating/DistortedHeightmap.h index 41a781b84..b2b235e61 100644 --- a/source/Generating/DistortedHeightmap.h +++ b/source/Generating/DistortedHeightmap.h @@ -1,102 +1,102 @@ - -// DistortedHeightmap.h - -// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "HeiGen.h" -#include "../Noise.h" - - - - - -#define NOISE_SIZE_Y (257 + 32) - - - - - -class cDistortedHeightmap : - public cTerrainHeightGen, - public cTerrainCompositionGen -{ -public: - cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen); - - void Initialize(cIniFile & a_IniFile); - -protected: - typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; - - // Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: - static const int INTERPOL_X = 8; - static const int INTERPOL_Y = 4; - static const int INTERPOL_Z = 8; - - // Linear upscaling buffer dimensions, calculated from the step sizes: - static const int DIM_X = 1 + (17 / INTERPOL_X); - static const int DIM_Y = 1 + (257 / INTERPOL_Y); - static const int DIM_Z = 1 + (17 / INTERPOL_Z); - - cPerlinNoise m_NoiseDistortX; - cPerlinNoise m_NoiseDistortZ; - cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor - - int m_SeaLevel; - NOISE_DATATYPE m_FrequencyX; - NOISE_DATATYPE m_FrequencyY; - NOISE_DATATYPE m_FrequencyZ; - - int m_CurChunkX; - int m_CurChunkZ; - NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17]; - - cBiomeGen & m_BiomeGen; - cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion) - cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen - - /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. - cChunkDef::HeightMap m_CurChunkHeights; - - // Per-biome terrain generator parameters: - struct sGenParam - { - NOISE_DATATYPE m_DistortAmpX; - NOISE_DATATYPE m_DistortAmpZ; - } ; - static const sGenParam m_GenParam[biNumBiomes]; - - // Distortion amplitudes for each direction, before linear upscaling - NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z]; - NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z]; - - - /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap) - void PrepareState(int a_ChunkX, int a_ChunkZ); - - /// Generates the m_DistortedHeightmap array for the current chunk - void GenerateHeightArray(void); - - /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords - int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z); - - /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ - void UpdateDistortAmps(void); - - /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes - void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ); - - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; -} ; + +// DistortedHeightmap.h + +// Declares the cDistortedHeightmap class representing the height and composition generator capable of overhangs + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "HeiGen.h" +#include "../Noise.h" + + + + + +#define NOISE_SIZE_Y (257 + 32) + + + + + +class cDistortedHeightmap : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cDistortedHeightmap(int a_Seed, cBiomeGen & a_BiomeGen); + + void Initialize(cIniFile & a_IniFile); + +protected: + typedef cChunkDef::BiomeMap BiomeNeighbors[3][3]; + + // Linear upscaling step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: + static const int INTERPOL_X = 8; + static const int INTERPOL_Y = 4; + static const int INTERPOL_Z = 8; + + // Linear upscaling buffer dimensions, calculated from the step sizes: + static const int DIM_X = 1 + (17 / INTERPOL_X); + static const int DIM_Y = 1 + (257 / INTERPOL_Y); + static const int DIM_Z = 1 + (17 / INTERPOL_Z); + + cPerlinNoise m_NoiseDistortX; + cPerlinNoise m_NoiseDistortZ; + cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor + + int m_SeaLevel; + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + + int m_CurChunkX; + int m_CurChunkZ; + NOISE_DATATYPE m_DistortedHeightmap[17 * 257 * 17]; + + cBiomeGen & m_BiomeGen; + cHeiGenBiomal m_UnderlyingHeiGen; // This generator provides us with base heightmap (before distortion) + cHeiGenCache m_HeightGen; // Cache above m_UnderlyingHeiGen + + /// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. + cChunkDef::HeightMap m_CurChunkHeights; + + // Per-biome terrain generator parameters: + struct sGenParam + { + NOISE_DATATYPE m_DistortAmpX; + NOISE_DATATYPE m_DistortAmpZ; + } ; + static const sGenParam m_GenParam[biNumBiomes]; + + // Distortion amplitudes for each direction, before linear upscaling + NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z]; + NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z]; + + + /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap) + void PrepareState(int a_ChunkX, int a_ChunkZ); + + /// Generates the m_DistortedHeightmap array for the current chunk + void GenerateHeightArray(void); + + /// Calculates the heightmap value (before distortion) at the specified (floating-point) coords + int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z); + + /// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ + void UpdateDistortAmps(void); + + /// Calculates the X and Z distortion amplitudes based on the neighbors' biomes + void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ); + + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; diff --git a/source/Generating/EndGen.cpp b/source/Generating/EndGen.cpp index 30fa457db..3eba5c47b 100644 --- a/source/Generating/EndGen.cpp +++ b/source/Generating/EndGen.cpp @@ -1,217 +1,217 @@ - -// EndGen.cpp - -// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen - -#include "Globals.h" -#include "EndGen.h" -#include "../../iniFile/iniFile.h" -#include "../LinearUpscale.h" - - - - - -enum -{ - // Interpolation cell size: - INTERPOL_X = 4, - INTERPOL_Y = 4, - INTERPOL_Z = 4, - - // Size of chunk data, downscaled before interpolation: - DIM_X = 16 / INTERPOL_X + 1, - DIM_Y = 256 / INTERPOL_Y + 1, - DIM_Z = 16 / INTERPOL_Z + 1, -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cEndGen: - -cEndGen::cEndGen(int a_Seed) : - m_Seed(a_Seed), - m_IslandSizeX(256), - m_IslandSizeY(96), - m_IslandSizeZ(256), - m_FrequencyX(80), - m_FrequencyY(80), - m_FrequencyZ(80) -{ - m_Perlin.AddOctave(1, 1); - m_Perlin.AddOctave(2, 0.5); - m_Perlin.AddOctave(4, 0.25); -} - - - - - -void cEndGen::Initialize(cIniFile & a_IniFile) -{ - m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX); - m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY); - m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ); - - m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX); - m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY); - m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ); - - // Recalculate the min and max chunk coords of the island - m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width; - m_MinChunkX = -m_MaxChunkX; - m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width; - m_MinChunkZ = -m_MaxChunkZ; -} - - - - - -/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) -void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ) -{ - ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function - - if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ)) - { - return; - } - - m_LastChunkX = a_ChunkX; - m_LastChunkZ = a_ChunkZ; - - GenerateNoiseArray(); -} - - - - - -/// Generates the m_NoiseArray array for the current chunk -void cEndGen::GenerateNoiseArray(void) -{ - NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] - NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] - - // Generate the downscaled noise: - NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX; - NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX; - NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ; - NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ; - NOISE_DATATYPE StartY = 0; - NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY; - m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace); - - // Add distance: - int idx = 0; - for (int y = 0; y < DIM_Y; y++) - { - NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY; - ValY = ValY * ValY; - for (int z = 0; z < DIM_Z; z++) - { - NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ; - ValZ = ValZ * ValZ; - for (int x = 0; x < DIM_X; x++) - { - // NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1); - NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX; - ValX = ValX * ValX; - NoiseData[idx++] += ValX + ValZ + ValY; - } // for x - } // for z - } // for y - - // Upscale into real chunk size: - LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y); -} - - - - - -/// Returns true if the chunk is outside of the island's dimensions -bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ) -{ - return ( - (a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) || - (a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ) - ); -} - - - - - -void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ)) - { - for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++) - { - a_HeightMap[i] = 0; - } - return; - } - - PrepareState(a_ChunkX, a_ChunkZ); - - int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - cChunkDef::SetHeight(a_HeightMap, x, z, MaxY); - for (int y = MaxY; y > 0; y--) - { - if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) - { - cChunkDef::SetHeight(a_HeightMap, x, z, y); - break; - } - } // for y - } // for x - } // for z -} - - - - - -void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ())) - { - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - return; - } - - PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); - - int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - for (int y = MaxY; y > 0; y--) - { - if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) - { - a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0); - } - else - { - a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0); - } - } // for y - } // for x - } // for z -} - - - - + +// EndGen.cpp + +// Implements the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen + +#include "Globals.h" +#include "EndGen.h" +#include "../../iniFile/iniFile.h" +#include "../LinearUpscale.h" + + + + + +enum +{ + // Interpolation cell size: + INTERPOL_X = 4, + INTERPOL_Y = 4, + INTERPOL_Z = 4, + + // Size of chunk data, downscaled before interpolation: + DIM_X = 16 / INTERPOL_X + 1, + DIM_Y = 256 / INTERPOL_Y + 1, + DIM_Z = 16 / INTERPOL_Z + 1, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cEndGen: + +cEndGen::cEndGen(int a_Seed) : + m_Seed(a_Seed), + m_IslandSizeX(256), + m_IslandSizeY(96), + m_IslandSizeZ(256), + m_FrequencyX(80), + m_FrequencyY(80), + m_FrequencyZ(80) +{ + m_Perlin.AddOctave(1, 1); + m_Perlin.AddOctave(2, 0.5); + m_Perlin.AddOctave(4, 0.25); +} + + + + + +void cEndGen::Initialize(cIniFile & a_IniFile) +{ + m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX); + m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY); + m_IslandSizeZ = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeZ", m_IslandSizeZ); + + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyX", m_FrequencyX); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyY", m_FrequencyY); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "EndGenFrequencyZ", m_FrequencyZ); + + // Recalculate the min and max chunk coords of the island + m_MaxChunkX = (m_IslandSizeX + cChunkDef::Width - 1) / cChunkDef::Width; + m_MinChunkX = -m_MaxChunkX; + m_MaxChunkZ = (m_IslandSizeZ + cChunkDef::Width - 1) / cChunkDef::Width; + m_MinChunkZ = -m_MaxChunkZ; +} + + + + + +/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) +void cEndGen::PrepareState(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(!IsChunkOutsideRange(a_ChunkX, a_ChunkZ)); // Should be filtered before calling this function + + if ((m_LastChunkX == a_ChunkX) && (m_LastChunkZ == a_ChunkZ)) + { + return; + } + + m_LastChunkX = a_ChunkX; + m_LastChunkZ = a_ChunkZ; + + GenerateNoiseArray(); +} + + + + + +/// Generates the m_NoiseArray array for the current chunk +void cEndGen::GenerateNoiseArray(void) +{ + NOISE_DATATYPE NoiseData[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] + NOISE_DATATYPE Workspace[DIM_X * DIM_Y * DIM_Z]; // [x + DIM_X * z + DIM_X * DIM_Z * y] + + // Generate the downscaled noise: + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((m_LastChunkX + 1) * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((m_LastChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)257) / m_FrequencyY; + m_Perlin.Generate3D(NoiseData, DIM_X, DIM_Z, DIM_Y, StartX, EndX, StartZ, EndZ, StartY, EndY, Workspace); + + // Add distance: + int idx = 0; + for (int y = 0; y < DIM_Y; y++) + { + NOISE_DATATYPE ValY = (NOISE_DATATYPE)(2 * INTERPOL_Y * y - m_IslandSizeY) / m_IslandSizeY; + ValY = ValY * ValY; + for (int z = 0; z < DIM_Z; z++) + { + NOISE_DATATYPE ValZ = (NOISE_DATATYPE)(m_LastChunkZ * cChunkDef::Width + (z * cChunkDef::Width / (DIM_Z - 1))) / m_IslandSizeZ; + ValZ = ValZ * ValZ; + for (int x = 0; x < DIM_X; x++) + { + // NOISE_DATATYPE ValX = StartX + (EndX - StartX) * x / (DIM_X - 1); + NOISE_DATATYPE ValX = (NOISE_DATATYPE)(m_LastChunkX * cChunkDef::Width + (x * cChunkDef::Width / (DIM_X - 1))) / m_IslandSizeX; + ValX = ValX * ValX; + NoiseData[idx++] += ValX + ValZ + ValY; + } // for x + } // for z + } // for y + + // Upscale into real chunk size: + LinearUpscale3DArray(NoiseData, DIM_X, DIM_Z, DIM_Y, m_NoiseArray, INTERPOL_X, INTERPOL_Z, INTERPOL_Y); +} + + + + + +/// Returns true if the chunk is outside of the island's dimensions +bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ) +{ + return ( + (a_ChunkX < m_MinChunkX) || (a_ChunkX > m_MaxChunkX) || + (a_ChunkZ < m_MinChunkZ) || (a_ChunkZ > m_MaxChunkZ) + ); +} + + + + + +void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ)) + { + for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++) + { + a_HeightMap[i] = 0; + } + return; + } + + PrepareState(a_ChunkX, a_ChunkZ); + + int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, MaxY); + for (int y = MaxY; y > 0; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ())) + { + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + return; + } + + PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1); + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = MaxY; y > 0; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0) + { + a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0); + } + else + { + a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0); + } + } // for y + } // for x + } // for z +} + + + + diff --git a/source/Generating/EndGen.h b/source/Generating/EndGen.h index cd316ed67..4904a0e3d 100644 --- a/source/Generating/EndGen.h +++ b/source/Generating/EndGen.h @@ -1,69 +1,69 @@ - -// EndGen.h - -// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cEndGen : - public cTerrainHeightGen, - public cTerrainCompositionGen -{ -public: - cEndGen(int a_Seed); - - void Initialize(cIniFile & a_IniFile); - -protected: - - /// Seed for the noise - int m_Seed; - - /// The Perlin noise used for generating - cPerlinNoise m_Perlin; - - // XYZ size of the "island", in blocks: - int m_IslandSizeX; - int m_IslandSizeY; - int m_IslandSizeZ; - - // XYZ Frequencies of the noise functions: - NOISE_DATATYPE m_FrequencyX; - NOISE_DATATYPE m_FrequencyY; - NOISE_DATATYPE m_FrequencyZ; - - // Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all - int m_MinChunkX, m_MaxChunkX; - int m_MinChunkZ, m_MaxChunkZ; - - // Noise array for the last chunk (in the noise range) - int m_LastChunkX; - int m_LastChunkZ; - NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y - - /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) - void PrepareState(int a_ChunkX, int a_ChunkZ); - - /// Generates the m_NoiseArray array for the current chunk - void GenerateNoiseArray(void); - - /// Returns true if the chunk is outside of the island's dimensions - bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ); - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; -} ; + +// EndGen.h + +// Declares the cEndGen class representing the generator for the End, both as a HeightGen and CompositionGen + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cEndGen : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cEndGen(int a_Seed); + + void Initialize(cIniFile & a_IniFile); + +protected: + + /// Seed for the noise + int m_Seed; + + /// The Perlin noise used for generating + cPerlinNoise m_Perlin; + + // XYZ size of the "island", in blocks: + int m_IslandSizeX; + int m_IslandSizeY; + int m_IslandSizeZ; + + // XYZ Frequencies of the noise functions: + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + + // Minimum and maximum chunk coords for chunks inside the island area. Chunks outside won't get calculated at all + int m_MinChunkX, m_MaxChunkX; + int m_MinChunkZ, m_MaxChunkZ; + + // Noise array for the last chunk (in the noise range) + int m_LastChunkX; + int m_LastChunkZ; + NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y + + /// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise array) + void PrepareState(int a_ChunkX, int a_ChunkZ); + + /// Generates the m_NoiseArray array for the current chunk + void GenerateNoiseArray(void); + + /// Returns true if the chunk is outside of the island's dimensions + bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; diff --git a/source/Generating/MineShafts.cpp b/source/Generating/MineShafts.cpp index 0f7f78e19..3131b5429 100644 --- a/source/Generating/MineShafts.cpp +++ b/source/Generating/MineShafts.cpp @@ -1,1423 +1,1423 @@ - -// MineShafts.cpp - -// Implements the cStructGenMineShafts class representing the structure generator for abandoned mineshafts - -/* -Algorithm: -The cStructGenMineShafts::cMineShaftSystem class is the main controller, which knows what mineshaft -classes there are and their random weights. It gets asked to produce a new class everytime a connection is to be made. -The cMineShaft class is a base class for each mineshaft structure. -Each cMineShaft descendant knows how large it is, how to imprint itself into the chunk data and where to connect to -other descendants. Its PivotPoint is always a walkable column. Its Direction determines in which direction the structure -is facing. - -The generation starts with the central dirt room, from there corridors, crossings and staircases are added -in a depth-first processing. Each of the descendants will branch randomly, if not beyond the allowed recursion level -*/ - -#include "Globals.h" -#include "MineShafts.h" -#include "../Cuboid.h" -#include "../BlockEntities/ChestEntity.h" - - - - - -static const int NEIGHBORHOOD_SIZE = 3; - - - - - -class cMineShaft abstract -{ -public: - enum eKind - { - mskDirtRoom, - mskCorridor, - mskCrossing, - mskStaircase, - } ; - - - enum eDirection - { - dirXP, - dirZP, - dirXM, - dirZM, - } ; - - - cStructGenMineShafts::cMineShaftSystem & m_ParentSystem; - eKind m_Kind; - cCuboid m_BoundingBox; - - - cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind) : - m_ParentSystem(a_ParentSystem), - m_Kind(a_Kind) - { - } - - cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind, const cCuboid & a_BoundingBox) : - m_ParentSystem(a_ParentSystem), - m_Kind(a_Kind), - m_BoundingBox(a_BoundingBox) - { - } - - /// Returns true if this mineshaft intersects the specified cuboid - bool DoesIntersect(const cCuboid & a_Other) - { - return m_BoundingBox.DoesIntersect(a_Other); - } - - /** If recursion level is not too large, appends more branches to the parent system, - using exit points specific to this class. - */ - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) = 0; - - /// Imprints this shape into the specified chunk's data - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) = 0; -} ; - -typedef std::vector cMineShafts; - - - - - -class cMineShaftDirtRoom : - public cMineShaft -{ - typedef cMineShaft super; - -public: - cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise); - - // cMineShaft overrides: - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cMineShaftCorridor : - public cMineShaft -{ - typedef cMineShaft super; - -public: - /** Creates a new Corridor attached to the specified pivot point and direction. - Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. - May return NULL if cannot fit. - */ - static cMineShaft * CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise - ); - -protected: - static const int MAX_SEGMENTS = 5; - - int m_NumSegments; - eDirection m_Direction; - bool m_HasFullBeam[MAX_SEGMENTS]; ///< If true, segment at that index has a full beam support (planks in the top center block) - int m_ChestPosition; ///< If <0, no chest; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction - int m_SpawnerPosition; ///< If <0, no spawner; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction - bool m_HasTracks; ///< If true, random tracks will be placed on the floor - - cMineShaftCorridor( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, - cNoise & a_Noise - ); - - // cMineShaft overrides: - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; - - /// Places a chest, if the corridor has one - void PlaceChest(cChunkDesc & a_ChunkDesc); - - /// If this corridor has tracks, places them randomly - void PlaceTracks(cChunkDesc & a_ChunkDesc); - - /// If this corridor has a spawner, places the spawner - void PlaceSpawner(cChunkDesc & a_ChunkDesc); - - /// Randomly places torches around the central beam block - void PlaceTorches(cChunkDesc & a_ChunkDesc); -} ; - - - - - -class cMineShaftCrossing : - public cMineShaft -{ - typedef cMineShaft super; - -public: - /** Creates a new Crossing attached to the specified pivot point and direction. - Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. - May return NULL if cannot fit. - */ - static cMineShaft * CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise - ); - -protected: - cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox); - - // cMineShaft overrides: - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cMineShaftStaircase : - public cMineShaft -{ - typedef cMineShaft super; - -public: - enum eSlope - { - sUp, - sDown, - } ; - - /** Creates a new Staircase attached to the specified pivot point and direction. - Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. - May return NULL if cannot fit. - */ - static cMineShaft * CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise - ); - -protected: - eDirection m_Direction; - eSlope m_Slope; - - - cMineShaftStaircase( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - const cCuboid & a_BoundingBox, - eDirection a_Direction, - eSlope a_Slope - ); - - // cMineShaft overrides: - virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; - virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; -} ; - - - - - -class cStructGenMineShafts::cMineShaftSystem -{ -public: - int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated - int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction - int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion) - int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor - int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor - int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing - int m_ChanceChest; ///< Chance [0 .. 250] that a corridor has a chest in it - int m_ChanceSpawner; ///< Chance [0 .. 250] that a corridor has a spawner in it - int m_ChanceTorch; ///< Chance [0 .. 10k] for a torch appearing attached to a corridor's beam - cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system - cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit - - /// Creates and generates the entire system - cMineShaftSystem( - int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, - int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase - ); - - ~cMineShaftSystem(); - - /// Carves the system into the chunk data - void ProcessChunk(cChunkDesc & a_Chunk); - - /** Creates new cMineShaft descendant connected at the specified point, heading the specified direction, - if it fits, appends it to the list and calls its AppendBranches() - */ - void AppendBranch( - int a_BlockX, int a_BlockY, int a_BlockZ, - cMineShaft::eDirection a_Direction, cNoise & a_Noise, - int a_RecursionLevel - ); - - /// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid - bool CanAppend(const cCuboid & a_BoundingBox); -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenMineShafts::cMineShaftSystem: - -cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem( - int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, - int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase -) : - m_BlockX(a_BlockX), - m_BlockZ(a_BlockZ), - m_GridSize(a_GridSize), - m_MaxRecursion(8), // TODO: settable - m_ProbLevelCorridor(a_ProbLevelCorridor), - m_ProbLevelCrossing(a_ProbLevelCrossing), - m_ProbLevelStaircase(a_ProbLevelStaircase + 1), - m_ChanceChest(12), // TODO: settable - m_ChanceSpawner(12), // TODO: settable - m_ChanceTorch(1000) // TODO: settable -{ - m_MineShafts.reserve(100); - - cMineShaft * Start = new cMineShaftDirtRoom(*this, a_Noise); - m_MineShafts.push_back(Start); - - m_BoundingBox.Assign( - Start->m_BoundingBox.p1.x - a_MaxSystemSize / 2, 2, Start->m_BoundingBox.p1.z - a_MaxSystemSize / 2, - Start->m_BoundingBox.p2.x + a_MaxSystemSize / 2, 50, Start->m_BoundingBox.p2.z + a_MaxSystemSize / 2 - ); - - Start->AppendBranches(0, a_Noise); - - for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) - { - ASSERT((*itr)->m_BoundingBox.IsSorted()); - } // for itr - m_MineShafts[] -} - - - - - -cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem() -{ - for (cMineShafts::iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - m_MineShafts[] - m_MineShafts.clear(); -} - - - - - -void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk) -{ - for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) - { - (*itr)->ProcessChunk(a_Chunk); - } // for itr - m_MineShafts[] -} - - - - - -void cStructGenMineShafts::cMineShaftSystem::AppendBranch( - int a_PivotX, int a_PivotY, int a_PivotZ, - cMineShaft::eDirection a_Direction, cNoise & a_Noise, - int a_RecursionLevel -) -{ - if (a_RecursionLevel > m_MaxRecursion) - { - return; - } - - cMineShaft * Next = NULL; - int rnd = (a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_RecursionLevel * 16, a_PivotZ) / 13) % m_ProbLevelStaircase; - if (rnd < m_ProbLevelCorridor) - { - Next = cMineShaftCorridor::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); - } - else if (rnd < m_ProbLevelCrossing) - { - Next = cMineShaftCrossing::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); - } - else - { - Next = cMineShaftStaircase::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); - } - if (Next == NULL) - { - return; - } - m_MineShafts.push_back(Next); - Next->AppendBranches(a_RecursionLevel + 1, a_Noise); -} - - - - - -bool cStructGenMineShafts::cMineShaftSystem::CanAppend(const cCuboid & a_BoundingBox) -{ - if (!a_BoundingBox.IsCompletelyInside(m_BoundingBox)) - { - // Too far away, or too low / too high - return false; - } - - // Check intersections: - for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) - { - if ((*itr)->DoesIntersect(a_BoundingBox)) - { - return false; - } - } // for itr - m_MineShafts[] - return true; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMineShaftDirtRoom: - -cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise) : - super(a_Parent, mskDirtRoom) -{ - // Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18: - int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7; - int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; - rnd >>= 12; - int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; - rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11; - m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX; - m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8); - rnd >>= 4; - m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ; - m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8); - rnd >>= 4; - m_BoundingBox.p1.y = 20; - m_BoundingBox.p2.y = 24 + rnd % 8; -} - - - - - -void cMineShaftDirtRoom::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) -{ - int Height = m_BoundingBox.DifY() - 3; - for (int x = m_BoundingBox.p1.x + 1; x < m_BoundingBox.p2.x; x += 4) - { - int rnd = a_Noise.IntNoise3DInt(x, a_RecursionLevel, m_BoundingBox.p1.z) / 7; - m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); - rnd >>= 4; - m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); - } - - for (int z = m_BoundingBox.p1.z + 1; z < m_BoundingBox.p2.z; z += 4) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, a_RecursionLevel, z) / 13; - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXM, a_Noise, a_RecursionLevel); - rnd >>= 4; - m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXP, a_Noise, a_RecursionLevel); - } -} - - - - - -void cMineShaftDirtRoom::ProcessChunk(cChunkDesc & a_ChunkDesc) -{ - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - if ( - (m_BoundingBox.p1.x > BlockX + cChunkDef::Width) || - (m_BoundingBox.p1.z > BlockZ + cChunkDef::Width) || - (m_BoundingBox.p2.x < BlockX) || - (m_BoundingBox.p2.z < BlockZ) - ) - { - // Early bailout - cannot intersect this chunk - return; - } - - // Chunk-relative coords of the boundaries: - int MinX = std::max(BlockX, m_BoundingBox.p1.x) - BlockX; - int MaxX = std::min(BlockX + cChunkDef::Width, m_BoundingBox.p2.x + 1) - BlockX; - int MinZ = std::max(BlockZ, m_BoundingBox.p1.z) - BlockZ; - int MaxZ = std::min(BlockZ + cChunkDef::Width, m_BoundingBox.p2.z + 1) - BlockZ; - - // Carve the room out: - for (int z = MinZ; z < MaxZ; z++) - { - for (int x = MinX; x < MaxX; x++) - { - for (int y = m_BoundingBox.p1.y + 1; y < m_BoundingBox.p2.y; y++) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); - } - if (a_ChunkDesc.GetBlockType(x, m_BoundingBox.p1.y, z) != E_BLOCK_AIR) - { - a_ChunkDesc.SetBlockType(x, m_BoundingBox.p1.y, z, E_BLOCK_DIRT); - } - } // for x - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMineShaftCorridor: - -cMineShaftCorridor::cMineShaftCorridor( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, - cNoise & a_Noise -) : - super(a_ParentSystem, mskCorridor, a_BoundingBox), - m_NumSegments(a_NumSegments), - m_Direction(a_Direction), - m_ChestPosition(-1), - m_SpawnerPosition(-1) -{ - int rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.x, a_BoundingBox.p1.y, a_BoundingBox.p1.z) / 7; - for (int i = 0; i < a_NumSegments; i++) - { - m_HasFullBeam[i] = (rnd % 4) < 3; // 75 % chance of full beam - rnd >>= 2; - } - m_HasTracks = ((rnd % 4) < 2); // 50 % chance of tracks - - rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.z, a_BoundingBox.p1.x, a_BoundingBox.p1.y) / 7; - int ChestCheck = rnd % 250; - rnd >>= 8; - int SpawnerCheck = rnd % 250; - rnd >>= 8; - if (ChestCheck < a_ParentSystem.m_ChanceChest) - { - m_ChestPosition = rnd % (a_NumSegments * 5); - } - if ((a_NumSegments < 4) && (SpawnerCheck < a_ParentSystem.m_ChanceSpawner)) - { - m_SpawnerPosition = rnd % (a_NumSegments * 5); - } -} - - - - - -cMineShaft * cMineShaftCorridor::CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise -) -{ - cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); - BoundingBox.p2.y += 3; - int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; - int NumSegments = 2 + (rnd) % (MAX_SEGMENTS - 1); // 2 .. MAX_SEGMENTS - switch (a_Direction) - { - case dirXP: BoundingBox.p2.x += NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; - case dirXM: BoundingBox.p1.x -= NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; - case dirZP: BoundingBox.p2.z += NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; - case dirZM: BoundingBox.p1.z -= NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; - } - if (!a_ParentSystem.CanAppend(BoundingBox)) - { - return NULL; - } - return new cMineShaftCorridor(a_ParentSystem, BoundingBox, NumSegments, a_Direction, a_Noise); -} - - - - - -void cMineShaftCorridor::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) -{ - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 7; - // Prefer the same height, but allow for up to one block height displacement: - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - switch (m_Direction) - { - case dirXM: - { - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); - for (int i = m_NumSegments; i >= 0; i--) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - rnd >>= 6; - int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); - } - break; - } - - case dirXP: - { - m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); - for (int i = m_NumSegments; i >= 0; i--) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - rnd >>= 6; - int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); - } - break; - } - - case dirZM: - { - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); - for (int i = m_NumSegments; i >= 0; i--) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - rnd >>= 6; - int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); - m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); - } - break; - } - - case dirZP: - { - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); - for (int i = m_NumSegments; i >= 0; i--) - { - int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; - int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; - rnd >>= 6; - int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); - m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); - } - break; - } - } // switch (m_Direction) -} - - - - - -void cMineShaftCorridor::ProcessChunk(cChunkDesc & a_ChunkDesc) -{ - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - cCuboid RelBoundingBox(m_BoundingBox); - RelBoundingBox.Move(-BlockX, 0, -BlockZ); - RelBoundingBox.p1.y += 1; - RelBoundingBox.p2.y -= 1; - cCuboid Top(RelBoundingBox); - Top.p2.y += 1; - Top.p1.y = Top.p2.y; - a_ChunkDesc.FillRelCuboid(RelBoundingBox, E_BLOCK_AIR, 0); - a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_AIR, 0, BlockX ^ BlockZ + BlockX, 8000); - if (m_SpawnerPosition >= 0) - { - // Cobwebs around the spider spawner - a_ChunkDesc.RandomFillRelCuboid(RelBoundingBox, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockZ, 8000); - a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX, 5000); - } - a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX + 10, 500); - RelBoundingBox.p1.y = m_BoundingBox.p1.y; - RelBoundingBox.p2.y = m_BoundingBox.p1.y; - a_ChunkDesc.FloorRelCuboid(RelBoundingBox, E_BLOCK_PLANKS, 0); - switch (m_Direction) - { - case dirXM: - case dirXP: - { - int y1 = m_BoundingBox.p1.y + 1; - int y2 = m_BoundingBox.p1.y + 2; - int y3 = m_BoundingBox.p1.y + 3; - int z1 = m_BoundingBox.p1.z - BlockZ; - int z2 = m_BoundingBox.p2.z - BlockZ; - for (int i = 0; i < m_NumSegments; i++) - { - int x = m_BoundingBox.p1.x + i * 5 + 2 - BlockX; - if ((x < 0) || (x >= cChunkDef::Width)) - { - continue; - } - if ((z1 >= 0) && (z1 < cChunkDef::Width)) - { - a_ChunkDesc.SetBlockTypeMeta(x, y1, z1, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x, y2, z1, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x, y3, z1, E_BLOCK_PLANKS, 0); - } - if ((z2 >= 0) && (z2 < cChunkDef::Width)) - { - a_ChunkDesc.SetBlockTypeMeta(x, y1, z2, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x, y2, z2, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x, y3, z2, E_BLOCK_PLANKS, 0); - } - if ((z1 >= -1) && (z1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) - { - a_ChunkDesc.SetBlockTypeMeta(x, y3, z1 + 1, E_BLOCK_PLANKS, 0); - } - } // for i - NumSegments - break; - } - - case dirZM: - case dirZP: - { - int y1 = m_BoundingBox.p1.y + 1; - int y2 = m_BoundingBox.p1.y + 2; - int y3 = m_BoundingBox.p1.y + 3; - int x1 = m_BoundingBox.p1.x - BlockX; - int x2 = m_BoundingBox.p2.x - BlockX; - for (int i = 0; i < m_NumSegments; i++) - { - int z = m_BoundingBox.p1.z + i * 5 + 2 - BlockZ; - if ((z < 0) || (z >= cChunkDef::Width)) - { - continue; - } - if ((x1 >= 0) && (x1 < cChunkDef::Width)) - { - a_ChunkDesc.SetBlockTypeMeta(x1, y1, z, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x1, y2, z, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x1, y3, z, E_BLOCK_PLANKS, 0); - } - if ((x2 >= 0) && (x2 < cChunkDef::Width)) - { - a_ChunkDesc.SetBlockTypeMeta(x2, y1, z, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x2, y2, z, E_BLOCK_FENCE, 0); - a_ChunkDesc.SetBlockTypeMeta(x2, y3, z, E_BLOCK_PLANKS, 0); - } - if ((x1 >= -1) && (x1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) - { - a_ChunkDesc.SetBlockTypeMeta(x1 + 1, y3, z, E_BLOCK_PLANKS, 0); - } - } // for i - NumSegments - break; - } // case dirZ? - } // for i - - PlaceChest(a_ChunkDesc); - PlaceTracks(a_ChunkDesc); - PlaceSpawner(a_ChunkDesc); // (must be after Tracks!) - PlaceTorches(a_ChunkDesc); -} - - - - - -void cMineShaftCorridor::PlaceChest(cChunkDesc & a_ChunkDesc) -{ - static const cLootProbab LootProbab[] = - { - // Item, MinAmount, MaxAmount, Weight - { cItem(E_ITEM_IRON), 1, 5, 10 }, - { cItem(E_ITEM_GOLD), 1, 3, 5 }, - { cItem(E_ITEM_REDSTONE_DUST), 4, 9, 5 }, - { cItem(E_ITEM_DIAMOND), 1, 2, 3 }, - { cItem(E_ITEM_DYE, 1, 4), 4, 9, 5 }, // lapis lazuli dye - { cItem(E_ITEM_COAL), 3, 8, 10 }, - { cItem(E_ITEM_BREAD), 1, 3, 15 }, - { cItem(E_ITEM_IRON_PICKAXE), 1, 1, 1 }, - { cItem(E_BLOCK_MINECART_TRACKS), 4, 8, 1 }, - { cItem(E_ITEM_MELON_SEEDS), 2, 4, 10 }, - { cItem(E_ITEM_PUMPKIN_SEEDS), 2, 4, 10 }, - } ; - - if (m_ChestPosition < 0) - { - return; - } - - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - int x, z; - NIBBLETYPE Meta = 0; - switch (m_Direction) - { - case dirXM: - case dirXP: - { - x = m_BoundingBox.p1.x + m_ChestPosition - BlockX; - z = m_BoundingBox.p1.z - BlockZ; - Meta = E_META_CHEST_FACING_ZP; - break; - } - - case dirZM: - case dirZP: - { - x = m_BoundingBox.p1.x - BlockX; - z = m_BoundingBox.p1.z + m_ChestPosition - BlockZ; - Meta = E_META_CHEST_FACING_XP; - break; - } - } // switch (Dir) - - if ( - (x >= 0) && (x < cChunkDef::Width) && - (z >= 0) && (z < cChunkDef::Width) - ) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p1.y + 1, z, E_BLOCK_CHEST, Meta); - cChestEntity * ChestEntity = new cChestEntity(BlockX + x, m_BoundingBox.p1.y + 1, BlockZ + z); - cNoise Noise(a_ChunkDesc.GetChunkX() ^ a_ChunkDesc.GetChunkZ()); - int NumSlots = 3 + ((Noise.IntNoise3DInt(x, m_BoundingBox.p1.y, z) / 11) % 4); - int Seed = Noise.IntNoise2DInt(x, z); - ChestEntity->GetContents().GenerateRandomLootWithBooks(LootProbab, ARRAYCOUNT(LootProbab), NumSlots, Seed); - a_ChunkDesc.AddBlockEntity(ChestEntity); - } -} - - - - - -void cMineShaftCorridor::PlaceTracks(cChunkDesc & a_ChunkDesc) -{ - if (!m_HasTracks) - { - return; - } - cCuboid Box(m_BoundingBox); - Box.Move(-a_ChunkDesc.GetChunkX() * cChunkDef::Width, 1, -a_ChunkDesc.GetChunkZ() * cChunkDef::Width); - Box.p2.y = Box.p1.y; - Box.p1.x += 1; - Box.p2.x -= 1; - Box.p1.z += 1; - Box.p2.z -= 1; - NIBBLETYPE Meta = 0; - switch (m_Direction) - { - case dirXM: - case dirXP: - { - Meta = E_META_TRACKS_X; - break; - } - - case dirZM: - case dirZP: - { - Meta = E_META_TRACKS_Z; - break; - } - } // switch (direction) - a_ChunkDesc.RandomFillRelCuboid(Box, E_BLOCK_MINECART_TRACKS, Meta, a_ChunkDesc.GetChunkX() + a_ChunkDesc.GetChunkZ(), 6000); -} - - - - - -void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc) -{ - if (m_SpawnerPosition < 0) - { - // No spawner in this corridor - return; - } - int SpawnerRelX = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int SpawnerRelZ = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - switch (m_Direction) - { - case dirXM: - case dirXP: - { - SpawnerRelX += m_SpawnerPosition - 1; - break; - } - case dirZM: - case dirZP: - { - SpawnerRelZ += m_SpawnerPosition - 1; - break; - } - } - if ( - (SpawnerRelX >= 0) && (SpawnerRelX < cChunkDef::Width) && - (SpawnerRelZ >= 0) && (SpawnerRelZ < cChunkDef::Width) - ) - { - a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0); - // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented - } -} - - - - - -void cMineShaftCorridor::PlaceTorches(cChunkDesc & a_ChunkDesc) -{ - cNoise Noise(m_BoundingBox.p1.x); - switch (m_Direction) - { - case dirXM: - case dirXP: - { - int z = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - if ((z < 0) || (z >= cChunkDef::Width)) - { - return; - } - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - for (int i = 0; i < m_NumSegments; i++) - { - if (!m_HasFullBeam[i]) - { - continue; - } - int x = m_BoundingBox.p1.x + i * 5 + 1 - BlockX; - if ((x >= 0) && (x < cChunkDef::Width)) - { - if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XP); - } - } - x += 2; - if ((x >= 0) && (x < cChunkDef::Width)) - { - if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XM); - } - } - } // for i - break; - } - - case dirZM: - case dirZP: - { - int x = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; - if ((x < 0) || (x >= cChunkDef::Width)) - { - return; - } - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - for (int i = 0; i < m_NumSegments; i++) - { - if (!m_HasFullBeam[i]) - { - continue; - } - int z = m_BoundingBox.p1.z + i * 5 + 1 - BlockZ; - if ((z >= 0) && (z < cChunkDef::Width)) - { - if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZP); - } - } - z += 2; - if ((z >= 0) && (z < cChunkDef::Width)) - { - if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) - { - a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZM); - } - } - } // for i - break; - } - } // switch (direction) -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMineShaftCrossing: - -cMineShaftCrossing::cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox) : - super(a_ParentSystem, mskCrossing, a_BoundingBox) -{ -} - - - - - -cMineShaft * cMineShaftCrossing::CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise -) -{ - cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); - int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; - BoundingBox.p2.y += 3; - if ((rnd % 4) < 2) - { - // 2-level crossing: - BoundingBox.p2.y += 4; - rnd >>= 2; - if ((rnd % 4) < 2) - { - // This is the higher level: - BoundingBox.p1.y -= 4; - BoundingBox.p2.y -= 4; - } - } - rnd >>= 2; - switch (a_Direction) - { - case dirXP: BoundingBox.p2.x += 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; - case dirXM: BoundingBox.p1.x -= 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; - case dirZP: BoundingBox.p2.z += 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; - case dirZM: BoundingBox.p1.z -= 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; - } - if (!a_ParentSystem.CanAppend(BoundingBox)) - { - return NULL; - } - return new cMineShaftCrossing(a_ParentSystem, BoundingBox); -} - - - - - -void cMineShaftCrossing::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) -{ - struct - { - int x, y, z; - eDirection dir; - } Exits[] = - { - // Bottom level: - {-1, 1, 2, dirXM}, - { 2, 1, -1, dirZM}, - { 5, 1, 2, dirXP}, - { 2, 1, 5, dirZP}, - // Top level: - {-1, 5, 2, dirXM}, - { 2, 5, -1, dirZM}, - { 5, 5, 2, dirXP}, - { 2, 5, 5, dirZP}, - } ; - for (int i = 0; i < ARRAYCOUNT(Exits); i++) - { - if (m_BoundingBox.p1.y + Exits[i].y >= m_BoundingBox.p2.y) - { - // This exit is not available (two-level exit on a one-level crossing) - continue; - } - - int Height = m_BoundingBox.p1.y + Exits[i].y; - m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Exits[i].x, Height, m_BoundingBox.p1.z + Exits[i].z, Exits[i].dir, a_Noise, a_RecursionLevel); - } // for i -} - - - - - -void cMineShaftCrossing::ProcessChunk(cChunkDesc & a_ChunkDesc) -{ - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - cCuboid box(m_BoundingBox); - box.Move(-BlockX, 0, -BlockZ); - if ((box.p2.x < 0) || (box.p2.z < 0) || (box.p1.x >= cChunkDef::Width) || (box.p1.z > cChunkDef::Width)) - { - // Does not intersect this chunk - return; - } - int Floor = box.p1.y + 1; - int Ceil = box.p2.y; - - // The supports: - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); - - // The air in between: - a_ChunkDesc.FillRelCuboid(box.p1.x + 2, box.p1.x + 2, Floor, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 2, box.p1.z + 2, E_BLOCK_AIR, 0); - - // The air on the edges: - int Mid = Floor + 2; - a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); - Mid += 2; - if (Mid < Ceil) - { - a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); - } - - // The floor, if needed: - box.p2.y = box.p1.y; - a_ChunkDesc.FloorRelCuboid(box, E_BLOCK_PLANKS, 0); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMineShaftStaircase: - -cMineShaftStaircase::cMineShaftStaircase( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - const cCuboid & a_BoundingBox, - eDirection a_Direction, - eSlope a_Slope -) : - super(a_ParentSystem, mskStaircase, a_BoundingBox), - m_Direction(a_Direction), - m_Slope(a_Slope) -{ -} - - - - - -cMineShaft * cMineShaftStaircase::CreateAndFit( - cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, - int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, - cNoise & a_Noise -) -{ - int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; - cCuboid Box; - switch (a_Direction) - { - case dirXM: - { - Box.Assign(a_PivotX - 7, a_PivotY - 1, a_PivotZ - 1, a_PivotX, a_PivotY + 6, a_PivotZ + 1); - break; - } - case dirXP: - { - Box.Assign(a_PivotX, a_PivotY - 1, a_PivotZ - 1, a_PivotX + 7, a_PivotY + 6, a_PivotZ + 1); - break; - } - case dirZM: - { - Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ - 7, a_PivotX + 1, a_PivotY + 6, a_PivotZ); - break; - } - case dirZP: - { - Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ, a_PivotX + 1, a_PivotY + 6, a_PivotZ + 7); - break; - } - } - eSlope Slope = sUp; - if ((rnd % 4) < 2) // 50 % - { - Slope = sDown; - Box.Move(0, -4, 0); - } - if (!a_ParentSystem.CanAppend(Box)) - { - return NULL; - } - return new cMineShaftStaircase(a_ParentSystem, Box, a_Direction, Slope); -} - - - - - -void cMineShaftStaircase::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) -{ - int Height = m_BoundingBox.p1.y + ((m_Slope == sDown) ? 1 : 5); - switch (m_Direction) - { - case dirXM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); break; - case dirXP: m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); break; - case dirZM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); break; - case dirZP: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); break; - } -} - - - - - -void cMineShaftStaircase::ProcessChunk(cChunkDesc & a_ChunkDesc) -{ - int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; - int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - cCuboid RelB(m_BoundingBox); - RelB.Move(-BlockX, 0, -BlockZ); - if ( - (RelB.p1.x >= cChunkDef::Width) || - (RelB.p1.z >= cChunkDef::Width) || - (RelB.p2.x < 0) || - (RelB.p2.z < 0) - ) - { - // No intersection between this staircase and this chunk - return; - } - - int SFloor = RelB.p1.y + ((m_Slope == sDown) ? 5 : 1); - int DFloor = RelB.p1.y + ((m_Slope == sDown) ? 1 : 5); - int Add = (m_Slope == sDown) ? -1 : 1; - int InitAdd = (m_Slope == sDown) ? -1 : 0; - cCuboid Box; - switch (m_Direction) - { - case dirXM: - { - a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); - Box.Assign(RelB.p2.x - 2, SFloor + InitAdd, RelB.p1.z, RelB.p2.x - 2, SFloor + 3 + InitAdd, RelB.p2.z); - for (int i = 0; i < 4; i++) - { - a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); - Box.Move(-1, Add, 0); - } - break; - } - - case dirXP: - { - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); - Box.Assign(RelB.p1.x + 2, SFloor + InitAdd, RelB.p1.z, RelB.p1.x + 2, SFloor + 3 + InitAdd, RelB.p2.z); - for (int i = 0; i < 4; i++) - { - a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); - Box.Move(1, Add, 0); - } - break; - } - - case dirZM: - { - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); - Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p2.z - 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p2.z - 2); - for (int i = 0; i < 4; i++) - { - a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); - Box.Move(0, Add, -1); - } - break; - } - - case dirZP: - { - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); - a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); - a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); - Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p1.z + 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p1.z + 2); - for (int i = 0; i < 4; i++) - { - a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); - a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); - Box.Move(0, Add, 1); - } - break; - } - - } // switch (m_Direction) -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenMineShafts: - -cStructGenMineShafts::cStructGenMineShafts( - int a_Seed, int a_GridSize, int a_MaxSystemSize, - int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase -) : - m_Noise(a_Seed), - m_GridSize(a_GridSize), - m_MaxSystemSize(a_MaxSystemSize), - m_ProbLevelCorridor(std::max(0, a_ChanceCorridor)), - m_ProbLevelCrossing(std::max(0, a_ChanceCorridor + a_ChanceCrossing)), - m_ProbLevelStaircase(std::max(0, a_ChanceCorridor + a_ChanceCrossing + a_ChanceStaircase)) -{ -} - - - - - -cStructGenMineShafts::~cStructGenMineShafts() -{ - ClearCache(); -} - - - - - -void cStructGenMineShafts::ClearCache(void) -{ - for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - m_Cache[] - m_Cache.clear(); -} - - - - - -void cStructGenMineShafts::GetMineShaftSystemsForChunk( - int a_ChunkX, int a_ChunkZ, - cStructGenMineShafts::cMineShaftSystems & a_MineShafts -) -{ - int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize; - int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize; - if (BaseX < 0) - { - --BaseX; - } - if (BaseZ < 0) - { - --BaseZ; - } - BaseX -= NEIGHBORHOOD_SIZE / 2; - BaseZ -= NEIGHBORHOOD_SIZE / 2; - - // Walk the cache, move each cave system that we want into a_Caves: - int StartX = BaseX * m_GridSize; - int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize; - int StartZ = BaseZ * m_GridSize; - int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize; - for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) - { - if ( - ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && - ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) - ) - { - // want - a_MineShafts.push_back(*itr); - itr = m_Cache.erase(itr); - } - else - { - // don't want - ++itr; - } - } // for itr - m_Cache[] - - for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) - { - int RealX = (BaseX + x) * m_GridSize; - for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) - { - int RealZ = (BaseZ + z) * m_GridSize; - bool Found = false; - for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr) - { - if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) - { - Found = true; - break; - } - } // for itr - a_Mineshafts - if (!Found) - { - a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase)); - } - } // for z - } // for x - - // Copy a_MineShafts into m_Cache to the beginning: - cMineShaftSystems MineShaftsCopy(a_MineShafts); - m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end()); - - // Trim the cache if it's too long: - if (m_Cache.size() > 100) - { - cMineShaftSystems::iterator itr = m_Cache.begin(); - std::advance(itr, 100); - for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } - itr = m_Cache.begin(); - std::advance(itr, 100); - m_Cache.erase(itr, m_Cache.end()); - } -} - - - - - - -void cStructGenMineShafts::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - cMineShaftSystems MineShafts; - GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts); - for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr) - { - (*itr)->ProcessChunk(a_ChunkDesc); - } // for itr - MineShafts[] -} - - - - + +// MineShafts.cpp + +// Implements the cStructGenMineShafts class representing the structure generator for abandoned mineshafts + +/* +Algorithm: +The cStructGenMineShafts::cMineShaftSystem class is the main controller, which knows what mineshaft +classes there are and their random weights. It gets asked to produce a new class everytime a connection is to be made. +The cMineShaft class is a base class for each mineshaft structure. +Each cMineShaft descendant knows how large it is, how to imprint itself into the chunk data and where to connect to +other descendants. Its PivotPoint is always a walkable column. Its Direction determines in which direction the structure +is facing. + +The generation starts with the central dirt room, from there corridors, crossings and staircases are added +in a depth-first processing. Each of the descendants will branch randomly, if not beyond the allowed recursion level +*/ + +#include "Globals.h" +#include "MineShafts.h" +#include "../Cuboid.h" +#include "../BlockEntities/ChestEntity.h" + + + + + +static const int NEIGHBORHOOD_SIZE = 3; + + + + + +class cMineShaft abstract +{ +public: + enum eKind + { + mskDirtRoom, + mskCorridor, + mskCrossing, + mskStaircase, + } ; + + + enum eDirection + { + dirXP, + dirZP, + dirXM, + dirZM, + } ; + + + cStructGenMineShafts::cMineShaftSystem & m_ParentSystem; + eKind m_Kind; + cCuboid m_BoundingBox; + + + cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind) : + m_ParentSystem(a_ParentSystem), + m_Kind(a_Kind) + { + } + + cMineShaft(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, eKind a_Kind, const cCuboid & a_BoundingBox) : + m_ParentSystem(a_ParentSystem), + m_Kind(a_Kind), + m_BoundingBox(a_BoundingBox) + { + } + + /// Returns true if this mineshaft intersects the specified cuboid + bool DoesIntersect(const cCuboid & a_Other) + { + return m_BoundingBox.DoesIntersect(a_Other); + } + + /** If recursion level is not too large, appends more branches to the parent system, + using exit points specific to this class. + */ + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) = 0; + + /// Imprints this shape into the specified chunk's data + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) = 0; +} ; + +typedef std::vector cMineShafts; + + + + + +class cMineShaftDirtRoom : + public cMineShaft +{ + typedef cMineShaft super; + +public: + cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cMineShaftCorridor : + public cMineShaft +{ + typedef cMineShaft super; + +public: + /** Creates a new Corridor attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + static const int MAX_SEGMENTS = 5; + + int m_NumSegments; + eDirection m_Direction; + bool m_HasFullBeam[MAX_SEGMENTS]; ///< If true, segment at that index has a full beam support (planks in the top center block) + int m_ChestPosition; ///< If <0, no chest; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction + int m_SpawnerPosition; ///< If <0, no spawner; otherwise an offset from m_BoundingBox's p1.x or p1.z, depenging on m_Direction + bool m_HasTracks; ///< If true, random tracks will be placed on the floor + + cMineShaftCorridor( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, + cNoise & a_Noise + ); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; + + /// Places a chest, if the corridor has one + void PlaceChest(cChunkDesc & a_ChunkDesc); + + /// If this corridor has tracks, places them randomly + void PlaceTracks(cChunkDesc & a_ChunkDesc); + + /// If this corridor has a spawner, places the spawner + void PlaceSpawner(cChunkDesc & a_ChunkDesc); + + /// Randomly places torches around the central beam block + void PlaceTorches(cChunkDesc & a_ChunkDesc); +} ; + + + + + +class cMineShaftCrossing : + public cMineShaft +{ + typedef cMineShaft super; + +public: + /** Creates a new Crossing attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cMineShaftStaircase : + public cMineShaft +{ + typedef cMineShaft super; + +public: + enum eSlope + { + sUp, + sDown, + } ; + + /** Creates a new Staircase attached to the specified pivot point and direction. + Checks all ParentSystem's objects and disallows intersecting. Initializes the new object to fit. + May return NULL if cannot fit. + */ + static cMineShaft * CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise + ); + +protected: + eDirection m_Direction; + eSlope m_Slope; + + + cMineShaftStaircase( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, + eDirection a_Direction, + eSlope a_Slope + ); + + // cMineShaft overrides: + virtual void AppendBranches(int a_RecursionLevel, cNoise & a_Noise) override; + virtual void ProcessChunk(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + +class cStructGenMineShafts::cMineShaftSystem +{ +public: + int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated + int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction + int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion) + int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor + int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor + int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing + int m_ChanceChest; ///< Chance [0 .. 250] that a corridor has a chest in it + int m_ChanceSpawner; ///< Chance [0 .. 250] that a corridor has a spawner in it + int m_ChanceTorch; ///< Chance [0 .. 10k] for a torch appearing attached to a corridor's beam + cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system + cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit + + /// Creates and generates the entire system + cMineShaftSystem( + int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, + int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase + ); + + ~cMineShaftSystem(); + + /// Carves the system into the chunk data + void ProcessChunk(cChunkDesc & a_Chunk); + + /** Creates new cMineShaft descendant connected at the specified point, heading the specified direction, + if it fits, appends it to the list and calls its AppendBranches() + */ + void AppendBranch( + int a_BlockX, int a_BlockY, int a_BlockZ, + cMineShaft::eDirection a_Direction, cNoise & a_Noise, + int a_RecursionLevel + ); + + /// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid + bool CanAppend(const cCuboid & a_BoundingBox); +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMineShafts::cMineShaftSystem: + +cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem( + int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise, + int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase +) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_GridSize(a_GridSize), + m_MaxRecursion(8), // TODO: settable + m_ProbLevelCorridor(a_ProbLevelCorridor), + m_ProbLevelCrossing(a_ProbLevelCrossing), + m_ProbLevelStaircase(a_ProbLevelStaircase + 1), + m_ChanceChest(12), // TODO: settable + m_ChanceSpawner(12), // TODO: settable + m_ChanceTorch(1000) // TODO: settable +{ + m_MineShafts.reserve(100); + + cMineShaft * Start = new cMineShaftDirtRoom(*this, a_Noise); + m_MineShafts.push_back(Start); + + m_BoundingBox.Assign( + Start->m_BoundingBox.p1.x - a_MaxSystemSize / 2, 2, Start->m_BoundingBox.p1.z - a_MaxSystemSize / 2, + Start->m_BoundingBox.p2.x + a_MaxSystemSize / 2, 50, Start->m_BoundingBox.p2.z + a_MaxSystemSize / 2 + ); + + Start->AppendBranches(0, a_Noise); + + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + ASSERT((*itr)->m_BoundingBox.IsSorted()); + } // for itr - m_MineShafts[] +} + + + + + +cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem() +{ + for (cMineShafts::iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_MineShafts[] + m_MineShafts.clear(); +} + + + + + +void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk) +{ + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(a_Chunk); + } // for itr - m_MineShafts[] +} + + + + + +void cStructGenMineShafts::cMineShaftSystem::AppendBranch( + int a_PivotX, int a_PivotY, int a_PivotZ, + cMineShaft::eDirection a_Direction, cNoise & a_Noise, + int a_RecursionLevel +) +{ + if (a_RecursionLevel > m_MaxRecursion) + { + return; + } + + cMineShaft * Next = NULL; + int rnd = (a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_RecursionLevel * 16, a_PivotZ) / 13) % m_ProbLevelStaircase; + if (rnd < m_ProbLevelCorridor) + { + Next = cMineShaftCorridor::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + else if (rnd < m_ProbLevelCrossing) + { + Next = cMineShaftCrossing::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + else + { + Next = cMineShaftStaircase::CreateAndFit(*this, a_PivotX, a_PivotY, a_PivotZ, a_Direction, a_Noise); + } + if (Next == NULL) + { + return; + } + m_MineShafts.push_back(Next); + Next->AppendBranches(a_RecursionLevel + 1, a_Noise); +} + + + + + +bool cStructGenMineShafts::cMineShaftSystem::CanAppend(const cCuboid & a_BoundingBox) +{ + if (!a_BoundingBox.IsCompletelyInside(m_BoundingBox)) + { + // Too far away, or too low / too high + return false; + } + + // Check intersections: + for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr) + { + if ((*itr)->DoesIntersect(a_BoundingBox)) + { + return false; + } + } // for itr - m_MineShafts[] + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftDirtRoom: + +cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem & a_Parent, cNoise & a_Noise) : + super(a_Parent, mskDirtRoom) +{ + // Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18: + int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7; + int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; + rnd >>= 12; + int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2; + rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11; + m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX; + m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8); + rnd >>= 4; + m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ; + m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8); + rnd >>= 4; + m_BoundingBox.p1.y = 20; + m_BoundingBox.p2.y = 24 + rnd % 8; +} + + + + + +void cMineShaftDirtRoom::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int Height = m_BoundingBox.DifY() - 3; + for (int x = m_BoundingBox.p1.x + 1; x < m_BoundingBox.p2.x; x += 4) + { + int rnd = a_Noise.IntNoise3DInt(x, a_RecursionLevel, m_BoundingBox.p1.z) / 7; + m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + rnd >>= 4; + m_ParentSystem.AppendBranch(x, m_BoundingBox.p1.y + (rnd % Height), m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + + for (int z = m_BoundingBox.p1.z + 1; z < m_BoundingBox.p2.z; z += 4) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, a_RecursionLevel, z) / 13; + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXM, a_Noise, a_RecursionLevel); + rnd >>= 4; + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, m_BoundingBox.p1.y + (rnd % Height), z, dirXP, a_Noise, a_RecursionLevel); + } +} + + + + + +void cMineShaftDirtRoom::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + if ( + (m_BoundingBox.p1.x > BlockX + cChunkDef::Width) || + (m_BoundingBox.p1.z > BlockZ + cChunkDef::Width) || + (m_BoundingBox.p2.x < BlockX) || + (m_BoundingBox.p2.z < BlockZ) + ) + { + // Early bailout - cannot intersect this chunk + return; + } + + // Chunk-relative coords of the boundaries: + int MinX = std::max(BlockX, m_BoundingBox.p1.x) - BlockX; + int MaxX = std::min(BlockX + cChunkDef::Width, m_BoundingBox.p2.x + 1) - BlockX; + int MinZ = std::max(BlockZ, m_BoundingBox.p1.z) - BlockZ; + int MaxZ = std::min(BlockZ + cChunkDef::Width, m_BoundingBox.p2.z + 1) - BlockZ; + + // Carve the room out: + for (int z = MinZ; z < MaxZ; z++) + { + for (int x = MinX; x < MaxX; x++) + { + for (int y = m_BoundingBox.p1.y + 1; y < m_BoundingBox.p2.y; y++) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR); + } + if (a_ChunkDesc.GetBlockType(x, m_BoundingBox.p1.y, z) != E_BLOCK_AIR) + { + a_ChunkDesc.SetBlockType(x, m_BoundingBox.p1.y, z, E_BLOCK_DIRT); + } + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftCorridor: + +cMineShaftCorridor::cMineShaftCorridor( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, int a_NumSegments, eDirection a_Direction, + cNoise & a_Noise +) : + super(a_ParentSystem, mskCorridor, a_BoundingBox), + m_NumSegments(a_NumSegments), + m_Direction(a_Direction), + m_ChestPosition(-1), + m_SpawnerPosition(-1) +{ + int rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.x, a_BoundingBox.p1.y, a_BoundingBox.p1.z) / 7; + for (int i = 0; i < a_NumSegments; i++) + { + m_HasFullBeam[i] = (rnd % 4) < 3; // 75 % chance of full beam + rnd >>= 2; + } + m_HasTracks = ((rnd % 4) < 2); // 50 % chance of tracks + + rnd = a_Noise.IntNoise3DInt(a_BoundingBox.p1.z, a_BoundingBox.p1.x, a_BoundingBox.p1.y) / 7; + int ChestCheck = rnd % 250; + rnd >>= 8; + int SpawnerCheck = rnd % 250; + rnd >>= 8; + if (ChestCheck < a_ParentSystem.m_ChanceChest) + { + m_ChestPosition = rnd % (a_NumSegments * 5); + } + if ((a_NumSegments < 4) && (SpawnerCheck < a_ParentSystem.m_ChanceSpawner)) + { + m_SpawnerPosition = rnd % (a_NumSegments * 5); + } +} + + + + + +cMineShaft * cMineShaftCorridor::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); + BoundingBox.p2.y += 3; + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + int NumSegments = 2 + (rnd) % (MAX_SEGMENTS - 1); // 2 .. MAX_SEGMENTS + switch (a_Direction) + { + case dirXP: BoundingBox.p2.x += NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; + case dirXM: BoundingBox.p1.x -= NumSegments * 5 - 1; BoundingBox.p1.z -= 1; BoundingBox.p2.z += 1; break; + case dirZP: BoundingBox.p2.z += NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; + case dirZM: BoundingBox.p1.z -= NumSegments * 5 - 1; BoundingBox.p1.x -= 1; BoundingBox.p2.x += 1; break; + } + if (!a_ParentSystem.CanAppend(BoundingBox)) + { + return NULL; + } + return new cMineShaftCorridor(a_ParentSystem, BoundingBox, NumSegments, a_Direction, a_Noise); +} + + + + + +void cMineShaftCorridor::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 7; + // Prefer the same height, but allow for up to one block height displacement: + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + switch (m_Direction) + { + case dirXM: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirXP: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Ofs, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirZM: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); + } + break; + } + + case dirZP: + { + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); + for (int i = m_NumSegments; i >= 0; i--) + { + int rnd = a_Noise.IntNoise3DInt(m_BoundingBox.p1.x + i + 10, m_BoundingBox.p1.y + a_RecursionLevel, m_BoundingBox.p1.z) / 11; + int Height = m_BoundingBox.p1.y + ((rnd % 4) + ((rnd >> 3) % 3)) / 2; + rnd >>= 6; + int Ofs = 1 + rnd % (m_NumSegments * 5 - 2); + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + Ofs, dirXM, a_Noise, a_RecursionLevel); + m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + Ofs, dirXP, a_Noise, a_RecursionLevel); + } + break; + } + } // switch (m_Direction) +} + + + + + +void cMineShaftCorridor::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid RelBoundingBox(m_BoundingBox); + RelBoundingBox.Move(-BlockX, 0, -BlockZ); + RelBoundingBox.p1.y += 1; + RelBoundingBox.p2.y -= 1; + cCuboid Top(RelBoundingBox); + Top.p2.y += 1; + Top.p1.y = Top.p2.y; + a_ChunkDesc.FillRelCuboid(RelBoundingBox, E_BLOCK_AIR, 0); + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_AIR, 0, BlockX ^ BlockZ + BlockX, 8000); + if (m_SpawnerPosition >= 0) + { + // Cobwebs around the spider spawner + a_ChunkDesc.RandomFillRelCuboid(RelBoundingBox, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockZ, 8000); + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX, 5000); + } + a_ChunkDesc.RandomFillRelCuboid(Top, E_BLOCK_COBWEB, 0, BlockX ^ BlockZ + BlockX + 10, 500); + RelBoundingBox.p1.y = m_BoundingBox.p1.y; + RelBoundingBox.p2.y = m_BoundingBox.p1.y; + a_ChunkDesc.FloorRelCuboid(RelBoundingBox, E_BLOCK_PLANKS, 0); + switch (m_Direction) + { + case dirXM: + case dirXP: + { + int y1 = m_BoundingBox.p1.y + 1; + int y2 = m_BoundingBox.p1.y + 2; + int y3 = m_BoundingBox.p1.y + 3; + int z1 = m_BoundingBox.p1.z - BlockZ; + int z2 = m_BoundingBox.p2.z - BlockZ; + for (int i = 0; i < m_NumSegments; i++) + { + int x = m_BoundingBox.p1.x + i * 5 + 2 - BlockX; + if ((x < 0) || (x >= cChunkDef::Width)) + { + continue; + } + if ((z1 >= 0) && (z1 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x, y1, z1, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y2, z1, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y3, z1, E_BLOCK_PLANKS, 0); + } + if ((z2 >= 0) && (z2 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x, y1, z2, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y2, z2, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x, y3, z2, E_BLOCK_PLANKS, 0); + } + if ((z1 >= -1) && (z1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) + { + a_ChunkDesc.SetBlockTypeMeta(x, y3, z1 + 1, E_BLOCK_PLANKS, 0); + } + } // for i - NumSegments + break; + } + + case dirZM: + case dirZP: + { + int y1 = m_BoundingBox.p1.y + 1; + int y2 = m_BoundingBox.p1.y + 2; + int y3 = m_BoundingBox.p1.y + 3; + int x1 = m_BoundingBox.p1.x - BlockX; + int x2 = m_BoundingBox.p2.x - BlockX; + for (int i = 0; i < m_NumSegments; i++) + { + int z = m_BoundingBox.p1.z + i * 5 + 2 - BlockZ; + if ((z < 0) || (z >= cChunkDef::Width)) + { + continue; + } + if ((x1 >= 0) && (x1 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x1, y1, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x1, y2, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x1, y3, z, E_BLOCK_PLANKS, 0); + } + if ((x2 >= 0) && (x2 < cChunkDef::Width)) + { + a_ChunkDesc.SetBlockTypeMeta(x2, y1, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x2, y2, z, E_BLOCK_FENCE, 0); + a_ChunkDesc.SetBlockTypeMeta(x2, y3, z, E_BLOCK_PLANKS, 0); + } + if ((x1 >= -1) && (x1 < cChunkDef::Width - 1) && m_HasFullBeam[i]) + { + a_ChunkDesc.SetBlockTypeMeta(x1 + 1, y3, z, E_BLOCK_PLANKS, 0); + } + } // for i - NumSegments + break; + } // case dirZ? + } // for i + + PlaceChest(a_ChunkDesc); + PlaceTracks(a_ChunkDesc); + PlaceSpawner(a_ChunkDesc); // (must be after Tracks!) + PlaceTorches(a_ChunkDesc); +} + + + + + +void cMineShaftCorridor::PlaceChest(cChunkDesc & a_ChunkDesc) +{ + static const cLootProbab LootProbab[] = + { + // Item, MinAmount, MaxAmount, Weight + { cItem(E_ITEM_IRON), 1, 5, 10 }, + { cItem(E_ITEM_GOLD), 1, 3, 5 }, + { cItem(E_ITEM_REDSTONE_DUST), 4, 9, 5 }, + { cItem(E_ITEM_DIAMOND), 1, 2, 3 }, + { cItem(E_ITEM_DYE, 1, 4), 4, 9, 5 }, // lapis lazuli dye + { cItem(E_ITEM_COAL), 3, 8, 10 }, + { cItem(E_ITEM_BREAD), 1, 3, 15 }, + { cItem(E_ITEM_IRON_PICKAXE), 1, 1, 1 }, + { cItem(E_BLOCK_MINECART_TRACKS), 4, 8, 1 }, + { cItem(E_ITEM_MELON_SEEDS), 2, 4, 10 }, + { cItem(E_ITEM_PUMPKIN_SEEDS), 2, 4, 10 }, + } ; + + if (m_ChestPosition < 0) + { + return; + } + + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + int x, z; + NIBBLETYPE Meta = 0; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + x = m_BoundingBox.p1.x + m_ChestPosition - BlockX; + z = m_BoundingBox.p1.z - BlockZ; + Meta = E_META_CHEST_FACING_ZP; + break; + } + + case dirZM: + case dirZP: + { + x = m_BoundingBox.p1.x - BlockX; + z = m_BoundingBox.p1.z + m_ChestPosition - BlockZ; + Meta = E_META_CHEST_FACING_XP; + break; + } + } // switch (Dir) + + if ( + (x >= 0) && (x < cChunkDef::Width) && + (z >= 0) && (z < cChunkDef::Width) + ) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p1.y + 1, z, E_BLOCK_CHEST, Meta); + cChestEntity * ChestEntity = new cChestEntity(BlockX + x, m_BoundingBox.p1.y + 1, BlockZ + z); + cNoise Noise(a_ChunkDesc.GetChunkX() ^ a_ChunkDesc.GetChunkZ()); + int NumSlots = 3 + ((Noise.IntNoise3DInt(x, m_BoundingBox.p1.y, z) / 11) % 4); + int Seed = Noise.IntNoise2DInt(x, z); + ChestEntity->GetContents().GenerateRandomLootWithBooks(LootProbab, ARRAYCOUNT(LootProbab), NumSlots, Seed); + a_ChunkDesc.AddBlockEntity(ChestEntity); + } +} + + + + + +void cMineShaftCorridor::PlaceTracks(cChunkDesc & a_ChunkDesc) +{ + if (!m_HasTracks) + { + return; + } + cCuboid Box(m_BoundingBox); + Box.Move(-a_ChunkDesc.GetChunkX() * cChunkDef::Width, 1, -a_ChunkDesc.GetChunkZ() * cChunkDef::Width); + Box.p2.y = Box.p1.y; + Box.p1.x += 1; + Box.p2.x -= 1; + Box.p1.z += 1; + Box.p2.z -= 1; + NIBBLETYPE Meta = 0; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + Meta = E_META_TRACKS_X; + break; + } + + case dirZM: + case dirZP: + { + Meta = E_META_TRACKS_Z; + break; + } + } // switch (direction) + a_ChunkDesc.RandomFillRelCuboid(Box, E_BLOCK_MINECART_TRACKS, Meta, a_ChunkDesc.GetChunkX() + a_ChunkDesc.GetChunkZ(), 6000); +} + + + + + +void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc) +{ + if (m_SpawnerPosition < 0) + { + // No spawner in this corridor + return; + } + int SpawnerRelX = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int SpawnerRelZ = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + switch (m_Direction) + { + case dirXM: + case dirXP: + { + SpawnerRelX += m_SpawnerPosition - 1; + break; + } + case dirZM: + case dirZP: + { + SpawnerRelZ += m_SpawnerPosition - 1; + break; + } + } + if ( + (SpawnerRelX >= 0) && (SpawnerRelX < cChunkDef::Width) && + (SpawnerRelZ >= 0) && (SpawnerRelZ < cChunkDef::Width) + ) + { + a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0); + // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented + } +} + + + + + +void cMineShaftCorridor::PlaceTorches(cChunkDesc & a_ChunkDesc) +{ + cNoise Noise(m_BoundingBox.p1.x); + switch (m_Direction) + { + case dirXM: + case dirXP: + { + int z = m_BoundingBox.p1.z + 1 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + if ((z < 0) || (z >= cChunkDef::Width)) + { + return; + } + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + for (int i = 0; i < m_NumSegments; i++) + { + if (!m_HasFullBeam[i]) + { + continue; + } + int x = m_BoundingBox.p1.x + i * 5 + 1 - BlockX; + if ((x >= 0) && (x < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XP); + } + } + x += 2; + if ((x >= 0) && (x < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_XM); + } + } + } // for i + break; + } + + case dirZM: + case dirZP: + { + int x = m_BoundingBox.p1.x + 1 - a_ChunkDesc.GetChunkX() * cChunkDef::Width; + if ((x < 0) || (x >= cChunkDef::Width)) + { + return; + } + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + for (int i = 0; i < m_NumSegments; i++) + { + if (!m_HasFullBeam[i]) + { + continue; + } + int z = m_BoundingBox.p1.z + i * 5 + 1 - BlockZ; + if ((z >= 0) && (z < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZP); + } + } + z += 2; + if ((z >= 0) && (z < cChunkDef::Width)) + { + if (((Noise.IntNoise2DInt(x, z) / 7) % 10000) < m_ParentSystem.m_ChanceTorch) + { + a_ChunkDesc.SetBlockTypeMeta(x, m_BoundingBox.p2.y, z, E_BLOCK_TORCH, E_META_TORCH_ZM); + } + } + } // for i + break; + } + } // switch (direction) +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftCrossing: + +cMineShaftCrossing::cMineShaftCrossing(cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, const cCuboid & a_BoundingBox) : + super(a_ParentSystem, mskCrossing, a_BoundingBox) +{ +} + + + + + +cMineShaft * cMineShaftCrossing::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + cCuboid BoundingBox(a_PivotX, a_PivotY - 1, a_PivotZ); + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + BoundingBox.p2.y += 3; + if ((rnd % 4) < 2) + { + // 2-level crossing: + BoundingBox.p2.y += 4; + rnd >>= 2; + if ((rnd % 4) < 2) + { + // This is the higher level: + BoundingBox.p1.y -= 4; + BoundingBox.p2.y -= 4; + } + } + rnd >>= 2; + switch (a_Direction) + { + case dirXP: BoundingBox.p2.x += 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; + case dirXM: BoundingBox.p1.x -= 4; BoundingBox.p1.z -= 2; BoundingBox.p2.z += 2; break; + case dirZP: BoundingBox.p2.z += 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; + case dirZM: BoundingBox.p1.z -= 4; BoundingBox.p1.x -= 2; BoundingBox.p2.x += 2; break; + } + if (!a_ParentSystem.CanAppend(BoundingBox)) + { + return NULL; + } + return new cMineShaftCrossing(a_ParentSystem, BoundingBox); +} + + + + + +void cMineShaftCrossing::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + struct + { + int x, y, z; + eDirection dir; + } Exits[] = + { + // Bottom level: + {-1, 1, 2, dirXM}, + { 2, 1, -1, dirZM}, + { 5, 1, 2, dirXP}, + { 2, 1, 5, dirZP}, + // Top level: + {-1, 5, 2, dirXM}, + { 2, 5, -1, dirZM}, + { 5, 5, 2, dirXP}, + { 2, 5, 5, dirZP}, + } ; + for (int i = 0; i < ARRAYCOUNT(Exits); i++) + { + if (m_BoundingBox.p1.y + Exits[i].y >= m_BoundingBox.p2.y) + { + // This exit is not available (two-level exit on a one-level crossing) + continue; + } + + int Height = m_BoundingBox.p1.y + Exits[i].y; + m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + Exits[i].x, Height, m_BoundingBox.p1.z + Exits[i].z, Exits[i].dir, a_Noise, a_RecursionLevel); + } // for i +} + + + + + +void cMineShaftCrossing::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid box(m_BoundingBox); + box.Move(-BlockX, 0, -BlockZ); + if ((box.p2.x < 0) || (box.p2.z < 0) || (box.p1.x >= cChunkDef::Width) || (box.p1.z > cChunkDef::Width)) + { + // Does not intersect this chunk + return; + } + int Floor = box.p1.y + 1; + int Ceil = box.p2.y; + + // The supports: + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 1, box.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p1.x + 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x - 1, box.p2.x - 1, Floor, Ceil, box.p2.z - 1, box.p2.z - 1, E_BLOCK_PLANKS, 0); + + // The air in between: + a_ChunkDesc.FillRelCuboid(box.p1.x + 2, box.p1.x + 2, Floor, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Ceil, box.p1.z + 2, box.p1.z + 2, E_BLOCK_AIR, 0); + + // The air on the edges: + int Mid = Floor + 2; + a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Floor, Mid, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Floor, Mid, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); + Mid += 2; + if (Mid < Ceil) + { + a_ChunkDesc.FillRelCuboid(box.p1.x, box.p1.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p2.x, box.p2.x, Mid, Ceil, box.p1.z + 1, box.p2.z - 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p1.z, box.p1.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid(box.p1.x + 1, box.p2.x - 1, Mid, Ceil, box.p2.z, box.p2.z, E_BLOCK_AIR, 0); + } + + // The floor, if needed: + box.p2.y = box.p1.y; + a_ChunkDesc.FloorRelCuboid(box, E_BLOCK_PLANKS, 0); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMineShaftStaircase: + +cMineShaftStaircase::cMineShaftStaircase( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + const cCuboid & a_BoundingBox, + eDirection a_Direction, + eSlope a_Slope +) : + super(a_ParentSystem, mskStaircase, a_BoundingBox), + m_Direction(a_Direction), + m_Slope(a_Slope) +{ +} + + + + + +cMineShaft * cMineShaftStaircase::CreateAndFit( + cStructGenMineShafts::cMineShaftSystem & a_ParentSystem, + int a_PivotX, int a_PivotY, int a_PivotZ, eDirection a_Direction, + cNoise & a_Noise +) +{ + int rnd = a_Noise.IntNoise3DInt(a_PivotX, a_PivotY + a_ParentSystem.m_MineShafts.size(), a_PivotZ) / 7; + cCuboid Box; + switch (a_Direction) + { + case dirXM: + { + Box.Assign(a_PivotX - 7, a_PivotY - 1, a_PivotZ - 1, a_PivotX, a_PivotY + 6, a_PivotZ + 1); + break; + } + case dirXP: + { + Box.Assign(a_PivotX, a_PivotY - 1, a_PivotZ - 1, a_PivotX + 7, a_PivotY + 6, a_PivotZ + 1); + break; + } + case dirZM: + { + Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ - 7, a_PivotX + 1, a_PivotY + 6, a_PivotZ); + break; + } + case dirZP: + { + Box.Assign(a_PivotX - 1, a_PivotY - 1, a_PivotZ, a_PivotX + 1, a_PivotY + 6, a_PivotZ + 7); + break; + } + } + eSlope Slope = sUp; + if ((rnd % 4) < 2) // 50 % + { + Slope = sDown; + Box.Move(0, -4, 0); + } + if (!a_ParentSystem.CanAppend(Box)) + { + return NULL; + } + return new cMineShaftStaircase(a_ParentSystem, Box, a_Direction, Slope); +} + + + + + +void cMineShaftStaircase::AppendBranches(int a_RecursionLevel, cNoise & a_Noise) +{ + int Height = m_BoundingBox.p1.y + ((m_Slope == sDown) ? 1 : 5); + switch (m_Direction) + { + case dirXM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x - 1, Height, m_BoundingBox.p1.z + 1, dirXM, a_Noise, a_RecursionLevel); break; + case dirXP: m_ParentSystem.AppendBranch(m_BoundingBox.p2.x + 1, Height, m_BoundingBox.p1.z + 1, dirXP, a_Noise, a_RecursionLevel); break; + case dirZM: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p1.z - 1, dirZM, a_Noise, a_RecursionLevel); break; + case dirZP: m_ParentSystem.AppendBranch(m_BoundingBox.p1.x + 1, Height, m_BoundingBox.p2.z + 1, dirZP, a_Noise, a_RecursionLevel); break; + } +} + + + + + +void cMineShaftStaircase::ProcessChunk(cChunkDesc & a_ChunkDesc) +{ + int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + cCuboid RelB(m_BoundingBox); + RelB.Move(-BlockX, 0, -BlockZ); + if ( + (RelB.p1.x >= cChunkDef::Width) || + (RelB.p1.z >= cChunkDef::Width) || + (RelB.p2.x < 0) || + (RelB.p2.z < 0) + ) + { + // No intersection between this staircase and this chunk + return; + } + + int SFloor = RelB.p1.y + ((m_Slope == sDown) ? 5 : 1); + int DFloor = RelB.p1.y + ((m_Slope == sDown) ? 1 : 5); + int Add = (m_Slope == sDown) ? -1 : 1; + int InitAdd = (m_Slope == sDown) ? -1 : 0; + cCuboid Box; + switch (m_Direction) + { + case dirXM: + { + a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p2.x - 2, SFloor + InitAdd, RelB.p1.z, RelB.p2.x - 2, SFloor + 3 + InitAdd, RelB.p2.z); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(-1, Add, 0); + } + break; + } + + case dirXP: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p1.x + 1, SFloor, SFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p2.x - 1, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p1.x + 1, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p2.x - 1, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x + 2, SFloor + InitAdd, RelB.p1.z, RelB.p1.x + 2, SFloor + 3 + InitAdd, RelB.p2.z); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(1, Add, 0); + } + break; + } + + case dirZM: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p2.z - 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p2.z - 2); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(0, Add, -1); + } + break; + } + + case dirZP: + { + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, SFloor, SFloor + 2, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_AIR, 0); + a_ChunkDesc.FillRelCuboid (RelB.p1.x, RelB.p2.x, DFloor, DFloor + 2, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, SFloor - 1, SFloor - 1, RelB.p1.z, RelB.p1.z + 1, E_BLOCK_PLANKS, 0); + a_ChunkDesc.FloorRelCuboid(RelB.p1.x, RelB.p2.x, DFloor - 1, DFloor - 1, RelB.p2.z - 1, RelB.p2.z, E_BLOCK_PLANKS, 0); + Box.Assign(RelB.p1.x, SFloor + InitAdd, RelB.p1.z + 2, RelB.p2.x, SFloor + 3 + InitAdd, RelB.p1.z + 2); + for (int i = 0; i < 4; i++) + { + a_ChunkDesc.FillRelCuboid(Box, E_BLOCK_AIR, 0); + a_ChunkDesc.FloorRelCuboid(Box.p1.x, Box.p2.x, Box.p1.y - 1, Box.p1.y - 1, Box.p1.z, Box.p2.z, E_BLOCK_PLANKS, 0); + Box.Move(0, Add, 1); + } + break; + } + + } // switch (m_Direction) +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenMineShafts: + +cStructGenMineShafts::cStructGenMineShafts( + int a_Seed, int a_GridSize, int a_MaxSystemSize, + int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase +) : + m_Noise(a_Seed), + m_GridSize(a_GridSize), + m_MaxSystemSize(a_MaxSystemSize), + m_ProbLevelCorridor(std::max(0, a_ChanceCorridor)), + m_ProbLevelCrossing(std::max(0, a_ChanceCorridor + a_ChanceCrossing)), + m_ProbLevelStaircase(std::max(0, a_ChanceCorridor + a_ChanceCrossing + a_ChanceStaircase)) +{ +} + + + + + +cStructGenMineShafts::~cStructGenMineShafts() +{ + ClearCache(); +} + + + + + +void cStructGenMineShafts::ClearCache(void) +{ + for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenMineShafts::GetMineShaftSystemsForChunk( + int a_ChunkX, int a_ChunkZ, + cStructGenMineShafts::cMineShaftSystems & a_MineShafts +) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= NEIGHBORHOOD_SIZE / 2; + BaseZ -= NEIGHBORHOOD_SIZE / 2; + + // Walk the cache, move each cave system that we want into a_Caves: + int StartX = BaseX * m_GridSize; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize; + int StartZ = BaseZ * m_GridSize; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize; + for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_MineShafts.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_GridSize; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_GridSize; + bool Found = false; + for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } // for itr - a_Mineshafts + if (!Found) + { + a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase)); + } + } // for z + } // for x + + // Copy a_MineShafts into m_Cache to the beginning: + cMineShaftSystems MineShaftsCopy(a_MineShafts); + m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cMineShaftSystems::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } +} + + + + + + +void cStructGenMineShafts::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cMineShaftSystems MineShafts; + GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts); + for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr) + { + (*itr)->ProcessChunk(a_ChunkDesc); + } // for itr - MineShafts[] +} + + + + diff --git a/source/Generating/MineShafts.h b/source/Generating/MineShafts.h index a2191b665..c53d3bc53 100644 --- a/source/Generating/MineShafts.h +++ b/source/Generating/MineShafts.h @@ -1,61 +1,61 @@ - -// MineShafts.h - -// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cStructGenMineShafts : - public cStructureGen -{ -public: - cStructGenMineShafts( - int a_Seed, int a_GridSize, int a_MaxSystemSize, - int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase - ); - - virtual ~cStructGenMineShafts(); - -protected: - friend class cMineShaft; - friend class cMineShaftDirtRoom; - friend class cMineShaftCorridor; - friend class cMineShaftCrossing; - friend class cMineShaftStaircase; - class cMineShaftSystem; // fwd: MineShafts.cpp - typedef std::list cMineShaftSystems; - - cNoise m_Noise; - int m_GridSize; ///< Average spacing of the systems - int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system - int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor - int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor - int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing - cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used. - - /// Clears everything from the cache - void ClearCache(void); - - /** Returns all systems that *may* intersect the given chunk. - All the systems are valid until the next call to this function (which may delete some of the pointers). - */ - void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems); - - // cStructureGen overrides: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - + +// MineShafts.h + +// Declares the cStructGenMineShafts class representing the structure generator for abandoned mineshafts + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenMineShafts : + public cStructureGen +{ +public: + cStructGenMineShafts( + int a_Seed, int a_GridSize, int a_MaxSystemSize, + int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase + ); + + virtual ~cStructGenMineShafts(); + +protected: + friend class cMineShaft; + friend class cMineShaftDirtRoom; + friend class cMineShaftCorridor; + friend class cMineShaftCrossing; + friend class cMineShaftStaircase; + class cMineShaftSystem; // fwd: MineShafts.cpp + typedef std::list cMineShaftSystems; + + cNoise m_Noise; + int m_GridSize; ///< Average spacing of the systems + int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system + int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor + int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor + int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing + cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used. + + /// Clears everything from the cache + void ClearCache(void); + + /** Returns all systems that *may* intersect the given chunk. + All the systems are valid until the next call to this function (which may delete some of the pointers). + */ + void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems); + + // cStructureGen overrides: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/source/Generating/Noise3DGenerator.cpp b/source/Generating/Noise3DGenerator.cpp index dcf84abeb..f47c64430 100644 --- a/source/Generating/Noise3DGenerator.cpp +++ b/source/Generating/Noise3DGenerator.cpp @@ -1,581 +1,581 @@ - -// Nosie3DGenerator.cpp - -// Generates terrain using 3D noise, rather than composing. Is a test. - -#include "Globals.h" -#include "Noise3DGenerator.h" -#include "../OSSupport/File.h" -#include "../../iniFile/iniFile.h" -#include "../LinearInterpolation.h" -#include "../LinearUpscale.h" - - - - - -/* -// Perform an automatic test of upscaling upon program start (use breakpoints to debug): - -class Test -{ -public: - Test(void) - { - DoTest1(); - DoTest2(); - } - - - void DoTest1(void) - { - float In[3 * 3 * 3]; - for (int i = 0; i < ARRAYCOUNT(In); i++) - { - In[i] = (float)(i % 5); - } - Debug3DNoise(In, 3, 3, 3, "Upscale3D in"); - float Out[17 * 33 * 35]; - LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17); - Debug3DNoise(Out, 17, 33, 35, "Upscale3D test"); - } - - - void DoTest2(void) - { - float In[3 * 3]; - for (int i = 0; i < ARRAYCOUNT(In); i++) - { - In[i] = (float)(i % 5); - } - Debug2DNoise(In, 3, 3, "Upscale2D in"); - float Out[17 * 33]; - LinearUpscale2DArray(In, 3, 3, Out, 8, 16); - Debug2DNoise(Out, 17, 33, "Upscale2D test"); - } - -} gTest; -//*/ - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cNoise3DGenerator: - -cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) : - super(a_ChunkGenerator), - m_Perlin(1000), - m_Cubic(1000) -{ - m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5); - m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1); - m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2); - - #if 0 - // DEBUG: Test the noise generation: - // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size - // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M - m_SeaLevel = 62; - m_HeightAmplification = 0; - m_MidPoint = 75; - m_FrequencyX = 4; - m_FrequencyY = 4; - m_FrequencyZ = 4; - m_AirThreshold = 0.5; - - const int NumChunks = 4; - NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height]; - for (int x = 0; x < NumChunks; x++) - { - GenerateNoiseArray(x, 5, Noise[x]); - } - - // Save in XY cuts: - cFile f1; - if (f1.Open("Test_XY.grab", cFile::fmWrite)) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int i = 0; i < NumChunks; i++) - { - int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; - unsigned char buf[cChunkDef::Width]; - for (int x = 0; x < cChunkDef::Width; x++) - { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); - } - f1.Write(buf, cChunkDef::Width); - } - } // for y - } // for z - } // if (XY file open) - - cFile f2; - if (f2.Open("Test_XZ.grab", cFile::fmWrite)) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int i = 0; i < NumChunks; i++) - { - int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; - unsigned char buf[cChunkDef::Width]; - for (int x = 0; x < cChunkDef::Width; x++) - { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); - } - f2.Write(buf, cChunkDef::Width); - } - } // for z - } // for y - } // if (XZ file open) - #endif // 0 -} - - - - - -cNoise3DGenerator::~cNoise3DGenerator() -{ - // Nothing needed yet -} - - - - - -void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) -{ - m_World = a_World; - - // Params: - m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); - m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); - m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); - m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8); - m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8); - m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8); - m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); -} - - - - - -void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) -{ - for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) - { - a_BiomeMap[i] = biExtremeHills; - } -} - - - - - -void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) -{ - NOISE_DATATYPE Noise[17 * 257 * 17]; - GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise); - - // Output noise into chunk: - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - int idx = z * 17 * 257 + y * 17; - for (int x = 0; x < cChunkDef::Width; x++) - { - NOISE_DATATYPE n = Noise[idx++]; - BLOCKTYPE BlockType; - if (n > m_AirThreshold) - { - BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER; - } - else - { - BlockType = E_BLOCK_STONE; - } - a_ChunkDesc.SetBlockType(x, y, z, BlockType); - } - } - } - - UpdateHeightmap(a_ChunkDesc); - ComposeTerrain (a_ChunkDesc); -} - - - - - -void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise) -{ - NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise - NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash - - // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed" - NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX; - NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX; - NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ; - NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ; - NOISE_DATATYPE StartY = 0; - NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY; - - m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW); - - // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ)); - - // Precalculate a "height" array: - NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source") - m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25); - for (int i = 0; i < ARRAYCOUNT(Height); i++) - { - Height[i] = abs(Height[i]) * m_HeightAmplification + 1; - } - - // Modify the noise by height data: - for (int y = 0; y < DIM_Y; y++) - { - NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20; - AddHeight *= AddHeight * AddHeight; - for (int z = 0; z < DIM_Z; z++) - { - NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]); - for (int x = 0; x < DIM_X; x++) - { - CurRow[x] += AddHeight / Height[x + DIM_X * z]; - } - } - } - - // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ)); - - // Upscale the Perlin noise into full-blown chunk dimensions: - LinearUpscale3DArray( - NoiseO, DIM_X, DIM_Y, DIM_Z, - a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z - ); - - // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ)); -} - - - - - -void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc) -{ - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - for (int y = cChunkDef::Height - 1; y > 0; y--) - { - if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) - { - a_ChunkDesc.SetHeight(x, z, y); - break; - } - } // for y - } // for x - } // for z -} - - - - - -void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - // Make basic terrain composition: - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; - bool HasHadWater = false; - for (int y = LastAir - 1; y > 0; y--) - { - switch (a_ChunkDesc.GetBlockType(x, y, z)) - { - case E_BLOCK_AIR: - { - LastAir = y; - break; - } - case E_BLOCK_STONE: - { - if (LastAir - y > 3) - { - break; - } - if (HasHadWater) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); - } - else - { - a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); - } - break; - } - case E_BLOCK_STATIONARY_WATER: - { - LastAir = y; - HasHadWater = true; - break; - } - } // switch (GetBlockType()) - } // for y - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - } // for x - } // for z -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cNoise3DComposable: - -cNoise3DComposable::cNoise3DComposable(int a_Seed) : - m_Noise1(a_Seed + 1000), - m_Noise2(a_Seed + 2000), - m_Noise3(a_Seed + 3000) -{ -} - - - - - -void cNoise3DComposable::Initialize(cIniFile & a_IniFile) -{ - // Params: - m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); - m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); - m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); - m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10); - m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10); - m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10); - m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); -} - - - - - -void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ) -{ - if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ)) - { - // The noise for this chunk is already generated in m_Noise - return; - } - m_LastChunkX = a_ChunkX; - m_LastChunkZ = a_ChunkZ; - - // Upscaling parameters: - const int UPSCALE_X = 8; - const int UPSCALE_Y = 4; - const int UPSCALE_Z = 8; - - const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; - const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; - const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; - - // Precalculate a "height" array: - NOISE_DATATYPE Height[17 * 17]; // x + 17 * z - for (int z = 0; z < 17; z += UPSCALE_Z) - { - NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; - for (int x = 0; x < 17; x += UPSCALE_X) - { - NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; - NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1; - Height[x + 17 * z] = val * val * val; - } - } - - int idx = 0; - for (int y = 0; y < 257; y += UPSCALE_Y) - { - NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY; - NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20; - AddHeight *= AddHeight * AddHeight; - NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); - for (int z = 0; z < 17; z += UPSCALE_Z) - { - NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; - for (int x = 0; x < 17; x += UPSCALE_X) - { - NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; - CurFloor[x + 17 * z] = - m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 + - m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) + - m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 + - AddHeight / Height[x + 17 * z]; - } - } - // Linear-interpolate this XZ floor: - LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z); - } - - // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis - for (int y = 1; y < cChunkDef::Height; y++) - { - if ((y % UPSCALE_Y) == 0) - { - // This is the interpolation source floor, already calculated - continue; - } - int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y; - int HiFloorY = LoFloorY + UPSCALE_Y; - NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]); - NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]); - NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); - NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y; - int idx = 0; - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio; - idx += 1; - } - idx += 1; // Skipping one X column - } - } - - // The noise array is now fully interpolated - /* - // DEBUG: Output two images of the array, sliced by XY and XZ: - cFile f1; - if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - int idx = y * 17 * 17 + z * 17; - unsigned char buf[16]; - for (int x = 0; x < cChunkDef::Width; x++) - { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); - } - f1.Write(buf, 16); - } // for y - } // for z - } // if (XY file open) - - cFile f2; - if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) - { - for (int y = 0; y < cChunkDef::Height; y++) - { - for (int z = 0; z < cChunkDef::Width; z++) - { - int idx = y * 17 * 17 + z * 17; - unsigned char buf[16]; - for (int x = 0; x < cChunkDef::Width; x++) - { - buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); - } - f2.Write(buf, 16); - } // for z - } // for y - } // if (XZ file open) - */ -} - - - - - -void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); - - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel); - for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--) - { - if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold) - { - cChunkDef::SetHeight(a_HeightMap, x, z, y); - break; - } - } // for y - } // for x - } // for z -} - - - - - -void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) -{ - GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); - - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); - - // Make basic terrain composition: - for (int z = 0; z < cChunkDef::Width; z++) - { - for (int x = 0; x < cChunkDef::Width; x++) - { - int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; - bool HasHadWater = false; - for (int y = LastAir; y < m_SeaLevel; y++) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); - } - for (int y = LastAir - 1; y > 0; y--) - { - if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold) - { - // "air" part - LastAir = y; - if (y < m_SeaLevel) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); - HasHadWater = true; - } - continue; - } - // "ground" part: - if (LastAir - y > 4) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); - continue; - } - if (HasHadWater) - { - a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); - } - else - { - a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); - } - } // for y - a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); - } // for x - } // for z -} - - - - + +// Nosie3DGenerator.cpp + +// Generates terrain using 3D noise, rather than composing. Is a test. + +#include "Globals.h" +#include "Noise3DGenerator.h" +#include "../OSSupport/File.h" +#include "../../iniFile/iniFile.h" +#include "../LinearInterpolation.h" +#include "../LinearUpscale.h" + + + + + +/* +// Perform an automatic test of upscaling upon program start (use breakpoints to debug): + +class Test +{ +public: + Test(void) + { + DoTest1(); + DoTest2(); + } + + + void DoTest1(void) + { + float In[3 * 3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + Debug3DNoise(In, 3, 3, 3, "Upscale3D in"); + float Out[17 * 33 * 35]; + LinearUpscale3DArray(In, 3, 3, 3, Out, 8, 16, 17); + Debug3DNoise(Out, 17, 33, 35, "Upscale3D test"); + } + + + void DoTest2(void) + { + float In[3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + Debug2DNoise(In, 3, 3, "Upscale2D in"); + float Out[17 * 33]; + LinearUpscale2DArray(In, 3, 3, Out, 8, 16); + Debug2DNoise(Out, 17, 33, "Upscale2D test"); + } + +} gTest; +//*/ + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise3DGenerator: + +cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) : + super(a_ChunkGenerator), + m_Perlin(1000), + m_Cubic(1000) +{ + m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5); + m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1); + m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2); + + #if 0 + // DEBUG: Test the noise generation: + // NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size + // In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M + m_SeaLevel = 62; + m_HeightAmplification = 0; + m_MidPoint = 75; + m_FrequencyX = 4; + m_FrequencyY = 4; + m_FrequencyZ = 4; + m_AirThreshold = 0.5; + + const int NumChunks = 4; + NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height]; + for (int x = 0; x < NumChunks; x++) + { + GenerateNoiseArray(x, 5, Noise[x]); + } + + // Save in XY cuts: + cFile f1; + if (f1.Open("Test_XY.grab", cFile::fmWrite)) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int i = 0; i < NumChunks; i++) + { + int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; + unsigned char buf[cChunkDef::Width]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); + } + f1.Write(buf, cChunkDef::Width); + } + } // for y + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open("Test_XZ.grab", cFile::fmWrite)) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int i = 0; i < NumChunks; i++) + { + int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height; + unsigned char buf[cChunkDef::Width]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++])))); + } + f2.Write(buf, cChunkDef::Width); + } + } // for z + } // for y + } // if (XZ file open) + #endif // 0 +} + + + + + +cNoise3DGenerator::~cNoise3DGenerator() +{ + // Nothing needed yet +} + + + + + +void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile) +{ + m_World = a_World; + + // Params: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); + m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); + m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8); + m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); +} + + + + + +void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) +{ + for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++) + { + a_BiomeMap[i] = biExtremeHills; + } +} + + + + + +void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) +{ + NOISE_DATATYPE Noise[17 * 257 * 17]; + GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise); + + // Output noise into chunk: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + int idx = z * 17 * 257 + y * 17; + for (int x = 0; x < cChunkDef::Width; x++) + { + NOISE_DATATYPE n = Noise[idx++]; + BLOCKTYPE BlockType; + if (n > m_AirThreshold) + { + BlockType = (y > m_SeaLevel) ? E_BLOCK_AIR : E_BLOCK_STATIONARY_WATER; + } + else + { + BlockType = E_BLOCK_STONE; + } + a_ChunkDesc.SetBlockType(x, y, z, BlockType); + } + } + } + + UpdateHeightmap(a_ChunkDesc); + ComposeTerrain (a_ChunkDesc); +} + + + + + +void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_OutNoise) +{ + NOISE_DATATYPE NoiseO[DIM_X * DIM_Y * DIM_Z]; // Output for the Perlin noise + NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash + + // Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed" + NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX; + NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX; + NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ; + NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ; + NOISE_DATATYPE StartY = 0; + NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY; + + m_Perlin.Generate3D(NoiseO, DIM_X, DIM_Y, DIM_Z, StartX, EndX, StartY, EndY, StartZ, EndZ, NoiseW); + + // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_orig", a_ChunkX, a_ChunkZ)); + + // Precalculate a "height" array: + NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source") + m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25); + for (int i = 0; i < ARRAYCOUNT(Height); i++) + { + Height[i] = abs(Height[i]) * m_HeightAmplification + 1; + } + + // Modify the noise by height data: + for (int y = 0; y < DIM_Y; y++) + { + NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20; + AddHeight *= AddHeight * AddHeight; + for (int z = 0; z < DIM_Z; z++) + { + NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]); + for (int x = 0; x < DIM_X; x++) + { + CurRow[x] += AddHeight / Height[x + DIM_X * z]; + } + } + } + + // DEBUG: Debug3DNoise(NoiseO, DIM_X, DIM_Y, DIM_Z, Printf("Chunk_%d_%d_hei", a_ChunkX, a_ChunkZ)); + + // Upscale the Perlin noise into full-blown chunk dimensions: + LinearUpscale3DArray( + NoiseO, DIM_X, DIM_Y, DIM_Z, + a_OutNoise, UPSCALE_X, UPSCALE_Y, UPSCALE_Z + ); + + // DEBUG: Debug3DNoise(a_OutNoise, 17, 257, 17, Printf("Chunk_%d_%d_lerp", a_ChunkX, a_ChunkZ)); +} + + + + + +void cNoise3DGenerator::UpdateHeightmap(cChunkDesc & a_ChunkDesc) +{ + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + for (int y = cChunkDef::Height - 1; y > 0; y--) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) + { + a_ChunkDesc.SetHeight(x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cNoise3DGenerator::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + // Make basic terrain composition: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir - 1; y > 0; y--) + { + switch (a_ChunkDesc.GetBlockType(x, y, z)) + { + case E_BLOCK_AIR: + { + LastAir = y; + break; + } + case E_BLOCK_STONE: + { + if (LastAir - y > 3) + { + break; + } + if (HasHadWater) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + } + break; + } + case E_BLOCK_STATIONARY_WATER: + { + LastAir = y; + HasHadWater = true; + break; + } + } // switch (GetBlockType()) + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNoise3DComposable: + +cNoise3DComposable::cNoise3DComposable(int a_Seed) : + m_Noise1(a_Seed + 1000), + m_Noise2(a_Seed + 2000), + m_Noise3(a_Seed + 3000) +{ +} + + + + + +void cNoise3DComposable::Initialize(cIniFile & a_IniFile) +{ + // Params: + m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62); + m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0); + m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75); + m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 10); + m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 10); + m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 10); + m_AirThreshold = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DAirThreshold", 0.5); +} + + + + + +void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ) +{ + if ((a_ChunkX == m_LastChunkX) && (a_ChunkZ == m_LastChunkZ)) + { + // The noise for this chunk is already generated in m_Noise + return; + } + m_LastChunkX = a_ChunkX; + m_LastChunkZ = a_ChunkZ; + + // Upscaling parameters: + const int UPSCALE_X = 8; + const int UPSCALE_Y = 4; + const int UPSCALE_Z = 8; + + const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; + const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; + const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; + + // Precalculate a "height" array: + NOISE_DATATYPE Height[17 * 17]; // x + 17 * z + for (int z = 0; z < 17; z += UPSCALE_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += UPSCALE_X) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; + NOISE_DATATYPE val = abs(m_Noise1.CubicNoise2D(NoiseX / 5, NoiseZ / 5)) * m_HeightAmplification + 1; + Height[x + 17 * z] = val * val * val; + } + } + + int idx = 0; + for (int y = 0; y < 257; y += UPSCALE_Y) + { + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)y) / m_FrequencyY; + NOISE_DATATYPE AddHeight = (y - m_MidPoint) / 20; + AddHeight *= AddHeight * AddHeight; + NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); + for (int z = 0; z < 17; z += UPSCALE_Z) + { + NOISE_DATATYPE NoiseZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + z)) / m_FrequencyZ; + for (int x = 0; x < 17; x += UPSCALE_X) + { + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + x)) / m_FrequencyX; + CurFloor[x + 17 * z] = + m_Noise1.CubicNoise3D(NoiseX, NoiseY, NoiseZ) * (NOISE_DATATYPE)0.5 + + m_Noise2.CubicNoise3D(NoiseX / 2, NoiseY / 2, NoiseZ / 2) + + m_Noise3.CubicNoise3D(NoiseX / 4, NoiseY / 4, NoiseZ / 4) * 2 + + AddHeight / Height[x + 17 * z]; + } + } + // Linear-interpolate this XZ floor: + LinearUpscale2DArrayInPlace(CurFloor, 17, 17, UPSCALE_X, UPSCALE_Z); + } + + // Finish the 3D linear interpolation by interpolating between each XZ-floors on the Y axis + for (int y = 1; y < cChunkDef::Height; y++) + { + if ((y % UPSCALE_Y) == 0) + { + // This is the interpolation source floor, already calculated + continue; + } + int LoFloorY = (y / UPSCALE_Y) * UPSCALE_Y; + int HiFloorY = LoFloorY + UPSCALE_Y; + NOISE_DATATYPE * LoFloor = &(m_NoiseArray[LoFloorY * 17 * 17]); + NOISE_DATATYPE * HiFloor = &(m_NoiseArray[HiFloorY * 17 * 17]); + NOISE_DATATYPE * CurFloor = &(m_NoiseArray[y * 17 * 17]); + NOISE_DATATYPE Ratio = ((NOISE_DATATYPE)(y % UPSCALE_Y)) / UPSCALE_Y; + int idx = 0; + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + CurFloor[idx] = LoFloor[idx] + (HiFloor[idx] - LoFloor[idx]) * Ratio; + idx += 1; + } + idx += 1; // Skipping one X column + } + } + + // The noise array is now fully interpolated + /* + // DEBUG: Output two images of the array, sliced by XY and XZ: + cFile f1; + if (f1.Open(Printf("Chunk_%d_%d_XY.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + int idx = y * 17 * 17 + z * 17; + unsigned char buf[16]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); + } + f1.Write(buf, 16); + } // for y + } // for z + } // if (XY file open) + + cFile f2; + if (f2.Open(Printf("Chunk_%d_%d_XZ.raw", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + for (int y = 0; y < cChunkDef::Height; y++) + { + for (int z = 0; z < cChunkDef::Width; z++) + { + int idx = y * 17 * 17 + z * 17; + unsigned char buf[16]; + for (int x = 0; x < cChunkDef::Width; x++) + { + buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 128 * m_Noise[idx++])))); + } + f2.Write(buf, 16); + } // for z + } // for y + } // if (XZ file open) + */ +} + + + + + +void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) +{ + GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); + + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel); + for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--) + { + if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold) + { + cChunkDef::SetHeight(a_HeightMap, x, z, y); + break; + } + } // for y + } // for x + } // for z +} + + + + + +void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) +{ + GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); + + a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + + // Make basic terrain composition: + for (int z = 0; z < cChunkDef::Width; z++) + { + for (int x = 0; x < cChunkDef::Width; x++) + { + int LastAir = a_ChunkDesc.GetHeight(x, z) + 1; + bool HasHadWater = false; + for (int y = LastAir; y < m_SeaLevel; y++) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + } + for (int y = LastAir - 1; y > 0; y--) + { + if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold) + { + // "air" part + LastAir = y; + if (y < m_SeaLevel) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER); + HasHadWater = true; + } + continue; + } + // "ground" part: + if (LastAir - y > 4) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE); + continue; + } + if (HasHadWater) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND); + } + else + { + a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT); + } + } // for y + a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK); + } // for x + } // for z +} + + + + diff --git a/source/Generating/Noise3DGenerator.h b/source/Generating/Noise3DGenerator.h index b23e8645b..0d211cddc 100644 --- a/source/Generating/Noise3DGenerator.h +++ b/source/Generating/Noise3DGenerator.h @@ -1,106 +1,106 @@ - -// Noise3DGenerator.h - -// Generates terrain using 3D noise, rather than composing. Is a test. - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cNoise3DGenerator : - public cChunkGenerator::cGenerator -{ - typedef cChunkGenerator::cGenerator super; - -public: - cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator); - virtual ~cNoise3DGenerator(); - - virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; - virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; - virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; - -protected: - // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: - static const int UPSCALE_X = 8; - static const int UPSCALE_Y = 4; - static const int UPSCALE_Z = 8; - - // Linear interpolation buffer dimensions, calculated from the step sizes: - static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; - static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; - static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; - - cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition - cCubicNoise m_Cubic; // The noise used for heightmap directing - - int m_SeaLevel; - NOISE_DATATYPE m_HeightAmplification; - NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be - NOISE_DATATYPE m_FrequencyX; - NOISE_DATATYPE m_FrequencyY; - NOISE_DATATYPE m_FrequencyZ; - NOISE_DATATYPE m_AirThreshold; - - /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size - void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise); - - /// Updates heightmap based on the chunk's contents - void UpdateHeightmap(cChunkDesc & a_ChunkDesc); - - /// Composes terrain - adds dirt, grass and sand - void ComposeTerrain(cChunkDesc & a_ChunkDesc); -} ; - - - - - -class cNoise3DComposable : - public cTerrainHeightGen, - public cTerrainCompositionGen -{ -public: - cNoise3DComposable(int a_Seed); - - void Initialize(cIniFile & a_IniFile); - -protected: - cNoise m_Noise1; - cNoise m_Noise2; - cNoise m_Noise3; - - int m_SeaLevel; - NOISE_DATATYPE m_HeightAmplification; - NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be - NOISE_DATATYPE m_FrequencyX; - NOISE_DATATYPE m_FrequencyY; - NOISE_DATATYPE m_FrequencyZ; - NOISE_DATATYPE m_AirThreshold; - - int m_LastChunkX; - int m_LastChunkZ; - NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y - - - /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given - void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ); - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - - // cTerrainCompositionGen overrides: - virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; -} ; - - - - + +// Noise3DGenerator.h + +// Generates terrain using 3D noise, rather than composing. Is a test. + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cNoise3DGenerator : + public cChunkGenerator::cGenerator +{ + typedef cChunkGenerator::cGenerator super; + +public: + cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator); + virtual ~cNoise3DGenerator(); + + virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override; + virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override; + virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override; + +protected: + // Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively: + static const int UPSCALE_X = 8; + static const int UPSCALE_Y = 4; + static const int UPSCALE_Z = 8; + + // Linear interpolation buffer dimensions, calculated from the step sizes: + static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X; + static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y; + static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z; + + cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition + cCubicNoise m_Cubic; // The noise used for heightmap directing + + int m_SeaLevel; + NOISE_DATATYPE m_HeightAmplification; + NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + NOISE_DATATYPE m_AirThreshold; + + /// Generates the 3D noise array used for terrain generation; a_Noise is of ChunkData-size + void GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DATATYPE * a_Noise); + + /// Updates heightmap based on the chunk's contents + void UpdateHeightmap(cChunkDesc & a_ChunkDesc); + + /// Composes terrain - adds dirt, grass and sand + void ComposeTerrain(cChunkDesc & a_ChunkDesc); +} ; + + + + + +class cNoise3DComposable : + public cTerrainHeightGen, + public cTerrainCompositionGen +{ +public: + cNoise3DComposable(int a_Seed); + + void Initialize(cIniFile & a_IniFile); + +protected: + cNoise m_Noise1; + cNoise m_Noise2; + cNoise m_Noise3; + + int m_SeaLevel; + NOISE_DATATYPE m_HeightAmplification; + NOISE_DATATYPE m_MidPoint; // Where the vertical "center" of the noise should be + NOISE_DATATYPE m_FrequencyX; + NOISE_DATATYPE m_FrequencyY; + NOISE_DATATYPE m_FrequencyZ; + NOISE_DATATYPE m_AirThreshold; + + int m_LastChunkX; + int m_LastChunkZ; + NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y + + + /// Generates the 3D noise array used for terrain generation, unless the LastChunk coords are equal to coords given + void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ); + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + + // cTerrainCompositionGen overrides: + virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/source/Generating/Ravines.cpp b/source/Generating/Ravines.cpp index 94d91c0bb..6413b963b 100644 --- a/source/Generating/Ravines.cpp +++ b/source/Generating/Ravines.cpp @@ -1,531 +1,531 @@ - -// Ravines.cpp - -// Implements the cStructGenRavines class representing the ravine structure generator - -#include "Globals.h" -#include "Ravines.h" - - - - -/// How many ravines in each direction are generated for a given chunk. Must be an even number -static const int NEIGHBORHOOD_SIZE = 8; - -static const int NUM_RAVINE_POINTS = 4; - - - - - -struct cRavDefPoint -{ - int m_BlockX; - int m_BlockZ; - int m_Radius; - int m_Top; - int m_Bottom; - - cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) : - m_BlockX(a_BlockX), - m_BlockZ(a_BlockZ), - m_Radius(a_Radius), - m_Top (a_Top), - m_Bottom(a_Bottom) - { - } -} ; - -typedef std::vector cRavDefPoints; - - - - - -class cStructGenRavines::cRavine -{ - cRavDefPoints m_Points; - - /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise - void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); - - /// Refines (adds and smooths) defpoints from a_Src into a_Dst - void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst); - - /// Does one round of smoothing, two passes of RefineDefPoints() - void Smooth(void); - - /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block - void FinishLinear(void); - -public: - // Coords for which the ravine was generated (not necessarily the center) - int m_BlockX; - int m_BlockZ; - - cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); - - /// Carves the ravine into the chunk specified - void ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap - ); - - #ifdef _DEBUG - /// Exports itself as a SVG line definition - AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const; - #endif // _DEBUG -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenRavines: - -cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) : - m_Noise(a_Seed), - m_Size(a_Size) -{ -} - - - - - -cStructGenRavines::~cStructGenRavines() -{ - ClearCache(); -} - - - - - -void cStructGenRavines::ClearCache(void) -{ - for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - m_Cache[] - m_Cache.clear(); -} - - - - - -void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc) -{ - int ChunkX = a_ChunkDesc.GetChunkX(); - int ChunkZ = a_ChunkDesc.GetChunkZ(); - cRavines Ravines; - GetRavinesForChunk(ChunkX, ChunkZ, Ravines); - for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr) - { - (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); - } // for itr - Ravines[] -} - - - - - -void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines) -{ - int BaseX = a_ChunkX * cChunkDef::Width / m_Size; - int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size; - if (BaseX < 0) - { - --BaseX; - } - if (BaseZ < 0) - { - --BaseZ; - } - BaseX -= 4; - BaseZ -= 4; - - // Walk the cache, move each ravine that we want into a_Ravines: - int StartX = BaseX * m_Size; - int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size; - int StartZ = BaseZ * m_Size; - int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size; - for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) - { - if ( - ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && - ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) - ) - { - // want - a_Ravines.push_back(*itr); - itr = m_Cache.erase(itr); - } - else - { - // don't want - ++itr; - } - } // for itr - m_Cache[] - - for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) - { - int RealX = (BaseX + x) * m_Size; - for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) - { - int RealZ = (BaseZ + z) * m_Size; - bool Found = false; - for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) - { - if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) - { - Found = true; - break; - } - } - if (!Found) - { - a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise)); - } - } - } - - // Copy a_Ravines into m_Cache to the beginning: - cRavines RavinesCopy(a_Ravines); - m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end()); - - // Trim the cache if it's too long: - if (m_Cache.size() > 100) - { - cRavines::iterator itr = m_Cache.begin(); - std::advance(itr, 100); - for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr) - { - delete *itr; - } - itr = m_Cache.begin(); - std::advance(itr, 100); - m_Cache.erase(itr, m_Cache.end()); - } - - /* - #ifdef _DEBUG - // DEBUG: Export as SVG into a file specific for the chunk, for visual verification: - AString SVG; - SVG.append("\n\n"); - for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) - { - SVG.append((*itr)->ExportAsSVG(0, 512, 512)); - } - SVG.append("\n"); - - AString fnam; - Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); - cFile File(fnam, cFile::fmWrite); - File.Write(SVG.c_str(), SVG.size()); - #endif // _DEBUG - //*/ -} - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cStructGenRavines::cRavine - -cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) : - m_BlockX(a_BlockX), - m_BlockZ(a_BlockZ) -{ - // Calculate the ravine shape-defining points: - GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise); - - // Smooth the ravine. A two passes are needed: - Smooth(); - Smooth(); - - // Linearly interpolate the neighbors so that they're close enough together: - FinishLinear(); -} - - - - - -void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) -{ - // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size): - a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024; - - // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction - int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2; - int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2; - int CenterX = a_BlockX + OffsetX; - int CenterZ = a_BlockZ + OffsetZ; - - // Get the base angle in which the ravine "axis" goes: - float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653); - float xc = sin(Angle); - float zc = cos(Angle); - - // Calculate the definition points and radii: - int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16)); - int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32); - int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32); - int Mid = (Top + Bottom) / 2; - int PointX = CenterX - (int)(xc * a_Size / 2); - int PointZ = CenterZ - (int)(zc * a_Size / 2); - m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2)); - for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++) - { - int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); - int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); - // Amplitude is the amount of blocks that this point is away from the ravine "axis" - int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size; - Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4] - int PointX = LineX + (int)(zc * Amplitude); - int PointZ = LineZ - (int)(xc * Amplitude); - int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function - int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4; - int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4; - m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom)); - } // for i - m_Points[] - PointX = CenterX + (int)(xc * a_Size / 2); - PointZ = CenterZ + (int)(zc * a_Size / 2); - m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid)); -} - - - - - -void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst) -{ - // Smoothing: for each line segment, add points on its 1/4 lengths - int Num = a_Src.size() - 2; // this many intermediary points - a_Dst.clear(); - a_Dst.reserve(Num * 2 + 2); - cRavDefPoints::const_iterator itr = a_Src.begin() + 1; - a_Dst.push_back(a_Src.front()); - int PrevX = a_Src.front().m_BlockX; - int PrevZ = a_Src.front().m_BlockZ; - int PrevR = a_Src.front().m_Radius; - int PrevT = a_Src.front().m_Top; - int PrevB = a_Src.front().m_Bottom; - for (int i = 0; i <= Num; ++i, ++itr) - { - int dx = itr->m_BlockX - PrevX; - int dz = itr->m_BlockZ - PrevZ; - if (abs(dx) + abs(dz) < 4) - { - // Too short a segment to smooth-subdivide into quarters - continue; - } - int dr = itr->m_Radius - PrevR; - int dt = itr->m_Top - PrevT; - int db = itr->m_Bottom - PrevB; - int Rad1 = std::max(PrevR + 1 * dr / 4, 1); - int Rad2 = std::max(PrevR + 3 * dr / 4, 1); - a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4)); - a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4)); - PrevX = itr->m_BlockX; - PrevZ = itr->m_BlockZ; - PrevR = itr->m_Radius; - PrevT = itr->m_Top; - PrevB = itr->m_Bottom; - } - a_Dst.push_back(a_Src.back()); -} - - - - - -void cStructGenRavines::cRavine::Smooth(void) -{ - cRavDefPoints Pts; - RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts - RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points -} - - - - - -void cStructGenRavines::cRavine::FinishLinear(void) -{ - // For each segment, use Bresenham's line algorithm to draw a "line" of defpoints - // _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point) - // But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points - - cRavDefPoints Pts; - std::swap(Pts, m_Points); - - m_Points.reserve(Pts.size() * 3); - int PrevX = Pts.front().m_BlockX; - int PrevZ = Pts.front().m_BlockZ; - for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) - { - int x1 = itr->m_BlockX; - int z1 = itr->m_BlockZ; - int dx = abs(x1 - PrevX); - int dz = abs(z1 - PrevZ); - int sx = (PrevX < x1) ? 1 : -1; - int sz = (PrevZ < z1) ? 1 : -1; - int err = dx - dz; - int R = itr->m_Radius; - int T = itr->m_Top; - int B = itr->m_Bottom; - while (true) - { - m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B)); - if ((PrevX == x1) && (PrevZ == z1)) - { - break; - } - int e2 = 2 * err; - if (e2 > -dz) - { - err -= dz; - PrevX += sx; - } - if (e2 < dx) - { - err += dx; - PrevZ += sz; - } - } // while (true) - } // for itr -} - - - - - -#ifdef _DEBUG -AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const -{ - AString SVG; - AppendPrintf(SVG, "m_BlockX, a_OffsetZ + itr->m_BlockZ); - Prefix = 'L'; - } - SVG.append("\"/>\n"); - - // Base point highlight: - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ - ); - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 - ); - - // A gray line from the base point to the first point of the ravine, for identification: - AppendPrintf(SVG, "\n", - a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ - ); - - // Offset guides: - if (a_OffsetX > 0) - { - AppendPrintf(SVG, "\n", - a_OffsetX, a_OffsetX - ); - } - if (a_OffsetZ > 0) - { - AppendPrintf(SVG, "\n", - a_OffsetZ, a_OffsetZ - ); - } - return SVG; -} -#endif // _DEBUG - - - - - -void cStructGenRavines::cRavine::ProcessChunk( - int a_ChunkX, int a_ChunkZ, - cChunkDef::BlockTypes & a_BlockTypes, - cChunkDef::HeightMap & a_HeightMap -) -{ - int BlockStartX = a_ChunkX * cChunkDef::Width; - int BlockStartZ = a_ChunkZ * cChunkDef::Width; - int BlockEndX = BlockStartX + cChunkDef::Width; - int BlockEndZ = BlockStartZ + cChunkDef::Width; - for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) - { - if ( - (itr->m_BlockX + itr->m_Radius < BlockStartX) || - (itr->m_BlockX - itr->m_Radius > BlockEndX) || - (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || - (itr->m_BlockZ - itr->m_Radius > BlockEndZ) - ) - { - // Cannot intersect, bail out early - continue; - } - - // Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top: - int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius - int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc - int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc - for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) - { - #ifdef _DEBUG - // DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on) - if ((DifX + x == 0) && (DifZ + z == 0)) - { - cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE); - } - #endif // _DEBUG - - int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z); - if (DistSq <= RadiusSq) - { - int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast - for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++) - { - switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) - { - // Only carve out these specific block types - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_STONE: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_GRAVEL: - case E_BLOCK_SAND: - case E_BLOCK_SANDSTONE: - case E_BLOCK_NETHERRACK: - case E_BLOCK_COAL_ORE: - case E_BLOCK_IRON_ORE: - case E_BLOCK_GOLD_ORE: - case E_BLOCK_DIAMOND_ORE: - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - { - cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); - break; - } - default: break; - } - } - } - } // for x, z - a_BlockTypes - } // for itr - m_Points[] -} - - - - + +// Ravines.cpp + +// Implements the cStructGenRavines class representing the ravine structure generator + +#include "Globals.h" +#include "Ravines.h" + + + + +/// How many ravines in each direction are generated for a given chunk. Must be an even number +static const int NEIGHBORHOOD_SIZE = 8; + +static const int NUM_RAVINE_POINTS = 4; + + + + + +struct cRavDefPoint +{ + int m_BlockX; + int m_BlockZ; + int m_Radius; + int m_Top; + int m_Bottom; + + cRavDefPoint(int a_BlockX, int a_BlockZ, int a_Radius, int a_Top, int a_Bottom) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ), + m_Radius(a_Radius), + m_Top (a_Top), + m_Bottom(a_Bottom) + { + } +} ; + +typedef std::vector cRavDefPoints; + + + + + +class cStructGenRavines::cRavine +{ + cRavDefPoints m_Points; + + /// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise + void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); + + /// Refines (adds and smooths) defpoints from a_Src into a_Dst + void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst); + + /// Does one round of smoothing, two passes of RefineDefPoints() + void Smooth(void); + + /// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block + void FinishLinear(void); + +public: + // Coords for which the ravine was generated (not necessarily the center) + int m_BlockX; + int m_BlockZ; + + cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise); + + /// Carves the ravine into the chunk specified + void ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap + ); + + #ifdef _DEBUG + /// Exports itself as a SVG line definition + AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const; + #endif // _DEBUG +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenRavines: + +cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) : + m_Noise(a_Seed), + m_Size(a_Size) +{ +} + + + + + +cStructGenRavines::~cStructGenRavines() +{ + ClearCache(); +} + + + + + +void cStructGenRavines::ClearCache(void) +{ + for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Cache[] + m_Cache.clear(); +} + + + + + +void cStructGenRavines::GenStructures(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX(); + int ChunkZ = a_ChunkDesc.GetChunkZ(); + cRavines Ravines; + GetRavinesForChunk(ChunkX, ChunkZ, Ravines); + for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr) + { + (*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap()); + } // for itr - Ravines[] +} + + + + + +void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines) +{ + int BaseX = a_ChunkX * cChunkDef::Width / m_Size; + int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size; + if (BaseX < 0) + { + --BaseX; + } + if (BaseZ < 0) + { + --BaseZ; + } + BaseX -= 4; + BaseZ -= 4; + + // Walk the cache, move each ravine that we want into a_Ravines: + int StartX = BaseX * m_Size; + int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size; + int StartZ = BaseZ * m_Size; + int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size; + for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;) + { + if ( + ((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) && + ((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ) + ) + { + // want + a_Ravines.push_back(*itr); + itr = m_Cache.erase(itr); + } + else + { + // don't want + ++itr; + } + } // for itr - m_Cache[] + + for (int x = 0; x < NEIGHBORHOOD_SIZE; x++) + { + int RealX = (BaseX + x) * m_Size; + for (int z = 0; z < NEIGHBORHOOD_SIZE; z++) + { + int RealZ = (BaseZ + z) * m_Size; + bool Found = false; + for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) + { + if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ)) + { + Found = true; + break; + } + } + if (!Found) + { + a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise)); + } + } + } + + // Copy a_Ravines into m_Cache to the beginning: + cRavines RavinesCopy(a_Ravines); + m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end()); + + // Trim the cache if it's too long: + if (m_Cache.size() > 100) + { + cRavines::iterator itr = m_Cache.begin(); + std::advance(itr, 100); + for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr) + { + delete *itr; + } + itr = m_Cache.begin(); + std::advance(itr, 100); + m_Cache.erase(itr, m_Cache.end()); + } + + /* + #ifdef _DEBUG + // DEBUG: Export as SVG into a file specific for the chunk, for visual verification: + AString SVG; + SVG.append("\n\n"); + for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr) + { + SVG.append((*itr)->ExportAsSVG(0, 512, 512)); + } + SVG.append("\n"); + + AString fnam; + Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ); + cFile File(fnam, cFile::fmWrite); + File.Write(SVG.c_str(), SVG.size()); + #endif // _DEBUG + //*/ +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStructGenRavines::cRavine + +cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) : + m_BlockX(a_BlockX), + m_BlockZ(a_BlockZ) +{ + // Calculate the ravine shape-defining points: + GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise); + + // Smooth the ravine. A two passes are needed: + Smooth(); + Smooth(); + + // Linearly interpolate the neighbors so that they're close enough together: + FinishLinear(); +} + + + + + +void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) +{ + // Modify the size slightly to have different-sized ravines (1/2 to 1/1 of a_Size): + a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024; + + // The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction + int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2; + int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2; + int CenterX = a_BlockX + OffsetX; + int CenterZ = a_BlockZ + OffsetZ; + + // Get the base angle in which the ravine "axis" goes: + float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653); + float xc = sin(Angle); + float zc = cos(Angle); + + // Calculate the definition points and radii: + int MaxRadius = (int)(sqrt(12.0 + ((a_Noise.IntNoise2DInt(61 * a_BlockX, 97 * a_BlockZ) / 13) % a_Size) / 16)); + int Top = 32 + ((a_Noise.IntNoise2DInt(13 * a_BlockX, 17 * a_BlockZ) / 23) % 32); + int Bottom = 5 + ((a_Noise.IntNoise2DInt(17 * a_BlockX, 29 * a_BlockZ) / 13) % 32); + int Mid = (Top + Bottom) / 2; + int PointX = CenterX - (int)(xc * a_Size / 2); + int PointZ = CenterZ - (int)(zc * a_Size / 2); + m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, (Mid + Top) / 2, (Mid + Bottom) / 2)); + for (int i = 1; i < NUM_RAVINE_POINTS - 1; i++) + { + int LineX = CenterX + (int)(xc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); + int LineZ = CenterZ + (int)(zc * a_Size * (i - NUM_RAVINE_POINTS / 2) / NUM_RAVINE_POINTS); + // Amplitude is the amount of blocks that this point is away from the ravine "axis" + int Amplitude = (a_Noise.IntNoise3DInt(70 * a_BlockX, 20 * a_BlockZ + 31 * i, 10000 * i) / 9) % a_Size; + Amplitude = Amplitude / 4 - a_Size / 8; // Amplitude is in interval [-a_Size / 4, a_Size / 4] + int PointX = LineX + (int)(zc * Amplitude); + int PointZ = LineZ - (int)(xc * Amplitude); + int Radius = MaxRadius - abs(i - NUM_RAVINE_POINTS / 2); // TODO: better radius function + int ThisTop = Top + ((a_Noise.IntNoise3DInt(7 * a_BlockX, 19 * a_BlockZ, i * 31) / 13) % 8) - 4; + int ThisBottom = Bottom + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 7 * a_BlockZ, i * 31) / 13) % 8) - 4; + m_Points.push_back(cRavDefPoint(PointX, PointZ, Radius, ThisTop, ThisBottom)); + } // for i - m_Points[] + PointX = CenterX + (int)(xc * a_Size / 2); + PointZ = CenterZ + (int)(zc * a_Size / 2); + m_Points.push_back(cRavDefPoint(PointX, PointZ, 0, Mid, Mid)); +} + + + + + +void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst) +{ + // Smoothing: for each line segment, add points on its 1/4 lengths + int Num = a_Src.size() - 2; // this many intermediary points + a_Dst.clear(); + a_Dst.reserve(Num * 2 + 2); + cRavDefPoints::const_iterator itr = a_Src.begin() + 1; + a_Dst.push_back(a_Src.front()); + int PrevX = a_Src.front().m_BlockX; + int PrevZ = a_Src.front().m_BlockZ; + int PrevR = a_Src.front().m_Radius; + int PrevT = a_Src.front().m_Top; + int PrevB = a_Src.front().m_Bottom; + for (int i = 0; i <= Num; ++i, ++itr) + { + int dx = itr->m_BlockX - PrevX; + int dz = itr->m_BlockZ - PrevZ; + if (abs(dx) + abs(dz) < 4) + { + // Too short a segment to smooth-subdivide into quarters + continue; + } + int dr = itr->m_Radius - PrevR; + int dt = itr->m_Top - PrevT; + int db = itr->m_Bottom - PrevB; + int Rad1 = std::max(PrevR + 1 * dr / 4, 1); + int Rad2 = std::max(PrevR + 3 * dr / 4, 1); + a_Dst.push_back(cRavDefPoint(PrevX + 1 * dx / 4, PrevZ + 1 * dz / 4, Rad1, PrevT + 1 * dt / 4, PrevB + 1 * db / 4)); + a_Dst.push_back(cRavDefPoint(PrevX + 3 * dx / 4, PrevZ + 3 * dz / 4, Rad2, PrevT + 3 * dt / 4, PrevB + 3 * db / 4)); + PrevX = itr->m_BlockX; + PrevZ = itr->m_BlockZ; + PrevR = itr->m_Radius; + PrevT = itr->m_Top; + PrevB = itr->m_Bottom; + } + a_Dst.push_back(a_Src.back()); +} + + + + + +void cStructGenRavines::cRavine::Smooth(void) +{ + cRavDefPoints Pts; + RefineDefPoints(m_Points, Pts); // Refine m_Points -> Pts + RefineDefPoints(Pts, m_Points); // Refine Pts -> m_Points +} + + + + + +void cStructGenRavines::cRavine::FinishLinear(void) +{ + // For each segment, use Bresenham's line algorithm to draw a "line" of defpoints + // _X 2012_07_20: I tried modifying this algorithm to produce "thick" lines (only one coord change per point) + // But the results were about the same as the original, so I disposed of it again - no need to use twice the count of points + + cRavDefPoints Pts; + std::swap(Pts, m_Points); + + m_Points.reserve(Pts.size() * 3); + int PrevX = Pts.front().m_BlockX; + int PrevZ = Pts.front().m_BlockZ; + for (cRavDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr) + { + int x1 = itr->m_BlockX; + int z1 = itr->m_BlockZ; + int dx = abs(x1 - PrevX); + int dz = abs(z1 - PrevZ); + int sx = (PrevX < x1) ? 1 : -1; + int sz = (PrevZ < z1) ? 1 : -1; + int err = dx - dz; + int R = itr->m_Radius; + int T = itr->m_Top; + int B = itr->m_Bottom; + while (true) + { + m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B)); + if ((PrevX == x1) && (PrevZ == z1)) + { + break; + } + int e2 = 2 * err; + if (e2 > -dz) + { + err -= dz; + PrevX += sx; + } + if (e2 < dx) + { + err += dx; + PrevZ += sz; + } + } // while (true) + } // for itr +} + + + + + +#ifdef _DEBUG +AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const +{ + AString SVG; + AppendPrintf(SVG, "m_BlockX, a_OffsetZ + itr->m_BlockZ); + Prefix = 'L'; + } + SVG.append("\"/>\n"); + + // Base point highlight: + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ + ); + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5 + ); + + // A gray line from the base point to the first point of the ravine, for identification: + AppendPrintf(SVG, "\n", + a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ + ); + + // Offset guides: + if (a_OffsetX > 0) + { + AppendPrintf(SVG, "\n", + a_OffsetX, a_OffsetX + ); + } + if (a_OffsetZ > 0) + { + AppendPrintf(SVG, "\n", + a_OffsetZ, a_OffsetZ + ); + } + return SVG; +} +#endif // _DEBUG + + + + + +void cStructGenRavines::cRavine::ProcessChunk( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::HeightMap & a_HeightMap +) +{ + int BlockStartX = a_ChunkX * cChunkDef::Width; + int BlockStartZ = a_ChunkZ * cChunkDef::Width; + int BlockEndX = BlockStartX + cChunkDef::Width; + int BlockEndZ = BlockStartZ + cChunkDef::Width; + for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr) + { + if ( + (itr->m_BlockX + itr->m_Radius < BlockStartX) || + (itr->m_BlockX - itr->m_Radius > BlockEndX) || + (itr->m_BlockZ + itr->m_Radius < BlockStartZ) || + (itr->m_BlockZ - itr->m_Radius > BlockEndZ) + ) + { + // Cannot intersect, bail out early + continue; + } + + // Carve out a cylinder around the xz point, m_Radius in diameter, from Bottom to Top: + int RadiusSq = itr->m_Radius * itr->m_Radius; // instead of doing sqrt for each distance, we do sqr of the radius + int DifX = BlockStartX - itr->m_BlockX; // substitution for faster calc + int DifZ = BlockStartZ - itr->m_BlockZ; // substitution for faster calc + for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++) + { + #ifdef _DEBUG + // DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on) + if ((DifX + x == 0) && (DifZ + z == 0)) + { + cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE); + } + #endif // _DEBUG + + int DistSq = (DifX + x) * (DifX + x) + (DifZ + z) * (DifZ + z); + if (DistSq <= RadiusSq) + { + int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast + for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++) + { + switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) + { + // Only carve out these specific block types + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_GRAVEL: + case E_BLOCK_SAND: + case E_BLOCK_SANDSTONE: + case E_BLOCK_NETHERRACK: + case E_BLOCK_COAL_ORE: + case E_BLOCK_IRON_ORE: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + { + cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR); + break; + } + default: break; + } + } + } + } // for x, z - a_BlockTypes + } // for itr - m_Points[] +} + + + + diff --git a/source/Generating/Ravines.h b/source/Generating/Ravines.h index 1c559c70d..05164a5b2 100644 --- a/source/Generating/Ravines.h +++ b/source/Generating/Ravines.h @@ -1,46 +1,46 @@ - -// Ravines.h - -// Interfaces to the cStructGenRavines class representing the ravine structure generator - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cStructGenRavines : - public cStructureGen -{ -public: - cStructGenRavines(int a_Seed, int a_Size); - ~cStructGenRavines(); - -protected: - class cRavine; // fwd: Ravines.cpp - typedef std::list cRavines; - - cNoise m_Noise; - int m_Size; // Max size, in blocks, of the ravines generated - cRavines m_Cache; - - /// Clears everything from the cache - void ClearCache(void); - - /// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function. - void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines); - - // cStructureGen override: - virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; -} ; - - - - + +// Ravines.h + +// Interfaces to the cStructGenRavines class representing the ravine structure generator + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Noise.h" + + + + + +class cStructGenRavines : + public cStructureGen +{ +public: + cStructGenRavines(int a_Seed, int a_Size); + ~cStructGenRavines(); + +protected: + class cRavine; // fwd: Ravines.cpp + typedef std::list cRavines; + + cNoise m_Noise; + int m_Size; // Max size, in blocks, of the ravines generated + cRavines m_Cache; + + /// Clears everything from the cache + void ClearCache(void); + + /// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function. + void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines); + + // cStructureGen override: + virtual void GenStructures(cChunkDesc & a_ChunkDesc) override; +} ; + + + + diff --git a/source/ItemGrid.cpp b/source/ItemGrid.cpp index 4e424a668..a977a9b6d 100644 --- a/source/ItemGrid.cpp +++ b/source/ItemGrid.cpp @@ -1,662 +1,662 @@ - -// ItemGrid.cpp - -// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) - -#include "Globals.h" -#include "ItemGrid.h" -#include "Items/ItemHandler.h" -#include "Noise.h" - - - - - -cItemGrid::cItemGrid(int a_Width, int a_Height) : - m_Width(a_Width), - m_Height(a_Height), - m_NumSlots(a_Width * a_Height), - m_Slots(new cItem[a_Width * a_Height]), - m_IsInTriggerListeners(false) -{ -} - - - - - -cItemGrid::~cItemGrid() -{ - delete[] m_Slots; -} - - - - - -int cItemGrid::GetSlotNum(int a_X, int a_Y) const -{ - if ( - (a_X < 0) || (a_X >= m_Width) || - (a_Y < 0) || (a_Y >= m_Height) - ) - { - LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)", - __FUNCTION__, a_X, a_Y, m_Width, m_Height - ); - return -1; - } - return a_X + m_Width * a_Y; -} - - - - - -void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: SlotNum out of range: %d in grid of range %d", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - a_X = -1; - a_Y = -1; - return; - } - a_X = a_SlotNum % m_Width; - a_Y = a_SlotNum / m_Width; -} - - - - - -const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const -{ - return GetSlot(GetSlotNum(a_X, a_Y)); -} - - - - - -const cItem & cItemGrid::GetSlot(int a_SlotNum) const -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number, %d out of %d slots", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return m_Slots[0]; - } - return m_Slots[a_SlotNum]; -} - - - - - -void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item) -{ - SetSlot(GetSlotNum(a_X, a_Y), a_Item); -} - - - - - -void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage) -{ - SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage)); -} - - - - - -void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return; - } - m_Slots[a_SlotNum] = a_Item; - TriggerListeners(a_SlotNum); -} - - - - - -void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage) -{ - SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage)); -} - - - - - -void cItemGrid::EmptySlot(int a_X, int a_Y) -{ - EmptySlot(GetSlotNum(a_X, a_Y)); -} - - - - - -void cItemGrid::EmptySlot(int a_SlotNum) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return; - } - - // Check if already empty: - if (m_Slots[a_SlotNum].IsEmpty()) - { - return; - } - - // Empty and notify - m_Slots[a_SlotNum].Empty(); - TriggerListeners(a_SlotNum); -} - - - - - -bool cItemGrid::IsSlotEmpty(int a_SlotNum) const -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return true; - } - return m_Slots[a_SlotNum].IsEmpty(); -} - - - - - -bool cItemGrid::IsSlotEmpty(int a_X, int a_Y) const -{ - return IsSlotEmpty(GetSlotNum(a_X, a_Y)); -} - - - - - -void cItemGrid::Clear(void) -{ - for (int i = 0; i < m_NumSlots; i++) - { - m_Slots[i].Empty(); - TriggerListeners(i); - } -} - - - - - -int cItemGrid::HowManyCanFit(const cItem & a_ItemStack) -{ - char NumLeft = a_ItemStack.m_ItemCount; - int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (m_Slots[i].IsEmpty()) - { - NumLeft -= MaxStack; - } - else if (m_Slots[i].IsStackableWith(a_ItemStack)) - { - NumLeft -= MaxStack - m_Slots[i].m_ItemCount; - } - if (NumLeft <= 0) - { - // All items fit - return a_ItemStack.m_ItemCount; - } - } // for i - m_Slots[] - return a_ItemStack.m_ItemCount - NumLeft; -} - - - - - -int cItemGrid::AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack) -{ - int PrevCount = 0; - if (m_Slots[a_Slot].IsEmpty()) - { - m_Slots[a_Slot] = a_ItemStack; - PrevCount = 0; - } - else - { - PrevCount = m_Slots[a_Slot].m_ItemCount; - } - m_Slots[a_Slot].m_ItemCount = std::min(a_MaxStack, PrevCount + a_Num); - int toReturn = m_Slots[a_Slot].m_ItemCount - PrevCount; - TriggerListeners(a_Slot); - return toReturn; -} - - - - - -int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_PrioritarySlot) -{ - int NumLeft = a_ItemStack.m_ItemCount; - int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); - - // Try prioritarySlot first: - if ( - (a_PrioritarySlot != -1) && - ( - m_Slots[a_PrioritarySlot].IsEmpty() || - m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack) - ) - ) - { - NumLeft -= AddItemToSlot(a_ItemStack, a_PrioritarySlot, NumLeft, MaxStack); - } - - // Scan existing stacks: - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (m_Slots[i].IsStackableWith(a_ItemStack)) - { - NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); - } - if (NumLeft <= 0) - { - // All items fit - return a_ItemStack.m_ItemCount; - } - } // for i - m_Slots[] - - if (!a_AllowNewStacks) - { - return (a_ItemStack.m_ItemCount - NumLeft); - } - - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (m_Slots[i].IsEmpty()) - { - NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); - } - if (NumLeft <= 0) - { - // All items fit - return a_ItemStack.m_ItemCount; - } - } // for i - m_Slots[] - return (a_ItemStack.m_ItemCount - NumLeft); -} - - - - - -int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, int a_PrioritarySlot) -{ - int TotalAdded = 0; - for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();) - { - int NumAdded = AddItem(*itr, a_AllowNewStacks, a_PrioritarySlot); - if (itr->m_ItemCount == NumAdded) - { - itr = a_ItemStackList.erase(itr); - } - else - { - itr->m_ItemCount -= NumAdded; - ++itr; - } - TotalAdded += NumAdded; - } - return TotalAdded; -} - - - - - -int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning -1", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return -1; - } - - if (m_Slots[a_SlotNum].IsEmpty()) - { - // The item is empty, it's not gonna change - return 0; - } - - if (m_Slots[a_SlotNum].m_ItemCount <= -a_AddToCount) - { - // Trying to remove more items than there already are, make the item empty - m_Slots[a_SlotNum].Empty(); - TriggerListeners(a_SlotNum); - return 0; - } - - m_Slots[a_SlotNum].m_ItemCount += a_AddToCount; - TriggerListeners(a_SlotNum); - return m_Slots[a_SlotNum].m_ItemCount; -} - - - - - -int cItemGrid::ChangeSlotCount(int a_X, int a_Y, int a_AddToCount) -{ - return ChangeSlotCount(GetSlotNum(a_X, a_Y), a_AddToCount); -} - - - - - -cItem cItemGrid::RemoveOneItem(int a_SlotNum) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item", - __FUNCTION__, a_SlotNum, m_NumSlots - ); - return cItem(); - } - - // If the slot is empty, return an empty item - if (m_Slots[a_SlotNum].IsEmpty()) - { - return cItem(); - } - - // Make a copy of the item in slot, set count to 1 and remove one from the slot - cItem res = m_Slots[a_SlotNum]; - res.m_ItemCount = 1; - m_Slots[a_SlotNum].m_ItemCount -= 1; - - // Emptying the slot correctly if appropriate - if (m_Slots[a_SlotNum].m_ItemCount == 0) - { - m_Slots[a_SlotNum].Empty(); - } - - // Notify everyone of the change - TriggerListeners(a_SlotNum); - - // Return the stored one item - return res; -} - - - - - -cItem cItemGrid::RemoveOneItem(int a_X, int a_Y) -{ - return RemoveOneItem(GetSlotNum(a_X, a_Y)); -} - - - - - -int cItemGrid::HowManyItems(const cItem & a_Item) -{ - int res = 0; - for (int i = 0; i < m_NumSlots; i++) - { - if (m_Slots[i].IsStackableWith(a_Item)) - { - res += m_Slots[i].m_ItemCount; - } - } - return res; -} - - - - - -bool cItemGrid::HasItems(const cItem & a_ItemStack) -{ - int CurrentlyHave = HowManyItems(a_ItemStack); - return (CurrentlyHave >= a_ItemStack.m_ItemCount); -} - - - - - -int cItemGrid::GetFirstEmptySlot(void) const -{ - return GetNextEmptySlot(-1); -} - - - - - -int cItemGrid::GetFirstUsedSlot(void) const -{ - return GetNextUsedSlot(-1); -} - - - - - -int cItemGrid::GetLastEmptySlot(void) const -{ - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (m_Slots[i].IsEmpty()) - { - return i; - } - } - return -1; -} - - - - - -int cItemGrid::GetLastUsedSlot(void) const -{ - for (int i = m_NumSlots - 1; i >= 0; i--) - { - if (!m_Slots[i].IsEmpty()) - { - return i; - } - } - return -1; -} - - - - - -int cItemGrid::GetNextEmptySlot(int a_StartFrom) const -{ - for (int i = a_StartFrom + 1; i < m_NumSlots; i++) - { - if (m_Slots[i].IsEmpty()) - { - return i; - } - } - return -1; -} - - - - - -int cItemGrid::GetNextUsedSlot(int a_StartFrom) const -{ - for (int i = a_StartFrom + 1; i < m_NumSlots; i++) - { - if (!m_Slots[i].IsEmpty()) - { - return i; - } - } - return -1; -} - - - - - -void cItemGrid::CopyToItems(cItems & a_Items) const -{ - for (int i = 0; i < m_NumSlots; i++) - { - if (!m_Slots[i].IsEmpty()) - { - a_Items.push_back(m_Slots[i]); - } - } // for i - m_Slots[] -} - - - - - -bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) - { - LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots); - return false; - } - return m_Slots[a_SlotNum].DamageItem(a_Amount); -} - - - - - -bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount) -{ - return DamageItem(GetSlotNum(a_X, a_Y), a_Amount); -} - - - - - -void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed) -{ - // Calculate the total weight: - int TotalProbab = 1; - for (int i = 0; i < a_CountLootProbabs; i++) - { - TotalProbab += a_LootProbabs[i].m_Weight; - } - - // Pick the loot items: - cNoise Noise(a_Seed); - for (int i = 0; i < a_NumSlots; i++) - { - int Rnd = (Noise.IntNoise1DInt(i) / 7); - int LootRnd = Rnd % TotalProbab; - Rnd >>= 8; - cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment - for (int j = 0; j < a_CountLootProbabs; j++) - { - LootRnd -= a_LootProbabs[i].m_Weight; - if (LootRnd < 0) - { - CurrentLoot = a_LootProbabs[i].m_Item; - CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount)); - Rnd >>= 8; - break; - } - } // for j - a_LootProbabs[] - SetSlot(Rnd % m_NumSlots, CurrentLoot); - } // for i - NumSlots -} - - - - - -void cItemGrid::AddListener(cListener & a_Listener) -{ - cCSLock Lock(m_CSListeners); - ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() - m_Listeners.push_back(&a_Listener); -} - - - - - -void cItemGrid::RemoveListener(cListener & a_Listener) -{ - cCSLock Lock(m_CSListeners); - ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() - for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr) - { - if (*itr == &a_Listener) - { - m_Listeners.erase(itr); - return; - } - } // for itr - m_Listeners[] -} - - - - - -void cItemGrid::TriggerListeners(int a_SlotNum) -{ - cListeners Listeners; - { - cCSLock Lock(m_CSListeners); - m_IsInTriggerListeners = true; - Listeners = m_Listeners; - } - for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr) - { - (*itr)->OnSlotChanged(this, a_SlotNum); - } // for itr - m_Listeners[] - m_IsInTriggerListeners = false; -} - - - - + +// ItemGrid.cpp + +// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) + +#include "Globals.h" +#include "ItemGrid.h" +#include "Items/ItemHandler.h" +#include "Noise.h" + + + + + +cItemGrid::cItemGrid(int a_Width, int a_Height) : + m_Width(a_Width), + m_Height(a_Height), + m_NumSlots(a_Width * a_Height), + m_Slots(new cItem[a_Width * a_Height]), + m_IsInTriggerListeners(false) +{ +} + + + + + +cItemGrid::~cItemGrid() +{ + delete[] m_Slots; +} + + + + + +int cItemGrid::GetSlotNum(int a_X, int a_Y) const +{ + if ( + (a_X < 0) || (a_X >= m_Width) || + (a_Y < 0) || (a_Y >= m_Height) + ) + { + LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)", + __FUNCTION__, a_X, a_Y, m_Width, m_Height + ); + return -1; + } + return a_X + m_Width * a_Y; +} + + + + + +void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: SlotNum out of range: %d in grid of range %d", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + a_X = -1; + a_Y = -1; + return; + } + a_X = a_SlotNum % m_Width; + a_Y = a_SlotNum / m_Width; +} + + + + + +const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const +{ + return GetSlot(GetSlotNum(a_X, a_Y)); +} + + + + + +const cItem & cItemGrid::GetSlot(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number, %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return m_Slots[0]; + } + return m_Slots[a_SlotNum]; +} + + + + + +void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item) +{ + SetSlot(GetSlotNum(a_X, a_Y), a_Item); +} + + + + + +void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage) +{ + SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage)); +} + + + + + +void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return; + } + m_Slots[a_SlotNum] = a_Item; + TriggerListeners(a_SlotNum); +} + + + + + +void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage) +{ + SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage)); +} + + + + + +void cItemGrid::EmptySlot(int a_X, int a_Y) +{ + EmptySlot(GetSlotNum(a_X, a_Y)); +} + + + + + +void cItemGrid::EmptySlot(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return; + } + + // Check if already empty: + if (m_Slots[a_SlotNum].IsEmpty()) + { + return; + } + + // Empty and notify + m_Slots[a_SlotNum].Empty(); + TriggerListeners(a_SlotNum); +} + + + + + +bool cItemGrid::IsSlotEmpty(int a_SlotNum) const +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return true; + } + return m_Slots[a_SlotNum].IsEmpty(); +} + + + + + +bool cItemGrid::IsSlotEmpty(int a_X, int a_Y) const +{ + return IsSlotEmpty(GetSlotNum(a_X, a_Y)); +} + + + + + +void cItemGrid::Clear(void) +{ + for (int i = 0; i < m_NumSlots; i++) + { + m_Slots[i].Empty(); + TriggerListeners(i); + } +} + + + + + +int cItemGrid::HowManyCanFit(const cItem & a_ItemStack) +{ + char NumLeft = a_ItemStack.m_ItemCount; + int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + NumLeft -= MaxStack; + } + else if (m_Slots[i].IsStackableWith(a_ItemStack)) + { + NumLeft -= MaxStack - m_Slots[i].m_ItemCount; + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + return a_ItemStack.m_ItemCount - NumLeft; +} + + + + + +int cItemGrid::AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack) +{ + int PrevCount = 0; + if (m_Slots[a_Slot].IsEmpty()) + { + m_Slots[a_Slot] = a_ItemStack; + PrevCount = 0; + } + else + { + PrevCount = m_Slots[a_Slot].m_ItemCount; + } + m_Slots[a_Slot].m_ItemCount = std::min(a_MaxStack, PrevCount + a_Num); + int toReturn = m_Slots[a_Slot].m_ItemCount - PrevCount; + TriggerListeners(a_Slot); + return toReturn; +} + + + + + +int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_PrioritarySlot) +{ + int NumLeft = a_ItemStack.m_ItemCount; + int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize(); + + // Try prioritarySlot first: + if ( + (a_PrioritarySlot != -1) && + ( + m_Slots[a_PrioritarySlot].IsEmpty() || + m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack) + ) + ) + { + NumLeft -= AddItemToSlot(a_ItemStack, a_PrioritarySlot, NumLeft, MaxStack); + } + + // Scan existing stacks: + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsStackableWith(a_ItemStack)) + { + NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + + if (!a_AllowNewStacks) + { + return (a_ItemStack.m_ItemCount - NumLeft); + } + + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack); + } + if (NumLeft <= 0) + { + // All items fit + return a_ItemStack.m_ItemCount; + } + } // for i - m_Slots[] + return (a_ItemStack.m_ItemCount - NumLeft); +} + + + + + +int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, int a_PrioritarySlot) +{ + int TotalAdded = 0; + for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();) + { + int NumAdded = AddItem(*itr, a_AllowNewStacks, a_PrioritarySlot); + if (itr->m_ItemCount == NumAdded) + { + itr = a_ItemStackList.erase(itr); + } + else + { + itr->m_ItemCount -= NumAdded; + ++itr; + } + TotalAdded += NumAdded; + } + return TotalAdded; +} + + + + + +int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning -1", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return -1; + } + + if (m_Slots[a_SlotNum].IsEmpty()) + { + // The item is empty, it's not gonna change + return 0; + } + + if (m_Slots[a_SlotNum].m_ItemCount <= -a_AddToCount) + { + // Trying to remove more items than there already are, make the item empty + m_Slots[a_SlotNum].Empty(); + TriggerListeners(a_SlotNum); + return 0; + } + + m_Slots[a_SlotNum].m_ItemCount += a_AddToCount; + TriggerListeners(a_SlotNum); + return m_Slots[a_SlotNum].m_ItemCount; +} + + + + + +int cItemGrid::ChangeSlotCount(int a_X, int a_Y, int a_AddToCount) +{ + return ChangeSlotCount(GetSlotNum(a_X, a_Y), a_AddToCount); +} + + + + + +cItem cItemGrid::RemoveOneItem(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item", + __FUNCTION__, a_SlotNum, m_NumSlots + ); + return cItem(); + } + + // If the slot is empty, return an empty item + if (m_Slots[a_SlotNum].IsEmpty()) + { + return cItem(); + } + + // Make a copy of the item in slot, set count to 1 and remove one from the slot + cItem res = m_Slots[a_SlotNum]; + res.m_ItemCount = 1; + m_Slots[a_SlotNum].m_ItemCount -= 1; + + // Emptying the slot correctly if appropriate + if (m_Slots[a_SlotNum].m_ItemCount == 0) + { + m_Slots[a_SlotNum].Empty(); + } + + // Notify everyone of the change + TriggerListeners(a_SlotNum); + + // Return the stored one item + return res; +} + + + + + +cItem cItemGrid::RemoveOneItem(int a_X, int a_Y) +{ + return RemoveOneItem(GetSlotNum(a_X, a_Y)); +} + + + + + +int cItemGrid::HowManyItems(const cItem & a_Item) +{ + int res = 0; + for (int i = 0; i < m_NumSlots; i++) + { + if (m_Slots[i].IsStackableWith(a_Item)) + { + res += m_Slots[i].m_ItemCount; + } + } + return res; +} + + + + + +bool cItemGrid::HasItems(const cItem & a_ItemStack) +{ + int CurrentlyHave = HowManyItems(a_ItemStack); + return (CurrentlyHave >= a_ItemStack.m_ItemCount); +} + + + + + +int cItemGrid::GetFirstEmptySlot(void) const +{ + return GetNextEmptySlot(-1); +} + + + + + +int cItemGrid::GetFirstUsedSlot(void) const +{ + return GetNextUsedSlot(-1); +} + + + + + +int cItemGrid::GetLastEmptySlot(void) const +{ + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetLastUsedSlot(void) const +{ + for (int i = m_NumSlots - 1; i >= 0; i--) + { + if (!m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetNextEmptySlot(int a_StartFrom) const +{ + for (int i = a_StartFrom + 1; i < m_NumSlots; i++) + { + if (m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +int cItemGrid::GetNextUsedSlot(int a_StartFrom) const +{ + for (int i = a_StartFrom + 1; i < m_NumSlots; i++) + { + if (!m_Slots[i].IsEmpty()) + { + return i; + } + } + return -1; +} + + + + + +void cItemGrid::CopyToItems(cItems & a_Items) const +{ + for (int i = 0; i < m_NumSlots; i++) + { + if (!m_Slots[i].IsEmpty()) + { + a_Items.push_back(m_Slots[i]); + } + } // for i - m_Slots[] +} + + + + + +bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots)) + { + LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots); + return false; + } + return m_Slots[a_SlotNum].DamageItem(a_Amount); +} + + + + + +bool cItemGrid::DamageItem(int a_X, int a_Y, short a_Amount) +{ + return DamageItem(GetSlotNum(a_X, a_Y), a_Amount); +} + + + + + +void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed) +{ + // Calculate the total weight: + int TotalProbab = 1; + for (int i = 0; i < a_CountLootProbabs; i++) + { + TotalProbab += a_LootProbabs[i].m_Weight; + } + + // Pick the loot items: + cNoise Noise(a_Seed); + for (int i = 0; i < a_NumSlots; i++) + { + int Rnd = (Noise.IntNoise1DInt(i) / 7); + int LootRnd = Rnd % TotalProbab; + Rnd >>= 8; + cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment + for (int j = 0; j < a_CountLootProbabs; j++) + { + LootRnd -= a_LootProbabs[i].m_Weight; + if (LootRnd < 0) + { + CurrentLoot = a_LootProbabs[i].m_Item; + CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount)); + Rnd >>= 8; + break; + } + } // for j - a_LootProbabs[] + SetSlot(Rnd % m_NumSlots, CurrentLoot); + } // for i - NumSlots +} + + + + + +void cItemGrid::AddListener(cListener & a_Listener) +{ + cCSLock Lock(m_CSListeners); + ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() + m_Listeners.push_back(&a_Listener); +} + + + + + +void cItemGrid::RemoveListener(cListener & a_Listener) +{ + cCSLock Lock(m_CSListeners); + ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners() + for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr) + { + if (*itr == &a_Listener) + { + m_Listeners.erase(itr); + return; + } + } // for itr - m_Listeners[] +} + + + + + +void cItemGrid::TriggerListeners(int a_SlotNum) +{ + cListeners Listeners; + { + cCSLock Lock(m_CSListeners); + m_IsInTriggerListeners = true; + Listeners = m_Listeners; + } + for (cListeners::iterator itr = Listeners.begin(), end = Listeners.end(); itr != end; ++itr) + { + (*itr)->OnSlotChanged(this, a_SlotNum); + } // for itr - m_Listeners[] + m_IsInTriggerListeners = false; +} + + + + diff --git a/source/ItemGrid.h b/source/ItemGrid.h index 45f978530..9eba5b452 100644 --- a/source/ItemGrid.h +++ b/source/ItemGrid.h @@ -1,191 +1,191 @@ - -// ItemGrid.h - -// Declares the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) - - - - -#pragma once - -#include "Item.h" - - - - - -// tolua_begin -class cItemGrid -{ -public: - // tolua_end - - /// This class is used as a callback for when a slot changes - class cListener - { - public: - /// Called whenever a slot changes - virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0; - } ; - typedef std::vector cListeners; - - cItemGrid(int a_Width, int a_Height); - - ~cItemGrid(); - - // tolua_begin - int GetWidth (void) const { return m_Width; } - int GetHeight (void) const { return m_Height; } - int GetNumSlots(void) const { return m_NumSlots; } - - /// Converts XY coords into slot number; returns -1 on invalid coords - int GetSlotNum(int a_X, int a_Y) const; - - // tolua_end - - /// Converts slot number into XY coords; sets coords to -1 on invalid slot number. Exported in ManualBindings.cpp - void GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const; - - // tolua_begin - - // Retrieve slots by coords or slot number; Logs warning and returns the first slot on invalid coords / slotnum - const cItem & GetSlot(int a_X, int a_Y) const; - const cItem & GetSlot(int a_SlotNum) const; - - // Set slot by coords or slot number; Logs warning and doesn't set on invalid coords / slotnum - void SetSlot(int a_X, int a_Y, const cItem & a_Item); - void SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage); - void SetSlot(int a_SlotNum, const cItem & a_Item); - void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage); - - // Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum - void EmptySlot(int a_X, int a_Y); - void EmptySlot(int a_SlotNum); - - /// Returns true if the specified slot is empty or the slot doesn't exist - bool IsSlotEmpty(int a_SlotNum) const; - - /// Returns true if the specified slot is empty or the slot doesn't exist - bool IsSlotEmpty(int a_X, int a_Y) const; - - /// Sets all items as empty - void Clear(void); - - /// Returns number of items out of a_ItemStack that can fit in the storage - int HowManyCanFit(const cItem & a_ItemStack); - - /** Adds as many items out of a_ItemStack as can fit. - If a_AllowNewStacks is set to false, only existing stacks can be topped up; - if a_AllowNewStacks is set to true, empty slots can be used for the rest. - If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in - first (if empty or compatible with added items) - if a_PrioritarySlot is set to -1, regular order apply - Returns the number of items that fit. - */ - int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); - - /** Same as AddItem, but works on an entire list of item stacks. - The a_ItemStackList is modified to reflect the leftover items. - If a_AllowNewStacks is set to false, only existing stacks can be topped up; - if a_AllowNewStacks is set to true, empty slots can be used for the rest. - If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in - first (if empty or compatible with added items) - if a_PrioritarySlot is set to -1, regular order apply - Returns the total number of items that fit. - */ - int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); - - /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. - If the slot is empty, ignores the call. - Returns the new count. - */ - int ChangeSlotCount(int a_SlotNum, int a_AddToCount); - - /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. - If the slot is empty, ignores the call. - Returns the new count. - */ - int ChangeSlotCount(int a_X, int a_Y, int a_AddToCount); - - /** Removes one item from the stack in the specified slot, and returns it. - If the slot was empty, returns an empty item - */ - cItem RemoveOneItem(int a_SlotNum); - - /** Removes one item from the stack in the specified slot, and returns it. - If the slot was empty, returns an empty item - */ - cItem RemoveOneItem(int a_X, int a_Y); - - /// Returns the number of items of type a_Item that are stored - int HowManyItems(const cItem & a_Item); - - /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack - bool HasItems(const cItem & a_ItemStack); - - /// Returns the index of the first empty slot; -1 if all full - int GetFirstEmptySlot(void) const; - - /// Returns the index of the first non-empty slot; -1 if all empty - int GetFirstUsedSlot(void) const; - - /// Returns the index of the last empty slot; -1 if all full - int GetLastEmptySlot(void) const; - - /// Returns the index of the last used slot; -1 if all empty - int GetLastUsedSlot(void) const; - - /// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked) - int GetNextEmptySlot(int a_StartFrom) const; - - /// Returns the index of the first used slot following a_StartFrom (a_StartFrom is not checked) - int GetNextUsedSlot(int a_StartFrom) const; - - /// Copies the contents into a cItems object; preserves the original a_Items contents - void CopyToItems(cItems & a_Items) const; - - /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) - bool DamageItem(int a_SlotNum, short a_Amount); - - /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) - bool DamageItem(int a_X, int a_Y, short a_Amount); - - // tolua_end - - - /** Generates random loot from the specified loot probability table, with a chance of enchanted books added. - A total of a_NumSlots are taken by the loot. - Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs - */ - void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed); - - /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback! - void AddListener(cListener & a_Listener); - - /// Removes a slot-change-callback. Must not be called from within the listener callback! - void RemoveListener(cListener & a_Listener); - - // tolua_begin - -protected: - int m_Width; - int m_Height; - int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions - cItem * m_Slots; // x + m_Width * y - - cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object - cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access - bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring - - /// Calls all m_Listeners for the specified slot number - void TriggerListeners(int a_SlotNum); - - /** Adds up to a_Num items out of a_ItemStack, as many as can fit, in specified slot - Returns the number of items that did fit. - */ - int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack); -} ; -// tolua_end - - - + +// ItemGrid.h + +// Declares the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.) + + + + +#pragma once + +#include "Item.h" + + + + + +// tolua_begin +class cItemGrid +{ +public: + // tolua_end + + /// This class is used as a callback for when a slot changes + class cListener + { + public: + /// Called whenever a slot changes + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0; + } ; + typedef std::vector cListeners; + + cItemGrid(int a_Width, int a_Height); + + ~cItemGrid(); + + // tolua_begin + int GetWidth (void) const { return m_Width; } + int GetHeight (void) const { return m_Height; } + int GetNumSlots(void) const { return m_NumSlots; } + + /// Converts XY coords into slot number; returns -1 on invalid coords + int GetSlotNum(int a_X, int a_Y) const; + + // tolua_end + + /// Converts slot number into XY coords; sets coords to -1 on invalid slot number. Exported in ManualBindings.cpp + void GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const; + + // tolua_begin + + // Retrieve slots by coords or slot number; Logs warning and returns the first slot on invalid coords / slotnum + const cItem & GetSlot(int a_X, int a_Y) const; + const cItem & GetSlot(int a_SlotNum) const; + + // Set slot by coords or slot number; Logs warning and doesn't set on invalid coords / slotnum + void SetSlot(int a_X, int a_Y, const cItem & a_Item); + void SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage); + void SetSlot(int a_SlotNum, const cItem & a_Item); + void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage); + + // Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum + void EmptySlot(int a_X, int a_Y); + void EmptySlot(int a_SlotNum); + + /// Returns true if the specified slot is empty or the slot doesn't exist + bool IsSlotEmpty(int a_SlotNum) const; + + /// Returns true if the specified slot is empty or the slot doesn't exist + bool IsSlotEmpty(int a_X, int a_Y) const; + + /// Sets all items as empty + void Clear(void); + + /// Returns number of items out of a_ItemStack that can fit in the storage + int HowManyCanFit(const cItem & a_ItemStack); + + /** Adds as many items out of a_ItemStack as can fit. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in + first (if empty or compatible with added items) + if a_PrioritarySlot is set to -1, regular order apply + Returns the number of items that fit. + */ + int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); + + /** Same as AddItem, but works on an entire list of item stacks. + The a_ItemStackList is modified to reflect the leftover items. + If a_AllowNewStacks is set to false, only existing stacks can be topped up; + if a_AllowNewStacks is set to true, empty slots can be used for the rest. + If a_PrioritarySlot is set to a positive value, then the corresponding slot will be used in + first (if empty or compatible with added items) + if a_PrioritarySlot is set to -1, regular order apply + Returns the total number of items that fit. + */ + int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks = true, int a_PrioritarySlot = -1); + + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. + If the slot is empty, ignores the call. + Returns the new count. + */ + int ChangeSlotCount(int a_SlotNum, int a_AddToCount); + + /** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot. + If the slot is empty, ignores the call. + Returns the new count. + */ + int ChangeSlotCount(int a_X, int a_Y, int a_AddToCount); + + /** Removes one item from the stack in the specified slot, and returns it. + If the slot was empty, returns an empty item + */ + cItem RemoveOneItem(int a_SlotNum); + + /** Removes one item from the stack in the specified slot, and returns it. + If the slot was empty, returns an empty item + */ + cItem RemoveOneItem(int a_X, int a_Y); + + /// Returns the number of items of type a_Item that are stored + int HowManyItems(const cItem & a_Item); + + /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack + bool HasItems(const cItem & a_ItemStack); + + /// Returns the index of the first empty slot; -1 if all full + int GetFirstEmptySlot(void) const; + + /// Returns the index of the first non-empty slot; -1 if all empty + int GetFirstUsedSlot(void) const; + + /// Returns the index of the last empty slot; -1 if all full + int GetLastEmptySlot(void) const; + + /// Returns the index of the last used slot; -1 if all empty + int GetLastUsedSlot(void) const; + + /// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked) + int GetNextEmptySlot(int a_StartFrom) const; + + /// Returns the index of the first used slot following a_StartFrom (a_StartFrom is not checked) + int GetNextUsedSlot(int a_StartFrom) const; + + /// Copies the contents into a cItems object; preserves the original a_Items contents + void CopyToItems(cItems & a_Items) const; + + /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) + bool DamageItem(int a_SlotNum, short a_Amount); + + /// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact) + bool DamageItem(int a_X, int a_Y, short a_Amount); + + // tolua_end + + + /** Generates random loot from the specified loot probability table, with a chance of enchanted books added. + A total of a_NumSlots are taken by the loot. + Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs + */ + void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed); + + /// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback! + void AddListener(cListener & a_Listener); + + /// Removes a slot-change-callback. Must not be called from within the listener callback! + void RemoveListener(cListener & a_Listener); + + // tolua_begin + +protected: + int m_Width; + int m_Height; + int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions + cItem * m_Slots; // x + m_Width * y + + cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object + cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access + bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring + + /// Calls all m_Listeners for the specified slot number + void TriggerListeners(int a_SlotNum); + + /** Adds up to a_Num items out of a_ItemStack, as many as can fit, in specified slot + Returns the number of items that did fit. + */ + int AddItemToSlot(const cItem & a_ItemStack, int a_Slot, int a_Num, int a_MaxStack); +} ; +// tolua_end + + + diff --git a/source/Items/ItemBed.h b/source/Items/ItemBed.h index c9fec9064..ab4182eea 100644 --- a/source/Items/ItemBed.h +++ b/source/Items/ItemBed.h @@ -1,56 +1,56 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Blocks/BlockBed.h" - - - - - -class cItemBedHandler : - public cItemHandler -{ -public: - cItemBedHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - if (a_BlockFace != BLOCK_FACE_TOP) - { - // Can only be placed on the floor - return false; - } - - a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation()); - - // Check if there is empty space for the foot section: - Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta); - if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) - { - return false; - } - - a_BlockType = E_BLOCK_BED; - return true; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Blocks/BlockBed.h" + + + + + +class cItemBedHandler : + public cItemHandler +{ +public: + cItemBedHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace != BLOCK_FACE_TOP) + { + // Can only be placed on the floor + return false; + } + + a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation()); + + // Check if there is empty space for the foot section: + Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta); + if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) + { + return false; + } + + a_BlockType = E_BLOCK_BED; + return true; + } +} ; + + + + diff --git a/source/Items/ItemBrewingStand.h b/source/Items/ItemBrewingStand.h index 07ee7dfeb..4ff14d4b4 100644 --- a/source/Items/ItemBrewingStand.h +++ b/source/Items/ItemBrewingStand.h @@ -1,41 +1,41 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemBrewingStandHandler : - public cItemHandler -{ -public: - cItemBrewingStandHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_BREWING_STAND; - a_BlockMeta = 0; - return true; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemBrewingStandHandler : + public cItemHandler +{ +public: + cItemBrewingStandHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_BREWING_STAND; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/source/Items/ItemBucket.h b/source/Items/ItemBucket.h index e4adc98b8..fa3d48da1 100644 --- a/source/Items/ItemBucket.h +++ b/source/Items/ItemBucket.h @@ -1,160 +1,160 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Simulator/FluidSimulator.h" -#include "../Blocks/BlockHandler.h" - - - - - -class cItemBucketHandler : - public cItemHandler -{ -public: - cItemBucketHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - - } - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - switch (m_ItemType) - { - case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir); - case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA); - case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER); - default: - { - ASSERT(!"Unhandled ItemType"); - return false; - } - } - } - - - - bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) - { - if (a_BlockFace < 0) - { - return false; - } - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - BLOCKTYPE ClickedBlock; - NIBBLETYPE ClickedMeta; - a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedMeta); - LOGD("Bucket Clicked BlockType %d, meta %d", ClickedBlock, ClickedMeta); - if (ClickedMeta != 0) - { - // Not a source block - return false; - } - - if (a_Player->GetGameMode() == gmCreative) - { - // In creative mode don't modify the inventory, just remove the fluid: - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - return true; - } - - ENUM_ITEM_ID NewItem = E_ITEM_EMPTY; - switch (ClickedBlock) - { - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - { - NewItem = E_ITEM_WATER_BUCKET; - break; - } - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - NewItem = E_ITEM_LAVA_BUCKET; - break; - } - - default: return false; - } - - // Remove the bucket from the inventory - if (!a_Player->GetInventory().RemoveOneEquippedItem()) - { - LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?"); - ASSERT(!"Inventory bucket mismatch"); - return true; - } - - // Give new bucket, filled with fluid: - cItem Item(NewItem, 1); - a_Player->GetInventory().AddItem(Item, true, true); - - // Remove water / lava block - a_Player->GetWorld()->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - return true; - } - - - bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_FluidBlock) - { - if (a_BlockFace < 0) - { - return false; - } - - BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock); - if (!CanWashAway) - { - // The block pointed at cannot be washed away, so put fluid on top of it / on its sides - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - } - if ( - !CanWashAway && - (CurrentBlock != E_BLOCK_AIR) && - (CurrentBlock != E_BLOCK_WATER) && - (CurrentBlock != E_BLOCK_STATIONARY_WATER) && - (CurrentBlock != E_BLOCK_LAVA) && - (CurrentBlock != E_BLOCK_STATIONARY_LAVA) - ) - { - // Cannot place water here - return false; - } - - if (a_Player->GetGameMode() != gmCreative) - { - // Remove fluid bucket, add empty bucket: - if (!a_Player->GetInventory().RemoveOneEquippedItem()) - { - LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?"); - ASSERT(!"Inventory bucket mismatch"); - return false; - } - cItem Item(E_ITEM_BUCKET, 1); - if (!a_Player->GetInventory().AddItem(Item,true,true)) - { - return false; - } - } - - // Wash away anything that was there prior to placing: - if (CanWashAway) - { - cBlockHandler * Handler = BlockHandler(CurrentBlock); - if (Handler->DoesDropOnUnsuitable()) - { - Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); - } - } - - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0); - - return true; - } - -}; + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Simulator/FluidSimulator.h" +#include "../Blocks/BlockHandler.h" + + + + + +class cItemBucketHandler : + public cItemHandler +{ +public: + cItemBucketHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + switch (m_ItemType) + { + case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir); + case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_LAVA); + case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_Dir, E_BLOCK_WATER); + default: + { + ASSERT(!"Unhandled ItemType"); + return false; + } + } + } + + + + bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) + { + if (a_BlockFace < 0) + { + return false; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedMeta; + a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedMeta); + LOGD("Bucket Clicked BlockType %d, meta %d", ClickedBlock, ClickedMeta); + if (ClickedMeta != 0) + { + // Not a source block + return false; + } + + if (a_Player->GetGameMode() == gmCreative) + { + // In creative mode don't modify the inventory, just remove the fluid: + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + return true; + } + + ENUM_ITEM_ID NewItem = E_ITEM_EMPTY; + switch (ClickedBlock) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + NewItem = E_ITEM_WATER_BUCKET; + break; + } + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + NewItem = E_ITEM_LAVA_BUCKET; + break; + } + + default: return false; + } + + // Remove the bucket from the inventory + if (!a_Player->GetInventory().RemoveOneEquippedItem()) + { + LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?"); + ASSERT(!"Inventory bucket mismatch"); + return true; + } + + // Give new bucket, filled with fluid: + cItem Item(NewItem, 1); + a_Player->GetInventory().AddItem(Item, true, true); + + // Remove water / lava block + a_Player->GetWorld()->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + return true; + } + + + bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_FluidBlock) + { + if (a_BlockFace < 0) + { + return false; + } + + BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock); + if (!CanWashAway) + { + // The block pointed at cannot be washed away, so put fluid on top of it / on its sides + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + } + if ( + !CanWashAway && + (CurrentBlock != E_BLOCK_AIR) && + (CurrentBlock != E_BLOCK_WATER) && + (CurrentBlock != E_BLOCK_STATIONARY_WATER) && + (CurrentBlock != E_BLOCK_LAVA) && + (CurrentBlock != E_BLOCK_STATIONARY_LAVA) + ) + { + // Cannot place water here + return false; + } + + if (a_Player->GetGameMode() != gmCreative) + { + // Remove fluid bucket, add empty bucket: + if (!a_Player->GetInventory().RemoveOneEquippedItem()) + { + LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?"); + ASSERT(!"Inventory bucket mismatch"); + return false; + } + cItem Item(E_ITEM_BUCKET, 1); + if (!a_Player->GetInventory().AddItem(Item,true,true)) + { + return false; + } + } + + // Wash away anything that was there prior to placing: + if (CanWashAway) + { + cBlockHandler * Handler = BlockHandler(CurrentBlock); + if (Handler->DoesDropOnUnsuitable()) + { + Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + } + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0); + + return true; + } + +}; diff --git a/source/Items/ItemCauldron.h b/source/Items/ItemCauldron.h index 216766cf8..8b2ddc29f 100644 --- a/source/Items/ItemCauldron.h +++ b/source/Items/ItemCauldron.h @@ -1,41 +1,41 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemCauldronHandler : - public cItemHandler -{ -public: - cItemCauldronHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_CAULDRON; - a_BlockMeta = 0; - return true; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemCauldronHandler : + public cItemHandler +{ +public: + cItemCauldronHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_CAULDRON; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/source/Items/ItemCloth.h b/source/Items/ItemCloth.h index f6e2c1906..aca27a299 100644 --- a/source/Items/ItemCloth.h +++ b/source/Items/ItemCloth.h @@ -1,23 +1,23 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemClothHandler : - public cItemHandler -{ -public: - cItemClothHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemClothHandler : + public cItemHandler +{ +public: + cItemClothHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } +} ; + + + + diff --git a/source/Items/ItemDoor.h b/source/Items/ItemDoor.h index 6ebeab43a..72ea0beed 100644 --- a/source/Items/ItemDoor.h +++ b/source/Items/ItemDoor.h @@ -1,45 +1,45 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" - - - - - -class cItemDoorHandler : - public cItemHandler -{ -public: - cItemDoorHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = (m_ItemType == E_ITEM_WOODEN_DOOR) ? E_BLOCK_WOODEN_DOOR : E_BLOCK_IRON_DOOR; - return BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta( - a_World, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" + + + + + +class cItemDoorHandler : + public cItemHandler +{ +public: + cItemDoorHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = (m_ItemType == E_ITEM_WOODEN_DOOR) ? E_BLOCK_WOODEN_DOOR : E_BLOCK_IRON_DOOR; + return BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + } +} ; + + + + diff --git a/source/Items/ItemDye.h b/source/Items/ItemDye.h index e4c31fa4b..984d452b7 100644 --- a/source/Items/ItemDye.h +++ b/source/Items/ItemDye.h @@ -1,44 +1,44 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Player.h" - - - - - -class cItemDyeHandler : - public cItemHandler -{ -public: - cItemDyeHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - // TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe) - - // Handle growing the plants: - if (a_Item.m_ItemDamage == E_META_DYE_WHITE) - { - if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true)) - { - if (a_Player->GetGameMode() != gmCreative) - { - a_Player->GetInventory().RemoveOneEquippedItem(); - return true; - } - } - } - return false; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" + + + + + +class cItemDyeHandler : + public cItemHandler +{ +public: + cItemDyeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + // TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe) + + // Handle growing the plants: + if (a_Item.m_ItemDamage == E_META_DYE_WHITE) + { + if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true)) + { + if (a_Player->GetGameMode() != gmCreative) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + return true; + } + } + } + return false; + } +} ; + + + + diff --git a/source/Items/ItemFlowerPot.h b/source/Items/ItemFlowerPot.h index 0e55ddb51..befa2ff21 100644 --- a/source/Items/ItemFlowerPot.h +++ b/source/Items/ItemFlowerPot.h @@ -1,41 +1,41 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemFlowerPotHandler : - public cItemHandler -{ -public: - cItemFlowerPotHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_FLOWER_POT; - a_BlockMeta = 0; - return true; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemFlowerPotHandler : + public cItemHandler +{ +public: + cItemFlowerPotHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_FLOWER_POT; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/source/Items/ItemFood.h b/source/Items/ItemFood.h index 4ec2f7a36..8a6898103 100644 --- a/source/Items/ItemFood.h +++ b/source/Items/ItemFood.h @@ -1,56 +1,56 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemFoodHandler : - public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemFoodHandler(int a_ItemType) - : super(a_ItemType) - { - } - - - virtual bool IsFood(void) override - { - return true; - } - - - virtual FoodInfo GetFoodInfo(void) override - { - switch(m_ItemType) - { - case E_ITEM_BREAD: return FoodInfo(5, 6); - case E_ITEM_COOKIE: return FoodInfo(2, 0.4); - case E_ITEM_MELON_SLICE: return FoodInfo(2, 1.2); - case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2, 30); - case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2); - case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8); - case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8); - case E_ITEM_STEAK: return FoodInfo(8, 12.8); - case E_ITEM_COOKED_PORKCHOP: return FoodInfo(8, 12.8); - case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2); - case E_ITEM_COOKED_FISH: return FoodInfo(5, 6); - case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4); - case E_ITEM_GOLDEN_APPLE: return FoodInfo(4, 9.6); - case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8, 80); - case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2, 100); - } - LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType); - return FoodInfo(0, 0.f); - } - -}; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemFoodHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemFoodHandler(int a_ItemType) + : super(a_ItemType) + { + } + + + virtual bool IsFood(void) override + { + return true; + } + + + virtual FoodInfo GetFoodInfo(void) override + { + switch(m_ItemType) + { + case E_ITEM_BREAD: return FoodInfo(5, 6); + case E_ITEM_COOKIE: return FoodInfo(2, 0.4); + case E_ITEM_MELON_SLICE: return FoodInfo(2, 1.2); + case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2, 30); + case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2); + case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8); + case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8); + case E_ITEM_STEAK: return FoodInfo(8, 12.8); + case E_ITEM_COOKED_PORKCHOP: return FoodInfo(8, 12.8); + case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2); + case E_ITEM_COOKED_FISH: return FoodInfo(5, 6); + case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4); + case E_ITEM_GOLDEN_APPLE: return FoodInfo(4, 9.6); + case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8, 80); + case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2, 100); + } + LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType); + return FoodInfo(0, 0.f); + } + +}; + + + + diff --git a/source/Items/ItemHandler.cpp b/source/Items/ItemHandler.cpp index acb6b6371..c81916f5f 100644 --- a/source/Items/ItemHandler.cpp +++ b/source/Items/ItemHandler.cpp @@ -1,500 +1,500 @@ - -#include "Globals.h" -#include "ItemHandler.h" -#include "../Item.h" -#include "../World.h" -#include "../Player.h" -#include "../FastRandom.h" - -// Handlers: -#include "ItemBed.h" -#include "ItemBrewingStand.h" -#include "ItemBucket.h" -#include "ItemCauldron.h" -#include "ItemCloth.h" -#include "ItemDoor.h" -#include "ItemDye.h" -#include "ItemFlowerPot.h" -#include "ItemFood.h" -#include "ItemHoe.h" -#include "ItemLeaves.h" -#include "ItemLighter.h" -#include "ItemMinecart.h" -#include "ItemPickaxe.h" -#include "ItemRedstoneDust.h" -#include "ItemRedstoneRepeater.h" -#include "ItemSapling.h" -#include "ItemSeeds.h" -#include "ItemShears.h" -#include "ItemShovel.h" -#include "ItemSign.h" -#include "ItemSlab.h" -#include "ItemSpawnEgg.h" -#include "ItemSugarcane.h" -#include "ItemSword.h" -#include "ItemWood.h" - -#include "../Blocks/BlockHandler.h" - - - - - -bool cItemHandler::m_HandlerInitialized = false; -cItemHandler * cItemHandler::m_ItemHandler[2268]; - - - - - -cItemHandler *cItemHandler::GetItemHandler(int a_ItemType) -{ - if(a_ItemType < 0) a_ItemType = 0; - - if(!m_HandlerInitialized) - { //We have to initialize - memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); - m_HandlerInitialized = true; - } - if(m_ItemHandler[a_ItemType]) - return m_ItemHandler[a_ItemType]; - m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType); - return m_ItemHandler[a_ItemType]; -} - - - - - -cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) -{ - switch(a_ItemType) - { - default: return new cItemHandler(a_ItemType); - - // Single item per handler, alphabetically sorted: - case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); - case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); - case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); - case E_ITEM_BED: return new cItemBedHandler(a_ItemType); - case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); - case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); - case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); - case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); - case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); - case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); - case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); - case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); - case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); - case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); - case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); - - case E_ITEM_WOODEN_HOE: - case E_ITEM_STONE_HOE: - case E_ITEM_IRON_HOE: - case E_ITEM_GOLD_HOE: - case E_ITEM_DIAMOND_HOE: - { - return new cItemHoeHandler(a_ItemType); - } - - case E_ITEM_WOODEN_PICKAXE: - case E_ITEM_STONE_PICKAXE: - case E_ITEM_IRON_PICKAXE: - case E_ITEM_GOLD_PICKAXE: - case E_ITEM_DIAMOND_PICKAXE: - { - return new cItemPickaxeHandler(a_ItemType); - } - - case E_ITEM_WOODEN_SHOVEL: - case E_ITEM_STONE_SHOVEL: - case E_ITEM_IRON_SHOVEL: - case E_ITEM_GOLD_SHOVEL: - case E_ITEM_DIAMOND_SHOVEL: - { - return new cItemShovelHandler(a_ItemType); - } - - case E_ITEM_WOODEN_SWORD: - case E_ITEM_STONE_SWORD: - case E_ITEM_IRON_SWORD: - case E_ITEM_GOLD_SWORD: - case E_ITEM_DIAMOND_SWORD: - { - return new cItemSwordHandler(a_ItemType); - } - - case E_BLOCK_STONE_SLAB: - case E_BLOCK_WOODEN_SLAB: - { - return new cItemSlabHandler(a_ItemType); - } - - case E_BLOCK_LOG: - case E_BLOCK_PLANKS: - { - return new cItemWoodHandler(a_ItemType); - } - - case E_ITEM_BUCKET: - case E_ITEM_WATER_BUCKET: - case E_ITEM_LAVA_BUCKET: - { - return new cItemBucketHandler(a_ItemType); - } - - case E_ITEM_CARROT: - case E_ITEM_MELON_SEEDS: - case E_ITEM_POTATO: - case E_ITEM_PUMPKIN_SEEDS: - case E_ITEM_SEEDS: - { - return new cItemSeedsHandler(a_ItemType); - } - - case E_ITEM_IRON_DOOR: - case E_ITEM_WOODEN_DOOR: - { - return new cItemDoorHandler(a_ItemType); - } - - case E_ITEM_MINECART: - case E_ITEM_CHEST_MINECART: - case E_ITEM_FURNACE_MINECART: - { - return new cItemMinecartHandler(a_ItemType); - } - - // Food: - case E_ITEM_BREAD: - case E_ITEM_COOKIE: - case E_ITEM_MELON_SLICE: - case E_ITEM_RAW_CHICKEN: - case E_ITEM_COOKED_CHICKEN: - case E_ITEM_RAW_BEEF: - case E_ITEM_RAW_PORKCHOP: - case E_ITEM_STEAK: - case E_ITEM_COOKED_PORKCHOP: - case E_ITEM_RAW_FISH: - case E_ITEM_COOKED_FISH: - case E_ITEM_RED_APPLE: - case E_ITEM_GOLDEN_APPLE: - case E_ITEM_ROTTEN_FLESH: - case E_ITEM_SPIDER_EYE: - { - return new cItemFoodHandler(a_ItemType); - } - } -} - - - - - -void cItemHandler::Deinit() -{ - for(int i = 0; i < 2267; i++) - { - delete m_ItemHandler[i]; - } - memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); // Don't leave any dangling pointers around, just in case - m_HandlerInitialized = false; -} - - - - - -cItemHandler::cItemHandler(int a_ItemType) -{ - m_ItemType = a_ItemType; -} - - - - - -bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) -{ - return false; -} - - - - - -bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) -{ - return false; -} - - - - - -void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block); - - if (a_Player->GetGameMode() == gmSurvival) - { - if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) - { - Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); - } - } - - a_Player->UseEquippedItem(); -} - - - - - -void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_Item) -{ - -} - - - - - -char cItemHandler::GetMaxStackSize(void) -{ - if (m_ItemType < 256) - { - // All blocks can stack up to 64 - return 64; - } - - switch (m_ItemType) //sorted by id - { - case E_ITEM_ARROW: return 64; - case E_ITEM_BAKED_POTATO: return 64; - case E_ITEM_BLAZE_POWDER: return 64; - case E_ITEM_BLAZE_ROD: return 64; - case E_ITEM_BONE: return 64; - case E_ITEM_BOOK: return 64; - case E_ITEM_BOTTLE_O_ENCHANTING: return 64; - case E_ITEM_BOWL: return 64; - case E_ITEM_BREAD: return 64; - case E_ITEM_BREWING_STAND: return 64; - case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3 - case E_ITEM_CARROT: return 64; - case E_ITEM_CAULDRON: return 64; - case E_ITEM_CLAY: return 64; - case E_ITEM_CLAY_BRICK: return 64; - case E_ITEM_CLOCK: return 64; - case E_ITEM_COAL: return 64; - case E_ITEM_COMPARATOR: return 64; - case E_ITEM_COMPASS: return 64; - case E_ITEM_COOKED_CHICKEN: return 64; - case E_ITEM_COOKED_FISH: return 64; - case E_ITEM_COOKED_PORKCHOP: return 64; - case E_ITEM_COOKIE: return 64; - case E_ITEM_DIAMOND: return 64; - case E_ITEM_DYE: return 64; - case E_ITEM_EGG: return 16; - case E_ITEM_EMERALD: return 64; - case E_ITEM_ENDER_PEARL: return 16; - case E_ITEM_EYE_OF_ENDER: return 64; - case E_ITEM_FEATHER: return 64; - case E_ITEM_FERMENTED_SPIDER_EYE: return 64; - case E_ITEM_FIRE_CHARGE: return 64; - case E_ITEM_FIREWORK_ROCKET: return 64; - case E_ITEM_FIREWORK_STAR: return 64; - case E_ITEM_FLINT: return 64; - case E_ITEM_FLOWER_POT: return 64; - case E_ITEM_GHAST_TEAR: return 64; - case E_ITEM_GLASS_BOTTLE: return 64; - case E_ITEM_GLISTERING_MELON: return 64; - case E_ITEM_GLOWSTONE_DUST: return 64; - case E_ITEM_GOLD: return 64; - case E_ITEM_GOLDEN_APPLE: return 64; - case E_ITEM_GOLDEN_CARROT: return 64; - case E_ITEM_GOLD_NUGGET: return 64; - case E_ITEM_GUNPOWDER: return 64; - case E_ITEM_HEAD: return 64; - case E_ITEM_IRON: return 64; - case E_ITEM_LEATHER: return 64; - case E_ITEM_MAGMA_CREAM: return 64; - case E_ITEM_MAP: return 64; - case E_ITEM_MELON_SEEDS: return 64; - case E_ITEM_MELON_SLICE: return 64; - case E_ITEM_NETHER_BRICK: return 64; - case E_ITEM_NETHER_WART: return 64; - case E_ITEM_PAINTINGS: return 64; - case E_ITEM_PAPER: return 64; - case E_ITEM_POISONOUS_POTATO: return 64; - case E_ITEM_POTATO: return 64; - case E_ITEM_PUMPKIN_PIE: return 64; - case E_ITEM_PUMPKIN_SEEDS: return 64; - case E_ITEM_RAW_BEEF: return 64; - case E_ITEM_RAW_CHICKEN: return 64; - case E_ITEM_RAW_FISH: return 64; - case E_ITEM_RAW_PORKCHOP: return 64; - case E_ITEM_RED_APPLE: return 64; - case E_ITEM_REDSTONE_DUST: return 64; - case E_ITEM_REDSTONE_REPEATER: return 64; - case E_ITEM_ROTTEN_FLESH: return 64; - case E_ITEM_SEEDS: return 64; - case E_ITEM_SIGN: return 16; - case E_ITEM_SLIMEBALL: return 64; - case E_ITEM_SNOWBALL: return 16; - case E_ITEM_SPAWN_EGG: return 64; - case E_ITEM_SPIDER_EYE: return 64; - case E_ITEM_STEAK: return 64; - case E_ITEM_STICK: return 64; - case E_ITEM_STRING: return 64; - case E_ITEM_SUGAR: return 64; - case E_ITEM_SUGAR_CANE: return 64; - case E_ITEM_WHEAT: return 64; - } - // By default items don't stack: - return 1; -} - - - - - -bool cItemHandler::IsTool() -{ - // TODO: Rewrite this to list all tools specifically - return - (m_ItemType >= 256 && m_ItemType <= 259) - || (m_ItemType == 261) - || (m_ItemType >= 267 && m_ItemType <= 279) - || (m_ItemType >= 283 && m_ItemType <= 286) - || (m_ItemType >= 290 && m_ItemType <= 294) - || (m_ItemType >= 256 && m_ItemType <= 259) - || (m_ItemType == 325) - || (m_ItemType == 346); -} - - - - - -bool cItemHandler::IsFood(void) -{ - switch (m_ItemType) - { - case E_ITEM_RED_APPLE: - case E_ITEM_GOLDEN_APPLE: - case E_ITEM_MUSHROOM_SOUP: - case E_ITEM_BREAD: - case E_ITEM_RAW_PORKCHOP: - case E_ITEM_COOKED_PORKCHOP: - case E_ITEM_MILK: - case E_ITEM_RAW_FISH: - case E_ITEM_COOKED_FISH: - case E_ITEM_COOKIE: - case E_ITEM_MELON_SLICE: - case E_ITEM_RAW_BEEF: - case E_ITEM_STEAK: - case E_ITEM_RAW_CHICKEN: - case E_ITEM_COOKED_CHICKEN: - case E_ITEM_ROTTEN_FLESH: - case E_ITEM_SPIDER_EYE: - case E_ITEM_CARROT: - case E_ITEM_POTATO: - case E_ITEM_BAKED_POTATO: - case E_ITEM_POISONOUS_POTATO: - { - return true; - } - } // switch (m_ItemType) - return false; -} - - - - - -bool cItemHandler::IsPlaceable(void) -{ - // We can place any block that has a corresponding E_BLOCK_TYPE: - return (m_ItemType >= 1) && (m_ItemType <= E_BLOCK_MAX_TYPE_ID); -} - - - - - -bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType) -{ - return false; -} - - - - - -bool cItemHandler::GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta -) -{ - ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers - - if (m_ItemType > 256) - { - LOGERROR("%s: Item %d has no valid block!", __FUNCTION__, m_ItemType); - return false; - } - - cBlockHandler * BlockH = BlockHandler(m_ItemType); - return BlockH->GetPlacementBlockTypeMeta( - a_World, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); -} - - - - - -bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item) -{ - FoodInfo Info = GetFoodInfo(); - - if ((Info.FoodLevel > 0) || (Info.Saturation > 0.f)) - { - bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation); - - // If consumed and there's chance of foodpoisoning, do it: - if (Success && (Info.PoisonChance > 0)) - { - cFastRandom r1; - if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0) - { - a_Player->FoodPoison(300); - } - } - - return Success; - } - - return false; -} - - - - - -cItemHandler::FoodInfo cItemHandler::GetFoodInfo() -{ - return FoodInfo(0, 0.f); -} - - - - + +#include "Globals.h" +#include "ItemHandler.h" +#include "../Item.h" +#include "../World.h" +#include "../Player.h" +#include "../FastRandom.h" + +// Handlers: +#include "ItemBed.h" +#include "ItemBrewingStand.h" +#include "ItemBucket.h" +#include "ItemCauldron.h" +#include "ItemCloth.h" +#include "ItemDoor.h" +#include "ItemDye.h" +#include "ItemFlowerPot.h" +#include "ItemFood.h" +#include "ItemHoe.h" +#include "ItemLeaves.h" +#include "ItemLighter.h" +#include "ItemMinecart.h" +#include "ItemPickaxe.h" +#include "ItemRedstoneDust.h" +#include "ItemRedstoneRepeater.h" +#include "ItemSapling.h" +#include "ItemSeeds.h" +#include "ItemShears.h" +#include "ItemShovel.h" +#include "ItemSign.h" +#include "ItemSlab.h" +#include "ItemSpawnEgg.h" +#include "ItemSugarcane.h" +#include "ItemSword.h" +#include "ItemWood.h" + +#include "../Blocks/BlockHandler.h" + + + + + +bool cItemHandler::m_HandlerInitialized = false; +cItemHandler * cItemHandler::m_ItemHandler[2268]; + + + + + +cItemHandler *cItemHandler::GetItemHandler(int a_ItemType) +{ + if(a_ItemType < 0) a_ItemType = 0; + + if(!m_HandlerInitialized) + { //We have to initialize + memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); + m_HandlerInitialized = true; + } + if(m_ItemHandler[a_ItemType]) + return m_ItemHandler[a_ItemType]; + m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType); + return m_ItemHandler[a_ItemType]; +} + + + + + +cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) +{ + switch(a_ItemType) + { + default: return new cItemHandler(a_ItemType); + + // Single item per handler, alphabetically sorted: + case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); + case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); + case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); + case E_ITEM_BED: return new cItemBedHandler(a_ItemType); + case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); + case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); + case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); + case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); + case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); + case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); + case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); + case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); + case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); + case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); + case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); + + case E_ITEM_WOODEN_HOE: + case E_ITEM_STONE_HOE: + case E_ITEM_IRON_HOE: + case E_ITEM_GOLD_HOE: + case E_ITEM_DIAMOND_HOE: + { + return new cItemHoeHandler(a_ItemType); + } + + case E_ITEM_WOODEN_PICKAXE: + case E_ITEM_STONE_PICKAXE: + case E_ITEM_IRON_PICKAXE: + case E_ITEM_GOLD_PICKAXE: + case E_ITEM_DIAMOND_PICKAXE: + { + return new cItemPickaxeHandler(a_ItemType); + } + + case E_ITEM_WOODEN_SHOVEL: + case E_ITEM_STONE_SHOVEL: + case E_ITEM_IRON_SHOVEL: + case E_ITEM_GOLD_SHOVEL: + case E_ITEM_DIAMOND_SHOVEL: + { + return new cItemShovelHandler(a_ItemType); + } + + case E_ITEM_WOODEN_SWORD: + case E_ITEM_STONE_SWORD: + case E_ITEM_IRON_SWORD: + case E_ITEM_GOLD_SWORD: + case E_ITEM_DIAMOND_SWORD: + { + return new cItemSwordHandler(a_ItemType); + } + + case E_BLOCK_STONE_SLAB: + case E_BLOCK_WOODEN_SLAB: + { + return new cItemSlabHandler(a_ItemType); + } + + case E_BLOCK_LOG: + case E_BLOCK_PLANKS: + { + return new cItemWoodHandler(a_ItemType); + } + + case E_ITEM_BUCKET: + case E_ITEM_WATER_BUCKET: + case E_ITEM_LAVA_BUCKET: + { + return new cItemBucketHandler(a_ItemType); + } + + case E_ITEM_CARROT: + case E_ITEM_MELON_SEEDS: + case E_ITEM_POTATO: + case E_ITEM_PUMPKIN_SEEDS: + case E_ITEM_SEEDS: + { + return new cItemSeedsHandler(a_ItemType); + } + + case E_ITEM_IRON_DOOR: + case E_ITEM_WOODEN_DOOR: + { + return new cItemDoorHandler(a_ItemType); + } + + case E_ITEM_MINECART: + case E_ITEM_CHEST_MINECART: + case E_ITEM_FURNACE_MINECART: + { + return new cItemMinecartHandler(a_ItemType); + } + + // Food: + case E_ITEM_BREAD: + case E_ITEM_COOKIE: + case E_ITEM_MELON_SLICE: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_RAW_BEEF: + case E_ITEM_RAW_PORKCHOP: + case E_ITEM_STEAK: + case E_ITEM_COOKED_PORKCHOP: + case E_ITEM_RAW_FISH: + case E_ITEM_COOKED_FISH: + case E_ITEM_RED_APPLE: + case E_ITEM_GOLDEN_APPLE: + case E_ITEM_ROTTEN_FLESH: + case E_ITEM_SPIDER_EYE: + { + return new cItemFoodHandler(a_ItemType); + } + } +} + + + + + +void cItemHandler::Deinit() +{ + for(int i = 0; i < 2267; i++) + { + delete m_ItemHandler[i]; + } + memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); // Don't leave any dangling pointers around, just in case + m_HandlerInitialized = false; +} + + + + + +cItemHandler::cItemHandler(int a_ItemType) +{ + m_ItemType = a_ItemType; +} + + + + + +bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) +{ + return false; +} + + + + + +bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) +{ + return false; +} + + + + + +void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + cBlockHandler * Handler = cBlockHandler::GetBlockHandler(Block); + + if (a_Player->GetGameMode() == gmSurvival) + { + if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) + { + Handler->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + } + + a_Player->UseEquippedItem(); +} + + + + + +void cItemHandler::OnFoodEaten(cWorld * a_World, cPlayer * a_Player, cItem * a_Item) +{ + +} + + + + + +char cItemHandler::GetMaxStackSize(void) +{ + if (m_ItemType < 256) + { + // All blocks can stack up to 64 + return 64; + } + + switch (m_ItemType) //sorted by id + { + case E_ITEM_ARROW: return 64; + case E_ITEM_BAKED_POTATO: return 64; + case E_ITEM_BLAZE_POWDER: return 64; + case E_ITEM_BLAZE_ROD: return 64; + case E_ITEM_BONE: return 64; + case E_ITEM_BOOK: return 64; + case E_ITEM_BOTTLE_O_ENCHANTING: return 64; + case E_ITEM_BOWL: return 64; + case E_ITEM_BREAD: return 64; + case E_ITEM_BREWING_STAND: return 64; + case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3 + case E_ITEM_CARROT: return 64; + case E_ITEM_CAULDRON: return 64; + case E_ITEM_CLAY: return 64; + case E_ITEM_CLAY_BRICK: return 64; + case E_ITEM_CLOCK: return 64; + case E_ITEM_COAL: return 64; + case E_ITEM_COMPARATOR: return 64; + case E_ITEM_COMPASS: return 64; + case E_ITEM_COOKED_CHICKEN: return 64; + case E_ITEM_COOKED_FISH: return 64; + case E_ITEM_COOKED_PORKCHOP: return 64; + case E_ITEM_COOKIE: return 64; + case E_ITEM_DIAMOND: return 64; + case E_ITEM_DYE: return 64; + case E_ITEM_EGG: return 16; + case E_ITEM_EMERALD: return 64; + case E_ITEM_ENDER_PEARL: return 16; + case E_ITEM_EYE_OF_ENDER: return 64; + case E_ITEM_FEATHER: return 64; + case E_ITEM_FERMENTED_SPIDER_EYE: return 64; + case E_ITEM_FIRE_CHARGE: return 64; + case E_ITEM_FIREWORK_ROCKET: return 64; + case E_ITEM_FIREWORK_STAR: return 64; + case E_ITEM_FLINT: return 64; + case E_ITEM_FLOWER_POT: return 64; + case E_ITEM_GHAST_TEAR: return 64; + case E_ITEM_GLASS_BOTTLE: return 64; + case E_ITEM_GLISTERING_MELON: return 64; + case E_ITEM_GLOWSTONE_DUST: return 64; + case E_ITEM_GOLD: return 64; + case E_ITEM_GOLDEN_APPLE: return 64; + case E_ITEM_GOLDEN_CARROT: return 64; + case E_ITEM_GOLD_NUGGET: return 64; + case E_ITEM_GUNPOWDER: return 64; + case E_ITEM_HEAD: return 64; + case E_ITEM_IRON: return 64; + case E_ITEM_LEATHER: return 64; + case E_ITEM_MAGMA_CREAM: return 64; + case E_ITEM_MAP: return 64; + case E_ITEM_MELON_SEEDS: return 64; + case E_ITEM_MELON_SLICE: return 64; + case E_ITEM_NETHER_BRICK: return 64; + case E_ITEM_NETHER_WART: return 64; + case E_ITEM_PAINTINGS: return 64; + case E_ITEM_PAPER: return 64; + case E_ITEM_POISONOUS_POTATO: return 64; + case E_ITEM_POTATO: return 64; + case E_ITEM_PUMPKIN_PIE: return 64; + case E_ITEM_PUMPKIN_SEEDS: return 64; + case E_ITEM_RAW_BEEF: return 64; + case E_ITEM_RAW_CHICKEN: return 64; + case E_ITEM_RAW_FISH: return 64; + case E_ITEM_RAW_PORKCHOP: return 64; + case E_ITEM_RED_APPLE: return 64; + case E_ITEM_REDSTONE_DUST: return 64; + case E_ITEM_REDSTONE_REPEATER: return 64; + case E_ITEM_ROTTEN_FLESH: return 64; + case E_ITEM_SEEDS: return 64; + case E_ITEM_SIGN: return 16; + case E_ITEM_SLIMEBALL: return 64; + case E_ITEM_SNOWBALL: return 16; + case E_ITEM_SPAWN_EGG: return 64; + case E_ITEM_SPIDER_EYE: return 64; + case E_ITEM_STEAK: return 64; + case E_ITEM_STICK: return 64; + case E_ITEM_STRING: return 64; + case E_ITEM_SUGAR: return 64; + case E_ITEM_SUGAR_CANE: return 64; + case E_ITEM_WHEAT: return 64; + } + // By default items don't stack: + return 1; +} + + + + + +bool cItemHandler::IsTool() +{ + // TODO: Rewrite this to list all tools specifically + return + (m_ItemType >= 256 && m_ItemType <= 259) + || (m_ItemType == 261) + || (m_ItemType >= 267 && m_ItemType <= 279) + || (m_ItemType >= 283 && m_ItemType <= 286) + || (m_ItemType >= 290 && m_ItemType <= 294) + || (m_ItemType >= 256 && m_ItemType <= 259) + || (m_ItemType == 325) + || (m_ItemType == 346); +} + + + + + +bool cItemHandler::IsFood(void) +{ + switch (m_ItemType) + { + case E_ITEM_RED_APPLE: + case E_ITEM_GOLDEN_APPLE: + case E_ITEM_MUSHROOM_SOUP: + case E_ITEM_BREAD: + case E_ITEM_RAW_PORKCHOP: + case E_ITEM_COOKED_PORKCHOP: + case E_ITEM_MILK: + case E_ITEM_RAW_FISH: + case E_ITEM_COOKED_FISH: + case E_ITEM_COOKIE: + case E_ITEM_MELON_SLICE: + case E_ITEM_RAW_BEEF: + case E_ITEM_STEAK: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_ROTTEN_FLESH: + case E_ITEM_SPIDER_EYE: + case E_ITEM_CARROT: + case E_ITEM_POTATO: + case E_ITEM_BAKED_POTATO: + case E_ITEM_POISONOUS_POTATO: + { + return true; + } + } // switch (m_ItemType) + return false; +} + + + + + +bool cItemHandler::IsPlaceable(void) +{ + // We can place any block that has a corresponding E_BLOCK_TYPE: + return (m_ItemType >= 1) && (m_ItemType <= E_BLOCK_MAX_TYPE_ID); +} + + + + + +bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType) +{ + return false; +} + + + + + +bool cItemHandler::GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta +) +{ + ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers + + if (m_ItemType > 256) + { + LOGERROR("%s: Item %d has no valid block!", __FUNCTION__, m_ItemType); + return false; + } + + cBlockHandler * BlockH = BlockHandler(m_ItemType); + return BlockH->GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); +} + + + + + +bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item) +{ + FoodInfo Info = GetFoodInfo(); + + if ((Info.FoodLevel > 0) || (Info.Saturation > 0.f)) + { + bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation); + + // If consumed and there's chance of foodpoisoning, do it: + if (Success && (Info.PoisonChance > 0)) + { + cFastRandom r1; + if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0) + { + a_Player->FoodPoison(300); + } + } + + return Success; + } + + return false; +} + + + + + +cItemHandler::FoodInfo cItemHandler::GetFoodInfo() +{ + return FoodInfo(0, 0.f); +} + + + + diff --git a/source/Items/ItemHandler.h b/source/Items/ItemHandler.h index f7985327a..44d43e8f7 100644 --- a/source/Items/ItemHandler.h +++ b/source/Items/ItemHandler.h @@ -1,96 +1,96 @@ - -#pragma once - -#include "../Defines.h" -#include "../Item.h" - - - - - -// fwd: -class cWorld; -class cPlayer; - - - - - -class cItemHandler -{ -public: - cItemHandler(int a_ItemType); - - /// Called when the player tries to use the item. Return false to make the item unusable. DEFAULT: False - virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); //eg for fishing or hoes - - /// Called while the player diggs a block using this item - virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace); - - /// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block - virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z); - - /// Called after the player has eaten this item. - virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item); - - /// Returns the maximum stack size for a given item - virtual char GetMaxStackSize(void); - - struct FoodInfo - { - int FoodLevel; - double Saturation; - int PoisonChance; // 0 - 100, in percent. 0 = no chance of poisoning, 100 = sure poisoning - - FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) : - FoodLevel(a_FoodLevel), - Saturation(a_Saturation), - PoisonChance(a_PoisonChance) - { - } - } ; - - /// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) - virtual FoodInfo GetFoodInfo(); - - /// Lets the player eat a selected item. Returns true if the player ate the item - virtual bool EatItem(cPlayer *a_Player, cItem *a_Item); - - /// Indicates if this item is a tool - virtual bool IsTool(void); - - /// Indicates if this item is food - virtual bool IsFood(void); - - /// Blocks simply get placed - virtual bool IsPlaceable(void); - - /** Called before a block is placed into a world. - The handler should return true to allow placement, false to refuse. - Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. - */ - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ); - - /// Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can´t) DEFAULT: False - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType); - - static cItemHandler * GetItemHandler(int a_ItemType); - static cItemHandler * GetItemHandler(const cItem & a_Item) { return GetItemHandler(a_Item.m_ItemType); } - - static void Deinit(); - -protected: - int m_ItemType; - static cItemHandler *CreateItemHandler(int m_ItemType); - - static cItemHandler *m_ItemHandler[2268]; - static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized -}; - -//Short function -inline cItemHandler *ItemHandler(int a_ItemType) { return cItemHandler::GetItemHandler(a_ItemType); } + +#pragma once + +#include "../Defines.h" +#include "../Item.h" + + + + + +// fwd: +class cWorld; +class cPlayer; + + + + + +class cItemHandler +{ +public: + cItemHandler(int a_ItemType); + + /// Called when the player tries to use the item. Return false to make the item unusable. DEFAULT: False + virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); //eg for fishing or hoes + + /// Called while the player diggs a block using this item + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace); + + /// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block + virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z); + + /// Called after the player has eaten this item. + virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item); + + /// Returns the maximum stack size for a given item + virtual char GetMaxStackSize(void); + + struct FoodInfo + { + int FoodLevel; + double Saturation; + int PoisonChance; // 0 - 100, in percent. 0 = no chance of poisoning, 100 = sure poisoning + + FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) : + FoodLevel(a_FoodLevel), + Saturation(a_Saturation), + PoisonChance(a_PoisonChance) + { + } + } ; + + /// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) + virtual FoodInfo GetFoodInfo(); + + /// Lets the player eat a selected item. Returns true if the player ate the item + virtual bool EatItem(cPlayer *a_Player, cItem *a_Item); + + /// Indicates if this item is a tool + virtual bool IsTool(void); + + /// Indicates if this item is food + virtual bool IsFood(void); + + /// Blocks simply get placed + virtual bool IsPlaceable(void); + + /** Called before a block is placed into a world. + The handler should return true to allow placement, false to refuse. + Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. + */ + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ); + + /// Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can´t) DEFAULT: False + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType); + + static cItemHandler * GetItemHandler(int a_ItemType); + static cItemHandler * GetItemHandler(const cItem & a_Item) { return GetItemHandler(a_Item.m_ItemType); } + + static void Deinit(); + +protected: + int m_ItemType; + static cItemHandler *CreateItemHandler(int m_ItemType); + + static cItemHandler *m_ItemHandler[2268]; + static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized +}; + +//Short function +inline cItemHandler *ItemHandler(int a_ItemType) { return cItemHandler::GetItemHandler(a_ItemType); } diff --git a/source/Items/ItemHoe.h b/source/Items/ItemHoe.h index 58f989341..577463f21 100644 --- a/source/Items/ItemHoe.h +++ b/source/Items/ItemHoe.h @@ -1,31 +1,31 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Player.h" - -class cItemHoeHandler : public cItemHandler -{ -public: - cItemHoeHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - - if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS)) - { - a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0); - - a_Player->UseEquippedItem(); - return true; - - } - return false; - } + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" + +class cItemHoeHandler : public cItemHandler +{ +public: + cItemHoeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + + if ((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS)) + { + a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0); + + a_Player->UseEquippedItem(); + return true; + + } + return false; + } }; \ No newline at end of file diff --git a/source/Items/ItemLeaves.h b/source/Items/ItemLeaves.h index 14d63a630..60222eaa9 100644 --- a/source/Items/ItemLeaves.h +++ b/source/Items/ItemLeaves.h @@ -1,41 +1,41 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemLeavesHandler : - public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemLeavesHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - bool res = super::GetPlacementBlockTypeMeta( - a_World, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); - a_BlockMeta = a_BlockMeta | 0x4; //0x4 bit set means this is a player-placed leaves block, not to be decayed - return res; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemLeavesHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemLeavesHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + bool res = super::GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + a_BlockMeta = a_BlockMeta | 0x4; //0x4 bit set means this is a player-placed leaves block, not to be decayed + return res; + } +} ; + + + + diff --git a/source/Items/ItemLighter.h b/source/Items/ItemLighter.h index dcca08d47..cf7822832 100644 --- a/source/Items/ItemLighter.h +++ b/source/Items/ItemLighter.h @@ -1,56 +1,56 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Player.h" -#include "../TNTEntity.h" - - - - - -class cItemLighterHandler : - public cItemHandler -{ -public: - cItemLighterHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override - { - if (a_BlockFace < 0) - { - return false; - } - - a_Player->UseEquippedItem(); - - switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) - { - case E_BLOCK_TNT: - { - // Activate the TNT: - a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); - a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom - a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0); - break; - } - default: - { - // Light a fire next to the block: - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0); - break; - } - } - - return false; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" +#include "../TNTEntity.h" + + + + + +class cItemLighterHandler : + public cItemHandler +{ +public: + cItemLighterHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + if (a_BlockFace < 0) + { + return false; + } + + a_Player->UseEquippedItem(); + + switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) + { + case E_BLOCK_TNT: + { + // Activate the TNT: + a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom + a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0); + break; + } + default: + { + // Light a fire next to the block: + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0); + break; + } + } + + return false; + } +} ; + + + + diff --git a/source/Items/ItemMinecart.h b/source/Items/ItemMinecart.h index ca8cc16aa..1470104a3 100644 --- a/source/Items/ItemMinecart.h +++ b/source/Items/ItemMinecart.h @@ -1,80 +1,80 @@ - -// ItemMinecart.h - -// Declares the various minecart ItemHandlers - - - - - -#pragma once - -// Not needed, we're being included only from ItemHandler.cpp which already has this file: #include "ItemHandler.h" -#include "../Minecart.h" - - - - - -class cItemMinecartHandler : - public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemMinecartHandler(int a_ItemType) : - super(a_ItemType) - { - } - - - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - if (a_Dir < 0) - { - return false; - } - - // Check that there's rail in there: - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - switch (Block) - { - case E_BLOCK_MINECART_TRACKS: - case E_BLOCK_POWERED_RAIL: - case E_BLOCK_DETECTOR_RAIL: - { - // These are allowed - break; - } - default: - { - LOGD("Used minecart on an unsuitable block %d (%s)", Block, ItemTypeToString(Block).c_str()); - return false; - } - } - - double x = (double)a_BlockX + 0.5; - double y = (double)a_BlockY + 0.5; - double z = (double)a_BlockZ + 0.5; - cMinecart * Minecart = NULL; - switch (m_ItemType) - { - case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break; - case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break; - case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace(x, y, z); break; - default: - { - ASSERT(!"Unhandled minecart item"); - return false; - } - } // switch (m_ItemType) - Minecart->Initialize(a_World); - return true; - } - -} ; - - - - + +// ItemMinecart.h + +// Declares the various minecart ItemHandlers + + + + + +#pragma once + +// Not needed, we're being included only from ItemHandler.cpp which already has this file: #include "ItemHandler.h" +#include "../Minecart.h" + + + + + +class cItemMinecartHandler : + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemMinecartHandler(int a_ItemType) : + super(a_ItemType) + { + } + + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + if (a_Dir < 0) + { + return false; + } + + // Check that there's rail in there: + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + switch (Block) + { + case E_BLOCK_MINECART_TRACKS: + case E_BLOCK_POWERED_RAIL: + case E_BLOCK_DETECTOR_RAIL: + { + // These are allowed + break; + } + default: + { + LOGD("Used minecart on an unsuitable block %d (%s)", Block, ItemTypeToString(Block).c_str()); + return false; + } + } + + double x = (double)a_BlockX + 0.5; + double y = (double)a_BlockY + 0.5; + double z = (double)a_BlockZ + 0.5; + cMinecart * Minecart = NULL; + switch (m_ItemType) + { + case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break; + case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break; + case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace(x, y, z); break; + default: + { + ASSERT(!"Unhandled minecart item"); + return false; + } + } // switch (m_ItemType) + Minecart->Initialize(a_World); + return true; + } + +} ; + + + + diff --git a/source/Items/ItemPickaxe.h b/source/Items/ItemPickaxe.h index 26d0d5ade..1a2c205c0 100644 --- a/source/Items/ItemPickaxe.h +++ b/source/Items/ItemPickaxe.h @@ -1,76 +1,76 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Player.h" - -class cItemPickaxeHandler : public cItemHandler -{ -public: - cItemPickaxeHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - char PickaxeLevel() - { - switch(m_ItemType) - { - case E_ITEM_WOODEN_PICKAXE: - case E_ITEM_GOLD_PICKAXE: - return 1; - case E_ITEM_STONE_PICKAXE: - return 2; - case E_ITEM_IRON_PICKAXE: - return 3; - case E_ITEM_DIAMOND_PICKAXE: - return 4; - default: - return 0; - } - } - - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override - { - switch(a_BlockType) - { - case E_BLOCK_OBSIDIAN: - return PickaxeLevel() >= 4; - case E_BLOCK_DIAMOND_BLOCK: - case E_BLOCK_DIAMOND_ORE: - case E_BLOCK_GOLD_BLOCK: - case E_BLOCK_GOLD_ORE: - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - case E_BLOCK_EMERALD_ORE: - return PickaxeLevel() >= 3; - case E_BLOCK_IRON_BLOCK: - case E_BLOCK_IRON_ORE: - case E_BLOCK_LAPIS_ORE: - case E_BLOCK_LAPIS_BLOCK: - return PickaxeLevel() >= 2; - case E_BLOCK_COAL_ORE: - case E_BLOCK_STONE: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_END_STONE: - case E_BLOCK_MOSSY_COBBLESTONE: - case E_BLOCK_SANDSTONE_STAIRS: - case E_BLOCK_SANDSTONE: - case E_BLOCK_STONE_BRICKS: - case E_BLOCK_NETHER_BRICK: - case E_BLOCK_NETHERRACK: - case E_BLOCK_STONE_SLAB: - case E_BLOCK_DOUBLE_STONE_SLAB: - case E_BLOCK_STONE_PRESSURE_PLATE: - case E_BLOCK_BRICK: - case E_BLOCK_COBBLESTONE_STAIRS: - case E_BLOCK_STONE_BRICK_STAIRS: - case E_BLOCK_NETHER_BRICK_STAIRS: - case E_BLOCK_CAULDRON: - return PickaxeLevel() >= 1; - } - return false; - } + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" + +class cItemPickaxeHandler : public cItemHandler +{ +public: + cItemPickaxeHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + char PickaxeLevel() + { + switch(m_ItemType) + { + case E_ITEM_WOODEN_PICKAXE: + case E_ITEM_GOLD_PICKAXE: + return 1; + case E_ITEM_STONE_PICKAXE: + return 2; + case E_ITEM_IRON_PICKAXE: + return 3; + case E_ITEM_DIAMOND_PICKAXE: + return 4; + default: + return 0; + } + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + switch(a_BlockType) + { + case E_BLOCK_OBSIDIAN: + return PickaxeLevel() >= 4; + case E_BLOCK_DIAMOND_BLOCK: + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_GOLD_BLOCK: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + case E_BLOCK_EMERALD_ORE: + return PickaxeLevel() >= 3; + case E_BLOCK_IRON_BLOCK: + case E_BLOCK_IRON_ORE: + case E_BLOCK_LAPIS_ORE: + case E_BLOCK_LAPIS_BLOCK: + return PickaxeLevel() >= 2; + case E_BLOCK_COAL_ORE: + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_END_STONE: + case E_BLOCK_MOSSY_COBBLESTONE: + case E_BLOCK_SANDSTONE_STAIRS: + case E_BLOCK_SANDSTONE: + case E_BLOCK_STONE_BRICKS: + case E_BLOCK_NETHER_BRICK: + case E_BLOCK_NETHERRACK: + case E_BLOCK_STONE_SLAB: + case E_BLOCK_DOUBLE_STONE_SLAB: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_BRICK: + case E_BLOCK_COBBLESTONE_STAIRS: + case E_BLOCK_STONE_BRICK_STAIRS: + case E_BLOCK_NETHER_BRICK_STAIRS: + case E_BLOCK_CAULDRON: + return PickaxeLevel() >= 1; + } + return false; + } }; \ No newline at end of file diff --git a/source/Items/ItemRedstoneDust.h b/source/Items/ItemRedstoneDust.h index 4179893d7..b7860b187 100644 --- a/source/Items/ItemRedstoneDust.h +++ b/source/Items/ItemRedstoneDust.h @@ -1,38 +1,38 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemRedstoneDustHandler : public cItemHandler -{ -public: - cItemRedstoneDustHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_REDSTONE_WIRE; - a_BlockMeta = 0; - return true; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemRedstoneDustHandler : public cItemHandler +{ +public: + cItemRedstoneDustHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_REDSTONE_WIRE; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/source/Items/ItemRedstoneRepeater.h b/source/Items/ItemRedstoneRepeater.h index ce3008d01..459070579 100644 --- a/source/Items/ItemRedstoneRepeater.h +++ b/source/Items/ItemRedstoneRepeater.h @@ -1,40 +1,40 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../Simulator/RedstoneSimulator.h" - - - - - -class cItemRedstoneRepeaterHandler : - public cItemHandler -{ -public: - cItemRedstoneRepeaterHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - } - - virtual bool IsPlaceable() override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF; - a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); - return true; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../Simulator/RedstoneSimulator.h" + + + + + +class cItemRedstoneRepeaterHandler : + public cItemHandler +{ +public: + cItemRedstoneRepeaterHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable() override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF; + a_BlockMeta = cRedstoneSimulator::RepeaterRotationToMetaData(a_Player->GetRotation()); + return true; + } +} ; + + + + diff --git a/source/Items/ItemSapling.h b/source/Items/ItemSapling.h index 313968ea5..dc0810a45 100644 --- a/source/Items/ItemSapling.h +++ b/source/Items/ItemSapling.h @@ -1,42 +1,42 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemSaplingHandler : public cItemHandler -{ - typedef cItemHandler super; - -public: - cItemSaplingHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - bool res = super::GetPlacementBlockTypeMeta( - a_World, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); - // Only the lowest 3 bits are important - a_BlockMeta = a_BlockMeta & 0x7; - return res; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemSaplingHandler : public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemSaplingHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + bool res = super::GetPlacementBlockTypeMeta( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, + a_CursorX, a_CursorY, a_CursorZ, + a_BlockType, a_BlockMeta + ); + // Only the lowest 3 bits are important + a_BlockMeta = a_BlockMeta & 0x7; + return res; + } +} ; + + + + diff --git a/source/Items/ItemSeeds.h b/source/Items/ItemSeeds.h index 7a5f77b80..8ca86663f 100644 --- a/source/Items/ItemSeeds.h +++ b/source/Items/ItemSeeds.h @@ -1,65 +1,65 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" - - - - - -class cItemSeedsHandler : - public cItemHandler -{ -public: - cItemSeedsHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - if (a_BlockFace != BLOCK_FACE_TOP) - { - // Only allow planting seeds from the top side of the block - return false; - } - - // Only allow placement on farmland - int X = a_BlockX; - int Y = a_BlockY; - int Z = a_BlockZ; - AddFaceDirection(X, Y, Z, a_BlockFace, true); - if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND) - { - return false; - } - - a_BlockMeta = 0; - switch (m_ItemType) - { - case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true; - case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true; - case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true; - case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true; - case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true; - default: a_BlockType = E_BLOCK_AIR; return true; - } - return false; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" + + + + + +class cItemSeedsHandler : + public cItemHandler +{ +public: + cItemSeedsHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace != BLOCK_FACE_TOP) + { + // Only allow planting seeds from the top side of the block + return false; + } + + // Only allow placement on farmland + int X = a_BlockX; + int Y = a_BlockY; + int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, a_BlockFace, true); + if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND) + { + return false; + } + + a_BlockMeta = 0; + switch (m_ItemType) + { + case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true; + case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true; + case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true; + case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true; + case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true; + default: a_BlockType = E_BLOCK_AIR; return true; + } + return false; + } +} ; + + + + diff --git a/source/Items/ItemShears.h b/source/Items/ItemShears.h index 5c1c248f4..a904dd1ae 100644 --- a/source/Items/ItemShears.h +++ b/source/Items/ItemShears.h @@ -1,63 +1,63 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Player.h" - - - - - -class cItemShearsHandler : - public cItemHandler -{ -public: - cItemShearsHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsTool(void) override - { - return true; - } - - - virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (Block == E_BLOCK_LEAVES) - { - cItems Drops; - Drops.push_back(cItem(E_BLOCK_LEAVES, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03)); - a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); - - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_Player->UseEquippedItem(); - return true; - } - // TODO: cobweb, vines - return false; - } - - - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override - { - switch (a_BlockType) - { - case E_BLOCK_COBWEB: - case E_BLOCK_VINES: - case E_BLOCK_LEAVES: - { - return true; - } - } // switch (a_BlockType) - return false; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" + + + + + +class cItemShearsHandler : + public cItemHandler +{ +public: + cItemShearsHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsTool(void) override + { + return true; + } + + + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (Block == E_BLOCK_LEAVES) + { + cItems Drops; + Drops.push_back(cItem(E_BLOCK_LEAVES, 1, a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x03)); + a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ); + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_Player->UseEquippedItem(); + return true; + } + // TODO: cobweb, vines + return false; + } + + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + switch (a_BlockType) + { + case E_BLOCK_COBWEB: + case E_BLOCK_VINES: + case E_BLOCK_LEAVES: + { + return true; + } + } // switch (a_BlockType) + return false; + } +} ; + + + + diff --git a/source/Items/ItemShovel.h b/source/Items/ItemShovel.h index f26e393c8..a26e7d34e 100644 --- a/source/Items/ItemShovel.h +++ b/source/Items/ItemShovel.h @@ -1,41 +1,41 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Player.h" - -#include "../Blocks/BlockHandler.h" - - - - - -class cItemShovelHandler : public cItemHandler -{ -public: - cItemShovelHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (Block == E_BLOCK_SNOW) - { - BlockHandler(Block)->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); - - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_Player->UseEquippedItem(); - return true; - } - return false; - } - - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override - { - return (a_BlockType == E_BLOCK_SNOW); - } + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" + +#include "../Blocks/BlockHandler.h" + + + + + +class cItemShovelHandler : public cItemHandler +{ +public: + cItemShovelHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (Block == E_BLOCK_SNOW) + { + BlockHandler(Block)->DropBlock(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_Player->UseEquippedItem(); + return true; + } + return false; + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + return (a_BlockType == E_BLOCK_SNOW); + } }; \ No newline at end of file diff --git a/source/Items/ItemSign.h b/source/Items/ItemSign.h index 7a0924147..5ccd79e29 100644 --- a/source/Items/ItemSign.h +++ b/source/Items/ItemSign.h @@ -1,51 +1,51 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Blocks/BlockSign.h" - - - - - -class cItemSignHandler : - public cItemHandler -{ -public: - cItemSignHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - - virtual bool IsPlaceable(void) override - { - return true; - } - - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - if (a_BlockFace == BLOCK_FACE_TOP) - { - a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation()); - a_BlockType = E_BLOCK_SIGN_POST; - } - else - { - a_BlockMeta = cBlockSignHandler::DirectionToMetaData(a_BlockFace); - a_BlockType = E_BLOCK_WALLSIGN; - } - return true; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Blocks/BlockSign.h" + + + + + +class cItemSignHandler : + public cItemHandler +{ +public: + cItemSignHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + if (a_BlockFace == BLOCK_FACE_TOP) + { + a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation()); + a_BlockType = E_BLOCK_SIGN_POST; + } + else + { + a_BlockMeta = cBlockSignHandler::DirectionToMetaData(a_BlockFace); + a_BlockType = E_BLOCK_WALLSIGN; + } + return true; + } +} ; + + + + diff --git a/source/Items/ItemSlab.h b/source/Items/ItemSlab.h index 9b3aaed03..80de05eb5 100644 --- a/source/Items/ItemSlab.h +++ b/source/Items/ItemSlab.h @@ -1,52 +1,52 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" - - - - - -class cItemSlabHandler : public cItemHandler -{ -public: - cItemSlabHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override - { - BLOCKTYPE Block; - NIBBLETYPE Meta; - a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta); - - if ( - ((a_Dir == 0) || (a_Dir == 1)) // Only when clicking on top or on bottom of the block - && ((Block == E_BLOCK_WOODEN_SLAB) || (Block == E_BLOCK_STONE_SLAB)) // It is a slab - && (Block == a_Item.m_ItemType) // Same slab - && ((Meta & 0x7) == (a_Item.m_ItemDamage & 0x7))) // Same Texture - { - if (a_Player->GetGameMode() == eGameMode_Creative) - { - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement - return true; - } - else - { - if (a_Player->GetInventory().RemoveOneEquippedItem()) - { - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement - return true; - } - } - } - return false; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" + + + + + +class cItemSlabHandler : public cItemHandler +{ +public: + cItemSlabHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override + { + BLOCKTYPE Block; + NIBBLETYPE Meta; + a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta); + + if ( + ((a_Dir == 0) || (a_Dir == 1)) // Only when clicking on top or on bottom of the block + && ((Block == E_BLOCK_WOODEN_SLAB) || (Block == E_BLOCK_STONE_SLAB)) // It is a slab + && (Block == a_Item.m_ItemType) // Same slab + && ((Meta & 0x7) == (a_Item.m_ItemDamage & 0x7))) // Same Texture + { + if (a_Player->GetGameMode() == eGameMode_Creative) + { + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement + return true; + } + else + { + if (a_Player->GetInventory().RemoveOneEquippedItem()) + { + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement + return true; + } + } + } + return false; + } +} ; + + + + diff --git a/source/Items/ItemSpawnEgg.h b/source/Items/ItemSpawnEgg.h index 7f5b253ef..e563d7e15 100644 --- a/source/Items/ItemSpawnEgg.h +++ b/source/Items/ItemSpawnEgg.h @@ -1,52 +1,52 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Player.h" - - - - - -class cItemSpawnEggHandler : public cItemHandler -{ -public: - cItemSpawnEggHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - - } - - - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override - { - if (a_BlockFace < 0) - { - return false; - } - - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - if (a_BlockFace == BLOCK_FACE_BOTTOM) - { - a_BlockY--; - } - - if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, a_Item.m_ItemDamage) >= 0) - { - if (a_Player->GetGameMode() != 1) - { - // The mob was spawned, "use" the item: - a_Player->GetInventory().RemoveOneEquippedItem(); - } - return true; - } - - return false; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" + + + + + +class cItemSpawnEggHandler : public cItemHandler +{ +public: + cItemSpawnEggHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + + } + + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override + { + if (a_BlockFace < 0) + { + return false; + } + + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if (a_BlockFace == BLOCK_FACE_BOTTOM) + { + a_BlockY--; + } + + if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, a_Item.m_ItemDamage) >= 0) + { + if (a_Player->GetGameMode() != 1) + { + // The mob was spawned, "use" the item: + a_Player->GetInventory().RemoveOneEquippedItem(); + } + return true; + } + + return false; + } +} ; + + + + diff --git a/source/Items/ItemSugarcane.h b/source/Items/ItemSugarcane.h index 9a0875939..ce93aa3e5 100644 --- a/source/Items/ItemSugarcane.h +++ b/source/Items/ItemSugarcane.h @@ -1,39 +1,39 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemSugarcaneHandler : - public cItemHandler -{ -public: - cItemSugarcaneHandler(int a_ItemType) : - cItemHandler(a_ItemType) - { - } - - virtual bool IsPlaceable(void) override - { - return true; - } - - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ) override - { - a_BlockType = E_BLOCK_SUGARCANE; - a_BlockMeta = 0; - return true; - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemSugarcaneHandler : + public cItemHandler +{ +public: + cItemSugarcaneHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + virtual bool IsPlaceable(void) override + { + return true; + } + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_SUGARCANE; + a_BlockMeta = 0; + return true; + } +} ; + + + + diff --git a/source/Items/ItemSword.h b/source/Items/ItemSword.h index 37b9ef32d..15351b55c 100644 --- a/source/Items/ItemSword.h +++ b/source/Items/ItemSword.h @@ -1,21 +1,21 @@ - -#pragma once - -#include "ItemHandler.h" -#include "../World.h" -#include "../Player.h" - -class cItemSwordHandler : public cItemHandler -{ -public: - cItemSwordHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - - } - - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override - { - return (a_BlockType == E_BLOCK_COBWEB); - } + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" + +class cItemSwordHandler : public cItemHandler +{ +public: + cItemSwordHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override + { + return (a_BlockType == E_BLOCK_COBWEB); + } }; \ No newline at end of file diff --git a/source/Items/ItemWood.h b/source/Items/ItemWood.h index 5304ec316..476256c5d 100644 --- a/source/Items/ItemWood.h +++ b/source/Items/ItemWood.h @@ -1,22 +1,22 @@ - -#pragma once - -#include "ItemHandler.h" - - - - - -class cItemWoodHandler : - public cItemHandler -{ -public: - cItemWoodHandler(int a_ItemType) - : cItemHandler(a_ItemType) - { - } -} ; - - - - + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemWoodHandler : + public cItemHandler +{ +public: + cItemWoodHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } +} ; + + + + diff --git a/source/LinearInterpolation.cpp b/source/LinearInterpolation.cpp index d4536662e..d4975418b 100644 --- a/source/LinearInterpolation.cpp +++ b/source/LinearInterpolation.cpp @@ -1,251 +1,251 @@ - -// LinearInterpolation.cpp - -// Implements methods for linear interpolation over 1D, 2D and 3D arrays - -#include "Globals.h" -#include "LinearInterpolation.h" - - - - - -/* -// Perform an automatic test upon program start (use breakpoints to debug): - -extern void Debug3DNoise(float * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase); - -class Test -{ -public: - Test(void) - { - // DoTest1(); - DoTest2(); - } - - - void DoTest1(void) - { - float In[8] = {0, 1, 2, 3, 1, 2, 2, 2}; - float Out[3 * 3 * 3]; - LinearInterpolate1DArray(In, 4, Out, 9); - LinearInterpolate2DArray(In, 2, 2, Out, 3, 3); - LinearInterpolate3DArray(In, 2, 2, 2, Out, 3, 3, 3); - LOGD("Out[0]: %f", Out[0]); - } - - - void DoTest2(void) - { - float In[3 * 3 * 3]; - for (int i = 0; i < ARRAYCOUNT(In); i++) - { - In[i] = (float)(i % 5); - } - float Out[15 * 16 * 17]; - LinearInterpolate3DArray(In, 3, 3, 3, Out, 15, 16, 17); - Debug3DNoise(Out, 15, 16, 17, "LERP test"); - } -} gTest; -//*/ - - - - - -// Puts linearly interpolated values from one array into another array. 1D version -void LinearInterpolate1DArray( - float * a_Src, - int a_SrcSizeX, - float * a_Dst, - int a_DstSizeX -) -{ - a_Dst[0] = a_Src[0]; - int DstSizeXm1 = a_DstSizeX - 1; - int SrcSizeXm1 = a_SrcSizeX - 1; - float fDstSizeXm1 = (float)DstSizeXm1; - float fSrcSizeXm1 = (float)SrcSizeXm1; - for (int x = 1; x < DstSizeXm1; x++) - { - int SrcIdx = x * SrcSizeXm1 / DstSizeXm1; - float ValLo = a_Src[SrcIdx]; - float ValHi = a_Src[SrcIdx + 1]; - float Ratio = (float)x * fSrcSizeXm1 / fDstSizeXm1 - SrcIdx; - a_Dst[x] = ValLo + (ValHi - ValLo) * Ratio; - } - a_Dst[a_DstSizeX - 1] = a_Src[a_SrcSizeX - 1]; -} - - - - - -// Puts linearly interpolated values from one array into another array. 2D version -void LinearInterpolate2DArray( - float * a_Src, - int a_SrcSizeX, int a_SrcSizeY, - float * a_Dst, - int a_DstSizeX, int a_DstSizeY -) -{ - ASSERT(a_DstSizeX > 0); - ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); - ASSERT(a_DstSizeY > 0); - ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); - - // Calculate interpolation ratios and src indices along each axis: - float RatioX[MAX_INTERPOL_SIZEX]; - float RatioY[MAX_INTERPOL_SIZEY]; - int SrcIdxX[MAX_INTERPOL_SIZEX]; - int SrcIdxY[MAX_INTERPOL_SIZEY]; - for (int x = 1; x < a_DstSizeX; x++) - { - SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); - RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; - } - for (int y = 1; y < a_DstSizeY; y++) - { - SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); - RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; - } - - // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: - SrcIdxX[0] = 0; - RatioX[0] = 0; - SrcIdxY[0] = 0; - RatioY[0] = 0; - SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; - RatioX[a_DstSizeX - 1] = 1; - SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; - RatioY[a_DstSizeY - 1] = 1; - - // Output all the dst array values using the indices and ratios: - int idx = 0; - for (int y = 0; y < a_DstSizeY; y++) - { - int idxLoY = a_SrcSizeX * SrcIdxY[y]; - int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); - float ry = RatioY[y]; - for (int x = 0; x < a_DstSizeX; x++) - { - // The four src corners of the current "cell": - float LoXLoY = a_Src[SrcIdxX[x] + idxLoY]; - float HiXLoY = a_Src[SrcIdxX[x] + 1 + idxLoY]; - float LoXHiY = a_Src[SrcIdxX[x] + idxHiY]; - float HiXHiY = a_Src[SrcIdxX[x] + 1 + idxHiY]; - - // Linear interpolation along the X axis: - float InterpXLoY = LoXLoY + (HiXLoY - LoXLoY) * RatioX[x]; - float InterpXHiY = LoXHiY + (HiXHiY - LoXHiY) * RatioX[x]; - - // Linear interpolation along the Y axis: - a_Dst[idx] = InterpXLoY + (InterpXHiY - InterpXLoY) * ry; - idx += 1; - } - } -} - - - - - -/// Puts linearly interpolated values from one array into another array. 3D version -void LinearInterpolate3DArray( - float * a_Src, - int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, - float * a_Dst, - int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ -) -{ - ASSERT(a_DstSizeX > 0); - ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); - ASSERT(a_DstSizeY > 0); - ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); - ASSERT(a_DstSizeZ > 0); - ASSERT(a_DstSizeZ < MAX_INTERPOL_SIZEZ); - - // Calculate interpolation ratios and src indices along each axis: - float RatioX[MAX_INTERPOL_SIZEX]; - float RatioY[MAX_INTERPOL_SIZEY]; - float RatioZ[MAX_INTERPOL_SIZEZ]; - int SrcIdxX[MAX_INTERPOL_SIZEX]; - int SrcIdxY[MAX_INTERPOL_SIZEY]; - int SrcIdxZ[MAX_INTERPOL_SIZEZ]; - for (int x = 1; x < a_DstSizeX; x++) - { - SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); - RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; - } - for (int y = 1; y < a_DstSizeY; y++) - { - SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); - RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; - } - for (int z = 1; z < a_DstSizeZ; z++) - { - SrcIdxZ[z] = z * (a_SrcSizeZ - 1) / (a_DstSizeZ - 1); - RatioZ[z] = ((float)(z * (a_SrcSizeZ - 1)) / (a_DstSizeZ - 1)) - SrcIdxZ[z]; - } - - // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: - SrcIdxX[0] = 0; - RatioX[0] = 0; - SrcIdxY[0] = 0; - RatioY[0] = 0; - SrcIdxZ[0] = 0; - RatioZ[0] = 0; - SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; - RatioX[a_DstSizeX - 1] = 1; - SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; - RatioY[a_DstSizeY - 1] = 1; - SrcIdxZ[a_DstSizeZ - 1] = a_SrcSizeZ - 2; - RatioZ[a_DstSizeZ - 1] = 1; - - // Output all the dst array values using the indices and ratios: - int idx = 0; - for (int z = 0; z < a_DstSizeZ; z++) - { - int idxLoZ = a_SrcSizeX * a_SrcSizeY * SrcIdxZ[z]; - int idxHiZ = a_SrcSizeX * a_SrcSizeY * (SrcIdxZ[z] + 1); - float rz = RatioZ[z]; - for (int y = 0; y < a_DstSizeY; y++) - { - int idxLoY = a_SrcSizeX * SrcIdxY[y]; - int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); - float ry = RatioY[y]; - for (int x = 0; x < a_DstSizeX; x++) - { - // The eight src corners of the current "cell": - float LoXLoYLoZ = a_Src[SrcIdxX[x] + idxLoY + idxLoZ]; - float HiXLoYLoZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxLoZ]; - float LoXHiYLoZ = a_Src[SrcIdxX[x] + idxHiY + idxLoZ]; - float HiXHiYLoZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxLoZ]; - float LoXLoYHiZ = a_Src[SrcIdxX[x] + idxLoY + idxHiZ]; - float HiXLoYHiZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxHiZ]; - float LoXHiYHiZ = a_Src[SrcIdxX[x] + idxHiY + idxHiZ]; - float HiXHiYHiZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxHiZ]; - - // Linear interpolation along the Z axis: - float LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * rz; - float HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * rz; - float LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * rz; - float HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * rz; - - // Linear interpolation along the Y axis: - float LoXInYInZ = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * ry; - float HiXInYInZ = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * ry; - - // Linear interpolation along the X axis: - a_Dst[idx] = LoXInYInZ + (HiXInYInZ - LoXInYInZ) * RatioX[x]; - idx += 1; - } // for x - } // for y - } // for z -} - - - - - + +// LinearInterpolation.cpp + +// Implements methods for linear interpolation over 1D, 2D and 3D arrays + +#include "Globals.h" +#include "LinearInterpolation.h" + + + + + +/* +// Perform an automatic test upon program start (use breakpoints to debug): + +extern void Debug3DNoise(float * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase); + +class Test +{ +public: + Test(void) + { + // DoTest1(); + DoTest2(); + } + + + void DoTest1(void) + { + float In[8] = {0, 1, 2, 3, 1, 2, 2, 2}; + float Out[3 * 3 * 3]; + LinearInterpolate1DArray(In, 4, Out, 9); + LinearInterpolate2DArray(In, 2, 2, Out, 3, 3); + LinearInterpolate3DArray(In, 2, 2, 2, Out, 3, 3, 3); + LOGD("Out[0]: %f", Out[0]); + } + + + void DoTest2(void) + { + float In[3 * 3 * 3]; + for (int i = 0; i < ARRAYCOUNT(In); i++) + { + In[i] = (float)(i % 5); + } + float Out[15 * 16 * 17]; + LinearInterpolate3DArray(In, 3, 3, 3, Out, 15, 16, 17); + Debug3DNoise(Out, 15, 16, 17, "LERP test"); + } +} gTest; +//*/ + + + + + +// Puts linearly interpolated values from one array into another array. 1D version +void LinearInterpolate1DArray( + float * a_Src, + int a_SrcSizeX, + float * a_Dst, + int a_DstSizeX +) +{ + a_Dst[0] = a_Src[0]; + int DstSizeXm1 = a_DstSizeX - 1; + int SrcSizeXm1 = a_SrcSizeX - 1; + float fDstSizeXm1 = (float)DstSizeXm1; + float fSrcSizeXm1 = (float)SrcSizeXm1; + for (int x = 1; x < DstSizeXm1; x++) + { + int SrcIdx = x * SrcSizeXm1 / DstSizeXm1; + float ValLo = a_Src[SrcIdx]; + float ValHi = a_Src[SrcIdx + 1]; + float Ratio = (float)x * fSrcSizeXm1 / fDstSizeXm1 - SrcIdx; + a_Dst[x] = ValLo + (ValHi - ValLo) * Ratio; + } + a_Dst[a_DstSizeX - 1] = a_Src[a_SrcSizeX - 1]; +} + + + + + +// Puts linearly interpolated values from one array into another array. 2D version +void LinearInterpolate2DArray( + float * a_Src, + int a_SrcSizeX, int a_SrcSizeY, + float * a_Dst, + int a_DstSizeX, int a_DstSizeY +) +{ + ASSERT(a_DstSizeX > 0); + ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); + ASSERT(a_DstSizeY > 0); + ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); + + // Calculate interpolation ratios and src indices along each axis: + float RatioX[MAX_INTERPOL_SIZEX]; + float RatioY[MAX_INTERPOL_SIZEY]; + int SrcIdxX[MAX_INTERPOL_SIZEX]; + int SrcIdxY[MAX_INTERPOL_SIZEY]; + for (int x = 1; x < a_DstSizeX; x++) + { + SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); + RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; + } + for (int y = 1; y < a_DstSizeY; y++) + { + SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); + RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; + } + + // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: + SrcIdxX[0] = 0; + RatioX[0] = 0; + SrcIdxY[0] = 0; + RatioY[0] = 0; + SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; + RatioX[a_DstSizeX - 1] = 1; + SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; + RatioY[a_DstSizeY - 1] = 1; + + // Output all the dst array values using the indices and ratios: + int idx = 0; + for (int y = 0; y < a_DstSizeY; y++) + { + int idxLoY = a_SrcSizeX * SrcIdxY[y]; + int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); + float ry = RatioY[y]; + for (int x = 0; x < a_DstSizeX; x++) + { + // The four src corners of the current "cell": + float LoXLoY = a_Src[SrcIdxX[x] + idxLoY]; + float HiXLoY = a_Src[SrcIdxX[x] + 1 + idxLoY]; + float LoXHiY = a_Src[SrcIdxX[x] + idxHiY]; + float HiXHiY = a_Src[SrcIdxX[x] + 1 + idxHiY]; + + // Linear interpolation along the X axis: + float InterpXLoY = LoXLoY + (HiXLoY - LoXLoY) * RatioX[x]; + float InterpXHiY = LoXHiY + (HiXHiY - LoXHiY) * RatioX[x]; + + // Linear interpolation along the Y axis: + a_Dst[idx] = InterpXLoY + (InterpXHiY - InterpXLoY) * ry; + idx += 1; + } + } +} + + + + + +/// Puts linearly interpolated values from one array into another array. 3D version +void LinearInterpolate3DArray( + float * a_Src, + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, + float * a_Dst, + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ +) +{ + ASSERT(a_DstSizeX > 0); + ASSERT(a_DstSizeX < MAX_INTERPOL_SIZEX); + ASSERT(a_DstSizeY > 0); + ASSERT(a_DstSizeY < MAX_INTERPOL_SIZEY); + ASSERT(a_DstSizeZ > 0); + ASSERT(a_DstSizeZ < MAX_INTERPOL_SIZEZ); + + // Calculate interpolation ratios and src indices along each axis: + float RatioX[MAX_INTERPOL_SIZEX]; + float RatioY[MAX_INTERPOL_SIZEY]; + float RatioZ[MAX_INTERPOL_SIZEZ]; + int SrcIdxX[MAX_INTERPOL_SIZEX]; + int SrcIdxY[MAX_INTERPOL_SIZEY]; + int SrcIdxZ[MAX_INTERPOL_SIZEZ]; + for (int x = 1; x < a_DstSizeX; x++) + { + SrcIdxX[x] = x * (a_SrcSizeX - 1) / (a_DstSizeX - 1); + RatioX[x] = ((float)(x * (a_SrcSizeX - 1)) / (a_DstSizeX - 1)) - SrcIdxX[x]; + } + for (int y = 1; y < a_DstSizeY; y++) + { + SrcIdxY[y] = y * (a_SrcSizeY - 1) / (a_DstSizeY - 1); + RatioY[y] = ((float)(y * (a_SrcSizeY - 1)) / (a_DstSizeY - 1)) - SrcIdxY[y]; + } + for (int z = 1; z < a_DstSizeZ; z++) + { + SrcIdxZ[z] = z * (a_SrcSizeZ - 1) / (a_DstSizeZ - 1); + RatioZ[z] = ((float)(z * (a_SrcSizeZ - 1)) / (a_DstSizeZ - 1)) - SrcIdxZ[z]; + } + + // Special values at the ends. Notice especially the last indices being (size - 2) with ratio set to 1, to avoid index overflow: + SrcIdxX[0] = 0; + RatioX[0] = 0; + SrcIdxY[0] = 0; + RatioY[0] = 0; + SrcIdxZ[0] = 0; + RatioZ[0] = 0; + SrcIdxX[a_DstSizeX - 1] = a_SrcSizeX - 2; + RatioX[a_DstSizeX - 1] = 1; + SrcIdxY[a_DstSizeY - 1] = a_SrcSizeY - 2; + RatioY[a_DstSizeY - 1] = 1; + SrcIdxZ[a_DstSizeZ - 1] = a_SrcSizeZ - 2; + RatioZ[a_DstSizeZ - 1] = 1; + + // Output all the dst array values using the indices and ratios: + int idx = 0; + for (int z = 0; z < a_DstSizeZ; z++) + { + int idxLoZ = a_SrcSizeX * a_SrcSizeY * SrcIdxZ[z]; + int idxHiZ = a_SrcSizeX * a_SrcSizeY * (SrcIdxZ[z] + 1); + float rz = RatioZ[z]; + for (int y = 0; y < a_DstSizeY; y++) + { + int idxLoY = a_SrcSizeX * SrcIdxY[y]; + int idxHiY = a_SrcSizeX * (SrcIdxY[y] + 1); + float ry = RatioY[y]; + for (int x = 0; x < a_DstSizeX; x++) + { + // The eight src corners of the current "cell": + float LoXLoYLoZ = a_Src[SrcIdxX[x] + idxLoY + idxLoZ]; + float HiXLoYLoZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxLoZ]; + float LoXHiYLoZ = a_Src[SrcIdxX[x] + idxHiY + idxLoZ]; + float HiXHiYLoZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxLoZ]; + float LoXLoYHiZ = a_Src[SrcIdxX[x] + idxLoY + idxHiZ]; + float HiXLoYHiZ = a_Src[SrcIdxX[x] + 1 + idxLoY + idxHiZ]; + float LoXHiYHiZ = a_Src[SrcIdxX[x] + idxHiY + idxHiZ]; + float HiXHiYHiZ = a_Src[SrcIdxX[x] + 1 + idxHiY + idxHiZ]; + + // Linear interpolation along the Z axis: + float LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * rz; + float HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * rz; + float LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * rz; + float HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * rz; + + // Linear interpolation along the Y axis: + float LoXInYInZ = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * ry; + float HiXInYInZ = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * ry; + + // Linear interpolation along the X axis: + a_Dst[idx] = LoXInYInZ + (HiXInYInZ - LoXInYInZ) * RatioX[x]; + idx += 1; + } // for x + } // for y + } // for z +} + + + + + diff --git a/source/LinearInterpolation.h b/source/LinearInterpolation.h index ea9e93455..4b798d9bc 100644 --- a/source/LinearInterpolation.h +++ b/source/LinearInterpolation.h @@ -1,60 +1,60 @@ - -// LinearInterpolation.h - -// Declares methods for linear interpolation over 1D, 2D and 3D arrays - - - - - -#pragma once - - - - - -// 2D and 3D Interpolation is optimized by precalculating the ratios into static-sized arrays -// These arrays enforce a max size of the dest array, but the limits are settable here: -const int MAX_INTERPOL_SIZEX = 256; ///< Maximum X-size of the interpolated array -const int MAX_INTERPOL_SIZEY = 512; ///< Maximum Y-size of the interpolated array -const int MAX_INTERPOL_SIZEZ = 256; ///< Maximum Z-size of the interpolated array - - - - - -/// Puts linearly interpolated values from one array into another array. 1D version -void LinearInterpolate1DArray( - float * a_Src, ///< Src array - int a_SrcSizeX, ///< Count of the src array - float * a_Dst, ///< Src array - int a_DstSizeX ///< Count of the dst array -); - - - - - -/// Puts linearly interpolated values from one array into another array. 2D version -void LinearInterpolate2DArray( - float * a_Src, ///< Src array, [x + a_SrcSizeX * y] - int a_SrcSizeX, int a_SrcSizeY, ///< Count of the src array, in each direction - float * a_Dst, ///< Dst array, [x + a_DstSizeX * y] - int a_DstSizeX, int a_DstSizeY ///< Count of the dst array, in each direction -); - - - - - -/// Puts linearly interpolated values from one array into another array. 3D version -void LinearInterpolate3DArray( - float * a_Src, ///< Src array, [x + a_SrcSizeX * y + a_SrcSizeX * a_SrcSizeY * z] - int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Count of the src array, in each direction - float * a_Dst, ///< Dst array, [x + a_DstSizeX * y + a_DstSizeX * a_DstSizeY * z] - int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ///< Count of the dst array, in each direction -); - - - - + +// LinearInterpolation.h + +// Declares methods for linear interpolation over 1D, 2D and 3D arrays + + + + + +#pragma once + + + + + +// 2D and 3D Interpolation is optimized by precalculating the ratios into static-sized arrays +// These arrays enforce a max size of the dest array, but the limits are settable here: +const int MAX_INTERPOL_SIZEX = 256; ///< Maximum X-size of the interpolated array +const int MAX_INTERPOL_SIZEY = 512; ///< Maximum Y-size of the interpolated array +const int MAX_INTERPOL_SIZEZ = 256; ///< Maximum Z-size of the interpolated array + + + + + +/// Puts linearly interpolated values from one array into another array. 1D version +void LinearInterpolate1DArray( + float * a_Src, ///< Src array + int a_SrcSizeX, ///< Count of the src array + float * a_Dst, ///< Src array + int a_DstSizeX ///< Count of the dst array +); + + + + + +/// Puts linearly interpolated values from one array into another array. 2D version +void LinearInterpolate2DArray( + float * a_Src, ///< Src array, [x + a_SrcSizeX * y] + int a_SrcSizeX, int a_SrcSizeY, ///< Count of the src array, in each direction + float * a_Dst, ///< Dst array, [x + a_DstSizeX * y] + int a_DstSizeX, int a_DstSizeY ///< Count of the dst array, in each direction +); + + + + + +/// Puts linearly interpolated values from one array into another array. 3D version +void LinearInterpolate3DArray( + float * a_Src, ///< Src array, [x + a_SrcSizeX * y + a_SrcSizeX * a_SrcSizeY * z] + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Count of the src array, in each direction + float * a_Dst, ///< Dst array, [x + a_DstSizeX * y + a_DstSizeX * a_DstSizeY * z] + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ///< Count of the dst array, in each direction +); + + + + diff --git a/source/LinearUpscale.h b/source/LinearUpscale.h index 9f40b5d81..b7ac84c6a 100644 --- a/source/LinearUpscale.h +++ b/source/LinearUpscale.h @@ -1,244 +1,244 @@ - -// LinearUpscale.h - -// Declares the functions for linearly upscaling arrays - -/* -Upscaling means that the array is divided into same-size "cells", and each cell is -linearly interpolated between its corners. The array's dimensions are therefore -1 + CellSize * NumCells, for each direction. - -Upscaling is more efficient than linear interpolation, because the cell sizes are integral -and therefore the cells' boundaries are on the array points. - -However, upscaling usually requires generating the "1 +" in each direction. - -Upscaling is implemented in templates, so that it's compatible with multiple datatypes. -Therefore, there is no cpp file. - -InPlace upscaling works on a single array and assumes that the values to work on have already -been interspersed into the array to the cell boundaries. -Specifically, a_Array[x * a_AnchorStepX + y * a_AnchorStepY] contains the anchor value. - -Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed. -*/ - - - - -/** -Linearly interpolates values in the array between the equidistant anchor points (upscales). -Works in-place (input is already present at the correct output coords) -*/ -template void LinearUpscale2DArrayInPlace( - TYPE * a_Array, - int a_SizeX, int a_SizeY, // Dimensions of the array - int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction -) -{ - // First interpolate columns where the anchor points are: - int LastYCell = a_SizeY - a_AnchorStepY; - for (int y = 0; y < LastYCell; y += a_AnchorStepY) - { - int Idx = a_SizeX * y; - for (int x = 0; x < a_SizeX; x += a_AnchorStepX) - { - TYPE StartValue = a_Array[Idx]; - TYPE EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY]; - TYPE Diff = EndValue - StartValue; - for (int CellY = 1; CellY < a_AnchorStepY; CellY++) - { - a_Array[Idx + a_SizeX * CellY] = StartValue + Diff * CellY / a_AnchorStepY; - } // for CellY - Idx += a_AnchorStepX; - } // for x - } // for y - - // Now interpolate in rows, each row has values in the anchor columns - int LastXCell = a_SizeX - a_AnchorStepX; - for (int y = 0; y < a_SizeY; y++) - { - int Idx = a_SizeX * y; - for (int x = 0; x < LastXCell; x += a_AnchorStepX) - { - TYPE StartValue = a_Array[Idx]; - TYPE EndValue = a_Array[Idx + a_AnchorStepX]; - TYPE Diff = EndValue - StartValue; - for (int CellX = 1; CellX < a_AnchorStepX; CellX++) - { - a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX; - } // for CellY - Idx += a_AnchorStepX; - } - } -} - - - - - -/** -Linearly interpolates values in the array between the equidistant anchor points (upscales). -Works on two arrays, input is packed and output is to be completely constructed. -*/ -template void LinearUpscale2DArray( - TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY - int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array - TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) - int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction -) -{ - // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes - // Feel free to enlarge them if needed, but keep in mind that they're on the stack - const int MAX_UPSCALE_X = 128; - const int MAX_UPSCALE_Y = 128; - - ASSERT(a_Src != NULL); - ASSERT(a_Dst != NULL); - ASSERT(a_SrcSizeX > 0); - ASSERT(a_SrcSizeY > 0); - ASSERT(a_UpscaleX > 0); - ASSERT(a_UpscaleY > 0); - ASSERT(a_UpscaleX <= MAX_UPSCALE_X); - ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); - - // Pre-calculate the upscaling ratios: - TYPE RatioX[MAX_UPSCALE_X]; - TYPE RatioY[MAX_UPSCALE_Y]; - for (int x = 0; x <= a_UpscaleX; x++) - { - RatioX[x] = (TYPE)x / a_UpscaleX; - } - for (int y = 0; y <= a_UpscaleY; y++) - { - RatioY[y] = (TYPE)y / a_UpscaleY; - } - - // Interpolate each XY cell: - int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; - int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; - for (int y = 0; y < (a_SrcSizeY - 1); y++) - { - int DstY = y * a_UpscaleY; - int idx = y * a_SrcSizeX; - for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) - { - int DstX = x * a_UpscaleX; - TYPE LoXLoY = a_Src[idx]; - TYPE LoXHiY = a_Src[idx + a_SrcSizeX]; - TYPE HiXLoY = a_Src[idx + 1]; - TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX]; - for (int CellY = 0; CellY <= a_UpscaleY; CellY++) - { - int DestIdx = (DstY + CellY) * DstSizeX + DstX; - ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY); - TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY]; - TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY]; - for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) - { - a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; - } - } // for CellY - } // for x - } // for y -} - - - - - -/** -Linearly interpolates values in the array between the equidistant anchor points (upscales). -Works on two arrays, input is packed and output is to be completely constructed. -*/ -template void LinearUpscale3DArray( - TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ - int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array - TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1) - int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction -) -{ - // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes - // Feel free to enlarge them if needed, but keep in mind that they're on the stack - const int MAX_UPSCALE_X = 128; - const int MAX_UPSCALE_Y = 128; - const int MAX_UPSCALE_Z = 128; - - ASSERT(a_Src != NULL); - ASSERT(a_Dst != NULL); - ASSERT(a_SrcSizeX > 0); - ASSERT(a_SrcSizeY > 0); - ASSERT(a_SrcSizeZ > 0); - ASSERT(a_UpscaleX > 0); - ASSERT(a_UpscaleY > 0); - ASSERT(a_UpscaleZ > 0); - ASSERT(a_UpscaleX <= MAX_UPSCALE_X); - ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); - ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z); - - // Pre-calculate the upscaling ratios: - TYPE RatioX[MAX_UPSCALE_X]; - TYPE RatioY[MAX_UPSCALE_Y]; - TYPE RatioZ[MAX_UPSCALE_Y]; - for (int x = 0; x <= a_UpscaleX; x++) - { - RatioX[x] = (TYPE)x / a_UpscaleX; - } - for (int y = 0; y <= a_UpscaleY; y++) - { - RatioY[y] = (TYPE)y / a_UpscaleY; - } - for (int z = 0; z <= a_UpscaleZ; z++) - { - RatioZ[z] = (TYPE)z / a_UpscaleZ; - } - - // Interpolate each XYZ cell: - int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; - int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; - int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1; - for (int z = 0; z < (a_SrcSizeZ - 1); z++) - { - int DstZ = z * a_UpscaleZ; - for (int y = 0; y < (a_SrcSizeY - 1); y++) - { - int DstY = y * a_UpscaleY; - int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY; - for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) - { - int DstX = x * a_UpscaleX; - TYPE LoXLoYLoZ = a_Src[idx]; - TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY]; - TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX]; - TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; - TYPE HiXLoYLoZ = a_Src[idx + 1]; - TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY]; - TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX]; - TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; - for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++) - { - TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ]; - TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ]; - TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ]; - TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ]; - for (int CellY = 0; CellY <= a_UpscaleY; CellY++) - { - int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX; - ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ); - TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY]; - TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY]; - for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) - { - a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; - } - } // for CellY - } // for CellZ - } // for x - } // for y - } // for z -} - - - - - + +// LinearUpscale.h + +// Declares the functions for linearly upscaling arrays + +/* +Upscaling means that the array is divided into same-size "cells", and each cell is +linearly interpolated between its corners. The array's dimensions are therefore +1 + CellSize * NumCells, for each direction. + +Upscaling is more efficient than linear interpolation, because the cell sizes are integral +and therefore the cells' boundaries are on the array points. + +However, upscaling usually requires generating the "1 +" in each direction. + +Upscaling is implemented in templates, so that it's compatible with multiple datatypes. +Therefore, there is no cpp file. + +InPlace upscaling works on a single array and assumes that the values to work on have already +been interspersed into the array to the cell boundaries. +Specifically, a_Array[x * a_AnchorStepX + y * a_AnchorStepY] contains the anchor value. + +Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed. +*/ + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works in-place (input is already present at the correct output coords) +*/ +template void LinearUpscale2DArrayInPlace( + TYPE * a_Array, + int a_SizeX, int a_SizeY, // Dimensions of the array + int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction +) +{ + // First interpolate columns where the anchor points are: + int LastYCell = a_SizeY - a_AnchorStepY; + for (int y = 0; y < LastYCell; y += a_AnchorStepY) + { + int Idx = a_SizeX * y; + for (int x = 0; x < a_SizeX; x += a_AnchorStepX) + { + TYPE StartValue = a_Array[Idx]; + TYPE EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY]; + TYPE Diff = EndValue - StartValue; + for (int CellY = 1; CellY < a_AnchorStepY; CellY++) + { + a_Array[Idx + a_SizeX * CellY] = StartValue + Diff * CellY / a_AnchorStepY; + } // for CellY + Idx += a_AnchorStepX; + } // for x + } // for y + + // Now interpolate in rows, each row has values in the anchor columns + int LastXCell = a_SizeX - a_AnchorStepX; + for (int y = 0; y < a_SizeY; y++) + { + int Idx = a_SizeX * y; + for (int x = 0; x < LastXCell; x += a_AnchorStepX) + { + TYPE StartValue = a_Array[Idx]; + TYPE EndValue = a_Array[Idx + a_AnchorStepX]; + TYPE Diff = EndValue - StartValue; + for (int CellX = 1; CellX < a_AnchorStepX; CellX++) + { + a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX; + } // for CellY + Idx += a_AnchorStepX; + } + } +} + + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works on two arrays, input is packed and output is to be completely constructed. +*/ +template void LinearUpscale2DArray( + TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY + int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array + TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) + int a_UpscaleX, int a_UpscaleY ///< Upscale factor for each direction +) +{ + // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes + // Feel free to enlarge them if needed, but keep in mind that they're on the stack + const int MAX_UPSCALE_X = 128; + const int MAX_UPSCALE_Y = 128; + + ASSERT(a_Src != NULL); + ASSERT(a_Dst != NULL); + ASSERT(a_SrcSizeX > 0); + ASSERT(a_SrcSizeY > 0); + ASSERT(a_UpscaleX > 0); + ASSERT(a_UpscaleY > 0); + ASSERT(a_UpscaleX <= MAX_UPSCALE_X); + ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); + + // Pre-calculate the upscaling ratios: + TYPE RatioX[MAX_UPSCALE_X]; + TYPE RatioY[MAX_UPSCALE_Y]; + for (int x = 0; x <= a_UpscaleX; x++) + { + RatioX[x] = (TYPE)x / a_UpscaleX; + } + for (int y = 0; y <= a_UpscaleY; y++) + { + RatioY[y] = (TYPE)y / a_UpscaleY; + } + + // Interpolate each XY cell: + int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; + int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; + for (int y = 0; y < (a_SrcSizeY - 1); y++) + { + int DstY = y * a_UpscaleY; + int idx = y * a_SrcSizeX; + for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) + { + int DstX = x * a_UpscaleX; + TYPE LoXLoY = a_Src[idx]; + TYPE LoXHiY = a_Src[idx + a_SrcSizeX]; + TYPE HiXLoY = a_Src[idx + 1]; + TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX]; + for (int CellY = 0; CellY <= a_UpscaleY; CellY++) + { + int DestIdx = (DstY + CellY) * DstSizeX + DstX; + ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY); + TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY]; + TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY]; + for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) + { + a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; + } + } // for CellY + } // for x + } // for y +} + + + + + +/** +Linearly interpolates values in the array between the equidistant anchor points (upscales). +Works on two arrays, input is packed and output is to be completely constructed. +*/ +template void LinearUpscale3DArray( + TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ + int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array + TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1) + int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ ///< Upscale factor for each direction +) +{ + // For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes + // Feel free to enlarge them if needed, but keep in mind that they're on the stack + const int MAX_UPSCALE_X = 128; + const int MAX_UPSCALE_Y = 128; + const int MAX_UPSCALE_Z = 128; + + ASSERT(a_Src != NULL); + ASSERT(a_Dst != NULL); + ASSERT(a_SrcSizeX > 0); + ASSERT(a_SrcSizeY > 0); + ASSERT(a_SrcSizeZ > 0); + ASSERT(a_UpscaleX > 0); + ASSERT(a_UpscaleY > 0); + ASSERT(a_UpscaleZ > 0); + ASSERT(a_UpscaleX <= MAX_UPSCALE_X); + ASSERT(a_UpscaleY <= MAX_UPSCALE_Y); + ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z); + + // Pre-calculate the upscaling ratios: + TYPE RatioX[MAX_UPSCALE_X]; + TYPE RatioY[MAX_UPSCALE_Y]; + TYPE RatioZ[MAX_UPSCALE_Y]; + for (int x = 0; x <= a_UpscaleX; x++) + { + RatioX[x] = (TYPE)x / a_UpscaleX; + } + for (int y = 0; y <= a_UpscaleY; y++) + { + RatioY[y] = (TYPE)y / a_UpscaleY; + } + for (int z = 0; z <= a_UpscaleZ; z++) + { + RatioZ[z] = (TYPE)z / a_UpscaleZ; + } + + // Interpolate each XYZ cell: + int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1; + int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1; + int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1; + for (int z = 0; z < (a_SrcSizeZ - 1); z++) + { + int DstZ = z * a_UpscaleZ; + for (int y = 0; y < (a_SrcSizeY - 1); y++) + { + int DstY = y * a_UpscaleY; + int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY; + for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++) + { + int DstX = x * a_UpscaleX; + TYPE LoXLoYLoZ = a_Src[idx]; + TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY]; + TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX]; + TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; + TYPE HiXLoYLoZ = a_Src[idx + 1]; + TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY]; + TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX]; + TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY]; + for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++) + { + TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ]; + TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ]; + TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ]; + TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ]; + for (int CellY = 0; CellY <= a_UpscaleY; CellY++) + { + int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX; + ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ); + TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY]; + TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY]; + for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++) + { + a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX]; + } + } // for CellY + } // for CellZ + } // for x + } // for y + } // for z +} + + + + + diff --git a/source/LuaWindow.cpp b/source/LuaWindow.cpp index 13d06eeb6..c36fabd3c 100644 --- a/source/LuaWindow.cpp +++ b/source/LuaWindow.cpp @@ -1,175 +1,175 @@ - -// LuaWindow.cpp - -// Implements the cLuaWindow class representing a virtual window that plugins may create and open for the player - -#include "Globals.h" -#include "LuaWindow.h" -#include "UI/SlotArea.h" -#include "Plugin_NewLua.h" -#include "Player.h" -#include "lauxlib.h" // Needed for LUA_REFNIL - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cLuaWindow: - -cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : - super(a_WindowType, a_Title), - m_Contents(a_SlotsX, a_SlotsY), - m_Plugin(NULL), - m_LuaRef(LUA_REFNIL), - m_OnClosingFnRef(LUA_REFNIL), - m_OnSlotChangedFnRef(LUA_REFNIL) -{ - m_Contents.AddListener(*this); - m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); - - // If appropriate, add an Armor slot area: - switch (a_WindowType) - { - case cWindow::Inventory: - case cWindow::Workbench: - { - m_SlotAreas.push_back(new cSlotAreaArmor(*this)); - break; - } - } - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -cLuaWindow::~cLuaWindow() -{ - ASSERT(m_OpenedBy.empty()); -} - - - - - -void cLuaWindow::SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef) -{ - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); - ASSERT(m_LuaRef == LUA_REFNIL); - m_Plugin = a_Plugin; - m_LuaRef = a_LuaRef; -} - - - - - -bool cLuaWindow::IsLuaReferenced(void) const -{ - return ((m_Plugin != NULL) && (m_LuaRef != LUA_REFNIL)); -} - - - - - -void cLuaWindow::SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef) -{ - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); - - // If there already was a function, unreference it first - if (m_OnClosingFnRef != LUA_REFNIL) - { - m_Plugin->Unreference(m_OnClosingFnRef); - } - - // Store the new reference - m_Plugin = a_Plugin; - m_OnClosingFnRef = a_FnRef; -} - - - - - -void cLuaWindow::SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef) -{ - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); - - // If there already was a function, unreference it first - if (m_OnSlotChangedFnRef != LUA_REFNIL) - { - m_Plugin->Unreference(m_OnSlotChangedFnRef); - } - - // Store the new reference - m_Plugin = a_Plugin; - m_OnSlotChangedFnRef = a_FnRef; -} - - - - - -bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) -{ - // First notify the plugin through the registered callback: - if (m_OnClosingFnRef != LUA_REFNIL) - { - ASSERT(m_Plugin != NULL); - if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) - { - // The callback disagrees (the higher levels check the CanRefuse flag compliance) - return false; - } - } - - return super::ClosedByPlayer(a_Player, a_CanRefuse); -} - - - - - -void cLuaWindow::Destroy(void) -{ - super::Destroy(); - - if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != NULL)) - { - // The object is referenced by Lua, un-reference it - m_Plugin->Unreference(m_LuaRef); - } - - // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it! - m_IsDestroyed = false; -} - - - - - -void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) -{ - if (a_ItemGrid != &m_Contents) - { - ASSERT(!"Invalid ItemGrid in callback"); - return; - } - - // If an OnSlotChanged callback has been registered, call it: - if (m_OnSlotChangedFnRef != LUA_REFNIL) - { - m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); - } -} - - - - + +// LuaWindow.cpp + +// Implements the cLuaWindow class representing a virtual window that plugins may create and open for the player + +#include "Globals.h" +#include "LuaWindow.h" +#include "UI/SlotArea.h" +#include "Plugin_NewLua.h" +#include "Player.h" +#include "lauxlib.h" // Needed for LUA_REFNIL + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLuaWindow: + +cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : + super(a_WindowType, a_Title), + m_Contents(a_SlotsX, a_SlotsY), + m_Plugin(NULL), + m_LuaRef(LUA_REFNIL), + m_OnClosingFnRef(LUA_REFNIL), + m_OnSlotChangedFnRef(LUA_REFNIL) +{ + m_Contents.AddListener(*this); + m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); + + // If appropriate, add an Armor slot area: + switch (a_WindowType) + { + case cWindow::Inventory: + case cWindow::Workbench: + { + m_SlotAreas.push_back(new cSlotAreaArmor(*this)); + break; + } + } + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +cLuaWindow::~cLuaWindow() +{ + ASSERT(m_OpenedBy.empty()); +} + + + + + +void cLuaWindow::SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + ASSERT(m_LuaRef == LUA_REFNIL); + m_Plugin = a_Plugin; + m_LuaRef = a_LuaRef; +} + + + + + +bool cLuaWindow::IsLuaReferenced(void) const +{ + return ((m_Plugin != NULL) && (m_LuaRef != LUA_REFNIL)); +} + + + + + +void cLuaWindow::SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + + // If there already was a function, unreference it first + if (m_OnClosingFnRef != LUA_REFNIL) + { + m_Plugin->Unreference(m_OnClosingFnRef); + } + + // Store the new reference + m_Plugin = a_Plugin; + m_OnClosingFnRef = a_FnRef; +} + + + + + +void cLuaWindow::SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef) +{ + // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object + ASSERT((m_Plugin == NULL) || (m_Plugin == a_Plugin)); + + // If there already was a function, unreference it first + if (m_OnSlotChangedFnRef != LUA_REFNIL) + { + m_Plugin->Unreference(m_OnSlotChangedFnRef); + } + + // Store the new reference + m_Plugin = a_Plugin; + m_OnSlotChangedFnRef = a_FnRef; +} + + + + + +bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) +{ + // First notify the plugin through the registered callback: + if (m_OnClosingFnRef != LUA_REFNIL) + { + ASSERT(m_Plugin != NULL); + if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) + { + // The callback disagrees (the higher levels check the CanRefuse flag compliance) + return false; + } + } + + return super::ClosedByPlayer(a_Player, a_CanRefuse); +} + + + + + +void cLuaWindow::Destroy(void) +{ + super::Destroy(); + + if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != NULL)) + { + // The object is referenced by Lua, un-reference it + m_Plugin->Unreference(m_LuaRef); + } + + // Lua will take care of this object, it will garbage-collect it, so we *must not* delete it! + m_IsDestroyed = false; +} + + + + + +void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + if (a_ItemGrid != &m_Contents) + { + ASSERT(!"Invalid ItemGrid in callback"); + return; + } + + // If an OnSlotChanged callback has been registered, call it: + if (m_OnSlotChangedFnRef != LUA_REFNIL) + { + m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); + } +} + + + + diff --git a/source/LuaWindow.h b/source/LuaWindow.h index de24170df..60c24996d 100644 --- a/source/LuaWindow.h +++ b/source/LuaWindow.h @@ -1,96 +1,96 @@ - -// LuaWindow.h - -// Declares the cLuaWindow class representing a virtual window that plugins may create and open for the player - - - - - -#pragma once - -#include "UI/Window.h" -#include "ItemGrid.h" - - - - - -// fwd: Plugin_NewLua.h -class cPlugin_NewLua; - - - - - -// tolua_begin - -/** A window that has been created by a Lua plugin and is handled entirely by that plugin -This object needs extra care with its lifetime management: -- It is created by Lua, so Lua expects to garbage-collect it later -- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them -To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() -delete the window, but rather leaves it dangling, with only Lua having the reference to it. -Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for -cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object. -This reference needs to be unreferenced in the Destroy() function. -*/ -class cLuaWindow : - public cWindow, - public cItemGrid::cListener -{ - typedef cWindow super; - -public: - /// Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size - cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); - - virtual ~cLuaWindow(); - - /// Returns the internal representation of the contents that are manipulated by Lua - cItemGrid & GetContents(void) { return m_Contents; } - - // tolua_end - - /** Sets the plugin reference and the internal Lua object reference index - used for preventing Lua's GC to collect this class while the window is open - */ - void SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef); - - /// Returns true if SetLuaRef() has been called - bool IsLuaReferenced(void) const; - - /// Sets the callback function (Lua reference) to call when the window is about to close - void SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef); - - /// Sets the callback function (Lua reference) to call when a slot is changed - void SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef); - -protected: - /// Contents of the non-inventory part - cItemGrid m_Contents; - - /// The plugin that has opened the window and owns the m_LuaRef - cPlugin_NewLua * m_Plugin; - - /// The Lua object reference, used for keeping the object alive as long as any player has the window open - int m_LuaRef; - - /// The Lua reference for the callback to call when the window is closing for any player - int m_OnClosingFnRef; - - /// The Lua reference for the callback to call when a slot has changed - int m_OnSlotChangedFnRef; - - // cWindow overrides: - virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; - virtual void Destroy(void) override; - - // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; -} ; // tolua_export - - - - - + +// LuaWindow.h + +// Declares the cLuaWindow class representing a virtual window that plugins may create and open for the player + + + + + +#pragma once + +#include "UI/Window.h" +#include "ItemGrid.h" + + + + + +// fwd: Plugin_NewLua.h +class cPlugin_NewLua; + + + + + +// tolua_begin + +/** A window that has been created by a Lua plugin and is handled entirely by that plugin +This object needs extra care with its lifetime management: +- It is created by Lua, so Lua expects to garbage-collect it later +- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them +To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() +delete the window, but rather leaves it dangling, with only Lua having the reference to it. +Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for +cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object. +This reference needs to be unreferenced in the Destroy() function. +*/ +class cLuaWindow : + public cWindow, + public cItemGrid::cListener +{ + typedef cWindow super; + +public: + /// Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size + cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + + virtual ~cLuaWindow(); + + /// Returns the internal representation of the contents that are manipulated by Lua + cItemGrid & GetContents(void) { return m_Contents; } + + // tolua_end + + /** Sets the plugin reference and the internal Lua object reference index + used for preventing Lua's GC to collect this class while the window is open + */ + void SetLuaRef(cPlugin_NewLua * a_Plugin, int a_LuaRef); + + /// Returns true if SetLuaRef() has been called + bool IsLuaReferenced(void) const; + + /// Sets the callback function (Lua reference) to call when the window is about to close + void SetOnClosing(cPlugin_NewLua * a_Plugin, int a_FnRef); + + /// Sets the callback function (Lua reference) to call when a slot is changed + void SetOnSlotChanged(cPlugin_NewLua * a_Plugin, int a_FnRef); + +protected: + /// Contents of the non-inventory part + cItemGrid m_Contents; + + /// The plugin that has opened the window and owns the m_LuaRef + cPlugin_NewLua * m_Plugin; + + /// The Lua object reference, used for keeping the object alive as long as any player has the window open + int m_LuaRef; + + /// The Lua reference for the callback to call when the window is closing for any player + int m_OnClosingFnRef; + + /// The Lua reference for the callback to call when a slot has changed + int m_OnSlotChangedFnRef; + + // cWindow overrides: + virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; + virtual void Destroy(void) override; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +} ; // tolua_export + + + + + diff --git a/source/Minecart.cpp b/source/Minecart.cpp index 232f44f7d..bae56d582 100644 --- a/source/Minecart.cpp +++ b/source/Minecart.cpp @@ -1,160 +1,160 @@ - -// Minecart.cpp - -// Implements the cMinecart class representing a minecart in the world - -#include "Globals.h" -#include "Minecart.h" -#include "World.h" -#include "ClientHandle.h" -#include "Player.h" - - - - - -cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : - super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7), - m_Payload(a_Payload) -{ -} - - - - -void cMinecart::Initialize(cWorld * a_World) -{ - super::Initialize(a_World); - a_World->BroadcastSpawnEntity(*this); -} - - - - - -void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) -{ - char Type = 0; - switch (m_Payload) - { - case mpNone: Type = 10; break; - case mpChest: Type = 11; break; - case mpFurnace: Type = 12; break; - default: - { - ASSERT(!"Unknown payload, cannot spawn on client"); - return; - } - } - a_ClientHandle.SendSpawnVehicle(*this, Type); -} - - - - - -void cMinecart::Tick(float a_Dt, cChunk & a_Chunk) -{ - // TODO: the physics -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cEmptyMinecart: - -cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) : - super(mpNone, a_X, a_Y, a_Z) -{ -} - - - - - -void cEmptyMinecart::OnRightClicked(cPlayer & a_Player) -{ - if (m_Attachee != NULL) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - // This player is already sitting in, they want out. - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - // Another player is already sitting in here, cannot attach - return; - } - - // Detach whatever is sitting in this minecart now: - m_Attachee->Detach(); - } - - // Attach the player to this minecart - a_Player.AttachTo(this); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithChest: - -cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) : - super(mpChest, a_X, a_Y, a_Z) -{ -} - - - - - -void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item) -{ - ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items))); - - m_Items[a_Idx] = a_Item; -} - - - - - -void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) -{ - // Show the chest UI window to the player - // TODO -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithFurnace: - -cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : - super(mpFurnace, a_X, a_Y, a_Z) -{ -} - - - - - -void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) -{ - // Try to power the furnace with whatever the player is holding - // TODO -} - - - - - + +// Minecart.cpp + +// Implements the cMinecart class representing a minecart in the world + +#include "Globals.h" +#include "Minecart.h" +#include "World.h" +#include "ClientHandle.h" +#include "Player.h" + + + + + +cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : + super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7), + m_Payload(a_Payload) +{ +} + + + + +void cMinecart::Initialize(cWorld * a_World) +{ + super::Initialize(a_World); + a_World->BroadcastSpawnEntity(*this); +} + + + + + +void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) +{ + char Type = 0; + switch (m_Payload) + { + case mpNone: Type = 10; break; + case mpChest: Type = 11; break; + case mpFurnace: Type = 12; break; + default: + { + ASSERT(!"Unknown payload, cannot spawn on client"); + return; + } + } + a_ClientHandle.SendSpawnVehicle(*this, Type); +} + + + + + +void cMinecart::Tick(float a_Dt, cChunk & a_Chunk) +{ + // TODO: the physics +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cEmptyMinecart: + +cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) : + super(mpNone, a_X, a_Y, a_Z) +{ +} + + + + + +void cEmptyMinecart::OnRightClicked(cPlayer & a_Player) +{ + if (m_Attachee != NULL) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + // This player is already sitting in, they want out. + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + // Another player is already sitting in here, cannot attach + return; + } + + // Detach whatever is sitting in this minecart now: + m_Attachee->Detach(); + } + + // Attach the player to this minecart + a_Player.AttachTo(this); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithChest: + +cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) : + super(mpChest, a_X, a_Y, a_Z) +{ +} + + + + + +void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item) +{ + ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items))); + + m_Items[a_Idx] = a_Item; +} + + + + + +void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) +{ + // Show the chest UI window to the player + // TODO +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMinecartWithFurnace: + +cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : + super(mpFurnace, a_X, a_Y, a_Z) +{ +} + + + + + +void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) +{ + // Try to power the furnace with whatever the player is holding + // TODO +} + + + + + diff --git a/source/Minecart.h b/source/Minecart.h index db61997d8..b7b4c2b04 100644 --- a/source/Minecart.h +++ b/source/Minecart.h @@ -1,117 +1,117 @@ - -// Minecart.h - -// Declares the cMinecart class representing a minecart in the world - - - - - -#pragma once - -#include "Entity.h" -#include "Item.h" - - - - - -class cMinecart : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cMinecart); - - enum ePayload - { - mpNone, // Empty minecart, ridable by player or mobs - mpChest, // Minecart-with-chest, can store a grid of 3*8 items - mpFurnace, // Minecart-with-furnace, can be powered - // TODO: Other 1.5 features: hopper, tnt, dispenser, spawner - } ; - - // cEntity overrides: - virtual void Initialize(cWorld * a_World) override; - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - ePayload GetPayload(void) const { return m_Payload; } - -protected: - ePayload m_Payload; - - cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); -} ; - - - - - -class cEmptyMinecart : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cEmptyMinecart); - - cEmptyMinecart(double a_X, double a_Y, double a_Z); - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; -} ; - - - - - -class cMinecartWithChest : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithChest); - - /// Number of item slots in the chest - static const int NumSlots = 9 * 3; - - cMinecartWithChest(double a_X, double a_Y, double a_Z); - - const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; } - cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; } - - void SetSlot(int a_Idx, const cItem & a_Item); - -protected: - - /// The chest contents: - cItem m_Items[NumSlots]; - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; -} ; - - - - - -class cMinecartWithFurnace : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithFurnace); - - cMinecartWithFurnace(double a_X, double a_Y, double a_Z); - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; -} ; - - - - + +// Minecart.h + +// Declares the cMinecart class representing a minecart in the world + + + + + +#pragma once + +#include "Entity.h" +#include "Item.h" + + + + + +class cMinecart : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cMinecart); + + enum ePayload + { + mpNone, // Empty minecart, ridable by player or mobs + mpChest, // Minecart-with-chest, can store a grid of 3*8 items + mpFurnace, // Minecart-with-furnace, can be powered + // TODO: Other 1.5 features: hopper, tnt, dispenser, spawner + } ; + + // cEntity overrides: + virtual void Initialize(cWorld * a_World) override; + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + + ePayload GetPayload(void) const { return m_Payload; } + +protected: + ePayload m_Payload; + + cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); +} ; + + + + + +class cEmptyMinecart : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cEmptyMinecart); + + cEmptyMinecart(double a_X, double a_Y, double a_Z); + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + + +class cMinecartWithChest : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithChest); + + /// Number of item slots in the chest + static const int NumSlots = 9 * 3; + + cMinecartWithChest(double a_X, double a_Y, double a_Z); + + const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; } + cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; } + + void SetSlot(int a_Idx, const cItem & a_Item); + +protected: + + /// The chest contents: + cItem m_Items[NumSlots]; + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + + +class cMinecartWithFurnace : + public cMinecart +{ + typedef cMinecart super; + +public: + CLASS_PROTODEF(cMinecartWithFurnace); + + cMinecartWithFurnace(double a_X, double a_Y, double a_Z); + + // cEntity overrides: + virtual void OnRightClicked(cPlayer & a_Player) override; +} ; + + + + diff --git a/source/Mobs/Bat.h b/source/Mobs/Bat.h index df6745fe5..8e4cde29f 100644 --- a/source/Mobs/Bat.h +++ b/source/Mobs/Bat.h @@ -1,27 +1,27 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cBat : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cBat(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) - { - } - - CLASS_PROTODEF(cBat); -} ; - - - - + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cBat : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cBat(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Bat", 65, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) + { + } + + CLASS_PROTODEF(cBat); +} ; + + + + diff --git a/source/Mobs/Blaze.cpp b/source/Mobs/Blaze.cpp index 069ee8934..dbbccf417 100644 --- a/source/Mobs/Blaze.cpp +++ b/source/Mobs/Blaze.cpp @@ -1,27 +1,27 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Blaze.h" - - - - - -cBlaze::cBlaze(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Blaze", 61, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8) -{ -} - - - - - -void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD); -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Blaze.h" + + + + + +cBlaze::cBlaze(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Blaze", 61, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8) +{ +} + + + + + +void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD); +} + + + + diff --git a/source/Mobs/Blaze.h b/source/Mobs/Blaze.h index 8b7a15848..9df57530e 100644 --- a/source/Mobs/Blaze.h +++ b/source/Mobs/Blaze.h @@ -1,25 +1,25 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cBlaze : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cBlaze(void); - - CLASS_PROTODEF(cBlaze); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cBlaze : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cBlaze(void); + + CLASS_PROTODEF(cBlaze); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/source/Mobs/Magmacube.cpp b/source/Mobs/Magmacube.cpp index 156a29607..0b9b57e3c 100644 --- a/source/Mobs/Magmacube.cpp +++ b/source/Mobs/Magmacube.cpp @@ -1,27 +1,27 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Magmacube.h" - - - - - -cMagmacube::cMagmacube(int a_Size) : - super("Magmacube", 62, "mob.magmacube.big", "mob.magmacube.big", 0.6 * a_Size, 0.6 * a_Size), - m_Size(a_Size) -{ -} - - - - - -void cMagmacube::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM); -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Magmacube.h" + + + + + +cMagmacube::cMagmacube(int a_Size) : + super("Magmacube", 62, "mob.magmacube.big", "mob.magmacube.big", 0.6 * a_Size, 0.6 * a_Size), + m_Size(a_Size) +{ +} + + + + + +void cMagmacube::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM); +} + + + + diff --git a/source/Mobs/Magmacube.h b/source/Mobs/Magmacube.h index da62b9615..e4df4f33d 100644 --- a/source/Mobs/Magmacube.h +++ b/source/Mobs/Magmacube.h @@ -1,31 +1,31 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cMagmacube : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - /// Creates a magmacube of the specified size; size is 1 .. 3, with 1 being the smallest - cMagmacube(int a_Size); - - CLASS_PROTODEF(cMagmacube); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - -protected: - - /// Size of the magmacube, 1 .. 3, with 1 being the smallest - int m_Size; -} ; - - - - + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cMagmacube : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + /// Creates a magmacube of the specified size; size is 1 .. 3, with 1 being the smallest + cMagmacube(int a_Size); + + CLASS_PROTODEF(cMagmacube); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + +protected: + + /// Size of the magmacube, 1 .. 3, with 1 being the smallest + int m_Size; +} ; + + + + diff --git a/source/Mobs/Mooshroom.cpp b/source/Mobs/Mooshroom.cpp index c85ceda3a..5d2c901ba 100644 --- a/source/Mobs/Mooshroom.cpp +++ b/source/Mobs/Mooshroom.cpp @@ -1,33 +1,33 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Mooshroom.h" - - - - - -// TODO: Milk Cow - - - - - -cMooshroom::cMooshroom(void) : - super("Mooshroom", 96, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) -{ -} - - - - - -void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); - AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Mooshroom.h" + + + + + +// TODO: Milk Cow + + + + + +cMooshroom::cMooshroom(void) : + super("Mooshroom", 96, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3) +{ +} + + + + + +void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER); + AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); +} + + + + diff --git a/source/Mobs/Mooshroom.h b/source/Mobs/Mooshroom.h index 20fc293b9..73f6348b6 100644 --- a/source/Mobs/Mooshroom.h +++ b/source/Mobs/Mooshroom.h @@ -1,25 +1,25 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cMooshroom : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cMooshroom(void); - - CLASS_PROTODEF(cMooshroom); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cMooshroom : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cMooshroom(void); + + CLASS_PROTODEF(cMooshroom); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/source/Mobs/Ocelot.h b/source/Mobs/Ocelot.h index 4d22575f2..98ea224e2 100644 --- a/source/Mobs/Ocelot.h +++ b/source/Mobs/Ocelot.h @@ -1,27 +1,27 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cOcelot : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cOcelot(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Ocelot", 98, "mob.cat.hitt", "mob.cat.hitt", 0.9, 0.5) - { - } - - CLASS_PROTODEF(cOcelot); -} ; - - - - + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cOcelot : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cOcelot(void) : + // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here + super("Ocelot", 98, "mob.cat.hitt", "mob.cat.hitt", 0.9, 0.5) + { + } + + CLASS_PROTODEF(cOcelot); +} ; + + + + diff --git a/source/Mobs/Villager.cpp b/source/Mobs/Villager.cpp index 0ec2cf76c..98e5276e1 100644 --- a/source/Mobs/Villager.cpp +++ b/source/Mobs/Villager.cpp @@ -1,17 +1,17 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Villager.h" - - - - - -cVillager::cVillager(void) : - super("Villager", 120, "", "", 0.6, 1.8) -{ -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Villager.h" + + + + + +cVillager::cVillager(void) : + super("Villager", 120, "", "", 0.6, 1.8) +{ +} + + + + diff --git a/source/Mobs/Villager.h b/source/Mobs/Villager.h index 4b50ed35e..92267a979 100644 --- a/source/Mobs/Villager.h +++ b/source/Mobs/Villager.h @@ -1,23 +1,23 @@ - -#pragma once - -#include "PassiveMonster.h" - - - - - -class cVillager : - public cPassiveMonster -{ - typedef cPassiveMonster super; - -public: - cVillager(); - - CLASS_PROTODEF(cVillager); -} ; - - - - + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cVillager : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cVillager(); + + CLASS_PROTODEF(cVillager); +} ; + + + + diff --git a/source/Mobs/Witch.cpp b/source/Mobs/Witch.cpp index 66295ed1b..b29783853 100644 --- a/source/Mobs/Witch.cpp +++ b/source/Mobs/Witch.cpp @@ -1,32 +1,32 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Witch.h" - - - - - -cWitch::cWitch(void) : - super("Witch", 66, "", "", 0.6, 1.8) -{ -} - - - - - -void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer) -{ - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK); - AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR); -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Witch.h" + + + + + +cWitch::cWitch(void) : + super("Witch", 66, "", "", 0.6, 1.8) +{ +} + + + + + +void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK); + AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR); +} + + + + diff --git a/source/Mobs/Witch.h b/source/Mobs/Witch.h index 2ca2c081f..ce0b49deb 100644 --- a/source/Mobs/Witch.h +++ b/source/Mobs/Witch.h @@ -1,25 +1,25 @@ - -#pragma once - -#include "AggressiveMonster.h" - - - - - -class cWitch : - public cAggressiveMonster -{ - typedef cAggressiveMonster super; - -public: - cWitch(); - - CLASS_PROTODEF(cWitch); - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; -} ; - - - - + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cWitch : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cWitch(); + + CLASS_PROTODEF(cWitch); + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; +} ; + + + + diff --git a/source/OSSupport/GZipFile.cpp b/source/OSSupport/GZipFile.cpp index c57de5198..cbf6be6c4 100644 --- a/source/OSSupport/GZipFile.cpp +++ b/source/OSSupport/GZipFile.cpp @@ -1,107 +1,107 @@ - -// GZipFile.cpp - -// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines - -#include "Globals.h" -#include "GZipFile.h" - - - - - -cGZipFile::cGZipFile(void) : - m_File(NULL) -{ -} - - - - - -cGZipFile::~cGZipFile() -{ - Close(); -} - - - - - -bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode) -{ - if (m_File != NULL) - { - ASSERT(!"A file is already open in this object"); - return false; - } - m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w"); - m_Mode = a_Mode; - return (m_File != NULL); -} - - - - - -void cGZipFile::Close(void) -{ - if (m_File != NULL) - { - gzclose(m_File); - m_File = NULL; - } -} - - - - - -int cGZipFile::ReadRestOfFile(AString & a_Contents) -{ - if (m_File == NULL) - { - ASSERT(!"No file has been opened"); - return -1; - } - - if (m_Mode != fmRead) - { - ASSERT(!"Bad file mode, cannot read"); - return -1; - } - - // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck! - int NumBytesRead = 0; - char Buffer[64 KiB]; - while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0) - { - a_Contents.append(Buffer, NumBytesRead); - } - return NumBytesRead; -} - - - - - -bool cGZipFile::Write(const char * a_Contents, int a_Size) -{ - if (m_File == NULL) - { - ASSERT(!"No file has been opened"); - return false; - } - - if (m_Mode != fmWrite) - { - ASSERT(!"Bad file mode, cannot write"); - return false; - } - - return (gzwrite(m_File, a_Contents, a_Size) != 0); -} - - - - + +// GZipFile.cpp + +// Implements the cGZipFile class representing a RAII wrapper over zlib's GZip file routines + +#include "Globals.h" +#include "GZipFile.h" + + + + + +cGZipFile::cGZipFile(void) : + m_File(NULL) +{ +} + + + + + +cGZipFile::~cGZipFile() +{ + Close(); +} + + + + + +bool cGZipFile::Open(const AString & a_FileName, eMode a_Mode) +{ + if (m_File != NULL) + { + ASSERT(!"A file is already open in this object"); + return false; + } + m_File = gzopen(a_FileName.c_str(), (a_Mode == fmRead) ? "r" : "w"); + m_Mode = a_Mode; + return (m_File != NULL); +} + + + + + +void cGZipFile::Close(void) +{ + if (m_File != NULL) + { + gzclose(m_File); + m_File = NULL; + } +} + + + + + +int cGZipFile::ReadRestOfFile(AString & a_Contents) +{ + if (m_File == NULL) + { + ASSERT(!"No file has been opened"); + return -1; + } + + if (m_Mode != fmRead) + { + ASSERT(!"Bad file mode, cannot read"); + return -1; + } + + // Since the gzip format doesn't really support getting the uncompressed length, we need to read incrementally. Yuck! + int NumBytesRead = 0; + char Buffer[64 KiB]; + while ((NumBytesRead = gzread(m_File, Buffer, sizeof(Buffer))) > 0) + { + a_Contents.append(Buffer, NumBytesRead); + } + return NumBytesRead; +} + + + + + +bool cGZipFile::Write(const char * a_Contents, int a_Size) +{ + if (m_File == NULL) + { + ASSERT(!"No file has been opened"); + return false; + } + + if (m_Mode != fmWrite) + { + ASSERT(!"Bad file mode, cannot write"); + return false; + } + + return (gzwrite(m_File, a_Contents, a_Size) != 0); +} + + + + diff --git a/source/OSSupport/GZipFile.h b/source/OSSupport/GZipFile.h index f20b4d11e..e5aa68afa 100644 --- a/source/OSSupport/GZipFile.h +++ b/source/OSSupport/GZipFile.h @@ -1,52 +1,52 @@ - -// GZipFile.h - -// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines - - - - - -#pragma once - -#include "zlib.h" - - - - - -class cGZipFile -{ -public: - enum eMode - { - fmRead, // Read-only. If the file doesn't exist, object will not be valid - fmWrite, // Write-only. If the file already exists, it will be overwritten - } ; - - cGZipFile(void); - ~cGZipFile(); - - /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object. - bool Open(const AString & a_FileName, eMode a_Mode); - - /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode - void Close(void); - - /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error - int ReadRestOfFile(AString & a_Contents); - - /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported. - bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); } - - bool Write(const char * a_Data, int a_Size); - -protected: - gzFile m_File; - eMode m_Mode; -} ; - - - - - + +// GZipFile.h + +// Declares the cGZipFile class representing a RAII wrapper over zlib's GZip file routines + + + + + +#pragma once + +#include "zlib.h" + + + + + +class cGZipFile +{ +public: + enum eMode + { + fmRead, // Read-only. If the file doesn't exist, object will not be valid + fmWrite, // Write-only. If the file already exists, it will be overwritten + } ; + + cGZipFile(void); + ~cGZipFile(); + + /// Opens the file. Returns true if successful. Fails if a file has already been opened through this object. + bool Open(const AString & a_FileName, eMode a_Mode); + + /// Closes the file, flushing all buffers. This object may be then reused for a different file and / or mode + void Close(void); + + /// Reads the rest of the file and decompresses it into a_Contents. Returns the number of decompressed bytes, <0 for error + int ReadRestOfFile(AString & a_Contents); + + /// Writes a_Contents into file, compressing it along the way. Returns true if successful. Multiple writes are supported. + bool Write(const AString & a_Contents) { return Write(a_Contents.data(), (int)(a_Contents.size())); } + + bool Write(const char * a_Data, int a_Size); + +protected: + gzFile m_File; + eMode m_Mode; +} ; + + + + + diff --git a/source/OSSupport/ListenThread.cpp b/source/OSSupport/ListenThread.cpp index 89aa6ea75..4542326ef 100644 --- a/source/OSSupport/ListenThread.cpp +++ b/source/OSSupport/ListenThread.cpp @@ -1,231 +1,231 @@ - -// ListenThread.cpp - -// Implements the cListenThread class representing the thread that listens for client connections - -#include "Globals.h" -#include "ListenThread.h" - - - - - -cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) : - super(Printf("ListenThread %s", a_ServiceName.c_str())), - m_Callback(a_Callback), - m_Family(a_Family), - m_ShouldReuseAddr(false), - m_ServiceName(a_ServiceName) -{ -} - - - - - -cListenThread::~cListenThread() -{ - // TODO -} - - - - - -bool cListenThread::Initialize(const AString & a_PortsString) -{ - ASSERT(m_Sockets.empty()); // Not yet started - - if (!CreateSockets(a_PortsString)) - { - return false; - } - - return true; -} - - - - - -bool cListenThread::Start(void) -{ - if (m_Sockets.empty()) - { - // There are no sockets listening, either forgotten to initialize or the user specified no listening ports - // Report as successful, though - return true; - } - return super::Start(); -} - - - - - -void cListenThread::Stop(void) -{ - if (m_Sockets.empty()) - { - // No sockets means no thread was running in the first place - return; - } - - m_ShouldTerminate = true; - - // Close one socket to wake the thread up from the select() call - m_Sockets[0].CloseSocket(); - - // Wait for the thread to finish - super::Wait(); - - // Clean up all sockets - m_Sockets.clear(); -} - - - - - -void cListenThread::SetReuseAddr(bool a_Reuse) -{ - ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet - - m_ShouldReuseAddr = a_Reuse; -} - - - - - -bool cListenThread::CreateSockets(const AString & a_PortsString) -{ - AStringVector Ports = StringSplitAndTrim(a_PortsString, ","); - - if (Ports.empty()) - { - return false; - } - - AString FamilyStr = m_ServiceName; - switch (m_Family) - { - case cSocket::IPv4: FamilyStr.append(" IPv4"); break; - case cSocket::IPv6: FamilyStr.append(" IPv6"); break; - default: - { - ASSERT(!"Unknown address family"); - break; - } - } - - for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr) - { - int Port = atoi(itr->c_str()); - if ((Port <= 0) || (Port > 65535)) - { - LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str()); - continue; - } - m_Sockets.push_back(cSocket::CreateSocket(m_Family)); - if (!m_Sockets.back().IsValid()) - { - LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - if (m_ShouldReuseAddr) - { - if (!m_Sockets.back().SetReuseAddress()) - { - LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - } - } - - // Bind to port: - bool res = false; - switch (m_Family) - { - case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break; - case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break; - default: - { - ASSERT(!"Unknown address family"); - res = false; - } - } - if (!res) - { - LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - if (!m_Sockets.back().Listen()) - { - LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); - m_Sockets.pop_back(); - continue; - } - - LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port); - } // for itr - Ports[] - - return !(m_Sockets.empty()); -} - - - - - -void cListenThread::Execute(void) -{ - if (m_Sockets.empty()) - { - LOGD("Empty cListenThread, ending thread now."); - return; - } - - // Find the highest socket number: - cSocket::xSocket Highest = m_Sockets[0].GetSocket(); - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - if (itr->GetSocket() > Highest) - { - Highest = itr->GetSocket(); - } - } // for itr - m_Sockets[] - - while (!m_ShouldTerminate) - { - // Put all sockets into a FD set: - fd_set fdRead; - FD_ZERO(&fdRead); - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - FD_SET(itr->GetSocket(), &fdRead); - } // for itr - m_Sockets[] - - timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait: - tv.tv_sec = 1; - tv.tv_usec = 0; - if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1) - { - LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str()); - continue; - } - for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) - { - if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) - { - cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); - m_Callback.OnConnectionAccepted(Client); - } - } // for itr - m_Sockets[] - } // while (!m_ShouldTerminate) -} - - - - + +// ListenThread.cpp + +// Implements the cListenThread class representing the thread that listens for client connections + +#include "Globals.h" +#include "ListenThread.h" + + + + + +cListenThread::cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName) : + super(Printf("ListenThread %s", a_ServiceName.c_str())), + m_Callback(a_Callback), + m_Family(a_Family), + m_ShouldReuseAddr(false), + m_ServiceName(a_ServiceName) +{ +} + + + + + +cListenThread::~cListenThread() +{ + // TODO +} + + + + + +bool cListenThread::Initialize(const AString & a_PortsString) +{ + ASSERT(m_Sockets.empty()); // Not yet started + + if (!CreateSockets(a_PortsString)) + { + return false; + } + + return true; +} + + + + + +bool cListenThread::Start(void) +{ + if (m_Sockets.empty()) + { + // There are no sockets listening, either forgotten to initialize or the user specified no listening ports + // Report as successful, though + return true; + } + return super::Start(); +} + + + + + +void cListenThread::Stop(void) +{ + if (m_Sockets.empty()) + { + // No sockets means no thread was running in the first place + return; + } + + m_ShouldTerminate = true; + + // Close one socket to wake the thread up from the select() call + m_Sockets[0].CloseSocket(); + + // Wait for the thread to finish + super::Wait(); + + // Clean up all sockets + m_Sockets.clear(); +} + + + + + +void cListenThread::SetReuseAddr(bool a_Reuse) +{ + ASSERT(m_Sockets.empty()); // Must not have been Initialize()d yet + + m_ShouldReuseAddr = a_Reuse; +} + + + + + +bool cListenThread::CreateSockets(const AString & a_PortsString) +{ + AStringVector Ports = StringSplitAndTrim(a_PortsString, ","); + + if (Ports.empty()) + { + return false; + } + + AString FamilyStr = m_ServiceName; + switch (m_Family) + { + case cSocket::IPv4: FamilyStr.append(" IPv4"); break; + case cSocket::IPv6: FamilyStr.append(" IPv6"); break; + default: + { + ASSERT(!"Unknown address family"); + break; + } + } + + for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr) + { + int Port = atoi(itr->c_str()); + if ((Port <= 0) || (Port > 65535)) + { + LOGWARNING("%s: Invalid port specified: \"%s\".", FamilyStr.c_str(), itr->c_str()); + continue; + } + m_Sockets.push_back(cSocket::CreateSocket(m_Family)); + if (!m_Sockets.back().IsValid()) + { + LOGWARNING("%s: Cannot create listening socket for port %d: \"%s\"", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + if (m_ShouldReuseAddr) + { + if (!m_Sockets.back().SetReuseAddress()) + { + LOG("%s: Port %d cannot reuse addr, syscall failed: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + } + } + + // Bind to port: + bool res = false; + switch (m_Family) + { + case cSocket::IPv4: res = m_Sockets.back().BindToAnyIPv4(Port); break; + case cSocket::IPv6: res = m_Sockets.back().BindToAnyIPv6(Port); break; + default: + { + ASSERT(!"Unknown address family"); + res = false; + } + } + if (!res) + { + LOGWARNING("%s: Cannot bind port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + if (!m_Sockets.back().Listen()) + { + LOGWARNING("%s: Cannot listen on port %d: \"%s\".", FamilyStr.c_str(), Port, cSocket::GetLastErrorString().c_str()); + m_Sockets.pop_back(); + continue; + } + + LOGINFO("%s: Port %d is open for connections", FamilyStr.c_str(), Port); + } // for itr - Ports[] + + return !(m_Sockets.empty()); +} + + + + + +void cListenThread::Execute(void) +{ + if (m_Sockets.empty()) + { + LOGD("Empty cListenThread, ending thread now."); + return; + } + + // Find the highest socket number: + cSocket::xSocket Highest = m_Sockets[0].GetSocket(); + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + if (itr->GetSocket() > Highest) + { + Highest = itr->GetSocket(); + } + } // for itr - m_Sockets[] + + while (!m_ShouldTerminate) + { + // Put all sockets into a FD set: + fd_set fdRead; + FD_ZERO(&fdRead); + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + FD_SET(itr->GetSocket(), &fdRead); + } // for itr - m_Sockets[] + + timeval tv; // On Linux select() doesn't seem to wake up when socket is closed, so let's kinda busy-wait: + tv.tv_sec = 1; + tv.tv_usec = 0; + if (select(Highest + 1, &fdRead, NULL, NULL, &tv) == -1) + { + LOG("select(R) call failed in cListenThread: \"%s\"", cSocket::GetLastErrorString().c_str()); + continue; + } + for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr) + { + if (itr->IsValid() && FD_ISSET(itr->GetSocket(), &fdRead)) + { + cSocket Client = (m_Family == cSocket::IPv4) ? itr->AcceptIPv4() : itr->AcceptIPv6(); + m_Callback.OnConnectionAccepted(Client); + } + } // for itr - m_Sockets[] + } // while (!m_ShouldTerminate) +} + + + + diff --git a/source/OSSupport/ListenThread.h b/source/OSSupport/ListenThread.h index c29140ed3..4e337d814 100644 --- a/source/OSSupport/ListenThread.h +++ b/source/OSSupport/ListenThread.h @@ -1,83 +1,83 @@ - -// ListenThread.h - -// Declares the cListenThread class representing the thread that listens for client connections - - - - - -#pragma once - -#include "IsThread.h" -#include "Socket.h" - - - - - -// fwd: -class cServer; - - - - - -class cListenThread : - public cIsThread -{ - typedef cIsThread super; - -public: - /// Used as the callback for connection events - class cCallback - { - public: - /// This callback is called whenever a socket connection is accepted - virtual void OnConnectionAccepted(cSocket & a_Socket) = 0; - } ; - - cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = ""); - ~cListenThread(); - - /// Creates all the sockets, returns trus if successful, false if not. - bool Initialize(const AString & a_PortsString); - - bool Start(void); - - void Stop(void); - - /// Call before Initialize() to set the "reuse" flag on the sockets - void SetReuseAddr(bool a_Reuse = true); - -protected: - typedef std::vector cSockets; - - /// The callback which to notify of incoming connections - cCallback & m_Callback; - - /// Socket address family to use - cSocket::eFamily m_Family; - - /// Sockets that are being monitored - cSockets m_Sockets; - - /// If set to true, the SO_REUSEADDR socket option is set to true - bool m_ShouldReuseAddr; - - /// Name of the service that's listening on the ports; for logging purposes only - AString m_ServiceName; - - - /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString. - Returns true if successful and at least one socket has been created - */ - bool CreateSockets(const AString & a_PortsString); - - // cIsThread override: - virtual void Execute(void) override; -} ; - - - - + +// ListenThread.h + +// Declares the cListenThread class representing the thread that listens for client connections + + + + + +#pragma once + +#include "IsThread.h" +#include "Socket.h" + + + + + +// fwd: +class cServer; + + + + + +class cListenThread : + public cIsThread +{ + typedef cIsThread super; + +public: + /// Used as the callback for connection events + class cCallback + { + public: + /// This callback is called whenever a socket connection is accepted + virtual void OnConnectionAccepted(cSocket & a_Socket) = 0; + } ; + + cListenThread(cCallback & a_Callback, cSocket::eFamily a_Family, const AString & a_ServiceName = ""); + ~cListenThread(); + + /// Creates all the sockets, returns trus if successful, false if not. + bool Initialize(const AString & a_PortsString); + + bool Start(void); + + void Stop(void); + + /// Call before Initialize() to set the "reuse" flag on the sockets + void SetReuseAddr(bool a_Reuse = true); + +protected: + typedef std::vector cSockets; + + /// The callback which to notify of incoming connections + cCallback & m_Callback; + + /// Socket address family to use + cSocket::eFamily m_Family; + + /// Sockets that are being monitored + cSockets m_Sockets; + + /// If set to true, the SO_REUSEADDR socket option is set to true + bool m_ShouldReuseAddr; + + /// Name of the service that's listening on the ports; for logging purposes only + AString m_ServiceName; + + + /** Fills in m_Sockets with individual sockets, each for one port specified in a_PortsString. + Returns true if successful and at least one socket has been created + */ + bool CreateSockets(const AString & a_PortsString); + + // cIsThread override: + virtual void Execute(void) override; +} ; + + + + diff --git a/source/Player.cpp b/source/Player.cpp index c90f3c99c..ac74f03b8 100644 --- a/source/Player.cpp +++ b/source/Player.cpp @@ -1,1423 +1,1423 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Player.h" -#include "Server.h" -#include "ClientHandle.h" -#include "UI/Window.h" -#include "UI/WindowOwner.h" -#include "World.h" -#include "Pickup.h" -#include "PluginManager.h" -#include "BlockEntities/BlockEntity.h" -#include "GroupManager.h" -#include "Group.h" -#include "ChatColor.h" -#include "Item.h" -#include "Tracer.h" -#include "Root.h" -#include "OSSupport/MakeDir.h" -#include "OSSupport/Timer.h" -#include "MersenneTwister.h" -#include "Chunk.h" -#include "Items/ItemHandler.h" - -#include "Vector3d.h" -#include "Vector3f.h" - -#include "../iniFile/iniFile.h" -#include - -#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) - - - - - -cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) - : super(etPlayer, 0.6, 1.8) - , m_GameMode(eGameMode_NotSet) - , m_IP("") - , m_LastBlockActionTime( 0 ) - , m_LastBlockActionCnt( 0 ) - , m_bVisible( true ) - , m_LastGroundHeight( 0 ) - , m_bTouchGround( false ) - , m_Stance( 0.0 ) - , m_Inventory(*this) - , m_CurrentWindow(NULL) - , m_InventoryWindow(NULL) - , m_TimeLastPickupCheck( 0.f ) - , m_Color('-') - , m_ClientHandle( a_Client ) - , m_FoodLevel(20) - , m_FoodSaturationLevel(5) - , m_FoodTickTimer(0) - , m_FoodExhaustionLevel(0) - , m_FoodPoisonedTicksRemaining(0) - , m_NormalMaxSpeed(0.1) - , m_SprintingMaxSpeed(0.13) - , m_IsCrouched(false) - , m_IsSprinting(false) - , m_EatingFinishTick(-1) -{ - LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", - a_PlayerName.c_str(), a_Client->GetIPString().c_str(), - this, GetUniqueID() - ); - - m_InventoryWindow = new cInventoryWindow(*this); - m_CurrentWindow = m_InventoryWindow; - m_InventoryWindow->OpenedByPlayer(*this); - - SetMaxHealth(20); - - cTimer t1; - m_LastPlayerListTime = t1.GetNowTime(); - - m_TimeLastTeleportPacket = 0; - m_TimeLastPickupCheck = 0; - - m_PlayerName = a_PlayerName; - m_bDirtyPosition = true; // So chunks are streamed to player at spawn - - if (!LoadFromDisk()) - { - m_Inventory.Clear(); - SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX()); - SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY()); - SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ()); - - LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", - a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() - ); - } - m_LastJumpHeight = (float)(GetPosY()); - m_LastGroundHeight = (float)(GetPosY()); - m_Stance = GetPosY() + 1.62; -} - - - - - -cPlayer::~cPlayer(void) -{ - LOG("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); - - SaveToDisk(); - - m_World->RemovePlayer( this ); - - m_ClientHandle = NULL; - - delete m_InventoryWindow; - - LOGD("Player %p deleted", this); -} - - - - - -void cPlayer::Initialize(cWorld * a_World) -{ - super::Initialize(a_World); - GetWorld()->AddPlayer(this); -} - - - - - -void cPlayer::Destroyed() -{ - CloseWindow(false); - m_ClientHandle = NULL; -} - - - - - -void cPlayer::SpawnOn(cClientHandle & a_Client) -{ - /* - LOGD("cPlayer::SpawnOn(%s) for \"%s\" at pos {%.2f, %.2f, %.2f}", - a_Client.GetUsername().c_str(), m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z - ); - */ - - if (m_bVisible && (m_ClientHandle != (&a_Client))) - { - a_Client.SendPlayerSpawn(*this); - a_Client.SendEntityHeadLook(*this); - a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() ); - a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() ); - a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() ); - a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() ); - a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() ); - } -} - - - - - -void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (!m_ClientHandle->IsPlaying()) - { - // We're not yet in the game, ignore everything - return; - } - - super::Tick(a_Dt, a_Chunk); - if (m_bDirtyPosition) - { - // Apply food exhaustion from movement: - ApplyFoodExhaustionFromMovement(a_Chunk); - - cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this); - BroadcastMovementUpdate(m_ClientHandle); - m_ClientHandle->StreamChunks(); - } - else - { - BroadcastMovementUpdate(m_ClientHandle); - } - - if (m_Health > 0) // make sure player is alive - { - m_World->CollectPickupsByPlayer(this); - - if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) - { - FinishEating(); - } - - HandleFood(); - } - - // Send Player List (Once per m_LastPlayerListTime/1000 ms) - cTimer t1; - if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) - { - m_World->SendPlayerList(this); - m_LastPlayerListTime = t1.GetNowTime(); - } -} - - - - - -void cPlayer::SetTouchGround(bool a_bTouchGround) -{ - // If just - m_bTouchGround = a_bTouchGround; - - if (!m_bTouchGround) - { - if (GetPosY() > m_LastJumpHeight) - { - m_LastJumpHeight = (float)GetPosY(); - } - cWorld * World = GetWorld(); - if ((GetPosY() >= 0) && (GetPosY() < 256)) - { - BLOCKTYPE BlockType = World->GetBlock( float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ()) ); - if (BlockType != E_BLOCK_AIR) - { - // LOGD("TouchGround set to true by server"); - m_bTouchGround = true; - } - if ( - (BlockType == E_BLOCK_WATER) || - (BlockType == E_BLOCK_STATIONARY_WATER) || - (BlockType == E_BLOCK_LADDER) || - (BlockType == E_BLOCK_VINES) - ) - { - // LOGD("Water / Ladder / Torch"); - m_LastGroundHeight = (float)GetPosY(); - } - } - } - - if (m_bTouchGround) - { - float Dist = (float)(m_LastGroundHeight - floor(GetPosY())); - int Damage = (int)(Dist - 3.f); - if(m_LastJumpHeight > m_LastGroundHeight) Damage++; - m_LastJumpHeight = (float)GetPosY(); - if (Damage > 0) - { - TakeDamage(dtFalling, NULL, Damage, Damage, 0); - } - - m_LastGroundHeight = (float)GetPosY(); - } -} - - - - - -void cPlayer::Heal(int a_Health) -{ - if (m_Health < GetMaxHealth()) - { - m_Health = (short)std::min((int)a_Health + m_Health, (int)GetMaxHealth()); - SendHealth(); - } -} - - - - - -void cPlayer::SetFoodLevel(int a_FoodLevel) -{ - m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL)); - SendHealth(); -} - - - - - -void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel) -{ - m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel)); -} - - - - - -void cPlayer::SetFoodTickTimer(int a_FoodTickTimer) -{ - m_FoodTickTimer = a_FoodTickTimer; -} - - - - - -void cPlayer::SetFoodExhaustionLevel(double a_FoodSaturationLevel) -{ - m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodSaturationLevel, 4.0)); -} - - - - - -void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining) -{ - m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining; -} - - - - - -bool cPlayer::Feed(int a_Food, double a_Saturation) -{ - if (m_FoodLevel >= MAX_FOOD_LEVEL) - { - return false; - } - - m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL); - m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel); - - SendHealth(); - return true; -} - - - - - -void cPlayer::FoodPoison(int a_NumTicks) -{ - bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0); - m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks); - if (!HasBeenFoodPoisoned) - { - // TODO: Send the poisoning indication to the client - how? - SendHealth(); - } -} - - - - - -void cPlayer::StartEating(void) -{ - // Set the timer: - m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; - - // Send the packets: - m_World->BroadcastPlayerAnimation(*this, 5); - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::FinishEating(void) -{ - // Reset the timer: - m_EatingFinishTick = -1; - - // Send the packets: - m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED); - m_World->BroadcastPlayerAnimation(*this, 0); - m_World->BroadcastEntityMetadata(*this); - - // consume the item: - cItem Item(GetEquippedItem()); - Item.m_ItemCount = 1; - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Item.m_ItemType); - if (!ItemHandler->EatItem(this, &Item)) - { - return; - } - ItemHandler->OnFoodEaten(m_World, this, &Item); - GetInventory().RemoveOneEquippedItem(); -} - - - - - -void cPlayer::AbortEating(void) -{ - m_EatingFinishTick = -1; - m_World->BroadcastPlayerAnimation(*this, 0); - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::SendHealth(void) -{ - if (m_ClientHandle != NULL) - { - m_ClientHandle->SendHealth(); - } -} - - - - - -void cPlayer::ClearInventoryPaintSlots(void) -{ - // Clear the list of slots that are being inventory-painted. Used by cWindow only - m_InventoryPaintSlots.clear(); -} - - - - - -void cPlayer::AddInventoryPaintSlot(int a_SlotNum) -{ - // Add a slot to the list for inventory painting. Used by cWindow only - m_InventoryPaintSlots.push_back(a_SlotNum); -} - - - - - -const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const -{ - // Return the list of slots currently stored for inventory painting. Used by cWindow only - return m_InventoryPaintSlots; -} - - - - - -double cPlayer::GetMaxSpeed(void) const -{ - return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed; -} - - - - - -void cPlayer::SetNormalMaxSpeed(double a_Speed) -{ - m_NormalMaxSpeed = a_Speed; - if (!m_IsSprinting) - { - m_ClientHandle->SendPlayerMaxSpeed(); - } -} - - - - - -void cPlayer::SetSprintingMaxSpeed(double a_Speed) -{ - m_SprintingMaxSpeed = a_Speed; - if (m_IsSprinting) - { - m_ClientHandle->SendPlayerMaxSpeed(); - } -} - - - - - -void cPlayer::SetCrouch(bool a_IsCrouched) -{ - // Set the crouch status, broadcast to all visible players - - if (a_IsCrouched == m_IsCrouched) - { - // No change - return; - } - m_IsCrouched = a_IsCrouched; - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::SetSprint(bool a_IsSprinting) -{ - if (a_IsSprinting == m_IsSprinting) - { - // No change - return; - } - - m_IsSprinting = a_IsSprinting; - m_ClientHandle->SendPlayerMaxSpeed(); -} - - - - - -void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - if (m_GameMode == eGameMode_Creative) - { - // No damage / health in creative mode - return; - } - - super::DoTakeDamage(a_TDI); - - // Any kind of damage adds food exhaustion - AddFoodExhaustion(0.3f); - - SendHealth(); -} - - - - - -void cPlayer::KilledBy(cEntity * a_Killer) -{ - super::KilledBy(a_Killer); - - if (m_Health > 0) - { - return; // not dead yet =] - } - - m_bVisible = false; // So new clients don't see the player - - // Puke out all the items - cItems Pickups; - m_Inventory.CopyToItems(Pickups); - m_Inventory.Clear(); - m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); - SaveToDisk(); // Save it, yeah the world is a tough place ! -} - - - - - -void cPlayer::Respawn(void) -{ - m_Health = GetMaxHealth(); - - // Reset food level: - m_FoodLevel = 20; - m_FoodSaturationLevel = 5; - - m_ClientHandle->SendRespawn(); - - // Extinguish the fire: - StopBurning(); - - TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); - - SetVisible(true); -} - - - - - -double cPlayer::GetEyeHeight(void) const -{ - return m_Stance; -} - - - - -Vector3d cPlayer::GetEyePosition(void) const -{ - return Vector3d( GetPosX(), m_Stance, GetPosZ() ); -} - - - - - -bool cPlayer::IsGameModeCreative(void) const -{ - return (m_GameMode == gmCreative) || // Either the player is explicitly in Creative - ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Creative -} - - - - - -bool cPlayer::IsGameModeSurvival(void) const -{ - return (m_GameMode == gmSurvival) || // Either the player is explicitly in Survival - ((m_GameMode == gmNotSet) && m_World->IsGameModeSurvival()); // or they inherit from the world and the world is Survival -} - - - - - -bool cPlayer::IsGameModeAdventure(void) const -{ - return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure - ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure -} - - - - - -void cPlayer::OpenWindow(cWindow * a_Window) -{ - if (a_Window != m_CurrentWindow) - { - CloseWindow(false); - } - a_Window->OpenedByPlayer(*this); - m_CurrentWindow = a_Window; - a_Window->SendWholeWindow(*GetClientHandle()); -} - - - - - -void cPlayer::CloseWindow(bool a_CanRefuse) -{ - if (m_CurrentWindow == NULL) - { - m_CurrentWindow = m_InventoryWindow; - return; - } - - if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) - { - // Close accepted, go back to inventory window (the default): - m_CurrentWindow = m_InventoryWindow; - } - else - { - // Re-open the window - m_CurrentWindow->OpenedByPlayer(*this); - m_CurrentWindow->SendWholeWindow(*GetClientHandle()); - } -} - - - - - -void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse) -{ - if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID)) - { - return; - } - CloseWindow(); -} - - - - - -void cPlayer::SetLastBlockActionTime() -{ - if (m_World != NULL) - { - m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f; - } -} - - - - - -void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) -{ - m_LastBlockActionCnt = a_LastBlockActionCnt; -} - - - - - -void cPlayer::SetGameMode(eGameMode a_GameMode) -{ - if ((a_GameMode >= gmMin) || (a_GameMode < gmMax)) - { - LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); - return; - } - - if (m_GameMode == a_GameMode) - { - // Gamemode already set - return; - } - - m_GameMode = a_GameMode; - m_ClientHandle->SendGameMode(a_GameMode); -} - - - - - -void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) -{ - m_GameMode = a_GameMode; -} - - - - - -void cPlayer::SetIP(const AString & a_IP) -{ - m_IP = a_IP; -} - - - - - -void cPlayer::SendMessage(const AString & a_Message) -{ - m_ClientHandle->SendChat(a_Message); -} - - - - - -void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) -{ - SetPosition( a_PosX, a_PosY, a_PosZ ); - - m_World->BroadcastTeleportEntity(*this, GetClientHandle()); - m_ClientHandle->SendPlayerMoveLook(); -} - - - - - -void cPlayer::MoveTo( const Vector3d & a_NewPos ) -{ - if ((a_NewPos.y < -990) && (GetPosY() > -100)) - { - // When attached to an entity, the client sends position packets with weird coords: - // Y = -999 and X, Z = attempting to create speed, usually up to 0.03 - // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while - // the client may still send more of these nonsensical packets. - if (m_AttachedTo != NULL) - { - Vector3d AddSpeed(a_NewPos); - AddSpeed.y = 0; - m_AttachedTo->AddSpeed(AddSpeed); - } - return; - } - - // TODO: should do some checks to see if player is not moving through terrain - // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too - - SetPosition( a_NewPos ); - SetStance(a_NewPos.y + 1.62); -} - - - - - -void cPlayer::SetVisible(bool a_bVisible) -{ - if (a_bVisible && !m_bVisible) // Make visible - { - m_bVisible = true; - m_World->BroadcastSpawnEntity(*this); - } - if (!a_bVisible && m_bVisible) - { - m_bVisible = false; - m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients - } -} - - - - - -void cPlayer::AddToGroup( const AString & a_GroupName ) -{ - cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); - m_Groups.push_back( Group ); - LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); - ResolveGroups(); - ResolvePermissions(); -} - - - - - -void cPlayer::RemoveFromGroup( const AString & a_GroupName ) -{ - bool bRemoved = false; - for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) - { - if( (*itr)->GetName().compare(a_GroupName ) == 0 ) - { - m_Groups.erase( itr ); - bRemoved = true; - break; - } - } - - if( bRemoved ) - { - LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); - ResolveGroups(); - ResolvePermissions(); - } - else - { - LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() ); - } -} - - - - - -bool cPlayer::CanUseCommand( const AString & a_Command ) -{ - for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) - { - if( (*itr)->HasCommand( a_Command ) ) return true; - } - return false; -} - - - - - -bool cPlayer::HasPermission(const AString & a_Permission) -{ - if (a_Permission.empty()) - { - // Empty permission request is always granted - return true; - } - - AStringVector Split = StringSplit( a_Permission, "." ); - PermissionMap Possibilities = m_ResolvedPermissions; - // Now search the namespaces - while( Possibilities.begin() != Possibilities.end() ) - { - PermissionMap::iterator itr = Possibilities.begin(); - if( itr->second ) - { - AStringVector OtherSplit = StringSplit( itr->first, "." ); - if( OtherSplit.size() <= Split.size() ) - { - unsigned int i; - for( i = 0; i < OtherSplit.size(); ++i ) - { - if( OtherSplit[i].compare( Split[i] ) != 0 ) - { - if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard! - break; - } - } - if( i == Split.size() ) return true; - } - } - Possibilities.erase( itr ); - } - - // Nothing that matched :( - return false; -} - - - - - -bool cPlayer::IsInGroup( const AString & a_Group ) -{ - for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) - { - if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 ) - return true; - } - return false; -} - - - - - -void cPlayer::ResolvePermissions() -{ - m_ResolvedPermissions.clear(); // Start with an empty map yo~ - - // Copy all player specific permissions into the resolved permissions map - for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - - for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) - { - const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); - for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - } -} - - - - - -void cPlayer::ResolveGroups() -{ - // Clear resolved groups first - m_ResolvedGroups.clear(); - - // Get a complete resolved list of all groups the player is in - std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates - GroupList ToIterate; - for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) - { - ToIterate.push_back( *GroupItr ); - } - while( ToIterate.begin() != ToIterate.end() ) - { - cGroup* CurrentGroup = *ToIterate.begin(); - if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) - { - LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", - m_PlayerName.c_str(), CurrentGroup->GetName().c_str() - ); - } - else - { - AllGroups[ CurrentGroup ] = true; - m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list - const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); - for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr ) - { - if( AllGroups.find( *itr ) != AllGroups.end() ) - { - LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() ); - continue; - } - ToIterate.push_back( *itr ); - } - } - ToIterate.erase( ToIterate.begin() ); - } -} - - - - - -AString cPlayer::GetColor(void) const -{ - if ( m_Color != '-' ) - { - return cChatColor::MakeColor( m_Color ); - } - - if ( m_Groups.size() < 1 ) - { - return cChatColor::White; - } - - return (*m_Groups.begin())->GetColor(); -} - - - - - -void cPlayer::TossItem( - bool a_bDraggingItem, - char a_Amount /* = 1 */, - short a_CreateType /* = 0 */, - short a_CreateHealth /* = 0 */ -) -{ - cItems Drops; - if (a_CreateType != 0) - { - // Just create item without touching the inventory (used in creative mode) - Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth)); - } - else - { - // Drop an item from the inventory: - if (a_bDraggingItem) - { - cItem & Item = GetDraggingItem(); - if (!Item.IsEmpty()) - { - char OriginalItemAmount = Item.m_ItemCount; - Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount); - Drops.push_back(Item); - if (OriginalItemAmount > a_Amount) - { - Item.m_ItemCount = OriginalItemAmount - (char)a_Amount; - } - else - { - Item.Empty(); - } - } - } - else - { - // Else drop equipped item - cItem DroppedItem(GetInventory().GetEquippedItem()); - if (!DroppedItem.IsEmpty()) - { - if (GetInventory().RemoveOneEquippedItem()) - { - DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again - Drops.push_back(DroppedItem); - } - } - } - } - double vX = 0, vY = 0, vZ = 0; - EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); - vY = -vY * 2 + 1.f; - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2); -} - - - - - -bool cPlayer::MoveToWorld(const char * a_WorldName) -{ - cWorld * World = cRoot::Get()->GetWorld(a_WorldName); - if (World == NULL) - { - LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName); - return false; - } - - eDimension OldDimension = m_World->GetDimension(); - - // Remove all links to the old world - m_World->RemovePlayer(this); - m_ClientHandle->RemoveFromAllChunks(); - m_World->RemoveEntity(this); - - // Add player to all the necessary parts of the new world - SetWorld(World); - World->AddEntity(this); - World->AddPlayer(this); - - // If the dimension is different, we can send the respawn packet - // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02 - if (OldDimension != World->GetDimension()) - { - m_ClientHandle->SendRespawn(); - } - - // Stream the new chunks: - m_ClientHandle->StreamChunks(); - return true; -} - - - - - -void cPlayer::LoadPermissionsFromDisk() -{ - m_Groups.clear(); - m_Permissions.clear(); - - cIniFile IniFile("users.ini"); - if( IniFile.ReadFile() ) - { - std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); - if( Groups.size() > 0 ) - { - AStringVector Split = StringSplit( Groups, "," ); - for( unsigned int i = 0; i < Split.size(); i++ ) - { - AddToGroup( Split[i].c_str() ); - } - } - else - { - AddToGroup("Default"); - } - - m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; - } - else - { - LOGWARN("WARNING: Failed to read ini file users.ini"); - AddToGroup("Default"); - } - ResolvePermissions(); -} - - - - -bool cPlayer::LoadFromDisk() -{ - LoadPermissionsFromDisk(); - - // Log player permissions, cause it's what the cool kids do - LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); - for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) - { - if( itr->second ) LOGINFO("%s", itr->first.c_str() ); - } - - AString SourceFile; - Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) - { - LOGWARNING("Cannot open player data file \"%s\" for reading", SourceFile.c_str()); - return false; - } - - AString buffer; - if (f.ReadRestOfFile(buffer) != f.GetSize()) - { - LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); - return false; - } - f.Close(); - - Json::Value root; - Json::Reader reader; - if (!reader.parse(buffer, root, false)) - { - LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); - } - - Json::Value & JSON_PlayerPosition = root["position"]; - if (JSON_PlayerPosition.size() == 3) - { - SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); - SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); - SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_LastFoodPos = GetPosition(); - } - - Json::Value & JSON_PlayerRotation = root["rotation"]; - if (JSON_PlayerRotation.size() == 3) - { - SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); - SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); - SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); - } - - m_Health = root.get("health", 0).asInt(); - - m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt(); - m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); - m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); - m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); - - m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); - - m_Inventory.LoadFromJson(root["inventory"]); - - m_LoadedWorldName = root.get("world", "world").asString(); - - LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", - m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() - ); - - return true; -} - - - - - -bool cPlayer::SaveToDisk() -{ - cMakeDir::MakeDir("players"); - - // create the JSON data - Json::Value JSON_PlayerPosition; - JSON_PlayerPosition.append(Json::Value(GetPosX())); - JSON_PlayerPosition.append(Json::Value(GetPosY())); - JSON_PlayerPosition.append(Json::Value(GetPosZ())); - - Json::Value JSON_PlayerRotation; - JSON_PlayerRotation.append(Json::Value(GetRotation())); - JSON_PlayerRotation.append(Json::Value(GetPitch())); - JSON_PlayerRotation.append(Json::Value(GetRoll())); - - Json::Value JSON_Inventory; - m_Inventory.SaveToJson(JSON_Inventory); - - Json::Value root; - root["position"] = JSON_PlayerPosition; - root["rotation"] = JSON_PlayerRotation; - root["inventory"] = JSON_Inventory; - root["health"] = m_Health; - root["food"] = m_FoodLevel; - root["foodSaturation"] = m_FoodSaturationLevel; - root["foodTickTimer"] = m_FoodTickTimer; - root["foodExhaustion"] = m_FoodExhaustionLevel; - root["world"] = GetWorld()->GetName(); - - if (m_GameMode == GetWorld()->GetGameMode()) - { - root["gamemode"] = (int) eGameMode_NotSet; - } - else - { - root["gamemode"] = (int) m_GameMode; - } - - Json::StyledWriter writer; - std::string JsonData = writer.write(root); - - AString SourceFile; - Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmWrite)) - { - LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); - return false; - } - if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) - { - LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); - return false; - } - return true; -} - - - - - -cPlayer::StringList cPlayer::GetResolvedPermissions() -{ - StringList Permissions; - - const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; - for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) - { - if( itr->second ) Permissions.push_back( itr->first ); - } - - return Permissions; -} - - - - - -void cPlayer::UseEquippedItem() -{ - if (GetGameMode() == gmCreative) // No damage in creative - { - return; - } - - GetInventory().DamageEquippedItem(); -} - - - - - -void cPlayer::HandleFood(void) -{ - // Ref.: http://www.minecraftwiki.net/wiki/Hunger - - // Remember the food level before processing, for later comparison - int LastFoodLevel = m_FoodLevel; - - // Heal or damage, based on the food level, using the m_FoodTickTimer: - if ((m_FoodLevel > 17) || (m_FoodLevel <= 0)) - { - m_FoodTickTimer++; - if (m_FoodTickTimer >= 80) - { - m_FoodTickTimer = 0; - - if (m_FoodLevel >= 17) - { - // Regenerate health from food, incur 3 pts of food exhaustion: - Heal(1); - m_FoodExhaustionLevel += 3; - } - else if (m_FoodLevel <= 0) - { - // Damage from starving - TakeDamage(dtStarving, NULL, 1, 1, 0); - } - } - } - - // Apply food poisoning food exhaustion: - if (m_FoodPoisonedTicksRemaining > 0) - { - m_FoodPoisonedTicksRemaining--; - m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick - } - - // Apply food exhaustion that has accumulated: - if (m_FoodExhaustionLevel >= 4) - { - m_FoodExhaustionLevel -= 4; - - if (m_FoodSaturationLevel >= 1) - { - m_FoodSaturationLevel -= 1; - } - else - { - m_FoodLevel = std::max(m_FoodLevel - 1, 0); - } - } - - if (m_FoodLevel != LastFoodLevel) - { - SendHealth(); - } -} - - - - - -void cPlayer::ApplyFoodExhaustionFromMovement(cChunk & a_Chunk) -{ - if (IsGameModeCreative()) - { - return; - } - - // Calculate the distance travelled, update the last pos: - Vector3d Movement(GetPosition() - m_LastFoodPos); - Movement.y = 0; // Only take XZ movement into account - m_LastFoodPos = GetPosition(); - - // If riding anything, apply no food exhaustion - if (m_AttachedTo != NULL) - { - return; - } - - // Get the type of block the player's standing in: - BLOCKTYPE BlockIn; - int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelY = (int)floor(m_LastPosY + 0.1); - int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width; - // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk - VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn)); - - // Apply the exhaustion based on distance travelled: - double BaseExhaustion = Movement.Length(); - if (IsSprinting()) - { - // 0.1 pt per meter sprinted - BaseExhaustion = BaseExhaustion * 0.1; - } - else if (IsBlockWater(BlockIn)) - { - // 0.015 pt per meter swum - BaseExhaustion = BaseExhaustion * 0.015; - } - else - { - // 0.01 pt per meter walked / sneaked - BaseExhaustion = BaseExhaustion * 0.01; - } - m_FoodExhaustionLevel += BaseExhaustion; -} - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Player.h" +#include "Server.h" +#include "ClientHandle.h" +#include "UI/Window.h" +#include "UI/WindowOwner.h" +#include "World.h" +#include "Pickup.h" +#include "PluginManager.h" +#include "BlockEntities/BlockEntity.h" +#include "GroupManager.h" +#include "Group.h" +#include "ChatColor.h" +#include "Item.h" +#include "Tracer.h" +#include "Root.h" +#include "OSSupport/MakeDir.h" +#include "OSSupport/Timer.h" +#include "MersenneTwister.h" +#include "Chunk.h" +#include "Items/ItemHandler.h" + +#include "Vector3d.h" +#include "Vector3f.h" + +#include "../iniFile/iniFile.h" +#include + +#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) + + + + + +cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) + : super(etPlayer, 0.6, 1.8) + , m_GameMode(eGameMode_NotSet) + , m_IP("") + , m_LastBlockActionTime( 0 ) + , m_LastBlockActionCnt( 0 ) + , m_bVisible( true ) + , m_LastGroundHeight( 0 ) + , m_bTouchGround( false ) + , m_Stance( 0.0 ) + , m_Inventory(*this) + , m_CurrentWindow(NULL) + , m_InventoryWindow(NULL) + , m_TimeLastPickupCheck( 0.f ) + , m_Color('-') + , m_ClientHandle( a_Client ) + , m_FoodLevel(20) + , m_FoodSaturationLevel(5) + , m_FoodTickTimer(0) + , m_FoodExhaustionLevel(0) + , m_FoodPoisonedTicksRemaining(0) + , m_NormalMaxSpeed(0.1) + , m_SprintingMaxSpeed(0.13) + , m_IsCrouched(false) + , m_IsSprinting(false) + , m_EatingFinishTick(-1) +{ + LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", + a_PlayerName.c_str(), a_Client->GetIPString().c_str(), + this, GetUniqueID() + ); + + m_InventoryWindow = new cInventoryWindow(*this); + m_CurrentWindow = m_InventoryWindow; + m_InventoryWindow->OpenedByPlayer(*this); + + SetMaxHealth(20); + + cTimer t1; + m_LastPlayerListTime = t1.GetNowTime(); + + m_TimeLastTeleportPacket = 0; + m_TimeLastPickupCheck = 0; + + m_PlayerName = a_PlayerName; + m_bDirtyPosition = true; // So chunks are streamed to player at spawn + + if (!LoadFromDisk()) + { + m_Inventory.Clear(); + SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX()); + SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY()); + SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ()); + + LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", + a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() + ); + } + m_LastJumpHeight = (float)(GetPosY()); + m_LastGroundHeight = (float)(GetPosY()); + m_Stance = GetPosY() + 1.62; +} + + + + + +cPlayer::~cPlayer(void) +{ + LOG("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); + + SaveToDisk(); + + m_World->RemovePlayer( this ); + + m_ClientHandle = NULL; + + delete m_InventoryWindow; + + LOGD("Player %p deleted", this); +} + + + + + +void cPlayer::Initialize(cWorld * a_World) +{ + super::Initialize(a_World); + GetWorld()->AddPlayer(this); +} + + + + + +void cPlayer::Destroyed() +{ + CloseWindow(false); + m_ClientHandle = NULL; +} + + + + + +void cPlayer::SpawnOn(cClientHandle & a_Client) +{ + /* + LOGD("cPlayer::SpawnOn(%s) for \"%s\" at pos {%.2f, %.2f, %.2f}", + a_Client.GetUsername().c_str(), m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z + ); + */ + + if (m_bVisible && (m_ClientHandle != (&a_Client))) + { + a_Client.SendPlayerSpawn(*this); + a_Client.SendEntityHeadLook(*this); + a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() ); + a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() ); + a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() ); + a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() ); + a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() ); + } +} + + + + + +void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (!m_ClientHandle->IsPlaying()) + { + // We're not yet in the game, ignore everything + return; + } + + super::Tick(a_Dt, a_Chunk); + if (m_bDirtyPosition) + { + // Apply food exhaustion from movement: + ApplyFoodExhaustionFromMovement(a_Chunk); + + cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this); + BroadcastMovementUpdate(m_ClientHandle); + m_ClientHandle->StreamChunks(); + } + else + { + BroadcastMovementUpdate(m_ClientHandle); + } + + if (m_Health > 0) // make sure player is alive + { + m_World->CollectPickupsByPlayer(this); + + if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) + { + FinishEating(); + } + + HandleFood(); + } + + // Send Player List (Once per m_LastPlayerListTime/1000 ms) + cTimer t1; + if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) + { + m_World->SendPlayerList(this); + m_LastPlayerListTime = t1.GetNowTime(); + } +} + + + + + +void cPlayer::SetTouchGround(bool a_bTouchGround) +{ + // If just + m_bTouchGround = a_bTouchGround; + + if (!m_bTouchGround) + { + if (GetPosY() > m_LastJumpHeight) + { + m_LastJumpHeight = (float)GetPosY(); + } + cWorld * World = GetWorld(); + if ((GetPosY() >= 0) && (GetPosY() < 256)) + { + BLOCKTYPE BlockType = World->GetBlock( float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ()) ); + if (BlockType != E_BLOCK_AIR) + { + // LOGD("TouchGround set to true by server"); + m_bTouchGround = true; + } + if ( + (BlockType == E_BLOCK_WATER) || + (BlockType == E_BLOCK_STATIONARY_WATER) || + (BlockType == E_BLOCK_LADDER) || + (BlockType == E_BLOCK_VINES) + ) + { + // LOGD("Water / Ladder / Torch"); + m_LastGroundHeight = (float)GetPosY(); + } + } + } + + if (m_bTouchGround) + { + float Dist = (float)(m_LastGroundHeight - floor(GetPosY())); + int Damage = (int)(Dist - 3.f); + if(m_LastJumpHeight > m_LastGroundHeight) Damage++; + m_LastJumpHeight = (float)GetPosY(); + if (Damage > 0) + { + TakeDamage(dtFalling, NULL, Damage, Damage, 0); + } + + m_LastGroundHeight = (float)GetPosY(); + } +} + + + + + +void cPlayer::Heal(int a_Health) +{ + if (m_Health < GetMaxHealth()) + { + m_Health = (short)std::min((int)a_Health + m_Health, (int)GetMaxHealth()); + SendHealth(); + } +} + + + + + +void cPlayer::SetFoodLevel(int a_FoodLevel) +{ + m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL)); + SendHealth(); +} + + + + + +void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel) +{ + m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel)); +} + + + + + +void cPlayer::SetFoodTickTimer(int a_FoodTickTimer) +{ + m_FoodTickTimer = a_FoodTickTimer; +} + + + + + +void cPlayer::SetFoodExhaustionLevel(double a_FoodSaturationLevel) +{ + m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodSaturationLevel, 4.0)); +} + + + + + +void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining) +{ + m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining; +} + + + + + +bool cPlayer::Feed(int a_Food, double a_Saturation) +{ + if (m_FoodLevel >= MAX_FOOD_LEVEL) + { + return false; + } + + m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL); + m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel); + + SendHealth(); + return true; +} + + + + + +void cPlayer::FoodPoison(int a_NumTicks) +{ + bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0); + m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks); + if (!HasBeenFoodPoisoned) + { + // TODO: Send the poisoning indication to the client - how? + SendHealth(); + } +} + + + + + +void cPlayer::StartEating(void) +{ + // Set the timer: + m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; + + // Send the packets: + m_World->BroadcastPlayerAnimation(*this, 5); + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::FinishEating(void) +{ + // Reset the timer: + m_EatingFinishTick = -1; + + // Send the packets: + m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED); + m_World->BroadcastPlayerAnimation(*this, 0); + m_World->BroadcastEntityMetadata(*this); + + // consume the item: + cItem Item(GetEquippedItem()); + Item.m_ItemCount = 1; + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Item.m_ItemType); + if (!ItemHandler->EatItem(this, &Item)) + { + return; + } + ItemHandler->OnFoodEaten(m_World, this, &Item); + GetInventory().RemoveOneEquippedItem(); +} + + + + + +void cPlayer::AbortEating(void) +{ + m_EatingFinishTick = -1; + m_World->BroadcastPlayerAnimation(*this, 0); + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::SendHealth(void) +{ + if (m_ClientHandle != NULL) + { + m_ClientHandle->SendHealth(); + } +} + + + + + +void cPlayer::ClearInventoryPaintSlots(void) +{ + // Clear the list of slots that are being inventory-painted. Used by cWindow only + m_InventoryPaintSlots.clear(); +} + + + + + +void cPlayer::AddInventoryPaintSlot(int a_SlotNum) +{ + // Add a slot to the list for inventory painting. Used by cWindow only + m_InventoryPaintSlots.push_back(a_SlotNum); +} + + + + + +const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const +{ + // Return the list of slots currently stored for inventory painting. Used by cWindow only + return m_InventoryPaintSlots; +} + + + + + +double cPlayer::GetMaxSpeed(void) const +{ + return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed; +} + + + + + +void cPlayer::SetNormalMaxSpeed(double a_Speed) +{ + m_NormalMaxSpeed = a_Speed; + if (!m_IsSprinting) + { + m_ClientHandle->SendPlayerMaxSpeed(); + } +} + + + + + +void cPlayer::SetSprintingMaxSpeed(double a_Speed) +{ + m_SprintingMaxSpeed = a_Speed; + if (m_IsSprinting) + { + m_ClientHandle->SendPlayerMaxSpeed(); + } +} + + + + + +void cPlayer::SetCrouch(bool a_IsCrouched) +{ + // Set the crouch status, broadcast to all visible players + + if (a_IsCrouched == m_IsCrouched) + { + // No change + return; + } + m_IsCrouched = a_IsCrouched; + m_World->BroadcastEntityMetadata(*this); +} + + + + + +void cPlayer::SetSprint(bool a_IsSprinting) +{ + if (a_IsSprinting == m_IsSprinting) + { + // No change + return; + } + + m_IsSprinting = a_IsSprinting; + m_ClientHandle->SendPlayerMaxSpeed(); +} + + + + + +void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + if (m_GameMode == eGameMode_Creative) + { + // No damage / health in creative mode + return; + } + + super::DoTakeDamage(a_TDI); + + // Any kind of damage adds food exhaustion + AddFoodExhaustion(0.3f); + + SendHealth(); +} + + + + + +void cPlayer::KilledBy(cEntity * a_Killer) +{ + super::KilledBy(a_Killer); + + if (m_Health > 0) + { + return; // not dead yet =] + } + + m_bVisible = false; // So new clients don't see the player + + // Puke out all the items + cItems Pickups; + m_Inventory.CopyToItems(Pickups); + m_Inventory.Clear(); + m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); + SaveToDisk(); // Save it, yeah the world is a tough place ! +} + + + + + +void cPlayer::Respawn(void) +{ + m_Health = GetMaxHealth(); + + // Reset food level: + m_FoodLevel = 20; + m_FoodSaturationLevel = 5; + + m_ClientHandle->SendRespawn(); + + // Extinguish the fire: + StopBurning(); + + TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); + + SetVisible(true); +} + + + + + +double cPlayer::GetEyeHeight(void) const +{ + return m_Stance; +} + + + + +Vector3d cPlayer::GetEyePosition(void) const +{ + return Vector3d( GetPosX(), m_Stance, GetPosZ() ); +} + + + + + +bool cPlayer::IsGameModeCreative(void) const +{ + return (m_GameMode == gmCreative) || // Either the player is explicitly in Creative + ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Creative +} + + + + + +bool cPlayer::IsGameModeSurvival(void) const +{ + return (m_GameMode == gmSurvival) || // Either the player is explicitly in Survival + ((m_GameMode == gmNotSet) && m_World->IsGameModeSurvival()); // or they inherit from the world and the world is Survival +} + + + + + +bool cPlayer::IsGameModeAdventure(void) const +{ + return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure + ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure +} + + + + + +void cPlayer::OpenWindow(cWindow * a_Window) +{ + if (a_Window != m_CurrentWindow) + { + CloseWindow(false); + } + a_Window->OpenedByPlayer(*this); + m_CurrentWindow = a_Window; + a_Window->SendWholeWindow(*GetClientHandle()); +} + + + + + +void cPlayer::CloseWindow(bool a_CanRefuse) +{ + if (m_CurrentWindow == NULL) + { + m_CurrentWindow = m_InventoryWindow; + return; + } + + if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) + { + // Close accepted, go back to inventory window (the default): + m_CurrentWindow = m_InventoryWindow; + } + else + { + // Re-open the window + m_CurrentWindow->OpenedByPlayer(*this); + m_CurrentWindow->SendWholeWindow(*GetClientHandle()); + } +} + + + + + +void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse) +{ + if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID)) + { + return; + } + CloseWindow(); +} + + + + + +void cPlayer::SetLastBlockActionTime() +{ + if (m_World != NULL) + { + m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f; + } +} + + + + + +void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) +{ + m_LastBlockActionCnt = a_LastBlockActionCnt; +} + + + + + +void cPlayer::SetGameMode(eGameMode a_GameMode) +{ + if ((a_GameMode >= gmMin) || (a_GameMode < gmMax)) + { + LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); + return; + } + + if (m_GameMode == a_GameMode) + { + // Gamemode already set + return; + } + + m_GameMode = a_GameMode; + m_ClientHandle->SendGameMode(a_GameMode); +} + + + + + +void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) +{ + m_GameMode = a_GameMode; +} + + + + + +void cPlayer::SetIP(const AString & a_IP) +{ + m_IP = a_IP; +} + + + + + +void cPlayer::SendMessage(const AString & a_Message) +{ + m_ClientHandle->SendChat(a_Message); +} + + + + + +void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) +{ + SetPosition( a_PosX, a_PosY, a_PosZ ); + + m_World->BroadcastTeleportEntity(*this, GetClientHandle()); + m_ClientHandle->SendPlayerMoveLook(); +} + + + + + +void cPlayer::MoveTo( const Vector3d & a_NewPos ) +{ + if ((a_NewPos.y < -990) && (GetPosY() > -100)) + { + // When attached to an entity, the client sends position packets with weird coords: + // Y = -999 and X, Z = attempting to create speed, usually up to 0.03 + // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while + // the client may still send more of these nonsensical packets. + if (m_AttachedTo != NULL) + { + Vector3d AddSpeed(a_NewPos); + AddSpeed.y = 0; + m_AttachedTo->AddSpeed(AddSpeed); + } + return; + } + + // TODO: should do some checks to see if player is not moving through terrain + // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too + + SetPosition( a_NewPos ); + SetStance(a_NewPos.y + 1.62); +} + + + + + +void cPlayer::SetVisible(bool a_bVisible) +{ + if (a_bVisible && !m_bVisible) // Make visible + { + m_bVisible = true; + m_World->BroadcastSpawnEntity(*this); + } + if (!a_bVisible && m_bVisible) + { + m_bVisible = false; + m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients + } +} + + + + + +void cPlayer::AddToGroup( const AString & a_GroupName ) +{ + cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); + m_Groups.push_back( Group ); + LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); + ResolveGroups(); + ResolvePermissions(); +} + + + + + +void cPlayer::RemoveFromGroup( const AString & a_GroupName ) +{ + bool bRemoved = false; + for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) + { + if( (*itr)->GetName().compare(a_GroupName ) == 0 ) + { + m_Groups.erase( itr ); + bRemoved = true; + break; + } + } + + if( bRemoved ) + { + LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); + ResolveGroups(); + ResolvePermissions(); + } + else + { + LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() ); + } +} + + + + + +bool cPlayer::CanUseCommand( const AString & a_Command ) +{ + for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) + { + if( (*itr)->HasCommand( a_Command ) ) return true; + } + return false; +} + + + + + +bool cPlayer::HasPermission(const AString & a_Permission) +{ + if (a_Permission.empty()) + { + // Empty permission request is always granted + return true; + } + + AStringVector Split = StringSplit( a_Permission, "." ); + PermissionMap Possibilities = m_ResolvedPermissions; + // Now search the namespaces + while( Possibilities.begin() != Possibilities.end() ) + { + PermissionMap::iterator itr = Possibilities.begin(); + if( itr->second ) + { + AStringVector OtherSplit = StringSplit( itr->first, "." ); + if( OtherSplit.size() <= Split.size() ) + { + unsigned int i; + for( i = 0; i < OtherSplit.size(); ++i ) + { + if( OtherSplit[i].compare( Split[i] ) != 0 ) + { + if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard! + break; + } + } + if( i == Split.size() ) return true; + } + } + Possibilities.erase( itr ); + } + + // Nothing that matched :( + return false; +} + + + + + +bool cPlayer::IsInGroup( const AString & a_Group ) +{ + for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) + { + if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 ) + return true; + } + return false; +} + + + + + +void cPlayer::ResolvePermissions() +{ + m_ResolvedPermissions.clear(); // Start with an empty map yo~ + + // Copy all player specific permissions into the resolved permissions map + for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + + for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) + { + const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); + for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + } +} + + + + + +void cPlayer::ResolveGroups() +{ + // Clear resolved groups first + m_ResolvedGroups.clear(); + + // Get a complete resolved list of all groups the player is in + std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates + GroupList ToIterate; + for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) + { + ToIterate.push_back( *GroupItr ); + } + while( ToIterate.begin() != ToIterate.end() ) + { + cGroup* CurrentGroup = *ToIterate.begin(); + if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) + { + LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", + m_PlayerName.c_str(), CurrentGroup->GetName().c_str() + ); + } + else + { + AllGroups[ CurrentGroup ] = true; + m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list + const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); + for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr ) + { + if( AllGroups.find( *itr ) != AllGroups.end() ) + { + LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() ); + continue; + } + ToIterate.push_back( *itr ); + } + } + ToIterate.erase( ToIterate.begin() ); + } +} + + + + + +AString cPlayer::GetColor(void) const +{ + if ( m_Color != '-' ) + { + return cChatColor::MakeColor( m_Color ); + } + + if ( m_Groups.size() < 1 ) + { + return cChatColor::White; + } + + return (*m_Groups.begin())->GetColor(); +} + + + + + +void cPlayer::TossItem( + bool a_bDraggingItem, + char a_Amount /* = 1 */, + short a_CreateType /* = 0 */, + short a_CreateHealth /* = 0 */ +) +{ + cItems Drops; + if (a_CreateType != 0) + { + // Just create item without touching the inventory (used in creative mode) + Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth)); + } + else + { + // Drop an item from the inventory: + if (a_bDraggingItem) + { + cItem & Item = GetDraggingItem(); + if (!Item.IsEmpty()) + { + char OriginalItemAmount = Item.m_ItemCount; + Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount); + Drops.push_back(Item); + if (OriginalItemAmount > a_Amount) + { + Item.m_ItemCount = OriginalItemAmount - (char)a_Amount; + } + else + { + Item.Empty(); + } + } + } + else + { + // Else drop equipped item + cItem DroppedItem(GetInventory().GetEquippedItem()); + if (!DroppedItem.IsEmpty()) + { + if (GetInventory().RemoveOneEquippedItem()) + { + DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again + Drops.push_back(DroppedItem); + } + } + } + } + double vX = 0, vY = 0, vZ = 0; + EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); + vY = -vY * 2 + 1.f; + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2); +} + + + + + +bool cPlayer::MoveToWorld(const char * a_WorldName) +{ + cWorld * World = cRoot::Get()->GetWorld(a_WorldName); + if (World == NULL) + { + LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName); + return false; + } + + eDimension OldDimension = m_World->GetDimension(); + + // Remove all links to the old world + m_World->RemovePlayer(this); + m_ClientHandle->RemoveFromAllChunks(); + m_World->RemoveEntity(this); + + // Add player to all the necessary parts of the new world + SetWorld(World); + World->AddEntity(this); + World->AddPlayer(this); + + // If the dimension is different, we can send the respawn packet + // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02 + if (OldDimension != World->GetDimension()) + { + m_ClientHandle->SendRespawn(); + } + + // Stream the new chunks: + m_ClientHandle->StreamChunks(); + return true; +} + + + + + +void cPlayer::LoadPermissionsFromDisk() +{ + m_Groups.clear(); + m_Permissions.clear(); + + cIniFile IniFile("users.ini"); + if( IniFile.ReadFile() ) + { + std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); + if( Groups.size() > 0 ) + { + AStringVector Split = StringSplit( Groups, "," ); + for( unsigned int i = 0; i < Split.size(); i++ ) + { + AddToGroup( Split[i].c_str() ); + } + } + else + { + AddToGroup("Default"); + } + + m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; + } + else + { + LOGWARN("WARNING: Failed to read ini file users.ini"); + AddToGroup("Default"); + } + ResolvePermissions(); +} + + + + +bool cPlayer::LoadFromDisk() +{ + LoadPermissionsFromDisk(); + + // Log player permissions, cause it's what the cool kids do + LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); + for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) LOGINFO("%s", itr->first.c_str() ); + } + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmRead)) + { + LOGWARNING("Cannot open player data file \"%s\" for reading", SourceFile.c_str()); + return false; + } + + AString buffer; + if (f.ReadRestOfFile(buffer) != f.GetSize()) + { + LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); + return false; + } + f.Close(); + + Json::Value root; + Json::Reader reader; + if (!reader.parse(buffer, root, false)) + { + LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); + } + + Json::Value & JSON_PlayerPosition = root["position"]; + if (JSON_PlayerPosition.size() == 3) + { + SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); + SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); + SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); + m_LastPosX = GetPosX(); + m_LastPosY = GetPosY(); + m_LastPosZ = GetPosZ(); + m_LastFoodPos = GetPosition(); + } + + Json::Value & JSON_PlayerRotation = root["rotation"]; + if (JSON_PlayerRotation.size() == 3) + { + SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); + SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); + SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); + } + + m_Health = root.get("health", 0).asInt(); + + m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt(); + m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); + m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); + m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); + + m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); + + m_Inventory.LoadFromJson(root["inventory"]); + + m_LoadedWorldName = root.get("world", "world").asString(); + + LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() + ); + + return true; +} + + + + + +bool cPlayer::SaveToDisk() +{ + cMakeDir::MakeDir("players"); + + // create the JSON data + Json::Value JSON_PlayerPosition; + JSON_PlayerPosition.append(Json::Value(GetPosX())); + JSON_PlayerPosition.append(Json::Value(GetPosY())); + JSON_PlayerPosition.append(Json::Value(GetPosZ())); + + Json::Value JSON_PlayerRotation; + JSON_PlayerRotation.append(Json::Value(GetRotation())); + JSON_PlayerRotation.append(Json::Value(GetPitch())); + JSON_PlayerRotation.append(Json::Value(GetRoll())); + + Json::Value JSON_Inventory; + m_Inventory.SaveToJson(JSON_Inventory); + + Json::Value root; + root["position"] = JSON_PlayerPosition; + root["rotation"] = JSON_PlayerRotation; + root["inventory"] = JSON_Inventory; + root["health"] = m_Health; + root["food"] = m_FoodLevel; + root["foodSaturation"] = m_FoodSaturationLevel; + root["foodTickTimer"] = m_FoodTickTimer; + root["foodExhaustion"] = m_FoodExhaustionLevel; + root["world"] = GetWorld()->GetName(); + + if (m_GameMode == GetWorld()->GetGameMode()) + { + root["gamemode"] = (int) eGameMode_NotSet; + } + else + { + root["gamemode"] = (int) m_GameMode; + } + + Json::StyledWriter writer; + std::string JsonData = writer.write(root); + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmWrite)) + { + LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); + return false; + } + if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) + { + LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); + return false; + } + return true; +} + + + + + +cPlayer::StringList cPlayer::GetResolvedPermissions() +{ + StringList Permissions; + + const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; + for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) Permissions.push_back( itr->first ); + } + + return Permissions; +} + + + + + +void cPlayer::UseEquippedItem() +{ + if (GetGameMode() == gmCreative) // No damage in creative + { + return; + } + + GetInventory().DamageEquippedItem(); +} + + + + + +void cPlayer::HandleFood(void) +{ + // Ref.: http://www.minecraftwiki.net/wiki/Hunger + + // Remember the food level before processing, for later comparison + int LastFoodLevel = m_FoodLevel; + + // Heal or damage, based on the food level, using the m_FoodTickTimer: + if ((m_FoodLevel > 17) || (m_FoodLevel <= 0)) + { + m_FoodTickTimer++; + if (m_FoodTickTimer >= 80) + { + m_FoodTickTimer = 0; + + if (m_FoodLevel >= 17) + { + // Regenerate health from food, incur 3 pts of food exhaustion: + Heal(1); + m_FoodExhaustionLevel += 3; + } + else if (m_FoodLevel <= 0) + { + // Damage from starving + TakeDamage(dtStarving, NULL, 1, 1, 0); + } + } + } + + // Apply food poisoning food exhaustion: + if (m_FoodPoisonedTicksRemaining > 0) + { + m_FoodPoisonedTicksRemaining--; + m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick + } + + // Apply food exhaustion that has accumulated: + if (m_FoodExhaustionLevel >= 4) + { + m_FoodExhaustionLevel -= 4; + + if (m_FoodSaturationLevel >= 1) + { + m_FoodSaturationLevel -= 1; + } + else + { + m_FoodLevel = std::max(m_FoodLevel - 1, 0); + } + } + + if (m_FoodLevel != LastFoodLevel) + { + SendHealth(); + } +} + + + + + +void cPlayer::ApplyFoodExhaustionFromMovement(cChunk & a_Chunk) +{ + if (IsGameModeCreative()) + { + return; + } + + // Calculate the distance travelled, update the last pos: + Vector3d Movement(GetPosition() - m_LastFoodPos); + Movement.y = 0; // Only take XZ movement into account + m_LastFoodPos = GetPosition(); + + // If riding anything, apply no food exhaustion + if (m_AttachedTo != NULL) + { + return; + } + + // Get the type of block the player's standing in: + BLOCKTYPE BlockIn; + int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelY = (int)floor(m_LastPosY + 0.1); + int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width; + // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk + VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn)); + + // Apply the exhaustion based on distance travelled: + double BaseExhaustion = Movement.Length(); + if (IsSprinting()) + { + // 0.1 pt per meter sprinted + BaseExhaustion = BaseExhaustion * 0.1; + } + else if (IsBlockWater(BlockIn)) + { + // 0.015 pt per meter swum + BaseExhaustion = BaseExhaustion * 0.015; + } + else + { + // 0.01 pt per meter walked / sneaked + BaseExhaustion = BaseExhaustion * 0.01; + } + m_FoodExhaustionLevel += BaseExhaustion; +} + + + + diff --git a/source/Plugin_Squirrel.cpp b/source/Plugin_Squirrel.cpp index 27fd01a09..adca4b519 100644 --- a/source/Plugin_Squirrel.cpp +++ b/source/Plugin_Squirrel.cpp @@ -1,409 +1,409 @@ - -#include "Globals.h" - - - - -#ifdef USE_SUIRREL - - - - - -#include "Plugin_Squirrel.h" -#include "squirrelbindings/SquirrelFunctions.h" -#include "squirrelbindings/SquirrelBindings.h" -#include "squirrelbindings/SquirrelBaseClass.h" - - -cPlugin_Squirrel::cPlugin_Squirrel( const char* a_PluginName ) - : cPlugin( a_PluginName ) -{ - SetLanguage( cPlugin::E_SQUIRREL ); - m_PluginName = a_PluginName; -} - -cPlugin_Squirrel::~cPlugin_Squirrel() -{ - delete m_Plugin; -} - -bool cPlugin_Squirrel::Initialize() -{ - cCSLock Lock(m_CriticalSection); - - std::string PluginPath = std::string("Plugins/") + m_PluginName + ".nut"; - - Sqrat::Script script; - script.CompileFile(PluginPath); - if(script.IsNull()) - { - LOGERROR("Unable to run script \"%s\"", m_PluginName); - } - - try { - script.Run(); - - Sqrat::Function construct(Sqrat::RootTable(), m_PluginName); - - if(construct.IsNull()) - { - LOGERROR("Constructor for Plugin \"%s\" not found.", m_PluginName); - return false; - } - - Sqrat::Object obj = construct.Evaluate(); - - ((cSquirrelBaseClass *) obj.GetInstanceUP())->setInstance(this); - - m_Plugin = new SquirrelObject(obj); - - - Sqrat::Object PluginName = obj.GetSlot("name"); - if(!PluginName.IsNull()) - { - this->SetName(PluginName.Cast()); - } - - Sqrat::Function init = m_Plugin->GetFunction("Initialize"); - if(init.IsNull()) - { - LOGERROR("Can not initialize plugin \"%s\"", m_PluginName); - return false; - } - - return init.Evaluate(); - - } catch(Sqrat::Exception &e) - { - LOGERROR("Initialisation of \"%s\" failed: %s", m_PluginName, e.Message().c_str()); - return false; - } -} - - - - - -void cPlugin_Squirrel::OnDisable() -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnDisable")) return; - - m_Plugin->GetFunction("OnDisable").Execute(); -} - - - - - -void cPlugin_Squirrel::Tick(float a_Dt) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnTick")) return; - - m_Plugin->GetFunction("OnTick").Execute(a_Dt); -} - - - - - -bool cPlugin_Squirrel::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup) -{ - cCSLock Lock(m_CriticalSection); - - if (!m_Plugin->HasFunction("OnCollectPickup")) - { - return false; - } - - return m_Plugin->GetFunction("OnCollectPickup").Evaluate(a_Player, a_Pickup); -} - - - - - -bool cPlugin_Squirrel::OnDisconnect(cPlayer* a_Player, const AString & a_Reason) -{ - cCSLock Lock( m_CriticalSection ); - - if (!m_Plugin->HasFunction("OnDisconnect")) return false; - - return m_Plugin->GetFunction("OnDisconnect").Evaluate(a_Player, a_Reason); -} - - - - - -bool cPlugin_Squirrel::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) -{ - cCSLock Lock(m_CriticalSection); - - if (!m_Plugin->HasFunction("OnBlockPlace")) return false; - - return m_Plugin->GetFunction("OnBlockPlace").Evaluate(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem); -} - - - - - -bool cPlugin_Squirrel::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) -{ - cCSLock Lock(m_CriticalSection); - - if (!m_Plugin->HasFunction("OnBlockDig")) return false; - - return m_Plugin->GetFunction("OnBlockDig").Evaluate(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, a_OldBlock, a_OldMeta); -} - - - - - -bool cPlugin_Squirrel::OnChat(cPlayer * a_Player, const AString & a_Message) -{ - cCSLock Lock(m_CriticalSection); - - if (!m_Plugin->HasFunction("OnChat")) return false; - - return m_Plugin->GetFunction("OnChat").Evaluate(a_Player, a_Message); - -} - - - - - -bool cPlugin_Squirrel::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) -{ - cCSLock Lock( m_CriticalSection ); - - if (!m_Plugin->HasFunction("OnLogin")) - { - return false; - } - - return m_Plugin->GetFunction("OnLogin").Evaluate(a_Client, a_ProtocolVersion, a_Username); -} - - - - - -void cPlugin_Squirrel::OnPlayerSpawn( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnPlayerSpawn")) return; - - return m_Plugin->GetFunction("OnPlayerSpawn").Execute(a_Player); - -} - - - - - -bool cPlugin_Squirrel::OnPlayerJoin( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnPlayerJoin")) return false; - - return m_Plugin->GetFunction("OnPlayerJoin").Evaluate(a_Player); -} - - - - - -void cPlugin_Squirrel::OnPlayerMove( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnPlayerMove")) return; - - return m_Plugin->GetFunction("OnPlayerMove").Execute(a_Player); - -} - - - - - -void cPlugin_Squirrel::OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnTakeDamage")) return; - - return m_Plugin->GetFunction("OnTakeDamage")(a_Pawn, a_TakeDamageInfo); -} - - - - - -bool cPlugin_Squirrel::OnKilled( cPawn* a_Killed, cEntity* a_Killer ) -{ - cCSLock Lock( m_CriticalSection ); - if(!m_Plugin->HasFunction("OnKilled")) return false; - return m_Plugin->GetFunction("OnKilled").Evaluate(a_Killed, a_Killer); -} - - - - - -void cPlugin_Squirrel::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CriticalSection); - if(!m_Plugin->HasFunction("OnChunkGenerated")) return; - return m_Plugin->GetFunction("OnChunkGenerated")(a_World, a_ChunkX, a_ChunkZ); -} - - - - - -bool cPlugin_Squirrel::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_pLuaChunk) -{ - cCSLock Lock(m_CriticalSection); - if(!m_Plugin->HasFunction("OnChunkGenerating")) return false; - return m_Plugin->GetFunction("OnChunkGenerating").Evaluate(a_World, a_ChunkX, a_ChunkZ, a_pLuaChunk); -} - - - - - -bool cPlugin_Squirrel::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - if(!m_Plugin->HasFunction("OnPreCrafting")) return false; - return m_Plugin->GetFunction("OnPreCrafting").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); - -} - - - - - -bool cPlugin_Squirrel::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - if(!m_Plugin->HasFunction("OnCraftingNoRecipe")) return false; - return m_Plugin->GetFunction("OnCraftingNoRecipe").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); -} - - - - - -bool cPlugin_Squirrel::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnPostCrafting")) return false; - return m_Plugin->GetFunction("OnPostCrafting").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); -} - - - - - -bool cPlugin_Squirrel::OnBlockToPickup( - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups -) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnBlockToPickup")) return false; - return m_Plugin->GetFunction("OnBlockToPickup").Evaluate(a_BlockType, a_BlockMeta, (cPlayer *) a_Player, a_EquippedItem, a_Pickups); - -} - - - - - -bool cPlugin_Squirrel::OnWeatherChanged(cWorld * a_World) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnWeatherChanged")) return false; - return m_Plugin->GetFunction("OnWeatherChanged").Evaluate(a_World); -} - - - - - -bool cPlugin_Squirrel::OnUpdatingSign( - cWorld * a_World, - int a_BlockX, int a_BlockY, int a_BlockZ, - AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, - cPlayer * a_Player -) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnUpdatingSign")) return false; - return m_Plugin->GetFunction("OnUpdatingSign") - .Evaluate( - a_World, - a_BlockX, - a_BlockY, - a_BlockZ, - a_Line1, - a_Line2, - a_Line3, - a_Line4, - a_Player - ); -} - - - - - -bool cPlugin_Squirrel::OnUpdatedSign( - cWorld * a_World, - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, - cPlayer * a_Player -) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnUpdatedSign")) return false; - return m_Plugin->GetFunction("OnUpdatedSign") - .Evaluate( - a_World, - a_BlockX, - a_BlockY, - a_BlockZ, - a_Line1, - a_Line2, - a_Line3, - a_Line4, - a_Player - ); -} - - - - - -#endif // USE_SQUIRREL - - - - + +#include "Globals.h" + + + + +#ifdef USE_SUIRREL + + + + + +#include "Plugin_Squirrel.h" +#include "squirrelbindings/SquirrelFunctions.h" +#include "squirrelbindings/SquirrelBindings.h" +#include "squirrelbindings/SquirrelBaseClass.h" + + +cPlugin_Squirrel::cPlugin_Squirrel( const char* a_PluginName ) + : cPlugin( a_PluginName ) +{ + SetLanguage( cPlugin::E_SQUIRREL ); + m_PluginName = a_PluginName; +} + +cPlugin_Squirrel::~cPlugin_Squirrel() +{ + delete m_Plugin; +} + +bool cPlugin_Squirrel::Initialize() +{ + cCSLock Lock(m_CriticalSection); + + std::string PluginPath = std::string("Plugins/") + m_PluginName + ".nut"; + + Sqrat::Script script; + script.CompileFile(PluginPath); + if(script.IsNull()) + { + LOGERROR("Unable to run script \"%s\"", m_PluginName); + } + + try { + script.Run(); + + Sqrat::Function construct(Sqrat::RootTable(), m_PluginName); + + if(construct.IsNull()) + { + LOGERROR("Constructor for Plugin \"%s\" not found.", m_PluginName); + return false; + } + + Sqrat::Object obj = construct.Evaluate(); + + ((cSquirrelBaseClass *) obj.GetInstanceUP())->setInstance(this); + + m_Plugin = new SquirrelObject(obj); + + + Sqrat::Object PluginName = obj.GetSlot("name"); + if(!PluginName.IsNull()) + { + this->SetName(PluginName.Cast()); + } + + Sqrat::Function init = m_Plugin->GetFunction("Initialize"); + if(init.IsNull()) + { + LOGERROR("Can not initialize plugin \"%s\"", m_PluginName); + return false; + } + + return init.Evaluate(); + + } catch(Sqrat::Exception &e) + { + LOGERROR("Initialisation of \"%s\" failed: %s", m_PluginName, e.Message().c_str()); + return false; + } +} + + + + + +void cPlugin_Squirrel::OnDisable() +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnDisable")) return; + + m_Plugin->GetFunction("OnDisable").Execute(); +} + + + + + +void cPlugin_Squirrel::Tick(float a_Dt) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnTick")) return; + + m_Plugin->GetFunction("OnTick").Execute(a_Dt); +} + + + + + +bool cPlugin_Squirrel::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup) +{ + cCSLock Lock(m_CriticalSection); + + if (!m_Plugin->HasFunction("OnCollectPickup")) + { + return false; + } + + return m_Plugin->GetFunction("OnCollectPickup").Evaluate(a_Player, a_Pickup); +} + + + + + +bool cPlugin_Squirrel::OnDisconnect(cPlayer* a_Player, const AString & a_Reason) +{ + cCSLock Lock( m_CriticalSection ); + + if (!m_Plugin->HasFunction("OnDisconnect")) return false; + + return m_Plugin->GetFunction("OnDisconnect").Evaluate(a_Player, a_Reason); +} + + + + + +bool cPlugin_Squirrel::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) +{ + cCSLock Lock(m_CriticalSection); + + if (!m_Plugin->HasFunction("OnBlockPlace")) return false; + + return m_Plugin->GetFunction("OnBlockPlace").Evaluate(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem); +} + + + + + +bool cPlugin_Squirrel::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + cCSLock Lock(m_CriticalSection); + + if (!m_Plugin->HasFunction("OnBlockDig")) return false; + + return m_Plugin->GetFunction("OnBlockDig").Evaluate(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, a_OldBlock, a_OldMeta); +} + + + + + +bool cPlugin_Squirrel::OnChat(cPlayer * a_Player, const AString & a_Message) +{ + cCSLock Lock(m_CriticalSection); + + if (!m_Plugin->HasFunction("OnChat")) return false; + + return m_Plugin->GetFunction("OnChat").Evaluate(a_Player, a_Message); + +} + + + + + +bool cPlugin_Squirrel::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + cCSLock Lock( m_CriticalSection ); + + if (!m_Plugin->HasFunction("OnLogin")) + { + return false; + } + + return m_Plugin->GetFunction("OnLogin").Evaluate(a_Client, a_ProtocolVersion, a_Username); +} + + + + + +void cPlugin_Squirrel::OnPlayerSpawn( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnPlayerSpawn")) return; + + return m_Plugin->GetFunction("OnPlayerSpawn").Execute(a_Player); + +} + + + + + +bool cPlugin_Squirrel::OnPlayerJoin( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnPlayerJoin")) return false; + + return m_Plugin->GetFunction("OnPlayerJoin").Evaluate(a_Player); +} + + + + + +void cPlugin_Squirrel::OnPlayerMove( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnPlayerMove")) return; + + return m_Plugin->GetFunction("OnPlayerMove").Execute(a_Player); + +} + + + + + +void cPlugin_Squirrel::OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnTakeDamage")) return; + + return m_Plugin->GetFunction("OnTakeDamage")(a_Pawn, a_TakeDamageInfo); +} + + + + + +bool cPlugin_Squirrel::OnKilled( cPawn* a_Killed, cEntity* a_Killer ) +{ + cCSLock Lock( m_CriticalSection ); + if(!m_Plugin->HasFunction("OnKilled")) return false; + return m_Plugin->GetFunction("OnKilled").Evaluate(a_Killed, a_Killer); +} + + + + + +void cPlugin_Squirrel::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + if(!m_Plugin->HasFunction("OnChunkGenerated")) return; + return m_Plugin->GetFunction("OnChunkGenerated")(a_World, a_ChunkX, a_ChunkZ); +} + + + + + +bool cPlugin_Squirrel::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_pLuaChunk) +{ + cCSLock Lock(m_CriticalSection); + if(!m_Plugin->HasFunction("OnChunkGenerating")) return false; + return m_Plugin->GetFunction("OnChunkGenerating").Evaluate(a_World, a_ChunkX, a_ChunkZ, a_pLuaChunk); +} + + + + + +bool cPlugin_Squirrel::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + if(!m_Plugin->HasFunction("OnPreCrafting")) return false; + return m_Plugin->GetFunction("OnPreCrafting").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); + +} + + + + + +bool cPlugin_Squirrel::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + if(!m_Plugin->HasFunction("OnCraftingNoRecipe")) return false; + return m_Plugin->GetFunction("OnCraftingNoRecipe").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); +} + + + + + +bool cPlugin_Squirrel::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnPostCrafting")) return false; + return m_Plugin->GetFunction("OnPostCrafting").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); +} + + + + + +bool cPlugin_Squirrel::OnBlockToPickup( + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups +) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnBlockToPickup")) return false; + return m_Plugin->GetFunction("OnBlockToPickup").Evaluate(a_BlockType, a_BlockMeta, (cPlayer *) a_Player, a_EquippedItem, a_Pickups); + +} + + + + + +bool cPlugin_Squirrel::OnWeatherChanged(cWorld * a_World) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnWeatherChanged")) return false; + return m_Plugin->GetFunction("OnWeatherChanged").Evaluate(a_World); +} + + + + + +bool cPlugin_Squirrel::OnUpdatingSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnUpdatingSign")) return false; + return m_Plugin->GetFunction("OnUpdatingSign") + .Evaluate( + a_World, + a_BlockX, + a_BlockY, + a_BlockZ, + a_Line1, + a_Line2, + a_Line3, + a_Line4, + a_Player + ); +} + + + + + +bool cPlugin_Squirrel::OnUpdatedSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnUpdatedSign")) return false; + return m_Plugin->GetFunction("OnUpdatedSign") + .Evaluate( + a_World, + a_BlockX, + a_BlockY, + a_BlockZ, + a_Line1, + a_Line2, + a_Line3, + a_Line4, + a_Player + ); +} + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/Plugin_Squirrel.h b/source/Plugin_Squirrel.h index 27b1dd909..1a56172e9 100644 --- a/source/Plugin_Squirrel.h +++ b/source/Plugin_Squirrel.h @@ -1,69 +1,69 @@ - -#pragma once - - - - - -#ifdef USE_SQUIRREL - - - - - -#include "Plugin.h" -#include -#include "squirrelbindings/SquirrelObject.h" - - - - - -class cPlugin_Squirrel : - public cPlugin -{ -public: - cPlugin_Squirrel(const char* a_PluginName); - ~cPlugin_Squirrel(); - - void OnDisable(); - bool Initialize(); - - void Tick(float a_Dt); - - virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) override; - virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) override; - virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); - virtual bool OnChat (cPlayer * a_Player, const AString & a_Message) override; - virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; - virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_pLuaChunk ) override; - virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup) override; - virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; - virtual bool OnKilled (cPawn* a_Killed, cEntity* a_Killer ) override; - virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; - virtual bool OnPlayerJoin (cPlayer* a_Player ) override; - virtual void OnPlayerMove (cPlayer* a_Player ) override; - virtual void OnPlayerSpawn (cPlayer* a_Player ) override; - virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual void OnTakeDamage (cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) override; - virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; - virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; - virtual bool OnWeatherChanged (cWorld * a_World) override; - -protected: - const char * m_PluginName; - cCriticalSection m_CriticalSection; - SquirrelObject *m_Plugin; -}; - - - - - -#endif // USE_SQUIRREL - - - - + +#pragma once + + + + + +#ifdef USE_SQUIRREL + + + + + +#include "Plugin.h" +#include +#include "squirrelbindings/SquirrelObject.h" + + + + + +class cPlugin_Squirrel : + public cPlugin +{ +public: + cPlugin_Squirrel(const char* a_PluginName); + ~cPlugin_Squirrel(); + + void OnDisable(); + bool Initialize(); + + void Tick(float a_Dt); + + virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) override; + virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) override; + virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); + virtual bool OnChat (cPlayer * a_Player, const AString & a_Message) override; + virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_pLuaChunk ) override; + virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup) override; + virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; + virtual bool OnKilled (cPawn* a_Killed, cEntity* a_Killer ) override; + virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; + virtual bool OnPlayerJoin (cPlayer* a_Player ) override; + virtual void OnPlayerMove (cPlayer* a_Player ) override; + virtual void OnPlayerSpawn (cPlayer* a_Player ) override; + virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual void OnTakeDamage (cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) override; + virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnWeatherChanged (cWorld * a_World) override; + +protected: + const char * m_PluginName; + cCriticalSection m_CriticalSection; + SquirrelObject *m_Plugin; +}; + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/ProbabDistrib.cpp b/source/ProbabDistrib.cpp index 98c18b8e1..5fa17c276 100644 --- a/source/ProbabDistrib.cpp +++ b/source/ProbabDistrib.cpp @@ -1,142 +1,142 @@ - -// ProbabDistrib.cpp - -// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator - -#include "Globals.h" -#include "ProbabDistrib.h" -#include "MersenneTwister.h" - - - - - - -cProbabDistrib::cProbabDistrib(int a_MaxValue) : - m_MaxValue(a_MaxValue), - m_Sum(-1) -{ -} - - - - - - -void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points) -{ - ASSERT(!a_Points.empty()); - m_Sum = 0; - m_Cumulative.clear(); - m_Cumulative.reserve(a_Points.size() + 1); - int ProbSum = 0; - int LastProb = 0; - int LastValue = -1; - if (a_Points[0].m_Value != 0) - { - m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds - LastValue = 0; - } - for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr) - { - if (itr->m_Value == LastValue) - { - continue; - } - - // Add the current trapezoid to the sum: - ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2; - LastProb = itr->m_Probability; - LastValue = itr->m_Value; - m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum)); - } // for itr - a_Points[] - if (LastValue != m_MaxValue) - { - m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds - } - m_Sum = ProbSum; -} - - - - - -bool cProbabDistrib::SetDefString(const AString & a_DefString) -{ - AStringVector Points = StringSplitAndTrim(a_DefString, ";"); - if (Points.empty()) - { - return false; - } - cPoints Pts; - for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr) - { - AStringVector Split = StringSplitAndTrim(*itr, ","); - if (Split.size() != 2) - { - // Bad format - return false; - } - int Value = atoi(Split[0].c_str()); - int Prob = atoi(Split[1].c_str()); - if ( - ((Value == 0) && (Split[0] != "0")) || - ((Prob == 0) && (Split[1] != "0")) - ) - { - // Number parse error - return false; - } - Pts.push_back(cPoint(Value, Prob)); - } // for itr - Points[] - - SetPoints(Pts); - return true; -} - - - - - -int cProbabDistrib::Random(MTRand & a_Rand) const -{ - int v = a_Rand.randInt(m_Sum); - return MapValue(v); -} - - - - - -int cProbabDistrib::MapValue(int a_OrigValue) const -{ - ASSERT(a_OrigValue >= 0); - ASSERT(a_OrigValue < m_Sum); - - // Binary search through m_Cumulative for placement: - size_t Lo = 0; - size_t Hi = m_Cumulative.size() - 1; - while (Hi - Lo > 1) - { - int Mid = (Lo + Hi) / 2; - int MidProbab = m_Cumulative[Mid].m_Probability; - if (MidProbab < a_OrigValue) - { - Lo = Mid; - } - else - { - Hi = Mid; - } - } - ASSERT(Hi - Lo == 1); - - // Linearly interpolate between Lo and Hi: - int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability; - int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value; - return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif; -} - - - - + +// ProbabDistrib.cpp + +// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator + +#include "Globals.h" +#include "ProbabDistrib.h" +#include "MersenneTwister.h" + + + + + + +cProbabDistrib::cProbabDistrib(int a_MaxValue) : + m_MaxValue(a_MaxValue), + m_Sum(-1) +{ +} + + + + + + +void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points) +{ + ASSERT(!a_Points.empty()); + m_Sum = 0; + m_Cumulative.clear(); + m_Cumulative.reserve(a_Points.size() + 1); + int ProbSum = 0; + int LastProb = 0; + int LastValue = -1; + if (a_Points[0].m_Value != 0) + { + m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds + LastValue = 0; + } + for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr) + { + if (itr->m_Value == LastValue) + { + continue; + } + + // Add the current trapezoid to the sum: + ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2; + LastProb = itr->m_Probability; + LastValue = itr->m_Value; + m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum)); + } // for itr - a_Points[] + if (LastValue != m_MaxValue) + { + m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds + } + m_Sum = ProbSum; +} + + + + + +bool cProbabDistrib::SetDefString(const AString & a_DefString) +{ + AStringVector Points = StringSplitAndTrim(a_DefString, ";"); + if (Points.empty()) + { + return false; + } + cPoints Pts; + for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr) + { + AStringVector Split = StringSplitAndTrim(*itr, ","); + if (Split.size() != 2) + { + // Bad format + return false; + } + int Value = atoi(Split[0].c_str()); + int Prob = atoi(Split[1].c_str()); + if ( + ((Value == 0) && (Split[0] != "0")) || + ((Prob == 0) && (Split[1] != "0")) + ) + { + // Number parse error + return false; + } + Pts.push_back(cPoint(Value, Prob)); + } // for itr - Points[] + + SetPoints(Pts); + return true; +} + + + + + +int cProbabDistrib::Random(MTRand & a_Rand) const +{ + int v = a_Rand.randInt(m_Sum); + return MapValue(v); +} + + + + + +int cProbabDistrib::MapValue(int a_OrigValue) const +{ + ASSERT(a_OrigValue >= 0); + ASSERT(a_OrigValue < m_Sum); + + // Binary search through m_Cumulative for placement: + size_t Lo = 0; + size_t Hi = m_Cumulative.size() - 1; + while (Hi - Lo > 1) + { + int Mid = (Lo + Hi) / 2; + int MidProbab = m_Cumulative[Mid].m_Probability; + if (MidProbab < a_OrigValue) + { + Lo = Mid; + } + else + { + Hi = Mid; + } + } + ASSERT(Hi - Lo == 1); + + // Linearly interpolate between Lo and Hi: + int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability; + int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value; + return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif; +} + + + + diff --git a/source/ProbabDistrib.h b/source/ProbabDistrib.h index ac5f24894..ddaadd9b7 100644 --- a/source/ProbabDistrib.h +++ b/source/ProbabDistrib.h @@ -1,74 +1,74 @@ - -// ProbabDistrib.h - -// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator - -/* -Usage: -1, Create a cProbabDistrib instance -2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string -3, Ask for random numbers in that probability distribution using the Random() function -*/ - - - - - -#pragma once - - - - - -// fwd: -class MTRand; - - - - - -class cProbabDistrib -{ -public: - class cPoint - { - public: - int m_Value; - int m_Probability; - - cPoint(int a_Value, int a_Probability) : - m_Value(a_Value), - m_Probability(a_Probability) - { - } - } ; - - typedef std::vector cPoints; - - - cProbabDistrib(int a_MaxValue); - - /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty. - void SetPoints(const cPoints & a_Points); - - /// Sets the distribution curve using a definition string; returns true on successful parse - bool SetDefString(const AString & a_DefString); - - /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value. - int Random(MTRand & a_Rand) const; - - /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability - int MapValue(int a_OrigValue) const; - - int GetSum(void) const { return m_Sum; } - -protected: - - int m_MaxValue; - cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup - int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set -} ; - - - - + +// ProbabDistrib.h + +// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator + +/* +Usage: +1, Create a cProbabDistrib instance +2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string +3, Ask for random numbers in that probability distribution using the Random() function +*/ + + + + + +#pragma once + + + + + +// fwd: +class MTRand; + + + + + +class cProbabDistrib +{ +public: + class cPoint + { + public: + int m_Value; + int m_Probability; + + cPoint(int a_Value, int a_Probability) : + m_Value(a_Value), + m_Probability(a_Probability) + { + } + } ; + + typedef std::vector cPoints; + + + cProbabDistrib(int a_MaxValue); + + /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty. + void SetPoints(const cPoints & a_Points); + + /// Sets the distribution curve using a definition string; returns true on successful parse + bool SetDefString(const AString & a_DefString); + + /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value. + int Random(MTRand & a_Rand) const; + + /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability + int MapValue(int a_OrigValue) const; + + int GetSum(void) const { return m_Sum; } + +protected: + + int m_MaxValue; + cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup + int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set +} ; + + + + diff --git a/source/Protocol/ChunkDataSerializer.cpp b/source/Protocol/ChunkDataSerializer.cpp index 7af07f331..2a9230fee 100644 --- a/source/Protocol/ChunkDataSerializer.cpp +++ b/source/Protocol/ChunkDataSerializer.cpp @@ -1,176 +1,176 @@ - -// ChunkDataSerializer.cpp - -// Implements the cChunkDataSerializer class representing the object that can: -// - serialize chunk data to different protocol versions -// - cache such serialized data for multiple clients - -#include "Globals.h" -#include "ChunkDataSerializer.h" -#include "zlib.h" - - - - -cChunkDataSerializer::cChunkDataSerializer( - const cChunkDef::BlockTypes & a_BlockTypes, - const cChunkDef::BlockNibbles & a_BlockMetas, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_BlockSkyLight, - const unsigned char * a_BiomeData -) : - m_BlockTypes(a_BlockTypes), - m_BlockMetas(a_BlockMetas), - m_BlockLight(a_BlockLight), - m_BlockSkyLight(a_BlockSkyLight), - m_BiomeData(a_BiomeData) -{ -} - - - - -const AString & cChunkDataSerializer::Serialize(int a_Version) -{ - Serializations::const_iterator itr = m_Serializations.find(a_Version); - if (itr != m_Serializations.end()) - { - return itr->second; - } - - AString data; - switch (a_Version) - { - case RELEASE_1_2_5: Serialize29(data); break; - case RELEASE_1_3_2: Serialize39(data); break; - // TODO: Other protocol versions may serialize the data differently; implement here - - default: - { - LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version); - ASSERT(!"Unknown chunk data serialization version"); - break; - } - } - m_Serializations[a_Version] = data; - return m_Serializations[a_Version]; -} - - - - - -void cChunkDataSerializer::Serialize29(AString & a_Data) -{ - // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) - - const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - const int MetadataOffset = sizeof(m_BlockTypes); - const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); - const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); - const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); - const int DataSize = BiomeOffset + BiomeDataSize; - - // Temporary buffer for the composed data: - char AllData [DataSize]; - - memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); - memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); - memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); - memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); - memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); - - // Compress the data: - // In order not to use allocation, use a fixed-size buffer, with the size - // that uses the same calculation as compressBound(): - const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; - char CompressedBlockData[CompressedMaxSize]; - - uLongf CompressedSize = compressBound(DataSize); - - // Run-time check that our compile-time guess about CompressedMaxSize was enough: - ASSERT(CompressedSize <= CompressedMaxSize); - - compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); - - // Now put all those data into a_Data: - - // "Ground-up continuous", or rather, "biome data present" flag: - a_Data.push_back('\x01'); - - // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively - // Also, no endian flipping is needed because of the const values - unsigned short BitMap1 = 0xffff; - unsigned short BitMap2 = 0; - a_Data.append((const char *)&BitMap1, sizeof(short)); - a_Data.append((const char *)&BitMap2, sizeof(short)); - - Int32 CompressedSizeBE = htonl(CompressedSize); - a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); - - Int32 UnusedInt32 = 0; - a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32)); - - a_Data.append(CompressedBlockData, CompressedSize); -} - - - - - -void cChunkDataSerializer::Serialize39(AString & a_Data) -{ - // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) - - const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - const int MetadataOffset = sizeof(m_BlockTypes); - const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); - const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); - const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); - const int DataSize = BiomeOffset + BiomeDataSize; - - // Temporary buffer for the composed data: - char AllData [DataSize]; - - memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); - memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); - memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); - memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); - memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); - - // Compress the data: - // In order not to use allocation, use a fixed-size buffer, with the size - // that uses the same calculation as compressBound(): - const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; - char CompressedBlockData[CompressedMaxSize]; - - uLongf CompressedSize = compressBound(DataSize); - - // Run-time check that our compile-time guess about CompressedMaxSize was enough: - ASSERT(CompressedSize <= CompressedMaxSize); - - compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); - - // Now put all those data into a_Data: - - // "Ground-up continuous", or rather, "biome data present" flag: - a_Data.push_back('\x01'); - - // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively - // Also, no endian flipping is needed because of the const values - unsigned short BitMap1 = 0xffff; - unsigned short BitMap2 = 0; - a_Data.append((const char *)&BitMap1, sizeof(short)); - a_Data.append((const char *)&BitMap2, sizeof(short)); - - Int32 CompressedSizeBE = htonl(CompressedSize); - a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); - - // Unlike 29, 39 doesn't have the "unused" int - - a_Data.append(CompressedBlockData, CompressedSize); -} - - - - + +// ChunkDataSerializer.cpp + +// Implements the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + +#include "Globals.h" +#include "ChunkDataSerializer.h" +#include "zlib.h" + + + + +cChunkDataSerializer::cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData +) : + m_BlockTypes(a_BlockTypes), + m_BlockMetas(a_BlockMetas), + m_BlockLight(a_BlockLight), + m_BlockSkyLight(a_BlockSkyLight), + m_BiomeData(a_BiomeData) +{ +} + + + + +const AString & cChunkDataSerializer::Serialize(int a_Version) +{ + Serializations::const_iterator itr = m_Serializations.find(a_Version); + if (itr != m_Serializations.end()) + { + return itr->second; + } + + AString data; + switch (a_Version) + { + case RELEASE_1_2_5: Serialize29(data); break; + case RELEASE_1_3_2: Serialize39(data); break; + // TODO: Other protocol versions may serialize the data differently; implement here + + default: + { + LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version); + ASSERT(!"Unknown chunk data serialization version"); + break; + } + } + m_Serializations[a_Version] = data; + return m_Serializations[a_Version]; +} + + + + + +void cChunkDataSerializer::Serialize29(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\x01'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + Int32 UnusedInt32 = 0; + a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32)); + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + + +void cChunkDataSerializer::Serialize39(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\x01'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + // Unlike 29, 39 doesn't have the "unused" int + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + diff --git a/source/Protocol/ChunkDataSerializer.h b/source/Protocol/ChunkDataSerializer.h index 513a232b9..a42856356 100644 --- a/source/Protocol/ChunkDataSerializer.h +++ b/source/Protocol/ChunkDataSerializer.h @@ -1,48 +1,48 @@ - -// ChunkDataSerializer.h - -// Interfaces to the cChunkDataSerializer class representing the object that can: -// - serialize chunk data to different protocol versions -// - cache such serialized data for multiple clients - - - - - -class cChunkDataSerializer -{ -protected: - const cChunkDef::BlockTypes & m_BlockTypes; - const cChunkDef::BlockNibbles & m_BlockMetas; - const cChunkDef::BlockNibbles & m_BlockLight; - const cChunkDef::BlockNibbles & m_BlockSkyLight; - const unsigned char * m_BiomeData; - - typedef std::map Serializations; - - Serializations m_Serializations; - - void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5 - void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2 - -public: - enum - { - RELEASE_1_2_5 = 29, - RELEASE_1_3_2 = 39, - } ; - - cChunkDataSerializer( - const cChunkDef::BlockTypes & a_BlockTypes, - const cChunkDef::BlockNibbles & a_BlockMetas, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_BlockSkyLight, - const unsigned char * a_BiomeData - ); - - const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[] -} ; - - - - + +// ChunkDataSerializer.h + +// Interfaces to the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + + + + + +class cChunkDataSerializer +{ +protected: + const cChunkDef::BlockTypes & m_BlockTypes; + const cChunkDef::BlockNibbles & m_BlockMetas; + const cChunkDef::BlockNibbles & m_BlockLight; + const cChunkDef::BlockNibbles & m_BlockSkyLight; + const unsigned char * m_BiomeData; + + typedef std::map Serializations; + + Serializations m_Serializations; + + void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5 + void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2 + +public: + enum + { + RELEASE_1_2_5 = 29, + RELEASE_1_3_2 = 39, + } ; + + cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData + ); + + const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[] +} ; + + + + diff --git a/source/Protocol/Protocol.h b/source/Protocol/Protocol.h index fe9d4d10e..ab1b632ff 100644 --- a/source/Protocol/Protocol.h +++ b/source/Protocol/Protocol.h @@ -1,192 +1,192 @@ - -// Protocol.h - -// Interfaces to the cProtocol class representing the generic interface that a protocol -// parser and serializer must implement - - - - - -#pragma once - -#include "../Defines.h" -#include "../Endianness.h" - - - - -class cPlayer; -class cEntity; -class cWindow; -class cInventory; -class cPawn; -class cPickup; -class cMonster; -class cChunkDataSerializer; -class cWorld; -class cFallingBlock; - - - - - -typedef unsigned char Byte; - - - - - -class cProtocol -{ -public: - cProtocol(cClientHandle * a_Client) : - m_Client(a_Client) - { - } - virtual ~cProtocol() {} - - /// Called when client sends some data - virtual void DataReceived(const char * a_Data, int a_Size) = 0; - - // Sending stuff to clients (alphabetically sorted): - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0; - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0; - virtual void SendChat (const AString & a_Message) = 0; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; - virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; - virtual void SendDisconnect (const AString & a_Reason) = 0; - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0; - virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0; - virtual void SendEntityLook (const cEntity & a_Entity) = 0; - virtual void SendEntityMetadata (const cEntity & a_Entity) = 0; - virtual void SendEntityProperties (const cEntity & a_Entity) = 0; - virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; - virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; - virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0; - virtual void SendEntityVelocity (const cEntity & a_Entity) = 0; - virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0; - virtual void SendGameMode (eGameMode a_GameMode) = 0; - virtual void SendHealth (void) = 0; - virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) = 0; - virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0; - virtual void SendKeepAlive (int a_PingID) = 0; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0; - virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; - virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0; - virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0; - virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+) - virtual void SendPlayerMoveLook (void) = 0; - virtual void SendPlayerPosition (void) = 0; - virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; - virtual void SendRespawn (void) = 0; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 - virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; - virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; - virtual void SendSpawnMob (const cMonster & a_Mob) = 0; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) = 0; - virtual void SendTeleportEntity (const cEntity & a_Entity) = 0; - virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0; - virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0; - virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0; - virtual void SendWeather (eWeather a_Weather) = 0; - virtual void SendWholeInventory (const cInventory & a_Inventory) = 0; - virtual void SendWholeInventory (const cWindow & a_Window) = 0; - virtual void SendWindowClose (const cWindow & a_Window) = 0; - virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) = 0; - - /// Returns the ServerID used for authentication through session.minecraft.net - virtual AString GetAuthServerID(void) = 0; - -protected: - cClientHandle * m_Client; - cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once - - /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it - virtual void SendData(const char * a_Data, int a_Size) = 0; - - /// Called after writing each packet, enables descendants to flush their buffers - virtual void Flush(void) {}; - - // Helpers for writing partial packet data, write using SendData() - void WriteByte(Byte a_Value) - { - SendData((const char *)&a_Value, 1); - } - - void WriteShort(short a_Value) - { - a_Value = htons(a_Value); - SendData((const char *)&a_Value, 2); - } - - /* - void WriteShort(unsigned short a_Value) - { - a_Value = htons(a_Value); - SendData((const char *)&a_Value, 2); - } - */ - - void WriteInt(int a_Value) - { - a_Value = htonl(a_Value); - SendData((const char *)&a_Value, 4); - } - - void WriteUInt(unsigned int a_Value) - { - a_Value = htonl(a_Value); - SendData((const char *)&a_Value, 4); - } - - void WriteInt64 (Int64 a_Value) - { - a_Value = HostToNetwork8(&a_Value); - SendData((const char *)&a_Value, 8); - } - - void WriteFloat (float a_Value) - { - unsigned int val = HostToNetwork4(&a_Value); - SendData((const char *)&val, 4); - } - - void WriteDouble(double a_Value) - { - unsigned long long val = HostToNetwork8(&a_Value); - SendData((const char *)&val, 8); - } - - void WriteString(const AString & a_Value) - { - AString UTF16; - UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16); - WriteShort((unsigned short)(UTF16.size() / 2)); - SendData(UTF16.data(), UTF16.size()); - } - - void WriteBool(bool a_Value) - { - WriteByte(a_Value ? 1 : 0); - } - - void WriteVectorI(const Vector3i & a_Vector) - { - WriteInt(a_Vector.x); - WriteInt(a_Vector.y); - WriteInt(a_Vector.z); - } -} ; - - - - - + +// Protocol.h + +// Interfaces to the cProtocol class representing the generic interface that a protocol +// parser and serializer must implement + + + + + +#pragma once + +#include "../Defines.h" +#include "../Endianness.h" + + + + +class cPlayer; +class cEntity; +class cWindow; +class cInventory; +class cPawn; +class cPickup; +class cMonster; +class cChunkDataSerializer; +class cWorld; +class cFallingBlock; + + + + + +typedef unsigned char Byte; + + + + + +class cProtocol +{ +public: + cProtocol(cClientHandle * a_Client) : + m_Client(a_Client) + { + } + virtual ~cProtocol() {} + + /// Called when client sends some data + virtual void DataReceived(const char * a_Data, int a_Size) = 0; + + // Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0; + virtual void SendChat (const AString & a_Message) = 0; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; + virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; + virtual void SendDisconnect (const AString & a_Reason) = 0; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0; + virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0; + virtual void SendEntityLook (const cEntity & a_Entity) = 0; + virtual void SendEntityMetadata (const cEntity & a_Entity) = 0; + virtual void SendEntityProperties (const cEntity & a_Entity) = 0; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0; + virtual void SendEntityVelocity (const cEntity & a_Entity) = 0; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0; + virtual void SendGameMode (eGameMode a_GameMode) = 0; + virtual void SendHealth (void) = 0; + virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) = 0; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0; + virtual void SendKeepAlive (int a_PingID) = 0; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0; + virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) = 0; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0; + virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+) + virtual void SendPlayerMoveLook (void) = 0; + virtual void SendPlayerPosition (void) = 0; + virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; + virtual void SendRespawn (void) = 0; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; + virtual void SendSpawnMob (const cMonster & a_Mob) = 0; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) = 0; + virtual void SendTeleportEntity (const cEntity & a_Entity) = 0; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0; + virtual void SendWeather (eWeather a_Weather) = 0; + virtual void SendWholeInventory (const cInventory & a_Inventory) = 0; + virtual void SendWholeInventory (const cWindow & a_Window) = 0; + virtual void SendWindowClose (const cWindow & a_Window) = 0; + virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) = 0; + + /// Returns the ServerID used for authentication through session.minecraft.net + virtual AString GetAuthServerID(void) = 0; + +protected: + cClientHandle * m_Client; + cCriticalSection m_CSPacket; //< Each SendXYZ() function must acquire this CS in order to send the whole packet at once + + /// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it + virtual void SendData(const char * a_Data, int a_Size) = 0; + + /// Called after writing each packet, enables descendants to flush their buffers + virtual void Flush(void) {}; + + // Helpers for writing partial packet data, write using SendData() + void WriteByte(Byte a_Value) + { + SendData((const char *)&a_Value, 1); + } + + void WriteShort(short a_Value) + { + a_Value = htons(a_Value); + SendData((const char *)&a_Value, 2); + } + + /* + void WriteShort(unsigned short a_Value) + { + a_Value = htons(a_Value); + SendData((const char *)&a_Value, 2); + } + */ + + void WriteInt(int a_Value) + { + a_Value = htonl(a_Value); + SendData((const char *)&a_Value, 4); + } + + void WriteUInt(unsigned int a_Value) + { + a_Value = htonl(a_Value); + SendData((const char *)&a_Value, 4); + } + + void WriteInt64 (Int64 a_Value) + { + a_Value = HostToNetwork8(&a_Value); + SendData((const char *)&a_Value, 8); + } + + void WriteFloat (float a_Value) + { + unsigned int val = HostToNetwork4(&a_Value); + SendData((const char *)&val, 4); + } + + void WriteDouble(double a_Value) + { + unsigned long long val = HostToNetwork8(&a_Value); + SendData((const char *)&val, 8); + } + + void WriteString(const AString & a_Value) + { + AString UTF16; + UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16); + WriteShort((unsigned short)(UTF16.size() / 2)); + SendData(UTF16.data(), UTF16.size()); + } + + void WriteBool(bool a_Value) + { + WriteByte(a_Value ? 1 : 0); + } + + void WriteVectorI(const Vector3i & a_Vector) + { + WriteInt(a_Vector.x); + WriteInt(a_Vector.y); + WriteInt(a_Vector.z); + } +} ; + + + + + diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp index fddf7d324..5ecb5b172 100644 --- a/source/Protocol/Protocol125.cpp +++ b/source/Protocol/Protocol125.cpp @@ -1,1636 +1,1636 @@ - -// Protocol125.cpp - -// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29) -/* -Documentation: - - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513 - - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262 - - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152 -*/ - -#include "Globals.h" - -#include "Protocol125.h" - -#include "../ClientHandle.h" -#include "ChunkDataSerializer.h" -#include "../Entity.h" -#include "../Mobs/Monster.h" -#include "../Pickup.h" -#include "../Player.h" -#include "../ChatColor.h" -#include "../UI/Window.h" -#include "../Root.h" -#include "../Server.h" -#include "../FallingBlock.h" - - - - - -enum -{ - PACKET_KEEP_ALIVE = 0x00, - PACKET_LOGIN = 0x01, - PACKET_HANDSHAKE = 0x02, - PACKET_CHAT = 0x03, - PACKET_UPDATE_TIME = 0x04, - PACKET_ENTITY_EQUIPMENT = 0x05, - PACKET_USE_ENTITY = 0x07, - PACKET_UPDATE_HEALTH = 0x08, - PACKET_RESPAWN = 0x09, - PACKET_PLAYER_ON_GROUND = 0x0a, - PACKET_PLAYER_POS = 0x0b, - PACKET_PLAYER_LOOK = 0x0c, - PACKET_PLAYER_MOVE_LOOK = 0x0d, - PACKET_BLOCK_DIG = 0x0e, - PACKET_BLOCK_PLACE = 0x0f, - PACKET_SLOT_SELECTED = 0x10, - PACKET_USE_BED = 0x11, - PACKET_ANIMATION = 0x12, - PACKET_PACKET_ENTITY_ACTION = 0x13, - PACKET_PLAYER_SPAWN = 0x14, - PACKET_PICKUP_SPAWN = 0x15, - PACKET_COLLECT_PICKUP = 0x16, - PACKET_SPAWN_OBJECT = 0x17, - PACKET_SPAWN_MOB = 0x18, - PACKET_ENTITY_VELOCITY = 0x1c, - PACKET_DESTROY_ENTITY = 0x1d, - PACKET_ENTITY = 0x1e, - PACKET_ENT_REL_MOVE = 0x1f, - PACKET_ENT_LOOK = 0x20, - PACKET_ENT_REL_MOVE_LOOK = 0x21, - PACKET_ENT_TELEPORT = 0x22, - PACKET_ENT_HEAD_LOOK = 0x23, - PACKET_ENT_STATUS = 0x26, - PACKET_ATTACH_ENTITY = 0x27, - PACKET_METADATA = 0x28, - PACKET_PRE_CHUNK = 0x32, - PACKET_MAP_CHUNK = 0x33, - PACKET_MULTI_BLOCK = 0x34, - PACKET_BLOCK_CHANGE = 0x35, - PACKET_BLOCK_ACTION = 0x36, - PACKET_EXPLOSION = 0x3C, - PACKET_SOUND_EFFECT = 0x3e, - PACKET_SOUND_PARTICLE_EFFECT = 0x3d, - PACKET_CHANGE_GAME_STATE = 0x46, - PACKET_THUNDERBOLT = 0x47, - PACKET_WINDOW_OPEN = 0x64, - PACKET_WINDOW_CLOSE = 0x65, - PACKET_WINDOW_CLICK = 0x66, - PACKET_INVENTORY_SLOT = 0x67, - PACKET_INVENTORY_WHOLE = 0x68, - PACKET_INVENTORY_PROGRESS = 0x69, - PACKET_CREATIVE_INVENTORY_ACTION = 0x6B, - PACKET_UPDATE_SIGN = 0x82, - PACKET_PLAYER_LIST_ITEM = 0xC9, - PACKET_PLAYER_ABILITIES = 0xca, - PACKET_PLUGIN_MESSAGE = 0xfa, - PACKET_PING = 0xfe, - PACKET_DISCONNECT = 0xff -} ; - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - -typedef unsigned char Byte; - - - - - -cProtocol125::cProtocol125(cClientHandle * a_Client) : - super(a_Client), - m_ReceivedData(32 KiB) -{ -} - - - - - -void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ATTACH_ENTITY); - WriteInt(a_Entity.GetUniqueID()); - WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); - Flush(); -} - - - - - -void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - UNUSED(a_BlockType); - - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_BLOCK_ACTION); - WriteInt (a_BlockX); - WriteShort((short)a_BlockY); - WriteInt (a_BlockZ); - WriteByte (a_Byte1); - WriteByte (a_Byte2); - Flush(); -} - - - - - -void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) -{ - // Not supported in this protocol version -} - - - - - -void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_BLOCK_CHANGE); - WriteInt (a_BlockX); - WriteByte((unsigned char)a_BlockY); - WriteInt (a_BlockZ); - WriteByte(a_BlockType); - WriteByte(a_BlockMeta); - Flush(); -} - - - - - -void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) -{ - cCSLock Lock(m_CSPacket); - if (a_Changes.size() == 1) - { - // Special packet for single-block changes - const sSetBlock & blk = a_Changes.front(); - SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta); - return; - } - - WriteByte (PACKET_MULTI_BLOCK); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - WriteShort((unsigned short)a_Changes.size()); - WriteUInt (sizeof(int) * a_Changes.size()); - for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) - { - unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); - unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); - WriteUInt(Coords << 16 | Blocks); - } - Flush(); -} - - - - - -void cProtocol125::SendChat(const AString & a_Message) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_CHAT); - WriteString(a_Message); - Flush(); -} - - - - - -void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - cCSLock Lock(m_CSPacket); - - // Send the pre-chunk: - SendPreChunk(a_ChunkX, a_ChunkZ, true); - - // Send the chunk data: - AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5); - WriteByte(PACKET_MAP_CHUNK); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - SendData(Serialized.data(), Serialized.size()); - Flush(); -} - - - - - -void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_COLLECT_PICKUP); - WriteInt (a_Pickup.GetUniqueID()); - WriteInt (a_Player.GetUniqueID()); - Flush(); -} - - - - - -void cProtocol125::SendDestroyEntity(const cEntity & a_Entity) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_DESTROY_ENTITY); - WriteInt (a_Entity.GetUniqueID()); - Flush(); -} - - - - - -void cProtocol125::SendDisconnect(const AString & a_Reason) -{ - cCSLock Lock(m_CSPacket); - WriteByte ((unsigned char)PACKET_DISCONNECT); - WriteString(a_Reason); - Flush(); -} - - - - -void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_ENTITY_EQUIPMENT); - WriteInt (a_Entity.GetUniqueID()); - WriteShort(a_SlotNum); - WriteShort(a_Item.m_ItemType); - WriteShort(a_Item.m_ItemDamage); - Flush(); -} - - - - - -void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_HEAD_LOOK); - WriteInt (a_Entity.GetUniqueID()); - WriteByte((char)((a_Entity.GetHeadYaw() / 360.f) * 256)); - Flush(); -} - - - - - -void cProtocol125::SendEntityLook(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_LOOK); - WriteInt (a_Entity.GetUniqueID()); - WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); - WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); - Flush(); -} - - - - - -void cProtocol125::SendEntityMetadata(const cEntity & a_Entity) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_METADATA); - WriteInt (a_Entity.GetUniqueID()); - AString MetaData = GetEntityMetaData(a_Entity); - SendData(MetaData.data(), MetaData.size()); - Flush(); -} - - - - - -void cProtocol125::SendEntityProperties(const cEntity & a_Entity) -{ - // Not supported in this protocol version -} - - - - - -void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_REL_MOVE); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_RelX); - WriteByte(a_RelY); - WriteByte(a_RelZ); - Flush(); -} - - - - - -void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_REL_MOVE_LOOK); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_RelX); - WriteByte(a_RelY); - WriteByte(a_RelZ); - WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); - WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); - Flush(); -} - - - - - -void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENT_STATUS); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_Status); - Flush(); -} - - - - - -void cProtocol125::SendEntityVelocity(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENTITY_VELOCITY); - WriteInt (a_Entity.GetUniqueID()); - WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20 - WriteShort((short) (a_Entity.GetSpeedY() * 400)); - WriteShort((short) (a_Entity.GetSpeedZ() * 400)); - Flush(); -} - - - - - -void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_EXPLOSION); - WriteDouble (a_BlockX); - WriteDouble (a_BlockY); - WriteDouble (a_BlockZ); - WriteFloat (a_Radius); - WriteInt (a_BlocksAffected.size()); - for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr) - { - WriteByte ((Byte)(itr->x - a_BlockX)); - WriteByte ((Byte)(itr->y - a_BlockY)); - WriteByte ((Byte)(itr->z - a_BlockZ)); - } - WriteFloat ((float)a_PlayerMotion.x); - WriteFloat ((float)a_PlayerMotion.y); - WriteFloat ((float)a_PlayerMotion.z); - Flush(); -} - - - - - -void cProtocol125::SendGameMode(eGameMode a_GameMode) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_CHANGE_GAME_STATE); - WriteByte(3); - WriteByte((char)a_GameMode); - Flush(); -} - - - - - -void cProtocol125::SendHandshake(const AString & a_ConnectionHash) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_HANDSHAKE); - WriteString(a_ConnectionHash); - Flush(); -} - - - - - -void cProtocol125::SendHealth(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_UPDATE_HEALTH); - WriteShort((short)m_Client->GetPlayer()->GetHealth()); - WriteShort(m_Client->GetPlayer()->GetFoodLevel()); - WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); - Flush(); -} - - - - - -void cProtocol125::SendInventoryProgress(char a_WindowID, short a_ProgressBar, short a_Value) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_INVENTORY_PROGRESS); - WriteByte (a_WindowID); - WriteShort(a_ProgressBar); - WriteShort(a_Value); - Flush(); -} - - - - - -void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_INVENTORY_SLOT); - WriteByte (a_WindowID); - WriteShort(a_SlotNum); - WriteItem (a_Item); - Flush(); -} - - - - - -void cProtocol125::SendKeepAlive(int a_PingID) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_KEEP_ALIVE); - WriteInt (a_PingID); - Flush(); -} - - - - - -void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World) -{ - UNUSED(a_World); - cCSLock Lock(m_CSPacket); - - WriteByte (PACKET_LOGIN); - WriteInt (a_Player.GetUniqueID()); // EntityID of the player - WriteString(""); // Username, not used - WriteString("default"); // Level type - WriteInt ((int)a_Player.GetGameMode()); - WriteInt ((int)(a_World.GetDimension())); - WriteByte (2); // TODO: Difficulty - WriteByte (0); // Unused - WriteByte (60); // Client list width or something - Flush(); -} - - - - - -void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_PICKUP_SPAWN); - WriteInt (a_Pickup.GetUniqueID()); - WriteShort (a_Pickup.GetItem().m_ItemType); - WriteByte (a_Pickup.GetItem().m_ItemCount); - WriteShort (a_Pickup.GetItem().m_ItemDamage); - WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); - WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); - WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); - WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); - Flush(); -} - - - - - -void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ANIMATION); - WriteInt (a_Player.GetUniqueID()); - WriteByte(a_Animation); - Flush(); -} - - - - - -void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) -{ - cCSLock Lock(m_CSPacket); - AString PlayerName(a_Player.GetColor()); - PlayerName.append(a_Player.GetName()); - if (PlayerName.length() > 14) - { - PlayerName.erase(14); - } - PlayerName += cChatColor::White; - - WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM); - WriteString(PlayerName); - WriteBool (a_IsOnline); - WriteShort (a_Player.GetClientHandle()->GetPing()); - Flush(); -} - - - - - -void cProtocol125::SendPlayerMaxSpeed(void) -{ - // Not supported by this protocol version -} - - - - - -void cProtocol125::SendPlayerMoveLook(void) -{ - cCSLock Lock(m_CSPacket); - - /* - LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", - m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 - ); - */ - - WriteByte (PACKET_PLAYER_MOVE_LOOK); - cPlayer * Player = m_Client->GetPlayer(); - WriteDouble(Player->GetPosX()); - WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block - WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block - WriteDouble(Player->GetPosZ()); - WriteFloat ((float)(Player->GetRotation())); - WriteFloat ((float)(Player->GetPitch())); - WriteBool (Player->IsOnGround()); - Flush(); -} - - - - - -void cProtocol125::SendPlayerPosition(void) -{ - cCSLock Lock(m_CSPacket); - LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now -} - - - - - -void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) -{ - const cItem & HeldItem = a_Player.GetEquippedItem(); - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_PLAYER_SPAWN); - WriteInt (a_Player.GetUniqueID()); - WriteString(a_Player.GetName()); - WriteInt ((int)(a_Player.GetPosX() * 32)); - WriteInt ((int)(a_Player.GetPosY() * 32)); - WriteInt ((int)(a_Player.GetPosZ() * 32)); - WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); - WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); - WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); - Flush(); -} - - - - - -void cProtocol125::SendRespawn(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_RESPAWN); - WriteInt ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension())); - WriteByte (2); // TODO: Difficulty; 2 = Normal - WriteByte ((char)m_Client->GetPlayer()->GetGameMode()); - WriteShort (256); // Current world height - WriteString("default"); -} - - - - - -void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) -{ - // Not needed in this protocol version -} - - - - - -void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - // Not implemented in this protocol version -} - - - - - -void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) -{ - // This protocol version implements falling blocks using the spawn object / vehicle packet: - SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0); -} - - - - - -void cProtocol125::SendSpawnMob(const cMonster & a_Mob) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SPAWN_MOB); - WriteInt (a_Mob.GetUniqueID()); - WriteByte (a_Mob.GetMobType()); - WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); - WriteByte (0); - WriteByte (0); - WriteByte (0); - AString MetaData = GetEntityMetaData(a_Mob); - SendData (MetaData.data(), MetaData.size()); - Flush(); -} - - - - - -void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) -{ - UNUSED(a_Yaw); - UNUSED(a_Pitch); - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_ObjectType); - WriteInt ((int)(a_Entity.GetPosX() * 32)); - WriteInt ((int)(a_Entity.GetPosY() * 32)); - WriteInt ((int)(a_Entity.GetPosZ() * 32)); - WriteByte(a_Pitch); - WriteByte(a_Yaw); - WriteInt (a_ObjectData); - if (a_ObjectData != 0) - { - WriteShort((short)(a_Entity.GetSpeedX() * 400)); - WriteShort((short)(a_Entity.GetSpeedY() * 400)); - WriteShort((short)(a_Entity.GetSpeedZ() * 400)); - } - Flush(); -} - - - - - -void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SPAWN_OBJECT); - WriteInt (a_Vehicle.GetUniqueID()); - WriteByte (a_VehicleType); - WriteInt ((int)(a_Vehicle.GetPosX() * 32)); - WriteInt ((int)(a_Vehicle.GetPosY() * 32)); - WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); - WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); - WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); - WriteInt (1); - WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); - WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); - WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); - Flush(); -} - - - - - -void cProtocol125::SendTeleportEntity(const cEntity & a_Entity) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_ENT_TELEPORT); - WriteInt (a_Entity.GetUniqueID()); - WriteInt ((int)(floor(a_Entity.GetPosX() * 32))); - WriteInt ((int)(floor(a_Entity.GetPosY() * 32))); - WriteInt ((int)(floor(a_Entity.GetPosZ() * 32))); - WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256)); - WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256)); - Flush(); -} - - - - - -void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_THUNDERBOLT); - WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one - WriteBool(true); // Unknown bool - WriteInt (a_BlockX * 32); - WriteInt (a_BlockY * 32); - WriteInt (a_BlockZ * 32); - Flush(); -} - - - - - -void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_UPDATE_TIME); - // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day: - WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000)); - Flush(); -} - - - - - -void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSPacket); - SendPreChunk(a_ChunkX, a_ChunkZ, false); -} - - - - - -void cProtocol125::SendUpdateSign( - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 -) -{ - cCSLock Lock(m_CSPacket); - WriteByte ((unsigned char)PACKET_UPDATE_SIGN); - WriteInt (a_BlockX); - WriteShort ((short)a_BlockY); - WriteInt (a_BlockZ); - WriteString(a_Line1); - WriteString(a_Line2); - WriteString(a_Line3); - WriteString(a_Line4); - Flush(); -} - - - - - -void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_USE_BED); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(0); // Unknown byte only 0 has been observed - WriteInt (a_BlockX); - WriteByte(a_BlockY); - WriteInt (a_BlockZ); - Flush(); -} - - - - - -void cProtocol125::SendWeather(eWeather a_Weather) -{ - cCSLock Lock(m_CSPacket); - switch( a_Weather ) - { - case eWeather_Sunny: - { - WriteByte(PACKET_CHANGE_GAME_STATE); - WriteByte(2); // Stop rain - WriteByte(0); // Unused - Flush(); - break; - } - - case eWeather_Rain: - case eWeather_ThunderStorm: - { - WriteByte(PACKET_CHANGE_GAME_STATE); - WriteByte(1); // Begin rain - WriteByte(0); // Unused - Flush(); - break; - } - } -} - - - - - -void cProtocol125::SendWholeInventory(const cInventory & a_Inventory) -{ - SendWholeInventory(*(a_Inventory.GetOwner().GetWindow())); -} - - - - - -void cProtocol125::SendWholeInventory(const cWindow & a_Window) -{ - cCSLock Lock(m_CSPacket); - cItems Slots; - a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); - SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0])); -} - - - - - -void cProtocol125::SendWindowClose(const cWindow & a_Window) -{ - if (a_Window.GetWindowType() == cWindow::Inventory) - { - // Do not send inventory-window-close - return; - } - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_WINDOW_CLOSE); - WriteByte(a_Window.GetWindowID()); - Flush(); -} - - - - - -void cProtocol125::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) -{ - if (a_WindowType < 0) - { - // Do not send for inventory windows - return; - } - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_WINDOW_OPEN); - WriteByte (a_WindowID); - WriteByte (a_WindowType); - WriteString(a_WindowTitle); - WriteByte (a_NumSlots); - Flush(); -} - - - - - -AString cProtocol125::GetAuthServerID(void) -{ - // http://wiki.vg/wiki/index.php?title=Session&oldid=2262 - // The server generates a random hash and that is used for all clients, unmodified - return cRoot::Get()->GetServer()->GetServerID(); -} - - - - - -void cProtocol125::SendData(const char * a_Data, int a_Size) -{ - m_Client->SendData(a_Data, a_Size); -} - - - - - -void cProtocol125::DataReceived(const char * a_Data, int a_Size) -{ - if (!m_ReceivedData.Write(a_Data, a_Size)) - { - // Too much data in the incoming queue, report to caller: - m_Client->PacketBufferFull(); - return; - } - - // Parse and handle all complete packets in m_ReceivedData: - while (m_ReceivedData.CanReadBytes(1)) - { - unsigned char PacketType; - m_ReceivedData.ReadByte(PacketType); - switch (ParsePacket(PacketType)) - { - case PARSE_UNKNOWN: - { - // An unknown packet has been received, notify the client and abort: - m_Client->PacketUnknown(PacketType); - return; - } - case PARSE_ERROR: - { - // An error occurred while parsing a known packet, notify the client and abort: - m_Client->PacketError(PacketType); - return; - } - case PARSE_INCOMPLETE: - { - // Incomplete packet, bail out and process with the next batch of data - m_ReceivedData.ResetRead(); - return; - } - default: - { - // Packet successfully parsed, commit the read data and try again one more packet - m_ReceivedData.CommitRead(); - break; - } - } - } -} - - - - - -int cProtocol125::ParsePacket(unsigned char a_PacketType) -{ - switch (a_PacketType) - { - default: return PARSE_UNKNOWN; - case PACKET_ANIMATION: return ParseArmAnim(); - case PACKET_BLOCK_DIG: return ParseBlockDig(); - case PACKET_BLOCK_PLACE: return ParseBlockPlace(); - case PACKET_CHAT: return ParseChat(); - case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction(); - case PACKET_DISCONNECT: return ParseDisconnect(); - case PACKET_HANDSHAKE: return ParseHandshake(); - case PACKET_KEEP_ALIVE: return ParseKeepAlive(); - case PACKET_LOGIN: return ParseLogin(); - case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction(); - case PACKET_PING: return ParsePing(); - case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities(); - case PACKET_PLAYER_LOOK: return ParsePlayerLook(); - case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook(); - case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround(); - case PACKET_PLAYER_POS: return ParsePlayerPosition(); - case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage(); - case PACKET_RESPAWN: return ParseRespawn(); - case PACKET_SLOT_SELECTED: return ParseSlotSelected(); - case PACKET_UPDATE_SIGN: return ParseUpdateSign(); - case PACKET_USE_ENTITY: return ParseUseEntity(); - case PACKET_WINDOW_CLICK: return ParseWindowClick(); - case PACKET_WINDOW_CLOSE: return ParseWindowClose(); - } -} - - - - - -#define HANDLE_PACKET_PARSE(Packet) \ - { \ - int res = Packet.Parse(m_ReceivedData); \ - if (res < 0) \ - { \ - return res; \ - } \ - } - - - - - -int cProtocol125::ParseArmAnim(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_PACKET_READ(ReadChar, char, Animation); - m_Client->HandleAnimation(Animation); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseBlockDig(void) -{ - HANDLE_PACKET_READ(ReadChar, char, Status); - HANDLE_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_PACKET_READ(ReadByte, Byte, PosY); - HANDLE_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_PACKET_READ(ReadChar, char, BlockFace); - m_Client->HandleLeftClick(PosX, PosY, PosZ, BlockFace, Status); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseBlockPlace(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_PACKET_READ(ReadByte, Byte, PosY); - HANDLE_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_PACKET_READ(ReadChar, char, BlockFace); - - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - - // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable. - m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, 8, 8, 8, HeldItem); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseChat(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message); - m_Client->HandleChat(Message); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseCreativeInventoryAction(void) -{ - HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - m_Client->HandleCreativeInventory(SlotNum, HeldItem); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseDisconnect(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason); - m_Client->HandleDisconnect(Reason); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseEntityAction(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_PACKET_READ(ReadChar, char, ActionID); - m_Client->HandleEntityAction(EntityID, ActionID); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseHandshake(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); - - AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565" - if (UserData.empty()) - { - m_Client->Kick("Did not receive username"); - return PARSE_OK; - } - m_Username = UserData[0]; - - LOGD("HANDSHAKE %s", Username.c_str()); - - if (!m_Client->HandleHandshake( m_Username )) - { - return PARSE_OK; // Player is not allowed into the server - } - - SendHandshake(cRoot::Get()->GetServer()->GetServerID()); - LOGD("User \"%s\" was sent a handshake response", m_Username.c_str()); - - return PARSE_OK; -} - - - - - -int cProtocol125::ParseKeepAlive(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID); - m_Client->HandleKeepAlive(KeepAliveID); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseLogin(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); - HANDLE_PACKET_READ(ReadBEInt, int, ServerMode); - HANDLE_PACKET_READ(ReadBEInt, int, Dimension); - HANDLE_PACKET_READ(ReadChar, char, Difficulty); - HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight); - HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers); - - if (ProtocolVersion < 29) - { - m_Client->Kick("Your client is outdated!"); - return PARSE_OK; - } - else if (ProtocolVersion > 29) - { - m_Client->Kick("Your client version is higher than the server!"); - return PARSE_OK; - } - - if (m_Username.compare(Username) != 0) - { - LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking", - Username.c_str(), - m_Username.c_str(), - m_Client->GetIPString().c_str() - ); - m_Client->Kick("Hacked client"); // Don't tell them why we don't want them - return PARSE_OK; - } - - m_Client->HandleLogin(ProtocolVersion, Username); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePing(void) -{ - // Packet has no more data - m_Client->HandlePing(); - return PARSE_OK; -} - - - - - - -int cProtocol125::ParsePlayerAbilities(void) -{ - HANDLE_PACKET_READ(ReadBool, bool, Invulnerable); - HANDLE_PACKET_READ(ReadBool, bool, IsFlying); - HANDLE_PACKET_READ(ReadBool, bool, CanFly); - HANDLE_PACKET_READ(ReadBool, bool, InstaMine); - // TODO: m_Client->HandlePlayerAbilities(...); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePlayerLook(void) -{ - HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); - HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); - HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); - m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePlayerMoveLook(void) -{ - HANDLE_PACKET_READ(ReadBEDouble, double, PosX); - HANDLE_PACKET_READ(ReadBEDouble, double, PosY); - HANDLE_PACKET_READ(ReadBEDouble, double, Stance); - HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); - HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); - HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); - HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); - // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0); - m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePlayerOnGround(void) -{ - HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); - // TODO: m_Client->HandleFlying(IsOnGround); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePlayerPosition(void) -{ - HANDLE_PACKET_READ(ReadBEDouble, double, PosX); - HANDLE_PACKET_READ(ReadBEDouble, double, PosY); - HANDLE_PACKET_READ(ReadBEDouble, double, Stance); - HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); - HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); - m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); - return PARSE_OK; -} - - - - - -int cProtocol125::ParsePluginMessage(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); - HANDLE_PACKET_READ(ReadBEShort, short, Length); - AString Data; - if (!m_ReceivedData.ReadString(Data, Length)) - { - m_ReceivedData.CheckValid(); - return PARSE_INCOMPLETE; - } - m_ReceivedData.CheckValid(); - - // TODO: Process the data - LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str()); - - return PARSE_OK; -} - - - - - -int cProtocol125::ParseRespawn(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, Dimension); - HANDLE_PACKET_READ(ReadChar, char, Difficulty); - HANDLE_PACKET_READ(ReadChar, char, CreativeMode); - HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); - m_Client->HandleRespawn(); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseSlotSelected(void) -{ - HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); - m_Client->HandleSlotSelected(SlotNum); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseUpdateSign(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_PACKET_READ(ReadBEShort, short, BlockY); - HANDLE_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4); - m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseUseEntity(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID); - HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID); - HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick); - m_Client->HandleUseEntity(TargetEntityID, IsLeftClick); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseWindowClick(void) -{ - HANDLE_PACKET_READ(ReadChar, char, WindowID); - HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); - HANDLE_PACKET_READ(ReadBool, bool, IsRightClick); - HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); - HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed); - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - - // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols: - eClickAction Action; - if (IsRightClick) - { - if (IsShiftPressed) - { - Action = caShiftRightClick; - } - else - { - if (SlotNum == -999) - { - Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside; - } - else - { - Action = caRightClick; - } - } - } - else - { - // IsLeftClick - if (IsShiftPressed) - { - Action = caShiftLeftClick; - } - else - { - if (SlotNum == -999) - { - Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside; - } - else - { - Action = caLeftClick; - } - } - } - m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); - return PARSE_OK; -} - - - - - -int cProtocol125::ParseWindowClose(void) -{ - HANDLE_PACKET_READ(ReadChar, char, WindowID); - m_Client->HandleWindowClose(WindowID); - return PARSE_OK; -} - - - - - -void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad) -{ - WriteByte(PACKET_PRE_CHUNK); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - WriteBool(a_ShouldLoad); - Flush(); -} - - - - - -void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items) -{ - WriteByte (PACKET_INVENTORY_WHOLE); - WriteByte (a_WindowID); - WriteShort((short)a_NumItems); - - for (int j = 0; j < a_NumItems; j++) - { - WriteItem(a_Items[j]); - } - Flush(); -} - - - - - -void cProtocol125::WriteItem(const cItem & a_Item) -{ - short ItemType = a_Item.m_ItemType; - ASSERT(ItemType >= -1); // Check validity of packets in debug runtime - if (ItemType <= 0) - { - // Fix, to make sure no invalid values are sent. - ItemType = -1; - } - - WriteShort(ItemType); - if (a_Item.IsEmpty()) - { - return; - } - - WriteByte (a_Item.m_ItemCount); - WriteShort(a_Item.m_ItemDamage); - - if (cItem::IsEnchantable(a_Item.m_ItemType)) - { - // TODO: Implement enchantments - WriteShort(-1); - } -} - - - - - -int cProtocol125::ParseItem(cItem & a_Item) -{ - HANDLE_PACKET_READ(ReadBEShort, short, ItemType); - - if (ItemType <= -1) - { - a_Item.Empty(); - return PARSE_OK; - } - a_Item.m_ItemType = ItemType; - - HANDLE_PACKET_READ(ReadChar, char, ItemCount); - HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); - a_Item.m_ItemCount = ItemCount; - a_Item.m_ItemDamage = ItemDamage; - if (ItemCount <= 0) - { - a_Item.Empty(); - } - - if (!cItem::IsEnchantable(ItemType)) - { - return PARSE_OK; - } - - HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes); - - if (EnchantNumBytes <= 0) - { - return PARSE_OK; - } - - // TODO: Enchantment not implemented yet! - if (!m_ReceivedData.SkipRead(EnchantNumBytes)) - { - return PARSE_INCOMPLETE; - } - - return PARSE_OK; -} - - - - - -AString cProtocol125::GetEntityMetaData(const cEntity & a_Entity) -{ - // We should send all the metadata here - AString MetaData; - // Common metadata (index 0, byte): - MetaData.push_back(0); - MetaData.push_back(GetEntityMetadataFlags(a_Entity)); - - // TODO: Add more entity-specific metadata - - MetaData.push_back(0x7f); // End metadata - return MetaData; -} - - - - - -char cProtocol125::GetEntityMetadataFlags(const cEntity & a_Entity) -{ - char Flags = 0; - if (a_Entity.IsOnFire()) - { - Flags |= 1; - } - if (a_Entity.IsCrouched()) - { - Flags |= 2; - } - if (a_Entity.IsRiding()) - { - Flags |= 4; - } - if (a_Entity.IsSprinting()) - { - Flags |= 8; - } - if (a_Entity.IsRclking()) - { - Flags |= 16; - } - return Flags; -} - - - - + +// Protocol125.cpp + +// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29) +/* +Documentation: + - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513 + - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152 +*/ + +#include "Globals.h" + +#include "Protocol125.h" + +#include "../ClientHandle.h" +#include "ChunkDataSerializer.h" +#include "../Entity.h" +#include "../Mobs/Monster.h" +#include "../Pickup.h" +#include "../Player.h" +#include "../ChatColor.h" +#include "../UI/Window.h" +#include "../Root.h" +#include "../Server.h" +#include "../FallingBlock.h" + + + + + +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_HANDSHAKE = 0x02, + PACKET_CHAT = 0x03, + PACKET_UPDATE_TIME = 0x04, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_USE_ENTITY = 0x07, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_RESPAWN = 0x09, + PACKET_PLAYER_ON_GROUND = 0x0a, + PACKET_PLAYER_POS = 0x0b, + PACKET_PLAYER_LOOK = 0x0c, + PACKET_PLAYER_MOVE_LOOK = 0x0d, + PACKET_BLOCK_DIG = 0x0e, + PACKET_BLOCK_PLACE = 0x0f, + PACKET_SLOT_SELECTED = 0x10, + PACKET_USE_BED = 0x11, + PACKET_ANIMATION = 0x12, + PACKET_PACKET_ENTITY_ACTION = 0x13, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_PICKUP_SPAWN = 0x15, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_OBJECT = 0x17, + PACKET_SPAWN_MOB = 0x18, + PACKET_ENTITY_VELOCITY = 0x1c, + PACKET_DESTROY_ENTITY = 0x1d, + PACKET_ENTITY = 0x1e, + PACKET_ENT_REL_MOVE = 0x1f, + PACKET_ENT_LOOK = 0x20, + PACKET_ENT_REL_MOVE_LOOK = 0x21, + PACKET_ENT_TELEPORT = 0x22, + PACKET_ENT_HEAD_LOOK = 0x23, + PACKET_ENT_STATUS = 0x26, + PACKET_ATTACH_ENTITY = 0x27, + PACKET_METADATA = 0x28, + PACKET_PRE_CHUNK = 0x32, + PACKET_MAP_CHUNK = 0x33, + PACKET_MULTI_BLOCK = 0x34, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_EXPLOSION = 0x3C, + PACKET_SOUND_EFFECT = 0x3e, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d, + PACKET_CHANGE_GAME_STATE = 0x46, + PACKET_THUNDERBOLT = 0x47, + PACKET_WINDOW_OPEN = 0x64, + PACKET_WINDOW_CLOSE = 0x65, + PACKET_WINDOW_CLICK = 0x66, + PACKET_INVENTORY_SLOT = 0x67, + PACKET_INVENTORY_WHOLE = 0x68, + PACKET_INVENTORY_PROGRESS = 0x69, + PACKET_CREATIVE_INVENTORY_ACTION = 0x6B, + PACKET_UPDATE_SIGN = 0x82, + PACKET_PLAYER_LIST_ITEM = 0xC9, + PACKET_PLAYER_ABILITIES = 0xca, + PACKET_PLUGIN_MESSAGE = 0xfa, + PACKET_PING = 0xfe, + PACKET_DISCONNECT = 0xff +} ; + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + +typedef unsigned char Byte; + + + + + +cProtocol125::cProtocol125(cClientHandle * a_Client) : + super(a_Client), + m_ReceivedData(32 KiB) +{ +} + + + + + +void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ATTACH_ENTITY); + WriteInt(a_Entity.GetUniqueID()); + WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + UNUSED(a_BlockType); + + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_ACTION); + WriteInt (a_BlockX); + WriteShort((short)a_BlockY); + WriteInt (a_BlockZ); + WriteByte (a_Byte1); + WriteByte (a_Byte2); + Flush(); +} + + + + + +void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + // Not supported in this protocol version +} + + + + + +void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_BLOCK_CHANGE); + WriteInt (a_BlockX); + WriteByte((unsigned char)a_BlockY); + WriteInt (a_BlockZ); + WriteByte(a_BlockType); + WriteByte(a_BlockMeta); + Flush(); +} + + + + + +void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + cCSLock Lock(m_CSPacket); + if (a_Changes.size() == 1) + { + // Special packet for single-block changes + const sSetBlock & blk = a_Changes.front(); + SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta); + return; + } + + WriteByte (PACKET_MULTI_BLOCK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteShort((unsigned short)a_Changes.size()); + WriteUInt (sizeof(int) * a_Changes.size()); + for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) + { + unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); + unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); + WriteUInt(Coords << 16 | Blocks); + } + Flush(); +} + + + + + +void cProtocol125::SendChat(const AString & a_Message) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_CHAT); + WriteString(a_Message); + Flush(); +} + + + + + +void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + cCSLock Lock(m_CSPacket); + + // Send the pre-chunk: + SendPreChunk(a_ChunkX, a_ChunkZ, true); + + // Send the chunk data: + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5); + WriteByte(PACKET_MAP_CHUNK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + SendData(Serialized.data(), Serialized.size()); + Flush(); +} + + + + + +void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COLLECT_PICKUP); + WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Player.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendDestroyEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DESTROY_ENTITY); + WriteInt (a_Entity.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol125::SendDisconnect(const AString & a_Reason) +{ + cCSLock Lock(m_CSPacket); + WriteByte ((unsigned char)PACKET_DISCONNECT); + WriteString(a_Reason); + Flush(); +} + + + + +void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENTITY_EQUIPMENT); + WriteInt (a_Entity.GetUniqueID()); + WriteShort(a_SlotNum); + WriteShort(a_Item.m_ItemType); + WriteShort(a_Item.m_ItemDamage); + Flush(); +} + + + + + +void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_HEAD_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte((char)((a_Entity.GetHeadYaw() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityMetadata(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_METADATA); + WriteInt (a_Entity.GetUniqueID()); + AString MetaData = GetEntityMetaData(a_Entity); + SendData(MetaData.data(), MetaData.size()); + Flush(); +} + + + + + +void cProtocol125::SendEntityProperties(const cEntity & a_Entity) +{ + // Not supported in this protocol version +} + + + + + +void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_REL_MOVE); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_RelX); + WriteByte(a_RelY); + WriteByte(a_RelZ); + Flush(); +} + + + + + +void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_REL_MOVE_LOOK); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_RelX); + WriteByte(a_RelY); + WriteByte(a_RelZ); + WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENT_STATUS); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_Status); + Flush(); +} + + + + + +void cProtocol125::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_VELOCITY); + WriteInt (a_Entity.GetUniqueID()); + WriteShort((short) (a_Entity.GetSpeedX() * 400)); //400 = 8000 / 20 + WriteShort((short) (a_Entity.GetSpeedY() * 400)); + WriteShort((short) (a_Entity.GetSpeedZ() * 400)); + Flush(); +} + + + + + +void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_EXPLOSION); + WriteDouble (a_BlockX); + WriteDouble (a_BlockY); + WriteDouble (a_BlockZ); + WriteFloat (a_Radius); + WriteInt (a_BlocksAffected.size()); + for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr) + { + WriteByte ((Byte)(itr->x - a_BlockX)); + WriteByte ((Byte)(itr->y - a_BlockY)); + WriteByte ((Byte)(itr->z - a_BlockZ)); + } + WriteFloat ((float)a_PlayerMotion.x); + WriteFloat ((float)a_PlayerMotion.y); + WriteFloat ((float)a_PlayerMotion.z); + Flush(); +} + + + + + +void cProtocol125::SendGameMode(eGameMode a_GameMode) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(3); + WriteByte((char)a_GameMode); + Flush(); +} + + + + + +void cProtocol125::SendHandshake(const AString & a_ConnectionHash) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_HANDSHAKE); + WriteString(a_ConnectionHash); + Flush(); +} + + + + + +void cProtocol125::SendHealth(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_HEALTH); + WriteShort((short)m_Client->GetPlayer()->GetHealth()); + WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); + Flush(); +} + + + + + +void cProtocol125::SendInventoryProgress(char a_WindowID, short a_ProgressBar, short a_Value) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_INVENTORY_PROGRESS); + WriteByte (a_WindowID); + WriteShort(a_ProgressBar); + WriteShort(a_Value); + Flush(); +} + + + + + +void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_INVENTORY_SLOT); + WriteByte (a_WindowID); + WriteShort(a_SlotNum); + WriteItem (a_Item); + Flush(); +} + + + + + +void cProtocol125::SendKeepAlive(int a_PingID) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_KEEP_ALIVE); + WriteInt (a_PingID); + Flush(); +} + + + + + +void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + UNUSED(a_World); + cCSLock Lock(m_CSPacket); + + WriteByte (PACKET_LOGIN); + WriteInt (a_Player.GetUniqueID()); // EntityID of the player + WriteString(""); // Username, not used + WriteString("default"); // Level type + WriteInt ((int)a_Player.GetGameMode()); + WriteInt ((int)(a_World.GetDimension())); + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused + WriteByte (60); // Client list width or something + Flush(); +} + + + + + +void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PICKUP_SPAWN); + WriteInt (a_Pickup.GetUniqueID()); + WriteShort (a_Pickup.GetItem().m_ItemType); + WriteByte (a_Pickup.GetItem().m_ItemCount); + WriteShort (a_Pickup.GetItem().m_ItemDamage); + WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); + WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); + Flush(); +} + + + + + +void cProtocol125::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ANIMATION); + WriteInt (a_Player.GetUniqueID()); + WriteByte(a_Animation); + Flush(); +} + + + + + +void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + cCSLock Lock(m_CSPacket); + AString PlayerName(a_Player.GetColor()); + PlayerName.append(a_Player.GetName()); + if (PlayerName.length() > 14) + { + PlayerName.erase(14); + } + PlayerName += cChatColor::White; + + WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM); + WriteString(PlayerName); + WriteBool (a_IsOnline); + WriteShort (a_Player.GetClientHandle()->GetPing()); + Flush(); +} + + + + + +void cProtocol125::SendPlayerMaxSpeed(void) +{ + // Not supported by this protocol version +} + + + + + +void cProtocol125::SendPlayerMoveLook(void) +{ + cCSLock Lock(m_CSPacket); + + /* + LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", + m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 + ); + */ + + WriteByte (PACKET_PLAYER_MOVE_LOOK); + cPlayer * Player = m_Client->GetPlayer(); + WriteDouble(Player->GetPosX()); + WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block + WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block + WriteDouble(Player->GetPosZ()); + WriteFloat ((float)(Player->GetRotation())); + WriteFloat ((float)(Player->GetPitch())); + WriteBool (Player->IsOnGround()); + Flush(); +} + + + + + +void cProtocol125::SendPlayerPosition(void) +{ + cCSLock Lock(m_CSPacket); + LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now +} + + + + + +void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player) +{ + const cItem & HeldItem = a_Player.GetEquippedItem(); + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PLAYER_SPAWN); + WriteInt (a_Player.GetUniqueID()); + WriteString(a_Player.GetName()); + WriteInt ((int)(a_Player.GetPosX() * 32)); + WriteInt ((int)(a_Player.GetPosY() * 32)); + WriteInt ((int)(a_Player.GetPosZ() * 32)); + WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); + WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); + Flush(); +} + + + + + +void cProtocol125::SendRespawn(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_RESPAWN); + WriteInt ((int)(m_Client->GetPlayer()->GetWorld()->GetDimension())); + WriteByte (2); // TODO: Difficulty; 2 = Normal + WriteByte ((char)m_Client->GetPlayer()->GetGameMode()); + WriteShort (256); // Current world height + WriteString("default"); +} + + + + + +void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + // Not needed in this protocol version +} + + + + + +void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + // Not implemented in this protocol version +} + + + + + +void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + // This protocol version implements falling blocks using the spawn object / vehicle packet: + SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0); +} + + + + + +void cProtocol125::SendSpawnMob(const cMonster & a_Mob) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_MOB); + WriteInt (a_Mob.GetUniqueID()); + WriteByte (a_Mob.GetMobType()); + WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); + WriteByte (0); + WriteByte (0); + WriteByte (0); + AString MetaData = GetEntityMetaData(a_Mob); + SendData (MetaData.data(), MetaData.size()); + Flush(); +} + + + + + +void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + UNUSED(a_Yaw); + UNUSED(a_Pitch); + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_ObjectType); + WriteInt ((int)(a_Entity.GetPosX() * 32)); + WriteInt ((int)(a_Entity.GetPosY() * 32)); + WriteInt ((int)(a_Entity.GetPosZ() * 32)); + WriteByte(a_Pitch); + WriteByte(a_Yaw); + WriteInt (a_ObjectData); + if (a_ObjectData != 0) + { + WriteShort((short)(a_Entity.GetSpeedX() * 400)); + WriteShort((short)(a_Entity.GetSpeedY() * 400)); + WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_OBJECT); + WriteInt (a_Vehicle.GetUniqueID()); + WriteByte (a_VehicleType); + WriteInt ((int)(a_Vehicle.GetPosX() * 32)); + WriteInt ((int)(a_Vehicle.GetPosY() * 32)); + WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); + WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteInt (1); + WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + Flush(); +} + + + + + +void cProtocol125::SendTeleportEntity(const cEntity & a_Entity) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENT_TELEPORT); + WriteInt (a_Entity.GetUniqueID()); + WriteInt ((int)(floor(a_Entity.GetPosX() * 32))); + WriteInt ((int)(floor(a_Entity.GetPosY() * 32))); + WriteInt ((int)(floor(a_Entity.GetPosZ() * 32))); + WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256)); + WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256)); + Flush(); +} + + + + + +void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_THUNDERBOLT); + WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one + WriteBool(true); // Unknown bool + WriteInt (a_BlockX * 32); + WriteInt (a_BlockY * 32); + WriteInt (a_BlockZ * 32); + Flush(); +} + + + + + +void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_TIME); + // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day: + WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000)); + Flush(); +} + + + + + +void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSPacket); + SendPreChunk(a_ChunkX, a_ChunkZ, false); +} + + + + + +void cProtocol125::SendUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 +) +{ + cCSLock Lock(m_CSPacket); + WriteByte ((unsigned char)PACKET_UPDATE_SIGN); + WriteInt (a_BlockX); + WriteShort ((short)a_BlockY); + WriteInt (a_BlockZ); + WriteString(a_Line1); + WriteString(a_Line2); + WriteString(a_Line3); + WriteString(a_Line4); + Flush(); +} + + + + + +void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_USE_BED); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(0); // Unknown byte only 0 has been observed + WriteInt (a_BlockX); + WriteByte(a_BlockY); + WriteInt (a_BlockZ); + Flush(); +} + + + + + +void cProtocol125::SendWeather(eWeather a_Weather) +{ + cCSLock Lock(m_CSPacket); + switch( a_Weather ) + { + case eWeather_Sunny: + { + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(2); // Stop rain + WriteByte(0); // Unused + Flush(); + break; + } + + case eWeather_Rain: + case eWeather_ThunderStorm: + { + WriteByte(PACKET_CHANGE_GAME_STATE); + WriteByte(1); // Begin rain + WriteByte(0); // Unused + Flush(); + break; + } + } +} + + + + + +void cProtocol125::SendWholeInventory(const cInventory & a_Inventory) +{ + SendWholeInventory(*(a_Inventory.GetOwner().GetWindow())); +} + + + + + +void cProtocol125::SendWholeInventory(const cWindow & a_Window) +{ + cCSLock Lock(m_CSPacket); + cItems Slots; + a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); + SendWindowSlots(a_Window.GetWindowID(), Slots.size(), &(Slots[0])); +} + + + + + +void cProtocol125::SendWindowClose(const cWindow & a_Window) +{ + if (a_Window.GetWindowType() == cWindow::Inventory) + { + // Do not send inventory-window-close + return; + } + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_WINDOW_CLOSE); + WriteByte(a_Window.GetWindowID()); + Flush(); +} + + + + + +void cProtocol125::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) +{ + if (a_WindowType < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_WindowID); + WriteByte (a_WindowType); + WriteString(a_WindowTitle); + WriteByte (a_NumSlots); + Flush(); +} + + + + + +AString cProtocol125::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + // The server generates a random hash and that is used for all clients, unmodified + return cRoot::Get()->GetServer()->GetServerID(); +} + + + + + +void cProtocol125::SendData(const char * a_Data, int a_Size) +{ + m_Client->SendData(a_Data, a_Size); +} + + + + + +void cProtocol125::DataReceived(const char * a_Data, int a_Size) +{ + if (!m_ReceivedData.Write(a_Data, a_Size)) + { + // Too much data in the incoming queue, report to caller: + m_Client->PacketBufferFull(); + return; + } + + // Parse and handle all complete packets in m_ReceivedData: + while (m_ReceivedData.CanReadBytes(1)) + { + unsigned char PacketType; + m_ReceivedData.ReadByte(PacketType); + switch (ParsePacket(PacketType)) + { + case PARSE_UNKNOWN: + { + // An unknown packet has been received, notify the client and abort: + m_Client->PacketUnknown(PacketType); + return; + } + case PARSE_ERROR: + { + // An error occurred while parsing a known packet, notify the client and abort: + m_Client->PacketError(PacketType); + return; + } + case PARSE_INCOMPLETE: + { + // Incomplete packet, bail out and process with the next batch of data + m_ReceivedData.ResetRead(); + return; + } + default: + { + // Packet successfully parsed, commit the read data and try again one more packet + m_ReceivedData.CommitRead(); + break; + } + } + } +} + + + + + +int cProtocol125::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + default: return PARSE_UNKNOWN; + case PACKET_ANIMATION: return ParseArmAnim(); + case PACKET_BLOCK_DIG: return ParseBlockDig(); + case PACKET_BLOCK_PLACE: return ParseBlockPlace(); + case PACKET_CHAT: return ParseChat(); + case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction(); + case PACKET_DISCONNECT: return ParseDisconnect(); + case PACKET_HANDSHAKE: return ParseHandshake(); + case PACKET_KEEP_ALIVE: return ParseKeepAlive(); + case PACKET_LOGIN: return ParseLogin(); + case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction(); + case PACKET_PING: return ParsePing(); + case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities(); + case PACKET_PLAYER_LOOK: return ParsePlayerLook(); + case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook(); + case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround(); + case PACKET_PLAYER_POS: return ParsePlayerPosition(); + case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage(); + case PACKET_RESPAWN: return ParseRespawn(); + case PACKET_SLOT_SELECTED: return ParseSlotSelected(); + case PACKET_UPDATE_SIGN: return ParseUpdateSign(); + case PACKET_USE_ENTITY: return ParseUseEntity(); + case PACKET_WINDOW_CLICK: return ParseWindowClick(); + case PACKET_WINDOW_CLOSE: return ParseWindowClose(); + } +} + + + + + +#define HANDLE_PACKET_PARSE(Packet) \ + { \ + int res = Packet.Parse(m_ReceivedData); \ + if (res < 0) \ + { \ + return res; \ + } \ + } + + + + + +int cProtocol125::ParseArmAnim(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, Animation); + m_Client->HandleAnimation(Animation); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseBlockDig(void) +{ + HANDLE_PACKET_READ(ReadChar, char, Status); + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + m_Client->HandleLeftClick(PosX, PosY, PosZ, BlockFace, Status); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseBlockPlace(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable. + m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, 8, 8, 8, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseChat(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message); + m_Client->HandleChat(Message); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseCreativeInventoryAction(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + m_Client->HandleCreativeInventory(SlotNum, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseDisconnect(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason); + m_Client->HandleDisconnect(Reason); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseEntityAction(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, ActionID); + m_Client->HandleEntityAction(EntityID, ActionID); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseHandshake(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + + AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565" + if (UserData.empty()) + { + m_Client->Kick("Did not receive username"); + return PARSE_OK; + } + m_Username = UserData[0]; + + LOGD("HANDSHAKE %s", Username.c_str()); + + if (!m_Client->HandleHandshake( m_Username )) + { + return PARSE_OK; // Player is not allowed into the server + } + + SendHandshake(cRoot::Get()->GetServer()->GetServerID()); + LOGD("User \"%s\" was sent a handshake response", m_Username.c_str()); + + return PARSE_OK; +} + + + + + +int cProtocol125::ParseKeepAlive(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID); + m_Client->HandleKeepAlive(KeepAliveID); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseLogin(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + HANDLE_PACKET_READ(ReadBEInt, int, ServerMode); + HANDLE_PACKET_READ(ReadBEInt, int, Dimension); + HANDLE_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight); + HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers); + + if (ProtocolVersion < 29) + { + m_Client->Kick("Your client is outdated!"); + return PARSE_OK; + } + else if (ProtocolVersion > 29) + { + m_Client->Kick("Your client version is higher than the server!"); + return PARSE_OK; + } + + if (m_Username.compare(Username) != 0) + { + LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking", + Username.c_str(), + m_Username.c_str(), + m_Client->GetIPString().c_str() + ); + m_Client->Kick("Hacked client"); // Don't tell them why we don't want them + return PARSE_OK; + } + + m_Client->HandleLogin(ProtocolVersion, Username); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePing(void) +{ + // Packet has no more data + m_Client->HandlePing(); + return PARSE_OK; +} + + + + + + +int cProtocol125::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, Invulnerable); + HANDLE_PACKET_READ(ReadBool, bool, IsFlying); + HANDLE_PACKET_READ(ReadBool, bool, CanFly); + HANDLE_PACKET_READ(ReadBool, bool, InstaMine); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerLook(void) +{ + HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); + HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerMoveLook(void) +{ + HANDLE_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_PACKET_READ(ReadBEFloat, float, Rotation); + HANDLE_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0); + m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerOnGround(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + // TODO: m_Client->HandleFlying(IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePlayerPosition(void) +{ + HANDLE_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_PACKET_READ(ReadBool, bool, IsOnGround); + m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround); + return PARSE_OK; +} + + + + + +int cProtocol125::ParsePluginMessage(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); + HANDLE_PACKET_READ(ReadBEShort, short, Length); + AString Data; + if (!m_ReceivedData.ReadString(Data, Length)) + { + m_ReceivedData.CheckValid(); + return PARSE_INCOMPLETE; + } + m_ReceivedData.CheckValid(); + + // TODO: Process the data + LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str()); + + return PARSE_OK; +} + + + + + +int cProtocol125::ParseRespawn(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, Dimension); + HANDLE_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_PACKET_READ(ReadChar, char, CreativeMode); + HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + m_Client->HandleRespawn(); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseSlotSelected(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + m_Client->HandleSlotSelected(SlotNum); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseUpdateSign(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4); + m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseUseEntity(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID); + HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID); + HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick); + m_Client->HandleUseEntity(TargetEntityID, IsLeftClick); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseWindowClick(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_PACKET_READ(ReadBool, bool, IsRightClick); + HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols: + eClickAction Action; + if (IsRightClick) + { + if (IsShiftPressed) + { + Action = caShiftRightClick; + } + else + { + if (SlotNum == -999) + { + Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside; + } + else + { + Action = caRightClick; + } + } + } + else + { + // IsLeftClick + if (IsShiftPressed) + { + Action = caShiftLeftClick; + } + else + { + if (SlotNum == -999) + { + Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside; + } + else + { + Action = caLeftClick; + } + } + } + m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol125::ParseWindowClose(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + m_Client->HandleWindowClose(WindowID); + return PARSE_OK; +} + + + + + +void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad) +{ + WriteByte(PACKET_PRE_CHUNK); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + WriteBool(a_ShouldLoad); + Flush(); +} + + + + + +void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items) +{ + WriteByte (PACKET_INVENTORY_WHOLE); + WriteByte (a_WindowID); + WriteShort((short)a_NumItems); + + for (int j = 0; j < a_NumItems; j++) + { + WriteItem(a_Items[j]); + } + Flush(); +} + + + + + +void cProtocol125::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + WriteShort(ItemType); + if (a_Item.IsEmpty()) + { + return; + } + + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (cItem::IsEnchantable(a_Item.m_ItemType)) + { + // TODO: Implement enchantments + WriteShort(-1); + } +} + + + + + +int cProtocol125::ParseItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + + if (ItemType <= -1) + { + a_Item.Empty(); + return PARSE_OK; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + if (!cItem::IsEnchantable(ItemType)) + { + return PARSE_OK; + } + + HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes); + + if (EnchantNumBytes <= 0) + { + return PARSE_OK; + } + + // TODO: Enchantment not implemented yet! + if (!m_ReceivedData.SkipRead(EnchantNumBytes)) + { + return PARSE_INCOMPLETE; + } + + return PARSE_OK; +} + + + + + +AString cProtocol125::GetEntityMetaData(const cEntity & a_Entity) +{ + // We should send all the metadata here + AString MetaData; + // Common metadata (index 0, byte): + MetaData.push_back(0); + MetaData.push_back(GetEntityMetadataFlags(a_Entity)); + + // TODO: Add more entity-specific metadata + + MetaData.push_back(0x7f); // End metadata + return MetaData; +} + + + + + +char cProtocol125::GetEntityMetadataFlags(const cEntity & a_Entity) +{ + char Flags = 0; + if (a_Entity.IsOnFire()) + { + Flags |= 1; + } + if (a_Entity.IsCrouched()) + { + Flags |= 2; + } + if (a_Entity.IsRiding()) + { + Flags |= 4; + } + if (a_Entity.IsSprinting()) + { + Flags |= 8; + } + if (a_Entity.IsRclking()) + { + Flags |= 16; + } + return Flags; +} + + + + diff --git a/source/Protocol/Protocol125.h b/source/Protocol/Protocol125.h index 2f769f362..2e4f80902 100644 --- a/source/Protocol/Protocol125.h +++ b/source/Protocol/Protocol125.h @@ -1,152 +1,152 @@ - -// Protocol125.h - -// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29) - - - - - -#pragma once - -#include "Protocol.h" -#include "../ByteBuffer.h" - - - - - -class cProtocol125 : - public cProtocol -{ - typedef cProtocol super; -public: - cProtocol125(cClientHandle * a_Client); - - /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; - - /// Sending stuff to clients (alphabetically sorted): - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; - virtual void SendChat (const AString & a_Message) override; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; - virtual void SendDestroyEntity (const cEntity & a_Entity) override; - virtual void SendDisconnect (const AString & a_Reason) override; - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; - virtual void SendEntityHeadLook (const cEntity & a_Entity) override; - virtual void SendEntityLook (const cEntity & a_Entity) override; - virtual void SendEntityMetadata (const cEntity & a_Entity) override; - virtual void SendEntityProperties (const cEntity & a_Entity) override; - virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; - virtual void SendEntityVelocity (const cEntity & a_Entity) override; - virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; - virtual void SendGameMode (eGameMode a_GameMode) override; - virtual void SendHealth (void) override; - virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) override; - virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; - virtual void SendKeepAlive (int a_PingID) override; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; - virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; - virtual void SendPlayerMaxSpeed (void) override; - virtual void SendPlayerMoveLook (void) override; - virtual void SendPlayerPosition (void) override; - virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendRespawn (void) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 - virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; - virtual void SendSpawnMob (const cMonster & a_Mob) override; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override; - virtual void SendTeleportEntity (const cEntity & a_Entity) override; - virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; - virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; - virtual void SendWeather (eWeather a_Weather) override; - virtual void SendWholeInventory (const cInventory & a_Inventory) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - virtual void SendWindowClose (const cWindow & a_Window) override; - virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; - - virtual AString GetAuthServerID(void) override; - -protected: - /// Results of packet-parsing: - enum { - PARSE_OK = 1, - PARSE_ERROR = -1, - PARSE_UNKNOWN = -2, - PARSE_INCOMPLETE = -3, - } ; - - cByteBuffer m_ReceivedData; //< Buffer for the received data - - AString m_Username; //< Stored in ParseHandshake(), compared to Login username - - virtual void SendData(const char * a_Data, int a_Size) override; - - /// Sends the Handshake packet - void SendHandshake(const AString & a_ConnectionHash); - - /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() ) - virtual int ParsePacket(unsigned char a_PacketType); - - // Specific packet parsers: - virtual int ParseArmAnim (void); - virtual int ParseBlockDig (void); - virtual int ParseBlockPlace (void); - virtual int ParseChat (void); - virtual int ParseCreativeInventoryAction(void); - virtual int ParseDisconnect (void); - virtual int ParseEntityAction (void); - virtual int ParseHandshake (void); - virtual int ParseKeepAlive (void); - virtual int ParseLogin (void); - virtual int ParsePing (void); - virtual int ParsePlayerAbilities (void); - virtual int ParsePlayerLook (void); - virtual int ParsePlayerMoveLook (void); - virtual int ParsePlayerOnGround (void); - virtual int ParsePlayerPosition (void); - virtual int ParsePluginMessage (void); - virtual int ParseRespawn (void); - virtual int ParseSlotSelected (void); - virtual int ParseUpdateSign (void); - virtual int ParseUseEntity (void); - virtual int ParseWindowClick (void); - virtual int ParseWindowClose (void); - - // Utility functions: - /// Writes a "pre-chunk" packet - void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad); - - /// Writes a "set window items" packet with the specified params - void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items); - - /// Writes one item, "slot" as the protocol wiki calls it - virtual void WriteItem(const cItem & a_Item); - - /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes - virtual int ParseItem(cItem & a_Item); - - /// Returns the entity metadata representation - AString GetEntityMetaData(const cEntity & a_Entity); - - /// Returns the entity common metadata, index 0 (generic flags) - char GetEntityMetadataFlags(const cEntity & a_Entity); -} ; - - - - + +// Protocol125.h + +// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29) + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" + + + + + +class cProtocol125 : + public cProtocol +{ + typedef cProtocol super; +public: + cProtocol125(cClientHandle * a_Client); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityHeadLook (const cEntity & a_Entity) override; + virtual void SendEntityLook (const cEntity & a_Entity) override; + virtual void SendEntityMetadata (const cEntity & a_Entity) override; + virtual void SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMaxSpeed (void) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; + virtual void SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cInventory & a_Inventory) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; + + virtual AString GetAuthServerID(void) override; + +protected: + /// Results of packet-parsing: + enum { + PARSE_OK = 1, + PARSE_ERROR = -1, + PARSE_UNKNOWN = -2, + PARSE_INCOMPLETE = -3, + } ; + + cByteBuffer m_ReceivedData; //< Buffer for the received data + + AString m_Username; //< Stored in ParseHandshake(), compared to Login username + + virtual void SendData(const char * a_Data, int a_Size) override; + + /// Sends the Handshake packet + void SendHandshake(const AString & a_ConnectionHash); + + /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ() ) + virtual int ParsePacket(unsigned char a_PacketType); + + // Specific packet parsers: + virtual int ParseArmAnim (void); + virtual int ParseBlockDig (void); + virtual int ParseBlockPlace (void); + virtual int ParseChat (void); + virtual int ParseCreativeInventoryAction(void); + virtual int ParseDisconnect (void); + virtual int ParseEntityAction (void); + virtual int ParseHandshake (void); + virtual int ParseKeepAlive (void); + virtual int ParseLogin (void); + virtual int ParsePing (void); + virtual int ParsePlayerAbilities (void); + virtual int ParsePlayerLook (void); + virtual int ParsePlayerMoveLook (void); + virtual int ParsePlayerOnGround (void); + virtual int ParsePlayerPosition (void); + virtual int ParsePluginMessage (void); + virtual int ParseRespawn (void); + virtual int ParseSlotSelected (void); + virtual int ParseUpdateSign (void); + virtual int ParseUseEntity (void); + virtual int ParseWindowClick (void); + virtual int ParseWindowClose (void); + + // Utility functions: + /// Writes a "pre-chunk" packet + void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad); + + /// Writes a "set window items" packet with the specified params + void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items); + + /// Writes one item, "slot" as the protocol wiki calls it + virtual void WriteItem(const cItem & a_Item); + + /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes + virtual int ParseItem(cItem & a_Item); + + /// Returns the entity metadata representation + AString GetEntityMetaData(const cEntity & a_Entity); + + /// Returns the entity common metadata, index 0 (generic flags) + char GetEntityMetadataFlags(const cEntity & a_Entity); +} ; + + + + diff --git a/source/Protocol/Protocol132.cpp b/source/Protocol/Protocol132.cpp index f34401b55..9a985ec1f 100644 --- a/source/Protocol/Protocol132.cpp +++ b/source/Protocol/Protocol132.cpp @@ -1,902 +1,902 @@ - -// Protocol132.cpp - -// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39) - -#include "Globals.h" -#include "Protocol132.h" -#include "../Root.h" -#include "../Server.h" -#include "../ClientHandle.h" -#include "../../CryptoPP/randpool.h" -#include "../Item.h" -#include "ChunkDataSerializer.h" -#include "../Player.h" -#include "../Mobs/Monster.h" -#include "../UI/Window.h" -#include "../Pickup.h" -#include "../WorldStorage/FastNBT.h" -#include "../StringCompression.h" - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - -typedef unsigned char Byte; - - - - - -using namespace CryptoPP; - - - - - -const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows... - - - - - -enum -{ - PACKET_KEEP_ALIVE = 0x00, - PACKET_LOGIN = 0x01, - PACKET_ENTITY_EQUIPMENT = 0x05, - PACKET_COMPASS = 0x06, - PACKET_PLAYER_SPAWN = 0x14, - PACKET_COLLECT_PICKUP = 0x16, - PACKET_SPAWN_MOB = 0x18, - PACKET_DESTROY_ENTITIES = 0x1d, - PACKET_CHUNK_DATA = 0x33, - PACKET_BLOCK_CHANGE = 0x35, - PACKET_BLOCK_ACTION = 0x36, - PACKET_BLOCK_BREAK_ANIM = 0x37, - PACKET_SOUND_EFFECT = 0x3e, - PACKET_SOUND_PARTICLE_EFFECT = 0x3d, - PACKET_LOCALE_VIEW_DISTANCE = 0xcc, - PACKET_CLIENT_STATUSES = 0xcd, - PACKET_ENCRYPTION_KEY_RESP = 0xfc, -} ; - - - - - -// Converts a raw 160-bit SHA1 digest into a Java Hex representation -// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802 -static void DigestToJava(byte a_Digest[20], AString & a_Out) -{ - bool IsNegative = (a_Digest[0] >= 0x80); - if (IsNegative) - { - // Two's complement: - bool carry = true; // Add one to the whole number - for (int i = 19; i >= 0; i--) - { - a_Digest[i] = ~a_Digest[i]; - if (carry) - { - carry = (a_Digest[i] == 0xff); - a_Digest[i]++; - } - } - } - a_Out.clear(); - a_Out.reserve(40); - for (int i = 0; i < 20; i++) - { - AppendPrintf(a_Out, "%02x", a_Digest[i]); - } - while ((a_Out.length() > 0) && (a_Out[0] == '0')) - { - a_Out.erase(0, 1); - } - if (IsNegative) - { - a_Out.insert(0, "-"); - } -} - - - - - -/* -// Self-test the hash formatting for known values: -// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48 -// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1 -// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6 - -class Test -{ -public: - Test(void) - { - AString DigestNotch, DigestJeb, DigestSimon; - byte Digest[20]; - CryptoPP::SHA1 Checksum; - Checksum.Update((const byte *)"Notch", 5); - Checksum.Final(Digest); - DigestToJava(Digest, DigestNotch); - Checksum.Restart(); - Checksum.Update((const byte *)"jeb_", 4); - Checksum.Final(Digest); - DigestToJava(Digest, DigestJeb); - Checksum.Restart(); - Checksum.Update((const byte *)"simon", 5); - Checksum.Final(Digest); - DigestToJava(Digest, DigestSimon); - printf("Notch: \"%s\"", DigestNotch.c_str()); - printf("jeb_: \"%s\"", DigestJeb.c_str()); - printf("simon: \"%s\"", DigestSimon.c_str()); - } -} test; -*/ - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol132: - -cProtocol132::cProtocol132(cClientHandle * a_Client) : - super(a_Client), - m_IsEncrypted(false) -{ -} - - - - - -cProtocol132::~cProtocol132() -{ - if (!m_DataToSend.empty()) - { - LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size()); - } -} - - - - - -void cProtocol132::DataReceived(const char * a_Data, int a_Size) -{ - if (m_IsEncrypted) - { - byte Decrypted[512]; - while (a_Size > 0) - { - int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; - m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); - super::DataReceived((const char *)Decrypted, NumBytes); - a_Size -= NumBytes; - a_Data += NumBytes; - } - } - else - { - super::DataReceived(a_Data, a_Size); - } -} - - - - - -void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_BLOCK_ACTION); - WriteInt (a_BlockX); - WriteShort((short)a_BlockY); - WriteInt (a_BlockZ); - WriteByte (a_Byte1); - WriteByte (a_Byte2); - WriteShort(a_BlockType); - Flush(); -} - - - - - -void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_BLOCK_BREAK_ANIM); - WriteInt (a_entityID); - WriteInt (a_BlockX); - WriteInt (a_BlockY); - WriteInt (a_BlockZ); - WriteByte (stage); - Flush(); -} - - - - - -void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_BLOCK_CHANGE); - WriteInt (a_BlockX); - WriteByte ((unsigned char)a_BlockY); - WriteInt (a_BlockZ); - WriteShort(a_BlockType); - WriteByte (a_BlockMeta); - Flush(); -} - - - - - -void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - cCSLock Lock(m_CSPacket); - - // Pre-chunk not used in 1.3.2. Finally. - - // Send the chunk data: - AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); - WriteByte(PACKET_CHUNK_DATA); - WriteInt (a_ChunkX); - WriteInt (a_ChunkZ); - SendData(Serialized.data(), Serialized.size()); - Flush(); -} - - - - - -void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_COLLECT_PICKUP); - WriteInt (a_Pickup.GetUniqueID()); - WriteInt (a_Player.GetUniqueID()); - Flush(); - - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - SendSoundEffect( - "random.pop", - (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8), - 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64) - ); -} - - - - - -void cProtocol132::SendDestroyEntity(const cEntity & a_Entity) -{ - if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID()) - { - // Do not send "destroy self" to the client, the client would crash (FS #254) - return; - } - - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_DESTROY_ENTITIES); - WriteByte(1); // entity count - WriteInt (a_Entity.GetUniqueID()); - Flush(); -} - - - - - -void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_ENTITY_EQUIPMENT); - WriteInt (a_Entity.GetUniqueID()); - WriteShort(a_SlotNum); - WriteItem (a_Item); - Flush(); -} - - - - - -void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_LOGIN); - WriteInt (a_Player.GetUniqueID()); // EntityID of the player - WriteString("default"); // Level type - WriteByte ((int)a_Player.GetGameMode()); - WriteByte ((Byte)(a_World.GetDimension())); - WriteByte (2); // TODO: Difficulty - WriteByte (0); // Unused, used to be world height - WriteByte (8); // Client list width or something - Flush(); - - SendCompass(a_World); - - // Send the initial position (so that confirmation works, FS #245): - SendPlayerMoveLook(); -} - - - - - -void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) -{ - const cItem & HeldItem = a_Player.GetEquippedItem(); - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_PLAYER_SPAWN); - WriteInt (a_Player.GetUniqueID()); - WriteString(a_Player.GetName()); - WriteInt ((int)(a_Player.GetPosX() * 32)); - WriteInt ((int)(a_Player.GetPosY() * 32)); - WriteInt ((int)(a_Player.GetPosZ() * 32)); - WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); - WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); - WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); - // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata: - WriteByte (0); // Index 0, byte (flags) - WriteByte (0); // Flags, empty - WriteByte (0x7f); // End of metadata - Flush(); -} - - - - - -void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SOUND_EFFECT); - WriteString (a_SoundName); - WriteInt (a_SrcX); - WriteInt (a_SrcY); - WriteInt (a_SrcZ); - WriteFloat (a_Volume); - WriteByte ((char)(a_Pitch * 63.0f)); - Flush(); -} - - - - - -void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SOUND_PARTICLE_EFFECT); - WriteInt (a_EffectID); - WriteInt (a_SrcX / 8); - WriteByte(a_SrcY / 8); - WriteInt (a_SrcZ / 8); - WriteInt (a_Data); - Flush(); -} - - - - - -void cProtocol132::SendSpawnMob(const cMonster & a_Mob) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_SPAWN_MOB); - WriteInt (a_Mob.GetUniqueID()); - WriteByte (a_Mob.GetMobType()); - WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); - WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256)); - WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256)); - WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256)); - WriteShort ((short)(a_Mob.GetSpeedX() * 400)); - WriteShort ((short)(a_Mob.GetSpeedY() * 400)); - WriteShort ((short)(a_Mob.GetSpeedZ() * 400)); - AString MetaData = GetEntityMetaData(a_Mob); - SendData (MetaData.data(), MetaData.size()); - Flush(); -} - - - - - -void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - // Not used in 1.3.2 - // Does it unload chunks on its own? -} - - - - - -void cProtocol132::SendWholeInventory(const cWindow & a_Window) -{ - // 1.3.2 requires player inventory slots to be sent as SetSlot packets, - // otherwise it sometimes fails to update the window - - // Send the entire window: - super::SendWholeInventory(a_Window); - - // Send the player inventory and hotbar: - const cInventory & Inventory = m_Client->GetPlayer()->GetInventory(); - int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots - char WindowID = a_Window.GetWindowID(); - for (int i = 0; i < cInventory::invInventoryCount; i++) - { - SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i)); - } // for i - Inventory[] - BaseOffset += cInventory::invInventoryCount; - for (int i = 0; i < cInventory::invHotbarCount; i++) - { - SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i)); - } // for i - Hotbar[] - - // Send even the item being dragged: - SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem()); -} - - - - - -AString cProtocol132::GetAuthServerID(void) -{ - // http://wiki.vg/wiki/index.php?title=Session&oldid=2615 - // Server uses SHA1 to mix ServerID, Client secret and server public key together - // The mixing is done in StartEncryption, the result is in m_AuthServerID - - return m_AuthServerID; -} - - - - - -int cProtocol132::ParsePacket(unsigned char a_PacketType) -{ - switch (a_PacketType) - { - default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125 - case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance(); - case PACKET_CLIENT_STATUSES: return ParseClientStatuses(); - case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse(); - } -} - - - - - -int cProtocol132::ParseBlockPlace(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_PACKET_READ(ReadByte, Byte, PosY); - HANDLE_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_PACKET_READ(ReadChar, char, BlockFace); - - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - - HANDLE_PACKET_READ(ReadChar, char, CursorX); - HANDLE_PACKET_READ(ReadChar, char, CursorY); - HANDLE_PACKET_READ(ReadChar, char, CursorZ); - - m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem); - return PARSE_OK; -} - - - - - -int cProtocol132::ParseHandshake(void) -{ - HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); - HANDLE_PACKET_READ(ReadBEInt, int, ServerPort); - m_Username = Username; - - if (!m_Client->HandleHandshake( m_Username )) - { - return PARSE_OK; // Player is not allowed into the server - } - - // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD - CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs - cRoot::Get()->GetServer()->GetPublicKey().Save(sink); - SendEncryptionKeyRequest(); - - return PARSE_OK; -} - - - - - -int cProtocol132::ParseClientStatuses(void) -{ - HANDLE_PACKET_READ(ReadByte, byte, Status); - if ((Status & 1) == 0) - { - m_Client->HandleLogin(39, m_Username); - } - else - { - m_Client->HandleRespawn(); - } - return PARSE_OK; -} - - - - - -int cProtocol132::ParseEncryptionKeyResponse(void) -{ - HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength); - AString EncKey; - if (!m_ReceivedData.ReadString(EncKey, EncKeyLength)) - { - return PARSE_INCOMPLETE; - } - HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength); - AString EncNonce; - if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength)) - { - return PARSE_INCOMPLETE; - } - if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) - { - LOGD("Too long encryption"); - m_Client->Kick("Hacked client"); - return PARSE_OK; - } - - HandleEncryptionKeyResponse(EncKey, EncNonce); - return PARSE_OK; -} - - - - - -int cProtocol132::ParseLocaleViewDistance(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); - HANDLE_PACKET_READ(ReadChar, char, ViewDistance); - HANDLE_PACKET_READ(ReadChar, char, ChatFlags); - HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); - // TODO: m_Client->HandleLocale(Locale); - // TODO: m_Client->HandleViewDistance(ViewDistance); - // TODO: m_Client->HandleChatFlags(ChatFlags); - // Ignoring client difficulty - return PARSE_OK; -} - - - - - -int cProtocol132::ParseLogin(void) -{ - // Login packet not used in 1.3.2 - return PARSE_ERROR; -} - - - - - -int cProtocol132::ParsePlayerAbilities(void) -{ - HANDLE_PACKET_READ(ReadBool, bool, Flags); - HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed); - HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed); - // TODO: m_Client->HandlePlayerAbilities(...); - return PARSE_OK; -} - - - - - -void cProtocol132::SendData(const char * a_Data, int a_Size) -{ - m_DataToSend.append(a_Data, a_Size); -} - - - - - -void cProtocol132::Flush(void) -{ - ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly? - - if (m_DataToSend.empty()) - { - LOGD("Flushing empty"); - return; - } - const char * a_Data = m_DataToSend.data(); - int a_Size = m_DataToSend.size(); - if (m_IsEncrypted) - { - byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) - while (a_Size > 0) - { - int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; - m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); - super::SendData((const char *)Encrypted, NumBytes); - a_Size -= NumBytes; - a_Data += NumBytes; - } - } - else - { - super::SendData(a_Data, a_Size); - } - m_DataToSend.clear(); -} - - - - - -void cProtocol132::WriteItem(const cItem & a_Item) -{ - short ItemType = a_Item.m_ItemType; - ASSERT(ItemType >= -1); // Check validity of packets in debug runtime - if (ItemType <= 0) - { - // Fix, to make sure no invalid values are sent. - ItemType = -1; - } - - if (a_Item.IsEmpty()) - { - WriteShort(-1); - return; - } - - WriteShort(ItemType); - WriteByte (a_Item.m_ItemCount); - WriteShort(a_Item.m_ItemDamage); - - if (a_Item.m_Enchantments.IsEmpty()) - { - WriteShort(-1); - return; - } - - // Send the enchantments: - cFastNBTWriter Writer; - const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; - a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); - Writer.Finish(); - AString Compressed; - CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); - WriteShort(Compressed.size()); - SendData(Compressed.data(), Compressed.size()); -} - - - - - -int cProtocol132::ParseItem(cItem & a_Item) -{ - HANDLE_PACKET_READ(ReadBEShort, short, ItemType); - - if (ItemType <= -1) - { - a_Item.Empty(); - return PARSE_OK; - } - a_Item.m_ItemType = ItemType; - - HANDLE_PACKET_READ(ReadChar, char, ItemCount); - HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); - a_Item.m_ItemCount = ItemCount; - a_Item.m_ItemDamage = ItemDamage; - if (ItemCount <= 0) - { - a_Item.Empty(); - } - - HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength); - if (MetadataLength <= 0) - { - return PARSE_OK; - } - - // Read the metadata - AString Metadata; - Metadata.resize(MetadataLength); - if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength)) - { - return PARSE_INCOMPLETE; - } - - return ParseItemMetadata(a_Item, Metadata); -} - - - - - -int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) -{ - // Uncompress the GZIPped data: - AString Uncompressed; - if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) - { - AString HexDump; - CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); - LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str()); - return PARSE_ERROR; - } - - // Parse into NBT: - cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); - if (!NBT.IsValid()) - { - AString HexDump; - CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); - LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str()); - return PARSE_ERROR; - } - - // Load enchantments from the NBT: - for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) - { - if ( - (NBT.GetType(tag) == TAG_List) && - ( - (NBT.GetName(tag) == "ench") || - (NBT.GetName(tag) == "StoredEnchantments") - ) - ) - { - a_Item.m_Enchantments.ParseFromNBT(NBT, tag); - } - } - - return PARSE_OK; -} - - - - - -void cProtocol132::SendCompass(const cWorld & a_World) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_COMPASS); - WriteInt((int)(a_World.GetSpawnX())); - WriteInt((int)(a_World.GetSpawnY())); - WriteInt((int)(a_World.GetSpawnZ())); - Flush(); -} - - - - - -void cProtocol132::SendEncryptionKeyRequest(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte((char)0xfd); - WriteString(cRoot::Get()->GetServer()->GetServerID()); - WriteShort((short)m_ServerPublicKey.size()); - SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size()); - WriteShort(4); - WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) - Flush(); -} - - - - - -void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce) -{ - // Decrypt EncNonce using privkey - RSAES::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey()); - time_t CurTime = time(NULL); - CryptoPP::RandomPool rng; - rng.Put((const byte *)&CurTime, sizeof(CurTime)); - byte DecryptedNonce[MAX_ENC_LEN]; - DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); - if (!res.isValidCoding || (res.messageLength != 4)) - { - LOGD("Bad nonce length"); - m_Client->Kick("Hacked client"); - return; - } - if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this) - { - LOGD("Bad nonce value"); - m_Client->Kick("Hacked client"); - return; - } - - // Decrypt the symmetric encryption key using privkey: - byte DecryptedKey[MAX_ENC_LEN]; - res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey); - if (!res.isValidCoding || (res.messageLength != 16)) - { - LOGD("Bad key length"); - m_Client->Kick("Hacked client"); - return; - } - - { - // Send encryption key response: - cCSLock Lock(m_CSPacket); - WriteByte((char)0xfc); - WriteShort(0); - WriteShort(0); - Flush(); - } - - StartEncryption(DecryptedKey); - return; -} - - - - - -void cProtocol132::StartEncryption(const byte * a_Key) -{ - m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); - m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); - m_IsEncrypted = true; - - // Prepare the m_AuthServerID: - CryptoPP::SHA1 Checksum; - AString ServerID = cRoot::Get()->GetServer()->GetServerID(); - Checksum.Update((const byte *)ServerID.c_str(), ServerID.length()); - Checksum.Update(a_Key, 16); - Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length()); - byte Digest[20]; - Checksum.Final(Digest); - DigestToJava(Digest, m_AuthServerID); -} - - - - + +// Protocol132.cpp + +// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39) + +#include "Globals.h" +#include "Protocol132.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../../CryptoPP/randpool.h" +#include "../Item.h" +#include "ChunkDataSerializer.h" +#include "../Player.h" +#include "../Mobs/Monster.h" +#include "../UI/Window.h" +#include "../Pickup.h" +#include "../WorldStorage/FastNBT.h" +#include "../StringCompression.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + +typedef unsigned char Byte; + + + + + +using namespace CryptoPP; + + + + + +const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows... + + + + + +enum +{ + PACKET_KEEP_ALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_COMPASS = 0x06, + PACKET_PLAYER_SPAWN = 0x14, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_MOB = 0x18, + PACKET_DESTROY_ENTITIES = 0x1d, + PACKET_CHUNK_DATA = 0x33, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_BLOCK_BREAK_ANIM = 0x37, + PACKET_SOUND_EFFECT = 0x3e, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d, + PACKET_LOCALE_VIEW_DISTANCE = 0xcc, + PACKET_CLIENT_STATUSES = 0xcd, + PACKET_ENCRYPTION_KEY_RESP = 0xfc, +} ; + + + + + +// Converts a raw 160-bit SHA1 digest into a Java Hex representation +// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802 +static void DigestToJava(byte a_Digest[20], AString & a_Out) +{ + bool IsNegative = (a_Digest[0] >= 0x80); + if (IsNegative) + { + // Two's complement: + bool carry = true; // Add one to the whole number + for (int i = 19; i >= 0; i--) + { + a_Digest[i] = ~a_Digest[i]; + if (carry) + { + carry = (a_Digest[i] == 0xff); + a_Digest[i]++; + } + } + } + a_Out.clear(); + a_Out.reserve(40); + for (int i = 0; i < 20; i++) + { + AppendPrintf(a_Out, "%02x", a_Digest[i]); + } + while ((a_Out.length() > 0) && (a_Out[0] == '0')) + { + a_Out.erase(0, 1); + } + if (IsNegative) + { + a_Out.insert(0, "-"); + } +} + + + + + +/* +// Self-test the hash formatting for known values: +// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48 +// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1 +// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6 + +class Test +{ +public: + Test(void) + { + AString DigestNotch, DigestJeb, DigestSimon; + byte Digest[20]; + CryptoPP::SHA1 Checksum; + Checksum.Update((const byte *)"Notch", 5); + Checksum.Final(Digest); + DigestToJava(Digest, DigestNotch); + Checksum.Restart(); + Checksum.Update((const byte *)"jeb_", 4); + Checksum.Final(Digest); + DigestToJava(Digest, DigestJeb); + Checksum.Restart(); + Checksum.Update((const byte *)"simon", 5); + Checksum.Final(Digest); + DigestToJava(Digest, DigestSimon); + printf("Notch: \"%s\"", DigestNotch.c_str()); + printf("jeb_: \"%s\"", DigestJeb.c_str()); + printf("simon: \"%s\"", DigestSimon.c_str()); + } +} test; +*/ + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol132: + +cProtocol132::cProtocol132(cClientHandle * a_Client) : + super(a_Client), + m_IsEncrypted(false) +{ +} + + + + + +cProtocol132::~cProtocol132() +{ + if (!m_DataToSend.empty()) + { + LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size()); + } +} + + + + + +void cProtocol132::DataReceived(const char * a_Data, int a_Size) +{ + if (m_IsEncrypted) + { + byte Decrypted[512]; + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; + m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); + super::DataReceived((const char *)Decrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + super::DataReceived(a_Data, a_Size); + } +} + + + + + +void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_ACTION); + WriteInt (a_BlockX); + WriteShort((short)a_BlockY); + WriteInt (a_BlockZ); + WriteByte (a_Byte1); + WriteByte (a_Byte2); + WriteShort(a_BlockType); + Flush(); +} + + + + + +void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_BREAK_ANIM); + WriteInt (a_entityID); + WriteInt (a_BlockX); + WriteInt (a_BlockY); + WriteInt (a_BlockZ); + WriteByte (stage); + Flush(); +} + + + + + +void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_BLOCK_CHANGE); + WriteInt (a_BlockX); + WriteByte ((unsigned char)a_BlockY); + WriteInt (a_BlockZ); + WriteShort(a_BlockType); + WriteByte (a_BlockMeta); + Flush(); +} + + + + + +void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + cCSLock Lock(m_CSPacket); + + // Pre-chunk not used in 1.3.2. Finally. + + // Send the chunk data: + AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2); + WriteByte(PACKET_CHUNK_DATA); + WriteInt (a_ChunkX); + WriteInt (a_ChunkZ); + SendData(Serialized.data(), Serialized.size()); + Flush(); +} + + + + + +void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COLLECT_PICKUP); + WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Player.GetUniqueID()); + Flush(); + + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + SendSoundEffect( + "random.pop", + (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8), + 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64) + ); +} + + + + + +void cProtocol132::SendDestroyEntity(const cEntity & a_Entity) +{ + if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID()) + { + // Do not send "destroy self" to the client, the client would crash (FS #254) + return; + } + + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_DESTROY_ENTITIES); + WriteByte(1); // entity count + WriteInt (a_Entity.GetUniqueID()); + Flush(); +} + + + + + +void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_ENTITY_EQUIPMENT); + WriteInt (a_Entity.GetUniqueID()); + WriteShort(a_SlotNum); + WriteItem (a_Item); + Flush(); +} + + + + + +void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_LOGIN); + WriteInt (a_Player.GetUniqueID()); // EntityID of the player + WriteString("default"); // Level type + WriteByte ((int)a_Player.GetGameMode()); + WriteByte ((Byte)(a_World.GetDimension())); + WriteByte (2); // TODO: Difficulty + WriteByte (0); // Unused, used to be world height + WriteByte (8); // Client list width or something + Flush(); + + SendCompass(a_World); + + // Send the initial position (so that confirmation works, FS #245): + SendPlayerMoveLook(); +} + + + + + +void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) +{ + const cItem & HeldItem = a_Player.GetEquippedItem(); + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PLAYER_SPAWN); + WriteInt (a_Player.GetUniqueID()); + WriteString(a_Player.GetName()); + WriteInt ((int)(a_Player.GetPosX() * 32)); + WriteInt ((int)(a_Player.GetPosY() * 32)); + WriteInt ((int)(a_Player.GetPosZ() * 32)); + WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256)); + WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256)); + WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType); + // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata: + WriteByte (0); // Index 0, byte (flags) + WriteByte (0); // Flags, empty + WriteByte (0x7f); // End of metadata + Flush(); +} + + + + + +void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SOUND_EFFECT); + WriteString (a_SoundName); + WriteInt (a_SrcX); + WriteInt (a_SrcY); + WriteInt (a_SrcZ); + WriteFloat (a_Volume); + WriteByte ((char)(a_Pitch * 63.0f)); + Flush(); +} + + + + + +void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SOUND_PARTICLE_EFFECT); + WriteInt (a_EffectID); + WriteInt (a_SrcX / 8); + WriteByte(a_SrcY / 8); + WriteInt (a_SrcZ / 8); + WriteInt (a_Data); + Flush(); +} + + + + + +void cProtocol132::SendSpawnMob(const cMonster & a_Mob) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_SPAWN_MOB); + WriteInt (a_Mob.GetUniqueID()); + WriteByte (a_Mob.GetMobType()); + WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32)); + WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256)); + WriteShort ((short)(a_Mob.GetSpeedX() * 400)); + WriteShort ((short)(a_Mob.GetSpeedY() * 400)); + WriteShort ((short)(a_Mob.GetSpeedZ() * 400)); + AString MetaData = GetEntityMetaData(a_Mob); + SendData (MetaData.data(), MetaData.size()); + Flush(); +} + + + + + +void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + // Not used in 1.3.2 + // Does it unload chunks on its own? +} + + + + + +void cProtocol132::SendWholeInventory(const cWindow & a_Window) +{ + // 1.3.2 requires player inventory slots to be sent as SetSlot packets, + // otherwise it sometimes fails to update the window + + // Send the entire window: + super::SendWholeInventory(a_Window); + + // Send the player inventory and hotbar: + const cInventory & Inventory = m_Client->GetPlayer()->GetInventory(); + int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots + char WindowID = a_Window.GetWindowID(); + for (int i = 0; i < cInventory::invInventoryCount; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i)); + } // for i - Inventory[] + BaseOffset += cInventory::invInventoryCount; + for (int i = 0; i < cInventory::invHotbarCount; i++) + { + SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i)); + } // for i - Hotbar[] + + // Send even the item being dragged: + SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem()); +} + + + + + +AString cProtocol132::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2615 + // Server uses SHA1 to mix ServerID, Client secret and server public key together + // The mixing is done in StartEncryption, the result is in m_AuthServerID + + return m_AuthServerID; +} + + + + + +int cProtocol132::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125 + case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance(); + case PACKET_CLIENT_STATUSES: return ParseClientStatuses(); + case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse(); + } +} + + + + + +int cProtocol132::ParseBlockPlace(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_PACKET_READ(ReadChar, char, BlockFace); + + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + HANDLE_PACKET_READ(ReadChar, char, CursorX); + HANDLE_PACKET_READ(ReadChar, char, CursorY); + HANDLE_PACKET_READ(ReadChar, char, CursorZ); + + m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseHandshake(void) +{ + HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); + HANDLE_PACKET_READ(ReadBEInt, int, ServerPort); + m_Username = Username; + + if (!m_Client->HandleHandshake( m_Username )) + { + return PARSE_OK; // Player is not allowed into the server + } + + // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD + CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs + cRoot::Get()->GetServer()->GetPublicKey().Save(sink); + SendEncryptionKeyRequest(); + + return PARSE_OK; +} + + + + + +int cProtocol132::ParseClientStatuses(void) +{ + HANDLE_PACKET_READ(ReadByte, byte, Status); + if ((Status & 1) == 0) + { + m_Client->HandleLogin(39, m_Username); + } + else + { + m_Client->HandleRespawn(); + } + return PARSE_OK; +} + + + + + +int cProtocol132::ParseEncryptionKeyResponse(void) +{ + HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength); + AString EncKey; + if (!m_ReceivedData.ReadString(EncKey, EncKeyLength)) + { + return PARSE_INCOMPLETE; + } + HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength); + AString EncNonce; + if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength)) + { + return PARSE_INCOMPLETE; + } + if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) + { + LOGD("Too long encryption"); + m_Client->Kick("Hacked client"); + return PARSE_OK; + } + + HandleEncryptionKeyResponse(EncKey, EncNonce); + return PARSE_OK; +} + + + + + +int cProtocol132::ParseLocaleViewDistance(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); + // TODO: m_Client->HandleLocale(Locale); + // TODO: m_Client->HandleViewDistance(ViewDistance); + // TODO: m_Client->HandleChatFlags(ChatFlags); + // Ignoring client difficulty + return PARSE_OK; +} + + + + + +int cProtocol132::ParseLogin(void) +{ + // Login packet not used in 1.3.2 + return PARSE_ERROR; +} + + + + + +int cProtocol132::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadBool, bool, Flags); + HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed); + HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +void cProtocol132::SendData(const char * a_Data, int a_Size) +{ + m_DataToSend.append(a_Data, a_Size); +} + + + + + +void cProtocol132::Flush(void) +{ + ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly? + + if (m_DataToSend.empty()) + { + LOGD("Flushing empty"); + return; + } + const char * a_Data = m_DataToSend.data(); + int a_Size = m_DataToSend.size(); + if (m_IsEncrypted) + { + byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) + while (a_Size > 0) + { + int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; + m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); + super::SendData((const char *)Encrypted, NumBytes); + a_Size -= NumBytes; + a_Data += NumBytes; + } + } + else + { + super::SendData(a_Data, a_Size); + } + m_DataToSend.clear(); +} + + + + + +void cProtocol132::WriteItem(const cItem & a_Item) +{ + short ItemType = a_Item.m_ItemType; + ASSERT(ItemType >= -1); // Check validity of packets in debug runtime + if (ItemType <= 0) + { + // Fix, to make sure no invalid values are sent. + ItemType = -1; + } + + if (a_Item.IsEmpty()) + { + WriteShort(-1); + return; + } + + WriteShort(ItemType); + WriteByte (a_Item.m_ItemCount); + WriteShort(a_Item.m_ItemDamage); + + if (a_Item.m_Enchantments.IsEmpty()) + { + WriteShort(-1); + return; + } + + // Send the enchantments: + cFastNBTWriter Writer; + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName); + Writer.Finish(); + AString Compressed; + CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed); + WriteShort(Compressed.size()); + SendData(Compressed.data(), Compressed.size()); +} + + + + + +int cProtocol132::ParseItem(cItem & a_Item) +{ + HANDLE_PACKET_READ(ReadBEShort, short, ItemType); + + if (ItemType <= -1) + { + a_Item.Empty(); + return PARSE_OK; + } + a_Item.m_ItemType = ItemType; + + HANDLE_PACKET_READ(ReadChar, char, ItemCount); + HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage); + a_Item.m_ItemCount = ItemCount; + a_Item.m_ItemDamage = ItemDamage; + if (ItemCount <= 0) + { + a_Item.Empty(); + } + + HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength); + if (MetadataLength <= 0) + { + return PARSE_OK; + } + + // Read the metadata + AString Metadata; + Metadata.resize(MetadataLength); + if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength)) + { + return PARSE_INCOMPLETE; + } + + return ParseItemMetadata(a_Item, Metadata); +} + + + + + +int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +{ + // Uncompress the GZIPped data: + AString Uncompressed; + if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) + { + AString HexDump; + CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); + LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str()); + return PARSE_ERROR; + } + + // Parse into NBT: + cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); + if (!NBT.IsValid()) + { + AString HexDump; + CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); + LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str()); + return PARSE_ERROR; + } + + // Load enchantments from the NBT: + for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) + { + if ( + (NBT.GetType(tag) == TAG_List) && + ( + (NBT.GetName(tag) == "ench") || + (NBT.GetName(tag) == "StoredEnchantments") + ) + ) + { + a_Item.m_Enchantments.ParseFromNBT(NBT, tag); + } + } + + return PARSE_OK; +} + + + + + +void cProtocol132::SendCompass(const cWorld & a_World) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_COMPASS); + WriteInt((int)(a_World.GetSpawnX())); + WriteInt((int)(a_World.GetSpawnY())); + WriteInt((int)(a_World.GetSpawnZ())); + Flush(); +} + + + + + +void cProtocol132::SendEncryptionKeyRequest(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte((char)0xfd); + WriteString(cRoot::Get()->GetServer()->GetServerID()); + WriteShort((short)m_ServerPublicKey.size()); + SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size()); + WriteShort(4); + WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) + Flush(); +} + + + + + +void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce) +{ + // Decrypt EncNonce using privkey + RSAES::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey()); + time_t CurTime = time(NULL); + CryptoPP::RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + byte DecryptedNonce[MAX_ENC_LEN]; + DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); + if (!res.isValidCoding || (res.messageLength != 4)) + { + LOGD("Bad nonce length"); + m_Client->Kick("Hacked client"); + return; + } + if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this) + { + LOGD("Bad nonce value"); + m_Client->Kick("Hacked client"); + return; + } + + // Decrypt the symmetric encryption key using privkey: + byte DecryptedKey[MAX_ENC_LEN]; + res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey); + if (!res.isValidCoding || (res.messageLength != 16)) + { + LOGD("Bad key length"); + m_Client->Kick("Hacked client"); + return; + } + + { + // Send encryption key response: + cCSLock Lock(m_CSPacket); + WriteByte((char)0xfc); + WriteShort(0); + WriteShort(0); + Flush(); + } + + StartEncryption(DecryptedKey); + return; +} + + + + + +void cProtocol132::StartEncryption(const byte * a_Key) +{ + m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); + m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1)); + m_IsEncrypted = true; + + // Prepare the m_AuthServerID: + CryptoPP::SHA1 Checksum; + AString ServerID = cRoot::Get()->GetServer()->GetServerID(); + Checksum.Update((const byte *)ServerID.c_str(), ServerID.length()); + Checksum.Update(a_Key, 16); + Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length()); + byte Digest[20]; + Checksum.Final(Digest); + DigestToJava(Digest, m_AuthServerID); +} + + + + diff --git a/source/Protocol/Protocol132.h b/source/Protocol/Protocol132.h index cf559f1ca..bf8b4ff0e 100644 --- a/source/Protocol/Protocol132.h +++ b/source/Protocol/Protocol132.h @@ -1,100 +1,100 @@ - -// Protocol132.h - -// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39) - - - - - -#pragma once - -#include "Protocol125.h" -#include "../../CryptoPP/modes.h" -#include "../../CryptoPP/aes.h" - - - - - -class cProtocol132 : - public cProtocol125 -{ - typedef cProtocol125 super; -public: - - cProtocol132(cClientHandle * a_Client); - virtual ~cProtocol132(); - - /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; - - // Sending commands (alphabetically sorted): - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; - virtual void SendDestroyEntity (const cEntity & a_Entity) override; - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; - virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 - virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendSpawnMob (const cMonster & a_Mob) override; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - - virtual AString GetAuthServerID(void) override; - - /// Handling of the additional packets: - virtual int ParsePacket(unsigned char a_PacketType) override; - - // Modified packets: - virtual int ParseBlockPlace (void) override; - virtual int ParseHandshake (void) override; - virtual int ParseLogin (void) override; - virtual int ParsePlayerAbilities(void) override; - - // New packets: - virtual int ParseClientStatuses (void); - virtual int ParseEncryptionKeyResponse(void); - virtual int ParseLocaleViewDistance (void); - -protected: - bool m_IsEncrypted; - CryptoPP::CFB_Mode::Decryption m_Decryptor; - CryptoPP::CFB_Mode::Encryption m_Encryptor; - AString m_DataToSend; - - /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID() - AString m_AuthServerID; - - /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption() - AString m_ServerPublicKey; - - virtual void SendData(const char * a_Data, int a_Size) override; - - // DEBUG: - virtual void Flush(void) override; - - // Items in slots are sent differently - virtual void WriteItem(const cItem & a_Item) override; - virtual int ParseItem(cItem & a_Item) override; - - /// Parses the metadata that may come with the item. - int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); - - virtual void SendCompass(const cWorld & a_World); - virtual void SendEncryptionKeyRequest(void); - - /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption - void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce); - - /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID - void StartEncryption(const byte * a_Key); -} ; - - - - + +// Protocol132.h + +// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39) + + + + + +#pragma once + +#include "Protocol125.h" +#include "../../CryptoPP/modes.h" +#include "../../CryptoPP/aes.h" + + + + + +class cProtocol132 : + public cProtocol125 +{ + typedef cProtocol125 super; +public: + + cProtocol132(cClientHandle * a_Client); + virtual ~cProtocol132(); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + // Sending commands (alphabetically sorted): + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + + virtual AString GetAuthServerID(void) override; + + /// Handling of the additional packets: + virtual int ParsePacket(unsigned char a_PacketType) override; + + // Modified packets: + virtual int ParseBlockPlace (void) override; + virtual int ParseHandshake (void) override; + virtual int ParseLogin (void) override; + virtual int ParsePlayerAbilities(void) override; + + // New packets: + virtual int ParseClientStatuses (void); + virtual int ParseEncryptionKeyResponse(void); + virtual int ParseLocaleViewDistance (void); + +protected: + bool m_IsEncrypted; + CryptoPP::CFB_Mode::Decryption m_Decryptor; + CryptoPP::CFB_Mode::Encryption m_Encryptor; + AString m_DataToSend; + + /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID() + AString m_AuthServerID; + + /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption() + AString m_ServerPublicKey; + + virtual void SendData(const char * a_Data, int a_Size) override; + + // DEBUG: + virtual void Flush(void) override; + + // Items in slots are sent differently + virtual void WriteItem(const cItem & a_Item) override; + virtual int ParseItem(cItem & a_Item) override; + + /// Parses the metadata that may come with the item. + int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); + + virtual void SendCompass(const cWorld & a_World); + virtual void SendEncryptionKeyRequest(void); + + /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption + void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce); + + /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID + void StartEncryption(const byte * a_Key); +} ; + + + + diff --git a/source/Protocol/Protocol14x.cpp b/source/Protocol/Protocol14x.cpp index b7d3c91df..35fe0ce1c 100644 --- a/source/Protocol/Protocol14x.cpp +++ b/source/Protocol/Protocol14x.cpp @@ -1,253 +1,253 @@ - -// Protocol14x.cpp - -/* -Implements the 1.4.x protocol classes representing these protocols: -- cProtocol142: - - release 1.4.2 protocol (#47) - - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) - - release 1.4.5 protocol (same as 1.4.4) -- cProtocol146: - - release 1.4.6 protocol (#51) -*/ - -#include "Globals.h" -#include "Protocol14x.h" -#include "../Root.h" -#include "../Server.h" -#include "../ClientHandle.h" -#include "../../CryptoPP/randpool.h" -#include "../Item.h" -#include "ChunkDataSerializer.h" -#include "../Player.h" -#include "../Mobs/Monster.h" -#include "../UI/Window.h" -#include "../Pickup.h" -#include "../FallingBlock.h" - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - - -enum -{ - PACKET_UPDATE_TIME = 0x04, - PACKET_PICKUP_SPAWN = 0x15, - PACKET_SPAWN_OBJECT = 0x17, - PACKET_ENTITY_METADATA = 0x28, - PACKET_SOUND_PARTICLE_EFFECT = 0x3d -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol142: - -cProtocol142::cProtocol142(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -int cProtocol142::ParseLocaleViewDistance(void) -{ - HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); - HANDLE_PACKET_READ(ReadChar, char, ViewDistance); - HANDLE_PACKET_READ(ReadChar, char, ChatFlags); - HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); - HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2 - // TODO: m_Client->HandleLocale(Locale); - // TODO: m_Client->HandleViewDistance(ViewDistance); - // TODO: m_Client->HandleChatFlags(ChatFlags); - // Ignoring client difficulty - return PARSE_OK; -} - - - - - -void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_PICKUP_SPAWN); - WriteInt (a_Pickup.GetUniqueID()); - WriteItem (a_Pickup.GetItem()); - WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); - WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); - WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); - WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); - Flush(); -} - - - - - -void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SOUND_PARTICLE_EFFECT); - WriteInt (a_EffectID); - WriteInt (a_SrcX / 8); - WriteByte(a_SrcY / 8); - WriteInt (a_SrcZ / 8); - WriteInt (a_Data); - WriteBool(0); - Flush(); -} - - - - - -void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_UPDATE_TIME); - WriteInt64(a_WorldAge); - WriteInt64(a_TimeOfDay); - Flush(); -} - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol146: - -cProtocol146::cProtocol146(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup) -{ - ASSERT(!a_Pickup.GetItem().IsEmpty()); - - cCSLock Lock(m_CSPacket); - - // Send a SPAWN_OBJECT packet for the base entity: - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_Pickup.GetUniqueID()); - WriteByte(0x02); - WriteInt ((int)(a_Pickup.GetPosX() * 32)); - WriteInt ((int)(a_Pickup.GetPosY() * 32)); - WriteInt ((int)(a_Pickup.GetPosZ() * 32)); - WriteInt (1); - WriteShort((short)(a_Pickup.GetSpeed().x * 32)); - WriteShort((short)(a_Pickup.GetSpeed().y * 32)); - WriteShort((short)(a_Pickup.GetSpeed().z * 32)); - WriteByte(0); - WriteByte(0); - - // Send a ENTITY_METADATA packet with the slot info: - WriteByte(PACKET_ENTITY_METADATA); - WriteInt(a_Pickup.GetUniqueID()); - WriteByte(0xaa); // a slot value at index 10 - WriteItem(a_Pickup.GetItem()); - WriteByte(0x7f); // End of metadata - Flush(); -} - - - - - -void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) -{ - // Send a spawn object / vehicle packet - cCSLock Lock(m_CSPacket); - - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_FallingBlock.GetUniqueID()); - WriteByte(70); - WriteInt ((int)(a_FallingBlock.GetPosX() * 32)); - WriteInt ((int)(a_FallingBlock.GetPosY() * 32)); - WriteInt ((int)(a_FallingBlock.GetPosZ() * 32)); - WriteByte (0); // Pitch - WriteByte (0); // Yaw - WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype - WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); - WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); - WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); - Flush(); -} - - - - - -void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_Entity.GetUniqueID()); - WriteByte(a_ObjectType); - WriteInt ((int)(a_Entity.GetPosX() * 32)); - WriteInt ((int)(a_Entity.GetPosY() * 32)); - WriteInt ((int)(a_Entity.GetPosZ() * 32)); - WriteByte(a_Pitch); - WriteByte(a_Yaw); - WriteInt (a_ObjectData); - if (a_ObjectData != 0) - { - WriteShort((short)(a_Entity.GetSpeedX() * 400)); - WriteShort((short)(a_Entity.GetSpeedY() * 400)); - WriteShort((short)(a_Entity.GetSpeedZ() * 400)); - } - Flush(); -} - - - - - -void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_SPAWN_OBJECT); - WriteInt (a_Vehicle.GetUniqueID()); - WriteByte(a_VehicleType); - WriteInt ((int)(a_Vehicle.GetPosX() * 32)); - WriteInt ((int)(a_Vehicle.GetPosY() * 32)); - WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); - WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); - WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); - WriteInt (1); - WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); - WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); - WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); - Flush(); -} - - - - - + +// Protocol14x.cpp + +/* +Implements the 1.4.x protocol classes representing these protocols: +- cProtocol142: + - release 1.4.2 protocol (#47) + - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) + - release 1.4.5 protocol (same as 1.4.4) +- cProtocol146: + - release 1.4.6 protocol (#51) +*/ + +#include "Globals.h" +#include "Protocol14x.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../../CryptoPP/randpool.h" +#include "../Item.h" +#include "ChunkDataSerializer.h" +#include "../Player.h" +#include "../Mobs/Monster.h" +#include "../UI/Window.h" +#include "../Pickup.h" +#include "../FallingBlock.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_UPDATE_TIME = 0x04, + PACKET_PICKUP_SPAWN = 0x15, + PACKET_SPAWN_OBJECT = 0x17, + PACKET_ENTITY_METADATA = 0x28, + PACKET_SOUND_PARTICLE_EFFECT = 0x3d +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol142: + +cProtocol142::cProtocol142(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +int cProtocol142::ParseLocaleViewDistance(void) +{ + HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); + HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2 + // TODO: m_Client->HandleLocale(Locale); + // TODO: m_Client->HandleViewDistance(ViewDistance); + // TODO: m_Client->HandleChatFlags(ChatFlags); + // Ignoring client difficulty + return PARSE_OK; +} + + + + + +void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_PICKUP_SPAWN); + WriteInt (a_Pickup.GetUniqueID()); + WriteItem (a_Pickup.GetItem()); + WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32)); + WriteByte ((char)(a_Pickup.GetSpeed().x * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().y * 8)); + WriteByte ((char)(a_Pickup.GetSpeed().z * 8)); + Flush(); +} + + + + + +void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SOUND_PARTICLE_EFFECT); + WriteInt (a_EffectID); + WriteInt (a_SrcX / 8); + WriteByte(a_SrcY / 8); + WriteInt (a_SrcZ / 8); + WriteInt (a_Data); + WriteBool(0); + Flush(); +} + + + + + +void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_TIME); + WriteInt64(a_WorldAge); + WriteInt64(a_TimeOfDay); + Flush(); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol146: + +cProtocol146::cProtocol146(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup) +{ + ASSERT(!a_Pickup.GetItem().IsEmpty()); + + cCSLock Lock(m_CSPacket); + + // Send a SPAWN_OBJECT packet for the base entity: + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Pickup.GetUniqueID()); + WriteByte(0x02); + WriteInt ((int)(a_Pickup.GetPosX() * 32)); + WriteInt ((int)(a_Pickup.GetPosY() * 32)); + WriteInt ((int)(a_Pickup.GetPosZ() * 32)); + WriteInt (1); + WriteShort((short)(a_Pickup.GetSpeed().x * 32)); + WriteShort((short)(a_Pickup.GetSpeed().y * 32)); + WriteShort((short)(a_Pickup.GetSpeed().z * 32)); + WriteByte(0); + WriteByte(0); + + // Send a ENTITY_METADATA packet with the slot info: + WriteByte(PACKET_ENTITY_METADATA); + WriteInt(a_Pickup.GetUniqueID()); + WriteByte(0xaa); // a slot value at index 10 + WriteItem(a_Pickup.GetItem()); + WriteByte(0x7f); // End of metadata + Flush(); +} + + + + + +void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + // Send a spawn object / vehicle packet + cCSLock Lock(m_CSPacket); + + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_FallingBlock.GetUniqueID()); + WriteByte(70); + WriteInt ((int)(a_FallingBlock.GetPosX() * 32)); + WriteInt ((int)(a_FallingBlock.GetPosY() * 32)); + WriteInt ((int)(a_FallingBlock.GetPosZ() * 32)); + WriteByte (0); // Pitch + WriteByte (0); // Yaw + WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype + WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); + WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); + WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); + Flush(); +} + + + + + +void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Entity.GetUniqueID()); + WriteByte(a_ObjectType); + WriteInt ((int)(a_Entity.GetPosX() * 32)); + WriteInt ((int)(a_Entity.GetPosY() * 32)); + WriteInt ((int)(a_Entity.GetPosZ() * 32)); + WriteByte(a_Pitch); + WriteByte(a_Yaw); + WriteInt (a_ObjectData); + if (a_ObjectData != 0) + { + WriteShort((short)(a_Entity.GetSpeedX() * 400)); + WriteShort((short)(a_Entity.GetSpeedY() * 400)); + WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + } + Flush(); +} + + + + + +void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_SPAWN_OBJECT); + WriteInt (a_Vehicle.GetUniqueID()); + WriteByte(a_VehicleType); + WriteInt ((int)(a_Vehicle.GetPosX() * 32)); + WriteInt ((int)(a_Vehicle.GetPosY() * 32)); + WriteInt ((int)(a_Vehicle.GetPosZ() * 32)); + WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256)); + WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256)); + WriteInt (1); + WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); + WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + Flush(); +} + + + + + diff --git a/source/Protocol/Protocol14x.h b/source/Protocol/Protocol14x.h index a9ff14d84..c3193a3e7 100644 --- a/source/Protocol/Protocol14x.h +++ b/source/Protocol/Protocol14x.h @@ -1,63 +1,63 @@ - -// Protocol14x.h - -/* -Interfaces to the 1.4.x protocol classes representing these protocols: -- cProtocol142: - - release 1.4.2 protocol (#47) - - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) - - release 1.4.5 protocol (same as 1.4.4) -- cProtocol146: - - release 1.4.6 protocol (#51) -*/ - - - - - -#pragma once - -#include "Protocol132.h" - - - - - -class cProtocol142 : - public cProtocol132 -{ - typedef cProtocol132 super; - -public: - cProtocol142(cClientHandle * a_Client); - - // Sending commands (alphabetically sorted): - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; - - // Specific packet parsers: - virtual int ParseLocaleViewDistance(void) override; -} ; - - - - - -class cProtocol146 : - public cProtocol142 -{ - typedef cProtocol142 super; - -public: - cProtocol146(cClientHandle * a_Client); - - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override; -} ; - - - - + +// Protocol14x.h + +/* +Interfaces to the 1.4.x protocol classes representing these protocols: +- cProtocol142: + - release 1.4.2 protocol (#47) + - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA) + - release 1.4.5 protocol (same as 1.4.4) +- cProtocol146: + - release 1.4.6 protocol (#51) +*/ + + + + + +#pragma once + +#include "Protocol132.h" + + + + + +class cProtocol142 : + public cProtocol132 +{ + typedef cProtocol132 super; + +public: + cProtocol142(cClientHandle * a_Client); + + // Sending commands (alphabetically sorted): + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + + // Specific packet parsers: + virtual int ParseLocaleViewDistance(void) override; +} ; + + + + + +class cProtocol146 : + public cProtocol142 +{ + typedef cProtocol142 super; + +public: + cProtocol146(cClientHandle * a_Client); + + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override; +} ; + + + + diff --git a/source/Protocol/Protocol15x.cpp b/source/Protocol/Protocol15x.cpp index cb7fc9fb6..cbae0700e 100644 --- a/source/Protocol/Protocol15x.cpp +++ b/source/Protocol/Protocol15x.cpp @@ -1,137 +1,137 @@ - -// Protocol15x.cpp - -/* -Implements the 1.5.x protocol classes: - - cProtocol150 - - release 1.5 protocol (#60) - - release 1.5.2 protocol (#61, no relevant changes found) -*/ - -#include "Globals.h" -#include "Protocol15x.h" -#include "../ClientHandle.h" -#include "../Item.h" - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - - -enum -{ - PACKET_WINDOW_OPEN = 0x64, -} ; - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol150: - -cProtocol150::cProtocol150(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -void cProtocol150::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) -{ - if (a_WindowType < 0) - { - // Do not send for inventory windows - return; - } - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_WINDOW_OPEN); - WriteByte (a_WindowID); - WriteByte (a_WindowType); - WriteString(a_WindowTitle); - WriteByte (a_NumSlots); - WriteByte (1); // Use title - Flush(); -} - - - - - -int cProtocol150::ParseWindowClick(void) -{ - HANDLE_PACKET_READ(ReadChar, char, WindowID); - HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); - HANDLE_PACKET_READ(ReadByte, Byte, Button); - HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); - HANDLE_PACKET_READ(ReadByte, Byte, Mode); - cItem HeldItem; - int res = ParseItem(HeldItem); - if (res < 0) - { - return res; - } - - // Convert Button, Mode, SlotNum and HeldItem into eClickAction: - eClickAction Action; - switch ((Mode << 8) | Button) - { - case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break; - case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break; - case 0x0100: Action = caShiftLeftClick; break; - case 0x0101: Action = caShiftRightClick; break; - case 0x0200: Action = caNumber1; break; - case 0x0201: Action = caNumber2; break; - case 0x0202: Action = caNumber3; break; - case 0x0203: Action = caNumber4; break; - case 0x0204: Action = caNumber5; break; - case 0x0205: Action = caNumber6; break; - case 0x0206: Action = caNumber7; break; - case 0x0207: Action = caNumber8; break; - case 0x0208: Action = caNumber9; break; - case 0x0300: Action = caMiddleClick; break; - case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break; - case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break; - case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break; - case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break; - case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break; - case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break; - case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break; - case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break; - case 0x0600: Action = caDblClick; break; - } - - if (Action == caUnknown) - { - LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.", - Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str() - ); - ASSERT(!"Unknown click action"); - return PARSE_OK; - } - - m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); - return PARSE_OK; -} - - - - - + +// Protocol15x.cpp + +/* +Implements the 1.5.x protocol classes: + - cProtocol150 + - release 1.5 protocol (#60) + - release 1.5.2 protocol (#61, no relevant changes found) +*/ + +#include "Globals.h" +#include "Protocol15x.h" +#include "../ClientHandle.h" +#include "../Item.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_WINDOW_OPEN = 0x64, +} ; + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol150: + +cProtocol150::cProtocol150(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol150::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) +{ + if (a_WindowType < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_WindowID); + WriteByte (a_WindowType); + WriteString(a_WindowTitle); + WriteByte (a_NumSlots); + WriteByte (1); // Use title + Flush(); +} + + + + + +int cProtocol150::ParseWindowClick(void) +{ + HANDLE_PACKET_READ(ReadChar, char, WindowID); + HANDLE_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_PACKET_READ(ReadByte, Byte, Button); + HANDLE_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_PACKET_READ(ReadByte, Byte, Mode); + cItem HeldItem; + int res = ParseItem(HeldItem); + if (res < 0) + { + return res; + } + + // Convert Button, Mode, SlotNum and HeldItem into eClickAction: + eClickAction Action; + switch ((Mode << 8) | Button) + { + case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break; + case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break; + case 0x0100: Action = caShiftLeftClick; break; + case 0x0101: Action = caShiftRightClick; break; + case 0x0200: Action = caNumber1; break; + case 0x0201: Action = caNumber2; break; + case 0x0202: Action = caNumber3; break; + case 0x0203: Action = caNumber4; break; + case 0x0204: Action = caNumber5; break; + case 0x0205: Action = caNumber6; break; + case 0x0206: Action = caNumber7; break; + case 0x0207: Action = caNumber8; break; + case 0x0208: Action = caNumber9; break; + case 0x0300: Action = caMiddleClick; break; + case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break; + case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break; + case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break; + case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break; + case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break; + case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break; + case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break; + case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break; + case 0x0600: Action = caDblClick; break; + } + + if (Action == caUnknown) + { + LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.", + Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str() + ); + ASSERT(!"Unknown click action"); + return PARSE_OK; + } + + m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem); + return PARSE_OK; +} + + + + + diff --git a/source/Protocol/Protocol15x.h b/source/Protocol/Protocol15x.h index 73d73780d..3e1547df8 100644 --- a/source/Protocol/Protocol15x.h +++ b/source/Protocol/Protocol15x.h @@ -1,38 +1,38 @@ - -// Protocol15x.h - -/* -Declares the 1.5.x protocol classes: - - cProtocol150 - - release 1.5 and 1.5.1 protocol (#60) - - release 1.5.2 protocol (#61; no relevant changes found) -*/ - - - - - -#pragma once - -#include "Protocol14x.h" - - - - - -class cProtocol150 : - public cProtocol146 -{ - typedef cProtocol146 super; - -public: - cProtocol150(cClientHandle * a_Client); - - virtual void SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; - - virtual int ParseWindowClick(void); -} ; - - - - + +// Protocol15x.h + +/* +Declares the 1.5.x protocol classes: + - cProtocol150 + - release 1.5 and 1.5.1 protocol (#60) + - release 1.5.2 protocol (#61; no relevant changes found) +*/ + + + + + +#pragma once + +#include "Protocol14x.h" + + + + + +class cProtocol150 : + public cProtocol146 +{ + typedef cProtocol146 super; + +public: + cProtocol150(cClientHandle * a_Client); + + virtual void SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; + + virtual int ParseWindowClick(void); +} ; + + + + diff --git a/source/Protocol/Protocol16x.cpp b/source/Protocol/Protocol16x.cpp index d06980228..820e71cd9 100644 --- a/source/Protocol/Protocol16x.cpp +++ b/source/Protocol/Protocol16x.cpp @@ -1,245 +1,245 @@ - -// Protocol16x.cpp - -/* -Implements the 1.6.x protocol classes: - - cProtocol161 - - release 1.6.1 protocol (#73) - - cProtocol162 - - release 1.6.2 protocol (#74) -(others may be added later in the future for the 1.6 release series) -*/ - -#include "Globals.h" -#include "Protocol16x.h" -#include "../ClientHandle.h" -#include "../Entity.h" -#include "../Player.h" - - - - - -#define HANDLE_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ReceivedData.Proc(Var)) \ - { \ - m_ReceivedData.CheckValid(); \ - return PARSE_INCOMPLETE; \ - } \ - m_ReceivedData.CheckValid(); \ - } - - - - - -enum -{ - PACKET_CHAT = 0x03, - PACKET_UPDATE_HEALTH = 0x08, - PACKET_STEER_VEHICLE = 0x1b, - PACKET_ATTACH_ENTITY = 0x27, - PACKET_ENTITY_PROPERTIES = 0x2c, - PACKET_WINDOW_OPEN = 0x64, - PACKET_PLAYER_ABILITIES = 0xca, -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol161: - -cProtocol161::cProtocol161(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ATTACH_ENTITY); - WriteInt(a_Entity.GetUniqueID()); - WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); - WriteBool(false); // TODO: "Should use leash?" -> no - Flush(); -} - - - - - -void cProtocol161::SendChat(const AString & a_Message) -{ - super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str())); -} - - - - -void cProtocol161::SendGameMode(eGameMode a_GameMode) -{ - super::SendGameMode(a_GameMode); - SendPlayerMaxSpeed(); -} - - - - - -void cProtocol161::SendHealth(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_UPDATE_HEALTH); - WriteFloat((float)m_Client->GetPlayer()->GetHealth()); - WriteShort(m_Client->GetPlayer()->GetFoodLevel()); - WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); - Flush(); -} - - - - - -void cProtocol161::SendPlayerMaxSpeed(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENTITY_PROPERTIES); - WriteInt(m_Client->GetPlayer()->GetUniqueID()); - WriteInt(1); - WriteString("generic.movementSpeed"); - WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); - Flush(); -} - - - - - -void cProtocol161::SendRespawn(void) -{ - // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast - super::SendRespawn(); - SendPlayerMaxSpeed(); -} - - - - - -void cProtocol161::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) -{ - if (a_WindowType < 0) - { - // Do not send for inventory windows - return; - } - cCSLock Lock(m_CSPacket); - WriteByte (PACKET_WINDOW_OPEN); - WriteByte (a_WindowID); - WriteByte (a_WindowType); - WriteString(a_WindowTitle); - WriteByte (a_NumSlots); - WriteByte (1); // Use title - if (a_WindowType == 11) // horse / donkey - { - WriteInt(0); // Unknown value sent only when window type is 11 (horse / donkey) - } - Flush(); -} - - - - - -int cProtocol161::ParseEntityAction(void) -{ - HANDLE_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_PACKET_READ(ReadChar, char, ActionID); - HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal); - m_Client->HandleEntityAction(EntityID, ActionID); - return PARSE_OK; -} - - - - - -int cProtocol161::ParsePlayerAbilities(void) -{ - HANDLE_PACKET_READ(ReadByte, Byte, Flags); - HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed); - HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed); - // TODO: m_Client->HandlePlayerAbilities(...); - return PARSE_OK; -} - - - - - -int cProtocol161::ParseSteerVehicle(void) -{ - HANDLE_PACKET_READ(ReadBEFloat, float, Sideways); - HANDLE_PACKET_READ(ReadBEFloat, float, Forward); - HANDLE_PACKET_READ(ReadBool, bool, Jump); - HANDLE_PACKET_READ(ReadBool, bool, Unmount); - // TODO: m_Client->HandleSteerVehicle(...); - if (Unmount) - { - m_Client->HandleUnmount(); - } - return PARSE_OK; -} - - - - - -int cProtocol161::ParsePacket(unsigned char a_PacketType) -{ - switch (a_PacketType) - { - case PACKET_STEER_VEHICLE: return ParseSteerVehicle(); - default: return super::ParsePacket(a_PacketType); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProtocol162: - -cProtocol162::cProtocol162(cClientHandle * a_Client) : - super(a_Client) -{ -} - - - - - -void cProtocol162::SendPlayerMaxSpeed(void) -{ - cCSLock Lock(m_CSPacket); - WriteByte(PACKET_ENTITY_PROPERTIES); - WriteInt(m_Client->GetPlayer()->GetUniqueID()); - WriteInt(1); - WriteString("generic.movementSpeed"); - WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); - WriteShort(0); - Flush(); -} - - - - + +// Protocol16x.cpp + +/* +Implements the 1.6.x protocol classes: + - cProtocol161 + - release 1.6.1 protocol (#73) + - cProtocol162 + - release 1.6.2 protocol (#74) +(others may be added later in the future for the 1.6 release series) +*/ + +#include "Globals.h" +#include "Protocol16x.h" +#include "../ClientHandle.h" +#include "../Entity.h" +#include "../Player.h" + + + + + +#define HANDLE_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ReceivedData.Proc(Var)) \ + { \ + m_ReceivedData.CheckValid(); \ + return PARSE_INCOMPLETE; \ + } \ + m_ReceivedData.CheckValid(); \ + } + + + + + +enum +{ + PACKET_CHAT = 0x03, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_STEER_VEHICLE = 0x1b, + PACKET_ATTACH_ENTITY = 0x27, + PACKET_ENTITY_PROPERTIES = 0x2c, + PACKET_WINDOW_OPEN = 0x64, + PACKET_PLAYER_ABILITIES = 0xca, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol161: + +cProtocol161::cProtocol161(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ATTACH_ENTITY); + WriteInt(a_Entity.GetUniqueID()); + WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID()); + WriteBool(false); // TODO: "Should use leash?" -> no + Flush(); +} + + + + + +void cProtocol161::SendChat(const AString & a_Message) +{ + super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str())); +} + + + + +void cProtocol161::SendGameMode(eGameMode a_GameMode) +{ + super::SendGameMode(a_GameMode); + SendPlayerMaxSpeed(); +} + + + + + +void cProtocol161::SendHealth(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_UPDATE_HEALTH); + WriteFloat((float)m_Client->GetPlayer()->GetHealth()); + WriteShort(m_Client->GetPlayer()->GetFoodLevel()); + WriteFloat((float)m_Client->GetPlayer()->GetFoodSaturationLevel()); + Flush(); +} + + + + + +void cProtocol161::SendPlayerMaxSpeed(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_PROPERTIES); + WriteInt(m_Client->GetPlayer()->GetUniqueID()); + WriteInt(1); + WriteString("generic.movementSpeed"); + WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); + Flush(); +} + + + + + +void cProtocol161::SendRespawn(void) +{ + // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast + super::SendRespawn(); + SendPlayerMaxSpeed(); +} + + + + + +void cProtocol161::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) +{ + if (a_WindowType < 0) + { + // Do not send for inventory windows + return; + } + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_WINDOW_OPEN); + WriteByte (a_WindowID); + WriteByte (a_WindowType); + WriteString(a_WindowTitle); + WriteByte (a_NumSlots); + WriteByte (1); // Use title + if (a_WindowType == 11) // horse / donkey + { + WriteInt(0); // Unknown value sent only when window type is 11 (horse / donkey) + } + Flush(); +} + + + + + +int cProtocol161::ParseEntityAction(void) +{ + HANDLE_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_PACKET_READ(ReadChar, char, ActionID); + HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal); + m_Client->HandleEntityAction(EntityID, ActionID); + return PARSE_OK; +} + + + + + +int cProtocol161::ParsePlayerAbilities(void) +{ + HANDLE_PACKET_READ(ReadByte, Byte, Flags); + HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed); + // TODO: m_Client->HandlePlayerAbilities(...); + return PARSE_OK; +} + + + + + +int cProtocol161::ParseSteerVehicle(void) +{ + HANDLE_PACKET_READ(ReadBEFloat, float, Sideways); + HANDLE_PACKET_READ(ReadBEFloat, float, Forward); + HANDLE_PACKET_READ(ReadBool, bool, Jump); + HANDLE_PACKET_READ(ReadBool, bool, Unmount); + // TODO: m_Client->HandleSteerVehicle(...); + if (Unmount) + { + m_Client->HandleUnmount(); + } + return PARSE_OK; +} + + + + + +int cProtocol161::ParsePacket(unsigned char a_PacketType) +{ + switch (a_PacketType) + { + case PACKET_STEER_VEHICLE: return ParseSteerVehicle(); + default: return super::ParsePacket(a_PacketType); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cProtocol162: + +cProtocol162::cProtocol162(cClientHandle * a_Client) : + super(a_Client) +{ +} + + + + + +void cProtocol162::SendPlayerMaxSpeed(void) +{ + cCSLock Lock(m_CSPacket); + WriteByte(PACKET_ENTITY_PROPERTIES); + WriteInt(m_Client->GetPlayer()->GetUniqueID()); + WriteInt(1); + WriteString("generic.movementSpeed"); + WriteDouble(m_Client->GetPlayer()->GetMaxSpeed()); + WriteShort(0); + Flush(); +} + + + + diff --git a/source/Protocol/Protocol16x.h b/source/Protocol/Protocol16x.h index e012585d1..4dba5eab6 100644 --- a/source/Protocol/Protocol16x.h +++ b/source/Protocol/Protocol16x.h @@ -1,73 +1,73 @@ - -// Protocol16x.h - -/* -Declares the 1.6.x protocol classes: - - cProtocol161 - - release 1.6.1 protocol (#73) - - cProtocol162 - - release 1.6.2 protocol (#74) -(others may be added later in the future for the 1.6 release series) -*/ - - - - - -#pragma once - -#include "Protocol15x.h" - - - - - -class cProtocol161 : - public cProtocol150 -{ - typedef cProtocol150 super; - -public: - cProtocol161(cClientHandle * a_Client); - -protected: - - // cProtocol150 overrides: - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; - virtual void SendChat (const AString & a_Message) override; - virtual void SendGameMode (eGameMode a_GameMode) override; - virtual void SendHealth (void) override; - virtual void SendPlayerMaxSpeed(void) override; - virtual void SendRespawn (void) override; - virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; - - virtual int ParseEntityAction (void) override; - virtual int ParsePlayerAbilities(void) override; - - // New packets: - virtual int ParseSteerVehicle(void); - - // Enable new packets' handling - virtual int ParsePacket(unsigned char a_PacketType) override; -} ; - - - - - -class cProtocol162 : - public cProtocol161 -{ - typedef cProtocol161 super; - -public: - cProtocol162(cClientHandle * a_Client); - -protected: - // cProtocol161 overrides: - virtual void SendPlayerMaxSpeed(void) override; -} ; - - - - + +// Protocol16x.h + +/* +Declares the 1.6.x protocol classes: + - cProtocol161 + - release 1.6.1 protocol (#73) + - cProtocol162 + - release 1.6.2 protocol (#74) +(others may be added later in the future for the 1.6 release series) +*/ + + + + + +#pragma once + +#include "Protocol15x.h" + + + + + +class cProtocol161 : + public cProtocol150 +{ + typedef cProtocol150 super; + +public: + cProtocol161(cClientHandle * a_Client); + +protected: + + // cProtocol150 overrides: + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendPlayerMaxSpeed(void) override; + virtual void SendRespawn (void) override; + virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; + + virtual int ParseEntityAction (void) override; + virtual int ParsePlayerAbilities(void) override; + + // New packets: + virtual int ParseSteerVehicle(void); + + // Enable new packets' handling + virtual int ParsePacket(unsigned char a_PacketType) override; +} ; + + + + + +class cProtocol162 : + public cProtocol161 +{ + typedef cProtocol161 super; + +public: + cProtocol162(cClientHandle * a_Client); + +protected: + // cProtocol161 overrides: + virtual void SendPlayerMaxSpeed(void) override; +} ; + + + + diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp index ec10eeb12..de4ccc03e 100644 --- a/source/Protocol/ProtocolRecognizer.cpp +++ b/source/Protocol/ProtocolRecognizer.cpp @@ -1,771 +1,771 @@ - -// ProtocolRecognizer.cpp - -// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple -// protocol versions and redirects everything to them - -#include "Globals.h" - -#include "ProtocolRecognizer.h" -#include "Protocol125.h" -#include "Protocol132.h" -#include "Protocol14x.h" -#include "Protocol15x.h" -#include "Protocol16x.h" -#include "../ClientHandle.h" -#include "../Root.h" -#include "../World.h" -#include "../ChatColor.h" - - - - - -cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : - super(a_Client), - m_Protocol(NULL), - m_Buffer(512) -{ -} - - - - - -cProtocolRecognizer::~cProtocolRecognizer() -{ - delete m_Protocol; -} - - - - - -AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) -{ - switch (a_ProtocolVersion) - { - case PROTO_VERSION_1_2_5: return "1.2.5"; - case PROTO_VERSION_1_3_2: return "1.3.2"; - case PROTO_VERSION_1_4_2: return "1.4.2"; - case PROTO_VERSION_1_4_4: return "1.4.4"; - case PROTO_VERSION_1_4_6: return "1.4.6"; - case PROTO_VERSION_1_5_0: return "1.5"; - case PROTO_VERSION_1_5_2: return "1.5.2"; - case PROTO_VERSION_1_6_1: return "1.6.1"; - case PROTO_VERSION_1_6_2: return "1.6.2"; - } - ASSERT(!"Unknown protocol version"); - return Printf("Unknown protocol (%d)", a_ProtocolVersion); -} - - - - - -void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size) -{ - if (m_Protocol == NULL) - { - if (!m_Buffer.Write(a_Data, a_Size)) - { - m_Client->Kick("Unsupported protocol version"); - return; - } - - if (!TryRecognizeProtocol()) - { - return; - } - - // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing: - AString Dump; - m_Buffer.ResetRead(); - m_Buffer.ReadAll(Dump); - m_Protocol->DataReceived(Dump.data(), Dump.size()); - } - else - { - m_Protocol->DataReceived(a_Data, a_Size); - } -} - - - - - -void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); -} - - - - - -void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); -} - - - - - -void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage); -} - - - - - -void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); -} - - - - - -void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); -} - - - - - -void cProtocolRecognizer::SendChat(const AString & a_Message) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendChat(a_Message); -} - - - - - -void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); -} - - - - - -void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendCollectPickup(a_Pickup, a_Player); -} - - - - - -void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendDestroyEntity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendDisconnect(const AString & a_Reason) -{ - if (m_Protocol != NULL) - { - m_Protocol->SendDisconnect(a_Reason); - } - else - { - // This is used when the client sends a server-ping, respond with the default packet: - WriteByte ((char)0xff); // PACKET_DISCONNECT - WriteString(a_Reason); - } -} - - - - -void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); -} - - - - - -void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityHeadLook(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityLook(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityMetadata(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityProperties(a_Entity); -} - - - - - -void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityStatus(a_Entity, a_Status); -} - - - - - -void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendEntityVelocity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion); -} - - - - - -void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendGameMode(a_GameMode); -} - - - - - -void cProtocolRecognizer::SendHealth(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendHealth(); -} - - - - - -void cProtocolRecognizer::SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendInventoryProgress(a_WindowID, a_Progressbar, a_Value); -} - - - - - -void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); -} - - - - - -void cProtocolRecognizer::SendKeepAlive(int a_PingID) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendKeepAlive(a_PingID); -} - - - - - -void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendLogin(a_Player, a_World); -} - - - - - -void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPickupSpawn(a_Pickup); -} - - - - - -void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerAnimation(a_Player, a_Animation); -} - - - - - -void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); -} - - - - - -void cProtocolRecognizer::SendPlayerMaxSpeed(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerMaxSpeed(); -} - - - - - -void cProtocolRecognizer::SendPlayerMoveLook(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerMoveLook(); -} - - - - - -void cProtocolRecognizer::SendPlayerPosition(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerPosition(); -} - - - - - -void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendPlayerSpawn(a_Player); -} - - - - - -void cProtocolRecognizer::SendRespawn(void) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendRespawn(); -} - - - - - -void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); -} - - - - - -void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); -} - - - - - -void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSpawnFallingBlock(a_FallingBlock); -} - - - - - -void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSpawnMob(a_Mob); -} - - - - - -void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch); -} - - - - - -void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType); -} - - - - - -void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendTeleportEntity(a_Entity); -} - - - - - -void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay); -} - - - - - -void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); -} - - - - - -void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cProtocolRecognizer::SendWeather(eWeather a_Weather) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWeather(a_Weather); -} - - - - - -void cProtocolRecognizer::SendWholeInventory(const cInventory & a_Inventory) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWholeInventory(a_Inventory); -} - - - - - -void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWholeInventory(a_Window); -} - - - - - -void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWindowClose(a_Window); -} - - - - - -void cProtocolRecognizer::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) -{ - ASSERT(m_Protocol != NULL); - m_Protocol->SendWindowOpen(a_WindowID, a_WindowType, a_WindowTitle, a_NumSlots); -} - - - - - -AString cProtocolRecognizer::GetAuthServerID(void) -{ - ASSERT(m_Protocol != NULL); - return m_Protocol->GetAuthServerID(); -} - - - - - -void cProtocolRecognizer::SendData(const char * a_Data, int a_Size) -{ - // This is used only when handling the server ping - m_Client->SendData(a_Data, a_Size); -} - - - - - -bool cProtocolRecognizer::TryRecognizeProtocol(void) -{ - // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and - // MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro - - // The first packet should be a Handshake, 0x02: - unsigned char PacketType; - if (!m_Buffer.ReadByte(PacketType)) - { - return false; - } - switch (PacketType) - { - case 0x02: break; // Handshake, continue recognizing - case 0xfe: HandleServerPing(); return false; - default: return false; - } - - // 1.3.2 starts with 0x02 0x39 - // 1.2.5 starts with 0x02 and name is expected to less than 0x3900 long :) - char ch; - if (!m_Buffer.ReadChar(ch)) - { - return false; - } - switch (ch) - { - case PROTO_VERSION_1_3_2: - { - m_Protocol = new cProtocol132(m_Client); - return true; - } - case PROTO_VERSION_1_4_2: - case PROTO_VERSION_1_4_4: - { - m_Protocol = new cProtocol142(m_Client); - return true; - } - case PROTO_VERSION_1_4_6: - { - m_Protocol = new cProtocol146(m_Client); - return true; - } - case PROTO_VERSION_1_5_0: - case PROTO_VERSION_1_5_2: - { - m_Protocol = new cProtocol150(m_Client); - return true; - } - case PROTO_VERSION_1_6_1: - { - m_Protocol = new cProtocol161(m_Client); - return true; - } - case PROTO_VERSION_1_6_2: - { - m_Protocol = new cProtocol162(m_Client); - return true; - } - } - m_Protocol = new cProtocol125(m_Client); - return true; -} - - - - - -void cProtocolRecognizer::HandleServerPing(void) -{ - AString Reply; - switch (cRoot::Get()->m_PrimaryServerVersion) - { - case PROTO_VERSION_1_2_5: - case PROTO_VERSION_1_3_2: - { - // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29 - Printf(Reply, "%s%s%i%s%i", - cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(), - cChatColor::Delimiter.c_str(), - cRoot::Get()->GetDefaultWorld()->GetNumPlayers(), - cChatColor::Delimiter.c_str(), - cRoot::Get()->GetDefaultWorld()->GetMaxPlayers() - ); - break; - } - - case PROTO_VERSION_1_4_2: - case PROTO_VERSION_1_4_4: - case PROTO_VERSION_1_4_6: - case PROTO_VERSION_1_5_0: - case PROTO_VERSION_1_5_2: - case PROTO_VERSION_1_6_1: - case PROTO_VERSION_1_6_2: - { - // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff. - // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29 - // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit. - // Who cares? We're disconnecting anyway. - if (m_Buffer.CanReadBytes(1)) - { - byte val; - m_Buffer.ReadByte(val); - ASSERT(val == 0x01); - } - - // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100 - AString NumPlayers; - Printf(NumPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetNumPlayers()); - AString MaxPlayers; - Printf(MaxPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()); - - AString ProtocolVersionNum; - Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion); - AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->m_PrimaryServerVersion)); - - // Cannot use Printf() because of in-string NUL bytes. - Reply = cChatColor::Delimiter; - Reply.append("1"); - Reply.push_back(0); - Reply.append(ProtocolVersionNum); - Reply.push_back(0); - Reply.append(ProtocolVersionTxt); - Reply.push_back(0); - Reply.append(cRoot::Get()->GetDefaultWorld()->GetDescription()); - Reply.push_back(0); - Reply.append(NumPlayers); - Reply.push_back(0); - Reply.append(MaxPlayers); - break; - } - } // switch (m_PrimaryServerVersion) - m_Client->Kick(Reply); -} - - - - + +// ProtocolRecognizer.cpp + +// Implements the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple +// protocol versions and redirects everything to them + +#include "Globals.h" + +#include "ProtocolRecognizer.h" +#include "Protocol125.h" +#include "Protocol132.h" +#include "Protocol14x.h" +#include "Protocol15x.h" +#include "Protocol16x.h" +#include "../ClientHandle.h" +#include "../Root.h" +#include "../World.h" +#include "../ChatColor.h" + + + + + +cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : + super(a_Client), + m_Protocol(NULL), + m_Buffer(512) +{ +} + + + + + +cProtocolRecognizer::~cProtocolRecognizer() +{ + delete m_Protocol; +} + + + + + +AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion) +{ + switch (a_ProtocolVersion) + { + case PROTO_VERSION_1_2_5: return "1.2.5"; + case PROTO_VERSION_1_3_2: return "1.3.2"; + case PROTO_VERSION_1_4_2: return "1.4.2"; + case PROTO_VERSION_1_4_4: return "1.4.4"; + case PROTO_VERSION_1_4_6: return "1.4.6"; + case PROTO_VERSION_1_5_0: return "1.5"; + case PROTO_VERSION_1_5_2: return "1.5.2"; + case PROTO_VERSION_1_6_1: return "1.6.1"; + case PROTO_VERSION_1_6_2: return "1.6.2"; + } + ASSERT(!"Unknown protocol version"); + return Printf("Unknown protocol (%d)", a_ProtocolVersion); +} + + + + + +void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size) +{ + if (m_Protocol == NULL) + { + if (!m_Buffer.Write(a_Data, a_Size)) + { + m_Client->Kick("Unsupported protocol version"); + return; + } + + if (!TryRecognizeProtocol()) + { + return; + } + + // The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing: + AString Dump; + m_Buffer.ResetRead(); + m_Buffer.ReadAll(Dump); + m_Protocol->DataReceived(Dump.data(), Dump.size()); + } + else + { + m_Protocol->DataReceived(a_Data, a_Size); + } +} + + + + + +void cProtocolRecognizer::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendAttachEntity(a_Entity, a_Vehicle); +} + + + + + +void cProtocolRecognizer::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); +} + + + + + +void cProtocolRecognizer::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockBreakAnim(a_entityID, a_BlockX, a_BlockY, a_BlockZ, stage); +} + + + + + +void cProtocolRecognizer::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); +} + + + + + +void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); +} + + + + + +void cProtocolRecognizer::SendChat(const AString & a_Message) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChat(a_Message); +} + + + + + +void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); +} + + + + + +void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendCollectPickup(a_Pickup, a_Player); +} + + + + + +void cProtocolRecognizer::SendDestroyEntity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendDestroyEntity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendDisconnect(const AString & a_Reason) +{ + if (m_Protocol != NULL) + { + m_Protocol->SendDisconnect(a_Reason); + } + else + { + // This is used when the client sends a server-ping, respond with the default packet: + WriteByte ((char)0xff); // PACKET_DISCONNECT + WriteString(a_Reason); + } +} + + + + +void cProtocolRecognizer::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); +} + + + + + +void cProtocolRecognizer::SendEntityHeadLook(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityHeadLook(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityLook(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityLook(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityMetadata(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityMetadata(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityProperties(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityProperties(a_Entity); +} + + + + + +void cProtocolRecognizer::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cProtocolRecognizer::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cProtocolRecognizer::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityStatus(a_Entity, a_Status); +} + + + + + +void cProtocolRecognizer::SendEntityVelocity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendEntityVelocity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendExplosion(a_BlockX,a_BlockY,a_BlockZ,a_Radius, a_BlocksAffected, a_PlayerMotion); +} + + + + + +void cProtocolRecognizer::SendGameMode(eGameMode a_GameMode) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendGameMode(a_GameMode); +} + + + + + +void cProtocolRecognizer::SendHealth(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendHealth(); +} + + + + + +void cProtocolRecognizer::SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendInventoryProgress(a_WindowID, a_Progressbar, a_Value); +} + + + + + +void cProtocolRecognizer::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); +} + + + + + +void cProtocolRecognizer::SendKeepAlive(int a_PingID) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendKeepAlive(a_PingID); +} + + + + + +void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendLogin(a_Player, a_World); +} + + + + + +void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPickupSpawn(a_Pickup); +} + + + + + +void cProtocolRecognizer::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerAnimation(a_Player, a_Animation); +} + + + + + +void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); +} + + + + + +void cProtocolRecognizer::SendPlayerMaxSpeed(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerMaxSpeed(); +} + + + + + +void cProtocolRecognizer::SendPlayerMoveLook(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerMoveLook(); +} + + + + + +void cProtocolRecognizer::SendPlayerPosition(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerPosition(); +} + + + + + +void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendPlayerSpawn(a_Player); +} + + + + + +void cProtocolRecognizer::SendRespawn(void) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendRespawn(); +} + + + + + +void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); +} + + + + + +void cProtocolRecognizer::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data); +} + + + + + +void cProtocolRecognizer::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnFallingBlock(a_FallingBlock); +} + + + + + +void cProtocolRecognizer::SendSpawnMob(const cMonster & a_Mob) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnMob(a_Mob); +} + + + + + +void cProtocolRecognizer::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch); +} + + + + + +void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType); +} + + + + + +void cProtocolRecognizer::SendTeleportEntity(const cEntity & a_Entity) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTeleportEntity(a_Entity); +} + + + + + +void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay); +} + + + + + +void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); +} + + + + + +void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cProtocolRecognizer::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cProtocolRecognizer::SendWeather(eWeather a_Weather) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWeather(a_Weather); +} + + + + + +void cProtocolRecognizer::SendWholeInventory(const cInventory & a_Inventory) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWholeInventory(a_Inventory); +} + + + + + +void cProtocolRecognizer::SendWholeInventory(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWholeInventory(a_Window); +} + + + + + +void cProtocolRecognizer::SendWindowClose(const cWindow & a_Window) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowClose(a_Window); +} + + + + + +void cProtocolRecognizer::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendWindowOpen(a_WindowID, a_WindowType, a_WindowTitle, a_NumSlots); +} + + + + + +AString cProtocolRecognizer::GetAuthServerID(void) +{ + ASSERT(m_Protocol != NULL); + return m_Protocol->GetAuthServerID(); +} + + + + + +void cProtocolRecognizer::SendData(const char * a_Data, int a_Size) +{ + // This is used only when handling the server ping + m_Client->SendData(a_Data, a_Size); +} + + + + + +bool cProtocolRecognizer::TryRecognizeProtocol(void) +{ + // NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and + // MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro + + // The first packet should be a Handshake, 0x02: + unsigned char PacketType; + if (!m_Buffer.ReadByte(PacketType)) + { + return false; + } + switch (PacketType) + { + case 0x02: break; // Handshake, continue recognizing + case 0xfe: HandleServerPing(); return false; + default: return false; + } + + // 1.3.2 starts with 0x02 0x39 + // 1.2.5 starts with 0x02 and name is expected to less than 0x3900 long :) + char ch; + if (!m_Buffer.ReadChar(ch)) + { + return false; + } + switch (ch) + { + case PROTO_VERSION_1_3_2: + { + m_Protocol = new cProtocol132(m_Client); + return true; + } + case PROTO_VERSION_1_4_2: + case PROTO_VERSION_1_4_4: + { + m_Protocol = new cProtocol142(m_Client); + return true; + } + case PROTO_VERSION_1_4_6: + { + m_Protocol = new cProtocol146(m_Client); + return true; + } + case PROTO_VERSION_1_5_0: + case PROTO_VERSION_1_5_2: + { + m_Protocol = new cProtocol150(m_Client); + return true; + } + case PROTO_VERSION_1_6_1: + { + m_Protocol = new cProtocol161(m_Client); + return true; + } + case PROTO_VERSION_1_6_2: + { + m_Protocol = new cProtocol162(m_Client); + return true; + } + } + m_Protocol = new cProtocol125(m_Client); + return true; +} + + + + + +void cProtocolRecognizer::HandleServerPing(void) +{ + AString Reply; + switch (cRoot::Get()->m_PrimaryServerVersion) + { + case PROTO_VERSION_1_2_5: + case PROTO_VERSION_1_3_2: + { + // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29 + Printf(Reply, "%s%s%i%s%i", + cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetDefaultWorld()->GetNumPlayers(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetDefaultWorld()->GetMaxPlayers() + ); + break; + } + + case PROTO_VERSION_1_4_2: + case PROTO_VERSION_1_4_4: + case PROTO_VERSION_1_4_6: + case PROTO_VERSION_1_5_0: + case PROTO_VERSION_1_5_2: + case PROTO_VERSION_1_6_1: + case PROTO_VERSION_1_6_2: + { + // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff. + // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29 + // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit. + // Who cares? We're disconnecting anyway. + if (m_Buffer.CanReadBytes(1)) + { + byte val; + m_Buffer.ReadByte(val); + ASSERT(val == 0x01); + } + + // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100 + AString NumPlayers; + Printf(NumPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetNumPlayers()); + AString MaxPlayers; + Printf(MaxPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()); + + AString ProtocolVersionNum; + Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion); + AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->m_PrimaryServerVersion)); + + // Cannot use Printf() because of in-string NUL bytes. + Reply = cChatColor::Delimiter; + Reply.append("1"); + Reply.push_back(0); + Reply.append(ProtocolVersionNum); + Reply.push_back(0); + Reply.append(ProtocolVersionTxt); + Reply.push_back(0); + Reply.append(cRoot::Get()->GetDefaultWorld()->GetDescription()); + Reply.push_back(0); + Reply.append(NumPlayers); + Reply.push_back(0); + Reply.append(MaxPlayers); + break; + } + } // switch (m_PrimaryServerVersion) + m_Client->Kick(Reply); +} + + + + diff --git a/source/Protocol/ProtocolRecognizer.h b/source/Protocol/ProtocolRecognizer.h index 96d03082d..2179f3b05 100644 --- a/source/Protocol/ProtocolRecognizer.h +++ b/source/Protocol/ProtocolRecognizer.h @@ -1,130 +1,130 @@ - -// ProtocolRecognizer.h - -// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple -// protocol versions and redirects everything to them - - - - - -#pragma once - -#include "Protocol.h" -#include "../ByteBuffer.h" - - - - - -// Adjust these if a new protocol is added or an old one is removed: -#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2" -#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74" - - - - - -class cProtocolRecognizer : - public cProtocol -{ - typedef cProtocol super; - -public: - enum - { - PROTO_VERSION_1_2_5 = 29, - PROTO_VERSION_1_3_2 = 39, - PROTO_VERSION_1_4_2 = 47, - PROTO_VERSION_1_4_4 = 49, - PROTO_VERSION_1_4_6 = 51, - PROTO_VERSION_1_5_0 = 60, - PROTO_VERSION_1_5_2 = 61, - PROTO_VERSION_1_6_1 = 73, - PROTO_VERSION_1_6_2 = 74, - - PROTO_VERSION_NEXT, - PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion - } ; - - cProtocolRecognizer(cClientHandle * a_Client); - virtual ~cProtocolRecognizer(); - - /// Translates protocol version number into protocol version text: 49 -> "1.4.4" - static AString GetVersionTextFromInt(int a_ProtocolVersion); - - /// Called when client sends some data: - virtual void DataReceived(const char * a_Data, int a_Size) override; - - /// Sending stuff to clients (alphabetically sorted): - virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; - virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; - virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; - virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; - virtual void SendChat (const AString & a_Message) override; - virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; - virtual void SendDestroyEntity (const cEntity & a_Entity) override; - virtual void SendDisconnect (const AString & a_Reason) override; - virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; - virtual void SendEntityHeadLook (const cEntity & a_Entity) override; - virtual void SendEntityLook (const cEntity & a_Entity) override; - virtual void SendEntityMetadata (const cEntity & a_Entity) override; - virtual void SendEntityProperties (const cEntity & a_Entity) override; - virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; - virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; - virtual void SendEntityVelocity (const cEntity & a_Entity) override; - virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; - virtual void SendGameMode (eGameMode a_GameMode) override; - virtual void SendHealth (void) override; - virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) override; - virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; - virtual void SendKeepAlive (int a_PingID) override; - virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; - virtual void SendPickupSpawn (const cPickup & a_Pickup) override; - virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; - virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; - virtual void SendPlayerMaxSpeed (void) override; - virtual void SendPlayerMoveLook (void) override; - virtual void SendPlayerPosition (void) override; - virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendRespawn (void) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; - virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; - virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; - virtual void SendSpawnMob (const cMonster & a_Mob) override; - virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; - virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override; - virtual void SendTeleportEntity (const cEntity & a_Entity) override; - virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; - virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; - virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; - virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; - virtual void SendWeather (eWeather a_Weather) override; - virtual void SendWholeInventory (const cInventory & a_Inventory) override; - virtual void SendWholeInventory (const cWindow & a_Window) override; - virtual void SendWindowClose (const cWindow & a_Window) override; - virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; - - virtual AString GetAuthServerID(void) override; - - virtual void SendData(const char * a_Data, int a_Size) override; - -protected: - cProtocol * m_Protocol; //< The recognized protocol - cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol - - /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized - bool TryRecognizeProtocol(void); - - /// Called when the recognizer gets a server ping packet; responds with server stats and destroys the client - void HandleServerPing(void); -} ; - - - - - + +// ProtocolRecognizer.h + +// Interfaces to the cProtocolRecognizer class representing the meta-protocol that recognizes possibly multiple +// protocol versions and redirects everything to them + + + + + +#pragma once + +#include "Protocol.h" +#include "../ByteBuffer.h" + + + + + +// Adjust these if a new protocol is added or an old one is removed: +#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2" +#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74" + + + + + +class cProtocolRecognizer : + public cProtocol +{ + typedef cProtocol super; + +public: + enum + { + PROTO_VERSION_1_2_5 = 29, + PROTO_VERSION_1_3_2 = 39, + PROTO_VERSION_1_4_2 = 47, + PROTO_VERSION_1_4_4 = 49, + PROTO_VERSION_1_4_6 = 51, + PROTO_VERSION_1_5_0 = 60, + PROTO_VERSION_1_5_2 = 61, + PROTO_VERSION_1_6_1 = 73, + PROTO_VERSION_1_6_2 = 74, + + PROTO_VERSION_NEXT, + PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion + } ; + + cProtocolRecognizer(cClientHandle * a_Client); + virtual ~cProtocolRecognizer(); + + /// Translates protocol version number into protocol version text: 49 -> "1.4.4" + static AString GetVersionTextFromInt(int a_ProtocolVersion); + + /// Called when client sends some data: + virtual void DataReceived(const char * a_Data, int a_Size) override; + + /// Sending stuff to clients (alphabetically sorted): + virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; + virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; + virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; + virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; + virtual void SendChat (const AString & a_Message) override; + virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; + virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendDestroyEntity (const cEntity & a_Entity) override; + virtual void SendDisconnect (const AString & a_Reason) override; + virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; + virtual void SendEntityHeadLook (const cEntity & a_Entity) override; + virtual void SendEntityLook (const cEntity & a_Entity) override; + virtual void SendEntityMetadata (const cEntity & a_Entity) override; + virtual void SendEntityProperties (const cEntity & a_Entity) override; + virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override; + virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override; + virtual void SendEntityVelocity (const cEntity & a_Entity) override; + virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override; + virtual void SendGameMode (eGameMode a_GameMode) override; + virtual void SendHealth (void) override; + virtual void SendInventoryProgress (char a_WindowID, short a_Progressbar, short a_Value) override; + virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override; + virtual void SendKeepAlive (int a_PingID) override; + virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; + virtual void SendPickupSpawn (const cPickup & a_Pickup) override; + virtual void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation) override; + virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override; + virtual void SendPlayerMaxSpeed (void) override; + virtual void SendPlayerMoveLook (void) override; + virtual void SendPlayerPosition (void) override; + virtual void SendPlayerSpawn (const cPlayer & a_Player) override; + virtual void SendRespawn (void) override; + virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; + virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; + virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; + virtual void SendSpawnMob (const cMonster & a_Mob) override; + virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override; + virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType) override; + virtual void SendTeleportEntity (const cEntity & a_Entity) override; + virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override; + virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override; + virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override; + virtual void SendWeather (eWeather a_Weather) override; + virtual void SendWholeInventory (const cInventory & a_Inventory) override; + virtual void SendWholeInventory (const cWindow & a_Window) override; + virtual void SendWindowClose (const cWindow & a_Window) override; + virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; + + virtual AString GetAuthServerID(void) override; + + virtual void SendData(const char * a_Data, int a_Size) override; + +protected: + cProtocol * m_Protocol; //< The recognized protocol + cByteBuffer m_Buffer; //< Buffer for the incoming data until we recognize the protocol + + /// Tries to recognize protocol based on m_Buffer contents; returns true if recognized + bool TryRecognizeProtocol(void); + + /// Called when the recognizer gets a server ping packet; responds with server stats and destroys the client + void HandleServerPing(void); +} ; + + + + + diff --git a/source/RCONServer.cpp b/source/RCONServer.cpp index cf6ed8b0c..04c06c887 100644 --- a/source/RCONServer.cpp +++ b/source/RCONServer.cpp @@ -1,332 +1,332 @@ - -// RCONServer.cpp - -// Implements the cRCONServer class representing the RCON server - -#include "Globals.h" -#include "../iniFile/iniFile.h" -#include "RCONServer.h" -#include "Server.h" -#include "Root.h" -#include "CommandOutput.h" - - - - - -// Disable MSVC warnings: -#if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4355) // 'this' : used in base member initializer list -#endif - - - - - -enum -{ - // Client -> Server: - RCON_PACKET_COMMAND = 2, - RCON_PACKET_LOGIN = 3, - - // Server -> Client: - RCON_PACKET_RESPONSE = 2, -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRCONCommandOutput: - -class cRCONCommandOutput : - public cCommandOutputCallback -{ -public: - cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) : - m_Connection(a_Connection), - m_RequestID(a_RequestID) - { - } - - // cCommandOutputCallback overrides: - virtual void Out(const AString & a_Text) override - { - m_Buffer.append(a_Text); - } - - virtual void Finished(void) override - { - m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str()); - delete this; - } - -protected: - cRCONServer::cConnection & m_Connection; - int m_RequestID; - AString m_Buffer; -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRCONServer: - -cRCONServer::cRCONServer(cServer & a_Server) : - m_Server(a_Server), - m_ListenThread4(*this, cSocket::IPv4, "RCON"), - m_ListenThread6(*this, cSocket::IPv6, "RCON") -{ -} - - - - - -cRCONServer::~cRCONServer() -{ - m_ListenThread4.Stop(); - m_ListenThread6.Stop(); -} - - - - - -void cRCONServer::Initialize(cIniFile & a_IniFile) -{ - if (!a_IniFile.GetValueSetB("RCON", "Enabled", false)) - { - return; - } - - // Read the password, don't allow an empty one: - m_Password = a_IniFile.GetValueSet("RCON", "Password", ""); - if (m_Password.empty()) - { - LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled."); - return; - } - - // Read and initialize both IPv4 and IPv6 ports for RCON - bool HasAnyPorts = false; - AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575"); - if (m_ListenThread4.Initialize(Ports4)) - { - HasAnyPorts = true; - m_ListenThread4.Start(); - } - AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575"); - if (m_ListenThread6.Initialize(Ports6)) - { - HasAnyPorts = true; - m_ListenThread6.Start(); - } - if (!HasAnyPorts) - { - LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled."); - return; - } -} - - - - - -void cRCONServer::OnConnectionAccepted(cSocket & a_Socket) -{ - if (!a_Socket.IsValid()) - { - return; - } - - LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str()); - - // Create a new cConnection object, it will be deleted when the connection is closed - m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket)); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRCONServer::cConnection: - -cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) : - m_IsAuthenticated(false), - m_RCONServer(a_RCONServer), - m_Socket(a_Socket), - m_IPAddress(a_Socket.GetIPString()) -{ -} - - - - - -void cRCONServer::cConnection::DataReceived(const char * a_Data, int a_Size) -{ - // Append data to the buffer: - m_Buffer.append(a_Data, a_Size); - - // Process the packets in the buffer: - while (m_Buffer.size() >= 14) - { - int Length = IntFromBuffer(m_Buffer.data()); - if (Length > 1500) - { - // Too long, drop the connection - LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.", - Length, m_IPAddress.c_str() - ); - m_RCONServer.m_SocketThreads.RemoveClient(this); - m_Socket.CloseSocket(); - delete this; - return; - } - if (Length > (int)(m_Buffer.size() + 4)) - { - // Incomplete packet yet, wait for more data to come - return; - } - - int RequestID = IntFromBuffer(m_Buffer.data() + 4); - int PacketType = IntFromBuffer(m_Buffer.data() + 8); - if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) - { - m_RCONServer.m_SocketThreads.RemoveClient(this); - m_Socket.CloseSocket(); - delete this; - return; - } - m_Buffer.erase(0, Length + 4); - } // while (m_Buffer.size() >= 14) -} - - - - - -void cRCONServer::cConnection::GetOutgoingData(AString & a_Data) -{ - a_Data.assign(m_Outgoing); - m_Outgoing.clear(); -} - - - - - -void cRCONServer::cConnection::SocketClosed(void) -{ - m_RCONServer.m_SocketThreads.RemoveClient(this); - delete this; -} - - - - - -bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) -{ - switch (a_PacketType) - { - case RCON_PACKET_LOGIN: - { - if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0) - { - LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str()); - return false; - } - m_IsAuthenticated = true; - - LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str()); - - // Send OK response: - SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); - return true; - } - - case RCON_PACKET_COMMAND: - { - if (!m_IsAuthenticated) - { - char AuthNeeded[] = "You need to authenticate first!"; - SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded); - return false; - } - - AString cmd(a_Payload, a_PayloadLength); - LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str()); - cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID))); - - // Send an empty response: - SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); - return true; - } - } - - // Unknown packet type, drop the connection: - LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.", - m_IPAddress.c_str(), a_PacketType - ); - return false; -} - - - - - -/// Reads 4 bytes from a_Buffer and returns the int they represent -int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer) -{ - return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0]; -} - - - - - -/// Puts 4 bytes representing the int into the buffer -void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer) -{ - a_Buffer[0] = a_Value & 0xff; - a_Buffer[1] = (a_Value >> 8) & 0xff; - a_Buffer[2] = (a_Value >> 16) & 0xff; - a_Buffer[3] = (a_Value >> 24) & 0xff; -} - - - - - -/// Sends a RCON packet back to the client -void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) -{ - ASSERT((a_PayloadLength == 0) || (a_Payload != NULL)); // Either zero data to send, or a valid payload ptr - - char Buffer[4]; - int Length = a_PayloadLength + 10; - IntToBuffer(Length, Buffer); - m_Outgoing.append(Buffer, 4); - IntToBuffer(a_RequestID, Buffer); - m_Outgoing.append(Buffer, 4); - IntToBuffer(a_PacketType, Buffer); - m_Outgoing.append(Buffer, 4); - if (a_PayloadLength > 0) - { - m_Outgoing.append(a_Payload, a_PayloadLength); - } - m_Outgoing.push_back(0); - m_Outgoing.push_back(0); - m_RCONServer.m_SocketThreads.NotifyWrite(this); -} - - - - + +// RCONServer.cpp + +// Implements the cRCONServer class representing the RCON server + +#include "Globals.h" +#include "../iniFile/iniFile.h" +#include "RCONServer.h" +#include "Server.h" +#include "Root.h" +#include "CommandOutput.h" + + + + + +// Disable MSVC warnings: +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4355) // 'this' : used in base member initializer list +#endif + + + + + +enum +{ + // Client -> Server: + RCON_PACKET_COMMAND = 2, + RCON_PACKET_LOGIN = 3, + + // Server -> Client: + RCON_PACKET_RESPONSE = 2, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONCommandOutput: + +class cRCONCommandOutput : + public cCommandOutputCallback +{ +public: + cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) : + m_Connection(a_Connection), + m_RequestID(a_RequestID) + { + } + + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override + { + m_Buffer.append(a_Text); + } + + virtual void Finished(void) override + { + m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str()); + delete this; + } + +protected: + cRCONServer::cConnection & m_Connection; + int m_RequestID; + AString m_Buffer; +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONServer: + +cRCONServer::cRCONServer(cServer & a_Server) : + m_Server(a_Server), + m_ListenThread4(*this, cSocket::IPv4, "RCON"), + m_ListenThread6(*this, cSocket::IPv6, "RCON") +{ +} + + + + + +cRCONServer::~cRCONServer() +{ + m_ListenThread4.Stop(); + m_ListenThread6.Stop(); +} + + + + + +void cRCONServer::Initialize(cIniFile & a_IniFile) +{ + if (!a_IniFile.GetValueSetB("RCON", "Enabled", false)) + { + return; + } + + // Read the password, don't allow an empty one: + m_Password = a_IniFile.GetValueSet("RCON", "Password", ""); + if (m_Password.empty()) + { + LOGWARNING("RCON is requested, but the password is not set. RCON is now disabled."); + return; + } + + // Read and initialize both IPv4 and IPv6 ports for RCON + bool HasAnyPorts = false; + AString Ports4 = a_IniFile.GetValueSet("RCON", "PortsIPv4", "25575"); + if (m_ListenThread4.Initialize(Ports4)) + { + HasAnyPorts = true; + m_ListenThread4.Start(); + } + AString Ports6 = a_IniFile.GetValueSet("RCON", "PortsIPv6", "25575"); + if (m_ListenThread6.Initialize(Ports6)) + { + HasAnyPorts = true; + m_ListenThread6.Start(); + } + if (!HasAnyPorts) + { + LOGWARNING("RCON is requested, but no ports are specified. Specify at least one port in PortsIPv4 or PortsIPv6. RCON is now disabled."); + return; + } +} + + + + + +void cRCONServer::OnConnectionAccepted(cSocket & a_Socket) +{ + if (!a_Socket.IsValid()) + { + return; + } + + LOG("RCON Client \"%s\" connected!", a_Socket.GetIPString().c_str()); + + // Create a new cConnection object, it will be deleted when the connection is closed + m_SocketThreads.AddClient(a_Socket, new cConnection(*this, a_Socket)); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONServer::cConnection: + +cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket) : + m_IsAuthenticated(false), + m_RCONServer(a_RCONServer), + m_Socket(a_Socket), + m_IPAddress(a_Socket.GetIPString()) +{ +} + + + + + +void cRCONServer::cConnection::DataReceived(const char * a_Data, int a_Size) +{ + // Append data to the buffer: + m_Buffer.append(a_Data, a_Size); + + // Process the packets in the buffer: + while (m_Buffer.size() >= 14) + { + int Length = IntFromBuffer(m_Buffer.data()); + if (Length > 1500) + { + // Too long, drop the connection + LOGWARNING("Received an invalid RCON packet length (%d), dropping RCON connection to %s.", + Length, m_IPAddress.c_str() + ); + m_RCONServer.m_SocketThreads.RemoveClient(this); + m_Socket.CloseSocket(); + delete this; + return; + } + if (Length > (int)(m_Buffer.size() + 4)) + { + // Incomplete packet yet, wait for more data to come + return; + } + + int RequestID = IntFromBuffer(m_Buffer.data() + 4); + int PacketType = IntFromBuffer(m_Buffer.data() + 8); + if (!ProcessPacket(RequestID, PacketType, Length - 10, m_Buffer.data() + 12)) + { + m_RCONServer.m_SocketThreads.RemoveClient(this); + m_Socket.CloseSocket(); + delete this; + return; + } + m_Buffer.erase(0, Length + 4); + } // while (m_Buffer.size() >= 14) +} + + + + + +void cRCONServer::cConnection::GetOutgoingData(AString & a_Data) +{ + a_Data.assign(m_Outgoing); + m_Outgoing.clear(); +} + + + + + +void cRCONServer::cConnection::SocketClosed(void) +{ + m_RCONServer.m_SocketThreads.RemoveClient(this); + delete this; +} + + + + + +bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) +{ + switch (a_PacketType) + { + case RCON_PACKET_LOGIN: + { + if (strncmp(a_Payload, m_RCONServer.m_Password.c_str(), a_PayloadLength) != 0) + { + LOGINFO("RCON: Invalid password from client %s, dropping connection.", m_IPAddress.c_str()); + return false; + } + m_IsAuthenticated = true; + + LOGD("RCON: Client at %s has successfully authenticated", m_IPAddress.c_str()); + + // Send OK response: + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); + return true; + } + + case RCON_PACKET_COMMAND: + { + if (!m_IsAuthenticated) + { + char AuthNeeded[] = "You need to authenticate first!"; + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded); + return false; + } + + AString cmd(a_Payload, a_PayloadLength); + LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str()); + cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID))); + + // Send an empty response: + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); + return true; + } + } + + // Unknown packet type, drop the connection: + LOGWARNING("RCON: Client at %s has sent an unknown packet type %d, dropping connection.", + m_IPAddress.c_str(), a_PacketType + ); + return false; +} + + + + + +/// Reads 4 bytes from a_Buffer and returns the int they represent +int cRCONServer::cConnection::IntFromBuffer(const char * a_Buffer) +{ + return ((unsigned char)a_Buffer[3] << 24) | ((unsigned char)a_Buffer[2] << 16) | ((unsigned char)a_Buffer[1] << 8) | (unsigned char)a_Buffer[0]; +} + + + + + +/// Puts 4 bytes representing the int into the buffer +void cRCONServer::cConnection::IntToBuffer(int a_Value, char * a_Buffer) +{ + a_Buffer[0] = a_Value & 0xff; + a_Buffer[1] = (a_Value >> 8) & 0xff; + a_Buffer[2] = (a_Value >> 16) & 0xff; + a_Buffer[3] = (a_Value >> 24) & 0xff; +} + + + + + +/// Sends a RCON packet back to the client +void cRCONServer::cConnection::SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload) +{ + ASSERT((a_PayloadLength == 0) || (a_Payload != NULL)); // Either zero data to send, or a valid payload ptr + + char Buffer[4]; + int Length = a_PayloadLength + 10; + IntToBuffer(Length, Buffer); + m_Outgoing.append(Buffer, 4); + IntToBuffer(a_RequestID, Buffer); + m_Outgoing.append(Buffer, 4); + IntToBuffer(a_PacketType, Buffer); + m_Outgoing.append(Buffer, 4); + if (a_PayloadLength > 0) + { + m_Outgoing.append(a_Payload, a_PayloadLength); + } + m_Outgoing.push_back(0); + m_Outgoing.push_back(0); + m_RCONServer.m_SocketThreads.NotifyWrite(this); +} + + + + diff --git a/source/RCONServer.h b/source/RCONServer.h index a4a0d20fe..0e89800a2 100644 --- a/source/RCONServer.h +++ b/source/RCONServer.h @@ -1,109 +1,109 @@ - -// RCONServer.h - -// Declares the cRCONServer class representing the RCON server - - - - - -#pragma once - -#include "OSSupport/SocketThreads.h" -#include "OSSupport/ListenThread.h" - - - - - -// fwd: -class cServer; -class cIniFile; - - - - - -class cRCONServer : - public cListenThread::cCallback -{ -public: - cRCONServer(cServer & a_Server); - ~cRCONServer(); - - void Initialize(cIniFile & a_IniFile); - -protected: - friend class cRCONCommandOutput; - - class cConnection : - public cSocketThreads::cCallback - { - public: - cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); - - protected: - friend class cRCONCommandOutput; - - /// Set to true if the client has successfully authenticated - bool m_IsAuthenticated; - - /// Buffer for the incoming data - AString m_Buffer; - - /// Buffer for the outgoing data - AString m_Outgoing; - - /// Server that owns this connection and processes requests - cRCONServer & m_RCONServer; - - /// The socket belonging to the client - cSocket & m_Socket; - - /// Address of the client - AString m_IPAddress; - - - // cSocketThreads::cCallback overrides: - virtual void DataReceived(const char * a_Data, int a_Size) override; - virtual void GetOutgoingData(AString & a_Data) override; - virtual void SocketClosed(void) override; - - /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped - bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); - - /// Reads 4 bytes from a_Buffer and returns the int they represent - int IntFromBuffer(const char * a_Buffer); - - /// Puts 4 bytes representing the int into the buffer - void IntToBuffer(int a_Value, char * a_Buffer); - - /// Sends a RCON packet back to the client - void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); - } ; - - - /// The server object that will process the commands received - cServer & m_Server; - - /// The thread(s) that take care of all the traffic on the RCON ports - cSocketThreads m_SocketThreads; - - /// The thread for accepting IPv4 RCON connections - cListenThread m_ListenThread4; - - /// The thread for accepting IPv6 RCON connections - cListenThread m_ListenThread6; - - /// Password for authentication - AString m_Password; - - - // cListenThread::cCallback overrides: - virtual void OnConnectionAccepted(cSocket & a_Socket) override; -} ; - - - - - + +// RCONServer.h + +// Declares the cRCONServer class representing the RCON server + + + + + +#pragma once + +#include "OSSupport/SocketThreads.h" +#include "OSSupport/ListenThread.h" + + + + + +// fwd: +class cServer; +class cIniFile; + + + + + +class cRCONServer : + public cListenThread::cCallback +{ +public: + cRCONServer(cServer & a_Server); + ~cRCONServer(); + + void Initialize(cIniFile & a_IniFile); + +protected: + friend class cRCONCommandOutput; + + class cConnection : + public cSocketThreads::cCallback + { + public: + cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); + + protected: + friend class cRCONCommandOutput; + + /// Set to true if the client has successfully authenticated + bool m_IsAuthenticated; + + /// Buffer for the incoming data + AString m_Buffer; + + /// Buffer for the outgoing data + AString m_Outgoing; + + /// Server that owns this connection and processes requests + cRCONServer & m_RCONServer; + + /// The socket belonging to the client + cSocket & m_Socket; + + /// Address of the client + AString m_IPAddress; + + + // cSocketThreads::cCallback overrides: + virtual void DataReceived(const char * a_Data, int a_Size) override; + virtual void GetOutgoingData(AString & a_Data) override; + virtual void SocketClosed(void) override; + + /// Processes the given packet and sends the response; returns true if successful, false if the connection is to be dropped + bool ProcessPacket(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); + + /// Reads 4 bytes from a_Buffer and returns the int they represent + int IntFromBuffer(const char * a_Buffer); + + /// Puts 4 bytes representing the int into the buffer + void IntToBuffer(int a_Value, char * a_Buffer); + + /// Sends a RCON packet back to the client + void SendResponse(int a_RequestID, int a_PacketType, int a_PayloadLength, const char * a_Payload); + } ; + + + /// The server object that will process the commands received + cServer & m_Server; + + /// The thread(s) that take care of all the traffic on the RCON ports + cSocketThreads m_SocketThreads; + + /// The thread for accepting IPv4 RCON connections + cListenThread m_ListenThread4; + + /// The thread for accepting IPv6 RCON connections + cListenThread m_ListenThread6; + + /// Password for authentication + AString m_Password; + + + // cListenThread::cCallback overrides: + virtual void OnConnectionAccepted(cSocket & a_Socket) override; +} ; + + + + + diff --git a/source/SQLite/urls.txt b/source/SQLite/urls.txt index 8b5869be6..131d70bbf 100644 --- a/source/SQLite/urls.txt +++ b/source/SQLite/urls.txt @@ -1,9 +1,9 @@ - -SQLite: -http://www.sqlite.org -SQLite is in public domain - -LuaSQLite3: -http://lua.sqlite.org -http://lua.sqlite.org/index.cgi/doc/tip/doc/lsqlite3.wiki -- documentation + +SQLite: +http://www.sqlite.org +SQLite is in public domain + +LuaSQLite3: +http://lua.sqlite.org +http://lua.sqlite.org/index.cgi/doc/tip/doc/lsqlite3.wiki -- documentation License for LuaSQLite is stored in $/install/LuaSQLite3-LICENSE.txt and distributed with the executables \ No newline at end of file diff --git a/source/Simulator/DelayedFluidSimulator.cpp b/source/Simulator/DelayedFluidSimulator.cpp index 6cc982e3d..a4645ca09 100644 --- a/source/Simulator/DelayedFluidSimulator.cpp +++ b/source/Simulator/DelayedFluidSimulator.cpp @@ -1,158 +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(); - } -} - - - - + +// 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(); + } +} + + + + diff --git a/source/Simulator/DelayedFluidSimulator.h b/source/Simulator/DelayedFluidSimulator.h index 6f7433877..c81500741 100644 --- a/source/Simulator/DelayedFluidSimulator.h +++ b/source/Simulator/DelayedFluidSimulator.h @@ -1,82 +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; -} ; - - - - + +// 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; +} ; + + + + diff --git a/source/Simulator/FloodyFluidSimulator.cpp b/source/Simulator/FloodyFluidSimulator.cpp index f2cc068e8..9374bbab3 100644 --- a/source/Simulator/FloodyFluidSimulator.cpp +++ b/source/Simulator/FloodyFluidSimulator.cpp @@ -1,334 +1,334 @@ - -// 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); - - // TODO: Sound effect - - 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); - - // TODO: Sound effect - - 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; -} - - - - + +// 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); + + // TODO: Sound effect + + 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); + + // TODO: Sound effect + + 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; +} + + + + diff --git a/source/Simulator/FloodyFluidSimulator.h b/source/Simulator/FloodyFluidSimulator.h index e6ce38d61..c4af2e246 100644 --- a/source/Simulator/FloodyFluidSimulator.h +++ b/source/Simulator/FloodyFluidSimulator.h @@ -1,53 +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); -} ; - - - - + +// 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); +} ; + + + + diff --git a/source/Simulator/NoopFluidSimulator.h b/source/Simulator/NoopFluidSimulator.h index 9120fe868..8f894433f 100644 --- a/source/Simulator/NoopFluidSimulator.h +++ b/source/Simulator/NoopFluidSimulator.h @@ -1,36 +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 {} -} ; - - - - + +// 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 {} +} ; + + + + diff --git a/source/Simulator/VaporizeFluidSimulator.cpp b/source/Simulator/VaporizeFluidSimulator.cpp index 792783915..4206c64d1 100644 --- a/source/Simulator/VaporizeFluidSimulator.cpp +++ b/source/Simulator/VaporizeFluidSimulator.cpp @@ -1,53 +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 -} - - - - + +// 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 +} + + + + diff --git a/source/Simulator/VaporizeFluidSimulator.h b/source/Simulator/VaporizeFluidSimulator.h index c179c8ec4..c8eb7802b 100644 --- a/source/Simulator/VaporizeFluidSimulator.h +++ b/source/Simulator/VaporizeFluidSimulator.h @@ -1,34 +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; -} ; - - - - + +// 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; +} ; + + + + diff --git a/source/SquirrelCommandBinder.cpp b/source/SquirrelCommandBinder.cpp index 9ff8a43f9..66b456ace 100644 --- a/source/SquirrelCommandBinder.cpp +++ b/source/SquirrelCommandBinder.cpp @@ -1,118 +1,118 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - - - - - -#ifdef USE_SQUIRREL - - - - - -#include "SquirrelCommandBinder.h" -#include "Player.h" -#include "Plugin.h" -#include "Plugin_Squirrel.h" -#include "squirrelbindings/SquirrelArray.h" - - -cSquirrelCommandBinder::cSquirrelCommandBinder() -{ -} - -cSquirrelCommandBinder::~cSquirrelCommandBinder() -{ -} - -void cSquirrelCommandBinder::ClearBindings() -{ - m_BoundCommands.clear(); -} - -void cSquirrelCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin ) -{ - for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); ) - { - if( itr->second.Plugin == a_Plugin ) - { - LOGINFO("Unbinding %s ", itr->first.c_str( ) ); - CommandMap::iterator eraseme = itr; - ++itr; - m_BoundCommands.erase( eraseme ); - continue; - } - ++itr; - } -} - -bool cSquirrelCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback ) -{ - if( !a_Plugin->CanBindCommands() ) - { - LOGERROR("ERROR: Trying to bind command \"%s\" to a plugin that is not initialized.", a_Command.c_str() ); - return false; - } - if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() ) - { - LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() ); - return false; - } - LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() ); - - BoundFunction Callback; - Callback.Callback = a_Callback; - Callback.Plugin = a_Plugin; - Callback.Permission = a_Permission; - - m_BoundCommands[ a_Command ] = Callback; - return true; -} - -bool cSquirrelCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player ) -{ - AStringVector Split = StringSplit(a_Command, " "); - if (Split.size() == 0) - { - return false; - } - - CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] ); - if( FoundCommand != m_BoundCommands.end() ) - { - const BoundFunction & func = FoundCommand->second; - if( func.Permission.size() > 0 ) - { - if( !a_Player->HasPermission( func.Permission.c_str() ) ) - { - return false; - } - } - - - // Push the split - SquirrelStringArray SplitData; - - std::vector::const_iterator iter = Split.begin(); - while(iter != Split.end()) { - SplitData.Add(*iter); - ++iter; - } - - // Push player - Sqrat::Function callback = func.Callback; - return callback.Evaluate(&SplitData, a_Player); - } - return false; -} - - - - - -#endif // USE_SQUIRREL - - - - + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + + + + + +#ifdef USE_SQUIRREL + + + + + +#include "SquirrelCommandBinder.h" +#include "Player.h" +#include "Plugin.h" +#include "Plugin_Squirrel.h" +#include "squirrelbindings/SquirrelArray.h" + + +cSquirrelCommandBinder::cSquirrelCommandBinder() +{ +} + +cSquirrelCommandBinder::~cSquirrelCommandBinder() +{ +} + +void cSquirrelCommandBinder::ClearBindings() +{ + m_BoundCommands.clear(); +} + +void cSquirrelCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin ) +{ + for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); ) + { + if( itr->second.Plugin == a_Plugin ) + { + LOGINFO("Unbinding %s ", itr->first.c_str( ) ); + CommandMap::iterator eraseme = itr; + ++itr; + m_BoundCommands.erase( eraseme ); + continue; + } + ++itr; + } +} + +bool cSquirrelCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback ) +{ + if( !a_Plugin->CanBindCommands() ) + { + LOGERROR("ERROR: Trying to bind command \"%s\" to a plugin that is not initialized.", a_Command.c_str() ); + return false; + } + if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() ) + { + LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() ); + return false; + } + LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() ); + + BoundFunction Callback; + Callback.Callback = a_Callback; + Callback.Plugin = a_Plugin; + Callback.Permission = a_Permission; + + m_BoundCommands[ a_Command ] = Callback; + return true; +} + +bool cSquirrelCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player ) +{ + AStringVector Split = StringSplit(a_Command, " "); + if (Split.size() == 0) + { + return false; + } + + CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] ); + if( FoundCommand != m_BoundCommands.end() ) + { + const BoundFunction & func = FoundCommand->second; + if( func.Permission.size() > 0 ) + { + if( !a_Player->HasPermission( func.Permission.c_str() ) ) + { + return false; + } + } + + + // Push the split + SquirrelStringArray SplitData; + + std::vector::const_iterator iter = Split.begin(); + while(iter != Split.end()) { + SplitData.Add(*iter); + ++iter; + } + + // Push player + Sqrat::Function callback = func.Callback; + return callback.Evaluate(&SplitData, a_Player); + } + return false; +} + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/SquirrelCommandBinder.h b/source/SquirrelCommandBinder.h index c2bc4bafe..49e6fd003 100644 --- a/source/SquirrelCommandBinder.h +++ b/source/SquirrelCommandBinder.h @@ -1,51 +1,51 @@ - -#pragma once - - - - - -#ifdef USE_SQUIRREL - - - - - -#include - -class cPlugin; -class cPlayer; - -class cSquirrelCommandBinder -{ -public: - cSquirrelCommandBinder(); - ~cSquirrelCommandBinder(); - - bool HandleCommand( const std::string & a_Command, cPlayer* a_Player ); - - bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback); - - void ClearBindings(); - void RemoveBindingsForPlugin( cPlugin* a_Plugin ); -private: - struct BoundFunction - { - Sqrat::Function Callback; - cPlugin *Plugin; - std::string Permission; - }; - - typedef std::map< std::string, BoundFunction > CommandMap; - CommandMap m_BoundCommands; -}; - - - - - -#endif // USE_SQUIRREL - - - - + +#pragma once + + + + + +#ifdef USE_SQUIRREL + + + + + +#include + +class cPlugin; +class cPlayer; + +class cSquirrelCommandBinder +{ +public: + cSquirrelCommandBinder(); + ~cSquirrelCommandBinder(); + + bool HandleCommand( const std::string & a_Command, cPlayer* a_Player ); + + bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback); + + void ClearBindings(); + void RemoveBindingsForPlugin( cPlugin* a_Plugin ); +private: + struct BoundFunction + { + Sqrat::Function Callback; + cPlugin *Plugin; + std::string Permission; + }; + + typedef std::map< std::string, BoundFunction > CommandMap; + CommandMap m_BoundCommands; +}; + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/TNTEntity.cpp b/source/TNTEntity.cpp index 22f343e88..e431464b3 100644 --- a/source/TNTEntity.cpp +++ b/source/TNTEntity.cpp @@ -1,72 +1,72 @@ -#include "Globals.h" - -#include "TNTEntity.h" -#include "World.h" -#include "ClientHandle.h" - - - - - -cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, float a_FuseTimeInSec) : - super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), - m_Counter(0), - m_MaxFuseTime(a_FuseTimeInSec) -{ -} - - - - - -cTNTEntity::cTNTEntity(const Vector3d & a_Pos, float a_FuseTimeInSec) : - super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), - m_Counter(0), - m_MaxFuseTime(a_FuseTimeInSec) -{ -} - - - - -void cTNTEntity::Initialize(cWorld * a_World) -{ - super::Initialize(a_World); - a_World->BroadcastSpawnEntity(*this); -} - - - - - -void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle) -{ - a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT - m_bDirtyPosition = false; - m_bDirtySpeed = false; - m_bDirtyOrientation = false; - m_bDirtyHead = false; -} - - - - - -void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - BroadcastMovementUpdate(); - float delta_time = a_Dt / 1000; // Convert miliseconds to seconds - m_Counter += delta_time; - if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM - { - Destroy(true); - LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ()); - m_World->DoExplosiontAt(4.0, (int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())); - return; - } -} - - - - +#include "Globals.h" + +#include "TNTEntity.h" +#include "World.h" +#include "ClientHandle.h" + + + + + +cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, float a_FuseTimeInSec) : + super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), + m_Counter(0), + m_MaxFuseTime(a_FuseTimeInSec) +{ +} + + + + + +cTNTEntity::cTNTEntity(const Vector3d & a_Pos, float a_FuseTimeInSec) : + super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), + m_Counter(0), + m_MaxFuseTime(a_FuseTimeInSec) +{ +} + + + + +void cTNTEntity::Initialize(cWorld * a_World) +{ + super::Initialize(a_World); + a_World->BroadcastSpawnEntity(*this); +} + + + + + +void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle) +{ + a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT + m_bDirtyPosition = false; + m_bDirtySpeed = false; + m_bDirtyOrientation = false; + m_bDirtyHead = false; +} + + + + + +void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + BroadcastMovementUpdate(); + float delta_time = a_Dt / 1000; // Convert miliseconds to seconds + m_Counter += delta_time; + if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM + { + Destroy(true); + LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ()); + m_World->DoExplosiontAt(4.0, (int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())); + return; + } +} + + + + diff --git a/source/TNTEntity.h b/source/TNTEntity.h index d20b6aca4..5dbfd2d43 100644 --- a/source/TNTEntity.h +++ b/source/TNTEntity.h @@ -1,34 +1,34 @@ - -#pragma once - -#include "Entity.h" -#include "Defines.h" - - - - - -class cTNTEntity : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cTNTEntity); - - cTNTEntity(double a_X, double a_Y, double a_Z, float a_FuseTimeInSec); - cTNTEntity(const Vector3d & a_Pos, float a_FuseTimeInSec); - - // cEntity overrides: - virtual void Initialize(cWorld * a_World) override; - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - -protected: - float m_Counter; ///< How much time has elapsed since the object was created, in seconds - float m_MaxFuseTime; ///< How long the fuse is, in seconds -}; - - - - + +#pragma once + +#include "Entity.h" +#include "Defines.h" + + + + + +class cTNTEntity : + public cEntity +{ + typedef cEntity super; + +public: + CLASS_PROTODEF(cTNTEntity); + + cTNTEntity(double a_X, double a_Y, double a_Z, float a_FuseTimeInSec); + cTNTEntity(const Vector3d & a_Pos, float a_FuseTimeInSec); + + // cEntity overrides: + virtual void Initialize(cWorld * a_World) override; + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +protected: + float m_Counter; ///< How much time has elapsed since the object was created, in seconds + float m_MaxFuseTime; ///< How long the fuse is, in seconds +}; + + + + diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp index fc3f248f5..dddb20885 100644 --- a/source/UI/SlotArea.cpp +++ b/source/UI/SlotArea.cpp @@ -1,815 +1,815 @@ - -// SlotArea.cpp - -// Implements the cSlotArea class and its descendants - -#include "Globals.h" -#include "SlotArea.h" -#include "../Player.h" -#include "../BlockEntities/ChestEntity.h" -#include "../BlockEntities/DropSpenserEntity.h" -#include "../BlockEntities/FurnaceEntity.h" -#include "../Items/ItemHandler.h" -#include "Window.h" -#include "../CraftingRecipes.h" -#include "../Root.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotArea: - -cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) : - m_NumSlots(a_NumSlots), - m_ParentWindow(a_ParentWindow) -{ -} - - - - - -void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) -{ - /* - LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s", - GetNumSlots(), a_SlotNum, - ItemToFullString(a_ClickedItem).c_str(), - ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str() - ); - */ - - ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots())); - - bool bAsync = false; - if (GetSlot(a_SlotNum, a_Player) == NULL) - { - LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum); - return; - } - - if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) - { - if (!a_Player.IsDraggingItem()) - { - ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); - return; - } - LOGD("Shift clicked, but the player is dragging an item: %s", ItemToFullString(a_Player.GetDraggingItem()).c_str()); - return; - } - - cItem Slot(*GetSlot(a_SlotNum, a_Player)); - if (!Slot.IsSameType(a_ClickedItem)) - { - LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots); - LOGWARNING("My item: %s", ItemToFullString(Slot).c_str()); - LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str()); - bAsync = true; - } - cItem & DraggingItem = a_Player.GetDraggingItem(); - switch (a_ClickAction) - { - case caRightClick: - { - if (DraggingItem.m_ItemType <= 0) // Empty-handed? - { - DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f); - Slot.m_ItemCount -= DraggingItem.m_ItemCount; - DraggingItem.m_ItemType = Slot.m_ItemType; - DraggingItem.m_ItemDamage = Slot.m_ItemDamage; - - if (Slot.m_ItemCount <= 0) - { - Slot.Empty(); - } - } - else if ((Slot.m_ItemType <= 0) || DraggingItem.IsEqual(Slot)) - { - // Drop one item in slot - cItemHandler * Handler = ItemHandler(Slot.m_ItemType); - if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize())) - { - Slot.m_ItemType = DraggingItem.m_ItemType; - Slot.m_ItemCount++; - Slot.m_ItemDamage = DraggingItem.m_ItemDamage; - DraggingItem.m_ItemCount--; - } - if (DraggingItem.m_ItemCount <= 0) - { - DraggingItem.Empty(); - } - } - else if (!DraggingItem.IsEqual(Slot)) - { - // Swap contents - cItem tmp(DraggingItem); - DraggingItem = Slot; - Slot = tmp; - } - break; - } - - case caLeftClick: - { - // Left-clicked - if (!DraggingItem.IsEqual(Slot)) - { - // Switch contents - cItem tmp(DraggingItem); - DraggingItem = Slot; - Slot = tmp; - } - else - { - // Same type, add items: - cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemType); - int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount; - if (FreeSlots < 0) - { - ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?"); - FreeSlots = 0; - } - int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots; - Slot.m_ItemCount += (char)Filling; - DraggingItem.m_ItemCount -= (char)Filling; - if (DraggingItem.m_ItemCount <= 0) - { - DraggingItem.Empty(); - } - } - break; - } - default: - { - LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction)); - m_ParentWindow.BroadcastWholeWindow(); - return; - } - } // switch (a_ClickAction - - SetSlot(a_SlotNum, a_Player, Slot); - if (bAsync) - { - m_ParentWindow.BroadcastWholeWindow(); - } - -} - - - - - -void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem) -{ - // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover: - cItem Slot(*GetSlot(a_SlotNum, a_Player)); - m_ParentWindow.DistributeStack(Slot, a_Player, this, true); - if (Slot.IsEmpty()) - { - // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count - Slot.Empty(); - } - SetSlot(a_SlotNum, a_Player, Slot); - - // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them: - m_ParentWindow.BroadcastWholeWindow(); -} - - - - - -void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots) -{ - for (int i = 0; i < m_NumSlots; i++) - { - const cItem * Slot = GetSlot(i, a_Player); - if (!Slot->IsSameType(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) - { - // Different items - continue; - } - int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; - if (NumFit <= 0) - { - // Full stack already - continue; - } - if (NumFit > a_ItemStack.m_ItemCount) - { - NumFit = a_ItemStack.m_ItemCount; - } - if (a_Apply) - { - cItem NewSlot(a_ItemStack); - NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; - SetSlot(i, a_Player, NewSlot); - } - a_ItemStack.m_ItemCount -= NumFit; - if (a_ItemStack.IsEmpty()) - { - return; - } - } // for i - Slots -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotAreaChest: - -cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) : - cSlotArea(27, a_ParentWindow), - m_Chest(a_Chest) -{ -} - - - - - -const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const -{ - // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly: - return &(m_Chest->GetSlot(a_SlotNum)); -} - - - - - -void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) -{ - m_Chest->SetSlot(a_SlotNum, a_Item); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotAreaDoubleChest: - -cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) : - cSlotArea(54, a_ParentWindow), - m_TopChest(a_TopChest), - m_BottomChest(a_BottomChest) -{ -} - - - - - -const cItem * cSlotAreaDoubleChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const -{ - // a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory: - if (a_SlotNum < 27) - { - return &(m_TopChest->GetSlot(a_SlotNum)); - } - else - { - return &(m_BottomChest->GetSlot(a_SlotNum - 27)); - } -} - - - - - -void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) -{ - if (a_SlotNum < 27) - { - m_TopChest->SetSlot(a_SlotNum, a_Item); - } - else - { - m_BottomChest->SetSlot(a_SlotNum - 27, a_Item); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotAreaCrafting: - -cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) : - cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow), - m_GridSize(a_GridSize) -{ - ASSERT((a_GridSize == 2) || (a_GridSize == 3)); -} - - - - - -void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) -{ - // Override for craft result slot - if (a_SlotNum == 0) - { - if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) - { - ShiftClickedResult(a_Player); - } - else - { - ClickedResult(a_Player); - } - return; - } - super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); - UpdateRecipe(a_Player); -} - - - - - -void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player) -{ - // Toss all items on the crafting grid: - TossItems(a_Player, 1, m_NumSlots); - - // Remove the current recipe from the player -> recipe map: - for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr) - { - if (itr->first == a_Player.GetUniqueID()) - { - // Remove the player from the recipe map: - m_Recipes.erase(itr); - return; - } - } // for itr - m_Recipes[] - // Player not found - that is acceptable -} - - - - - -void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) -{ - const cItem * ResultSlot = GetSlot(0, a_Player); - cItem & DraggingItem = a_Player.GetDraggingItem(); - - // Get the current recipe: - cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); - - cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; - cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); - - // If possible, craft: - if (DraggingItem.IsEmpty()) - { - DraggingItem = Recipe.GetResult(); - Recipe.ConsumeIngredients(Grid); - Grid.CopyToItems(PlayerSlots); - } - else if (DraggingItem.IsEqual(Recipe.GetResult())) - { - cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemType); - if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize()) - { - DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount; - Recipe.ConsumeIngredients(Grid); - Grid.CopyToItems(PlayerSlots); - } - } - - // Get the new recipe and update the result slot: - UpdateRecipe(a_Player); - - // We're done. Send all changes to the client and bail out: - m_ParentWindow.BroadcastWholeWindow(); -} - - - - - -void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) -{ - cItem Result(*GetSlot(0, a_Player)); - if (Result.IsEmpty()) - { - return; - } - cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; - do - { - // Try distributing the result. If it fails, bail out: - cItem ResultCopy(Result); - m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false); - if (!ResultCopy.IsEmpty()) - { - // Couldn't distribute all of it. Bail out - return; - } - - // Distribute the result, this time for real: - ResultCopy = Result; - m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true); - - // Remove the ingredients from the crafting grid and update the recipe: - cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); - cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); - Recipe.ConsumeIngredients(Grid); - Grid.CopyToItems(PlayerSlots); - UpdateRecipe(a_Player); - if (!Recipe.GetResult().IsEqual(Result)) - { - // The recipe has changed, bail out - return; - } - } while (true); -} - - - - - -void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player) -{ - cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); - cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); - cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe); - SetSlot(0, a_Player, Recipe.GetResult()); - m_ParentWindow.SendSlot(a_Player, this, 0); -} - - - - - -cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player) -{ - for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr) - { - if (itr->first == a_Player.GetUniqueID()) - { - return itr->second; - } - } // for itr - m_Recipes[] - - // Not found. Add a new one: - cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); - cCraftingRecipe Recipe(Grid); - cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe); - m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe)); - return m_Recipes.back().second; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotAreaFurnace: - -cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) : - cSlotArea(3, a_ParentWindow), - m_Furnace(a_Furnace) -{ - m_Furnace->GetContents().AddListener(*this); -} - - - - - -cSlotAreaFurnace::~cSlotAreaFurnace() -{ - m_Furnace->GetContents().RemoveListener(*this); -} - - - - - -void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) -{ - super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); - - if (m_Furnace == NULL) - { - LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); - ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); - return; - } -} - - - - - -const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const -{ - // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace: - return &(m_Furnace->GetSlot(a_SlotNum)); -} - - - - - -void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) -{ - m_Furnace->SetSlot(a_SlotNum, a_Item); -} - - - - - -void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) -{ - // Something has changed in the window, broadcast the entire window to all clients - ASSERT(a_ItemGrid == &(m_Furnace->GetContents())); - - m_ParentWindow.BroadcastWholeWindow(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotAreaInventoryBase: - -cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) : - cSlotArea(a_NumSlots, a_ParentWindow), - m_SlotOffset(a_SlotOffset) -{ -} - - - - - -void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) -{ - if ((a_Player.GetGameMode() == eGameMode_Creative) && (m_ParentWindow.GetWindowType() == cWindow::Inventory)) - { - // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it - SetSlot(a_SlotNum, a_Player, a_ClickedItem); - return; - } - - // Survival inventory and all other windows' inventory has the same handling as normal slot areas - super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); - return; -} - - - - - -const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player) const -{ - // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset - return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset); -} - - - - - -void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) -{ - a_Player.GetInventory().SetSlot(a_SlotNum + m_SlotOffset, a_Item); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotAreaArmor: - -void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) -{ - if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty()) - { - if (a_ShouldApply) - { - SetSlot(0, a_Player, a_ItemStack.CopyOne()); - } - a_ItemStack.m_ItemCount -= 1; - } - else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty()) - { - if (a_ShouldApply) - { - SetSlot(1, a_Player, a_ItemStack.CopyOne()); - } - a_ItemStack.m_ItemCount -= 1; - } - else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty()) - { - if (a_ShouldApply) - { - SetSlot(2, a_Player, a_ItemStack.CopyOne()); - } - a_ItemStack.m_ItemCount -= 1; - } - else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty()) - { - if (a_ShouldApply) - { - SetSlot(3, a_Player, a_ItemStack.CopyOne()); - } - a_ItemStack.m_ItemCount -= 1; - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotAreaItemGrid: - -cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) : - super(a_ItemGrid.GetNumSlots(), a_ParentWindow), - m_ItemGrid(a_ItemGrid) -{ - m_ItemGrid.AddListener(*this); -} - - - - - -cSlotAreaItemGrid::~cSlotAreaItemGrid() -{ - m_ItemGrid.RemoveListener(*this); -} - - - - - -const cItem * cSlotAreaItemGrid::GetSlot(int a_SlotNum, cPlayer & a_Player) const -{ - return &m_ItemGrid.GetSlot(a_SlotNum); -} - - - - - -void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) -{ - m_ItemGrid.SetSlot(a_SlotNum, a_Item); -} - - - - - -void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) -{ - ASSERT(a_ItemGrid == &m_ItemGrid); - m_ParentWindow.BroadcastSlot(this, a_SlotNum); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSlotAreaTemporary: - -cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) : - cSlotArea(a_NumSlots, a_ParentWindow) -{ -} - - - - - -const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) const -{ - cItemMap::const_iterator itr = m_Items.find(a_Player.GetUniqueID()); - if (itr == m_Items.end()) - { - LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum); - ASSERT(!"cSlotAreaTemporary: player not found!"); - - // Player not found, this should not happen, ever! Return NULL, but things may break by this. - return NULL; - } - - if (a_SlotNum >= (int)(itr->second.size())) - { - LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!"); - ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!"); - return NULL; - } - - return &(itr->second[a_SlotNum]); -} - - - - - -void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) -{ - cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); - if (itr == m_Items.end()) - { - // Player not found - LOGWARNING("cSlotAreaTemporary: player not found!"); - return; - } - - if (a_SlotNum >= (int)(itr->second.size())) - { - LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!"); - return; - } - - itr->second[a_SlotNum] = a_Item; -} - - - - - -void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player) -{ - ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak - m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items -} - - - - - -void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player) -{ - cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); - ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched - m_Items.erase(itr); -} - - - - - -void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End) -{ - cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); - if (itr == m_Items.end()) - { - LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str()); - return; - } - - cItems Drops; - for (int i = a_Begin; i < a_End; i++) - { - cItem & Item = itr->second[i]; - if (!Item.IsEmpty()) - { - Drops.push_back(Item); - } - Item.Empty(); - } // for i - itr->second[] - - double vX = 0, vY = 0, vZ = 0; - EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY); - vY = -vY * 2 + 1.f; - a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2); -} - - - - - -cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player) -{ - cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); - if (itr == m_Items.end()) - { - return NULL; - } - return &(itr->second[0]); -} - - - - + +// SlotArea.cpp + +// Implements the cSlotArea class and its descendants + +#include "Globals.h" +#include "SlotArea.h" +#include "../Player.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DropSpenserEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../Items/ItemHandler.h" +#include "Window.h" +#include "../CraftingRecipes.h" +#include "../Root.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotArea: + +cSlotArea::cSlotArea(int a_NumSlots, cWindow & a_ParentWindow) : + m_NumSlots(a_NumSlots), + m_ParentWindow(a_ParentWindow) +{ +} + + + + + +void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + /* + LOGD("Slot area with %d slots clicked at slot number %d, clicked item %s, slot item %s", + GetNumSlots(), a_SlotNum, + ItemToFullString(a_ClickedItem).c_str(), + ItemToFullString(*GetSlot(a_SlotNum, a_Player)).c_str() + ); + */ + + ASSERT((a_SlotNum >= 0) && (a_SlotNum < GetNumSlots())); + + bool bAsync = false; + if (GetSlot(a_SlotNum, a_Player) == NULL) + { + LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum); + return; + } + + if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) + { + if (!a_Player.IsDraggingItem()) + { + ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); + return; + } + LOGD("Shift clicked, but the player is dragging an item: %s", ItemToFullString(a_Player.GetDraggingItem()).c_str()); + return; + } + + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + if (!Slot.IsSameType(a_ClickedItem)) + { + LOGWARNING("*** Window lost sync at item %d in SlotArea with %d items ***", a_SlotNum, m_NumSlots); + LOGWARNING("My item: %s", ItemToFullString(Slot).c_str()); + LOGWARNING("Their item: %s", ItemToFullString(a_ClickedItem).c_str()); + bAsync = true; + } + cItem & DraggingItem = a_Player.GetDraggingItem(); + switch (a_ClickAction) + { + case caRightClick: + { + if (DraggingItem.m_ItemType <= 0) // Empty-handed? + { + DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f); + Slot.m_ItemCount -= DraggingItem.m_ItemCount; + DraggingItem.m_ItemType = Slot.m_ItemType; + DraggingItem.m_ItemDamage = Slot.m_ItemDamage; + + if (Slot.m_ItemCount <= 0) + { + Slot.Empty(); + } + } + else if ((Slot.m_ItemType <= 0) || DraggingItem.IsEqual(Slot)) + { + // Drop one item in slot + cItemHandler * Handler = ItemHandler(Slot.m_ItemType); + if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize())) + { + Slot.m_ItemType = DraggingItem.m_ItemType; + Slot.m_ItemCount++; + Slot.m_ItemDamage = DraggingItem.m_ItemDamage; + DraggingItem.m_ItemCount--; + } + if (DraggingItem.m_ItemCount <= 0) + { + DraggingItem.Empty(); + } + } + else if (!DraggingItem.IsEqual(Slot)) + { + // Swap contents + cItem tmp(DraggingItem); + DraggingItem = Slot; + Slot = tmp; + } + break; + } + + case caLeftClick: + { + // Left-clicked + if (!DraggingItem.IsEqual(Slot)) + { + // Switch contents + cItem tmp(DraggingItem); + DraggingItem = Slot; + Slot = tmp; + } + else + { + // Same type, add items: + cItemHandler * Handler = ItemHandler(DraggingItem.m_ItemType); + int FreeSlots = Handler->GetMaxStackSize() - Slot.m_ItemCount; + if (FreeSlots < 0) + { + ASSERT(!"Bad item stack size - where did we get more items in a slot than allowed?"); + FreeSlots = 0; + } + int Filling = (FreeSlots > DraggingItem.m_ItemCount) ? DraggingItem.m_ItemCount : FreeSlots; + Slot.m_ItemCount += (char)Filling; + DraggingItem.m_ItemCount -= (char)Filling; + if (DraggingItem.m_ItemCount <= 0) + { + DraggingItem.Empty(); + } + } + break; + } + default: + { + LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction)); + m_ParentWindow.BroadcastWholeWindow(); + return; + } + } // switch (a_ClickAction + + SetSlot(a_SlotNum, a_Player, Slot); + if (bAsync) + { + m_ParentWindow.BroadcastWholeWindow(); + } + +} + + + + + +void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem) +{ + // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover: + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + m_ParentWindow.DistributeStack(Slot, a_Player, this, true); + if (Slot.IsEmpty()) + { + // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count + Slot.Empty(); + } + SetSlot(a_SlotNum, a_Player, Slot); + + // Some clients try to guess our actions and not always right (armor slots in 1.2.5), so we fix them: + m_ParentWindow.BroadcastWholeWindow(); +} + + + + + +void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots) +{ + for (int i = 0; i < m_NumSlots; i++) + { + const cItem * Slot = GetSlot(i, a_Player); + if (!Slot->IsSameType(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) + { + // Different items + continue; + } + int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; + if (NumFit <= 0) + { + // Full stack already + continue; + } + if (NumFit > a_ItemStack.m_ItemCount) + { + NumFit = a_ItemStack.m_ItemCount; + } + if (a_Apply) + { + cItem NewSlot(a_ItemStack); + NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; + SetSlot(i, a_Player, NewSlot); + } + a_ItemStack.m_ItemCount -= NumFit; + if (a_ItemStack.IsEmpty()) + { + return; + } + } // for i - Slots +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaChest: + +cSlotAreaChest::cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow) : + cSlotArea(27, a_ParentWindow), + m_Chest(a_Chest) +{ +} + + + + + +const cItem * cSlotAreaChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly: + return &(m_Chest->GetSlot(a_SlotNum)); +} + + + + + +void cSlotAreaChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_Chest->SetSlot(a_SlotNum, a_Item); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaDoubleChest: + +cSlotAreaDoubleChest::cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow) : + cSlotArea(54, a_ParentWindow), + m_TopChest(a_TopChest), + m_BottomChest(a_BottomChest) +{ +} + + + + + +const cItem * cSlotAreaDoubleChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 53, use that to index the correct chest's inventory: + if (a_SlotNum < 27) + { + return &(m_TopChest->GetSlot(a_SlotNum)); + } + else + { + return &(m_BottomChest->GetSlot(a_SlotNum - 27)); + } +} + + + + + +void cSlotAreaDoubleChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + if (a_SlotNum < 27) + { + m_TopChest->SetSlot(a_SlotNum, a_Item); + } + else + { + m_BottomChest->SetSlot(a_SlotNum - 27, a_Item); + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaCrafting: + +cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) : + cSlotAreaTemporary(1 + a_GridSize * a_GridSize, a_ParentWindow), + m_GridSize(a_GridSize) +{ + ASSERT((a_GridSize == 2) || (a_GridSize == 3)); +} + + + + + +void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + // Override for craft result slot + if (a_SlotNum == 0) + { + if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) + { + ShiftClickedResult(a_Player); + } + else + { + ClickedResult(a_Player); + } + return; + } + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); + UpdateRecipe(a_Player); +} + + + + + +void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player) +{ + // Toss all items on the crafting grid: + TossItems(a_Player, 1, m_NumSlots); + + // Remove the current recipe from the player -> recipe map: + for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr) + { + if (itr->first == a_Player.GetUniqueID()) + { + // Remove the player from the recipe map: + m_Recipes.erase(itr); + return; + } + } // for itr - m_Recipes[] + // Player not found - that is acceptable +} + + + + + +void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) +{ + const cItem * ResultSlot = GetSlot(0, a_Player); + cItem & DraggingItem = a_Player.GetDraggingItem(); + + // Get the current recipe: + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + + cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; + cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); + + // If possible, craft: + if (DraggingItem.IsEmpty()) + { + DraggingItem = Recipe.GetResult(); + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + } + else if (DraggingItem.IsEqual(Recipe.GetResult())) + { + cItemHandler * Handler = ItemHandler(Recipe.GetResult().m_ItemType); + if (DraggingItem.m_ItemCount + Recipe.GetResult().m_ItemCount <= Handler->GetMaxStackSize()) + { + DraggingItem.m_ItemCount += Recipe.GetResult().m_ItemCount; + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + } + } + + // Get the new recipe and update the result slot: + UpdateRecipe(a_Player); + + // We're done. Send all changes to the client and bail out: + m_ParentWindow.BroadcastWholeWindow(); +} + + + + + +void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) +{ + cItem Result(*GetSlot(0, a_Player)); + if (Result.IsEmpty()) + { + return; + } + cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; + do + { + // Try distributing the result. If it fails, bail out: + cItem ResultCopy(Result); + m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false); + if (!ResultCopy.IsEmpty()) + { + // Couldn't distribute all of it. Bail out + return; + } + + // Distribute the result, this time for real: + ResultCopy = Result; + m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true); + + // Remove the ingredients from the crafting grid and update the recipe: + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + UpdateRecipe(a_Player); + if (!Recipe.GetResult().IsEqual(Result)) + { + // The recipe has changed, bail out + return; + } + } while (true); +} + + + + + +void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player) +{ + cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe); + SetSlot(0, a_Player, Recipe.GetResult()); + m_ParentWindow.SendSlot(a_Player, this, 0); +} + + + + + +cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player) +{ + for (cRecipeMap::iterator itr = m_Recipes.begin(), end = m_Recipes.end(); itr != end; ++itr) + { + if (itr->first == a_Player.GetUniqueID()) + { + return itr->second; + } + } // for itr - m_Recipes[] + + // Not found. Add a new one: + cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); + cCraftingRecipe Recipe(Grid); + cRoot::Get()->GetCraftingRecipes()->GetRecipe(&a_Player, Grid, Recipe); + m_Recipes.push_back(std::make_pair(a_Player.GetUniqueID(), Recipe)); + return m_Recipes.back().second; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaFurnace: + +cSlotAreaFurnace::cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow) : + cSlotArea(3, a_ParentWindow), + m_Furnace(a_Furnace) +{ + m_Furnace->GetContents().AddListener(*this); +} + + + + + +cSlotAreaFurnace::~cSlotAreaFurnace() +{ + m_Furnace->GetContents().RemoveListener(*this); +} + + + + + +void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); + + if (m_Furnace == NULL) + { + LOGERROR("cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); + ASSERT(!"cSlotAreaFurnace::Clicked(): m_Furnace == NULL"); + return; + } +} + + + + + +const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 2, query the items from the underlying furnace: + return &(m_Furnace->GetSlot(a_SlotNum)); +} + + + + + +void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_Furnace->SetSlot(a_SlotNum, a_Item); +} + + + + + +void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + // Something has changed in the window, broadcast the entire window to all clients + ASSERT(a_ItemGrid == &(m_Furnace->GetContents())); + + m_ParentWindow.BroadcastWholeWindow(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaInventoryBase: + +cSlotAreaInventoryBase::cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow) : + cSlotArea(a_NumSlots, a_ParentWindow), + m_SlotOffset(a_SlotOffset) +{ +} + + + + + +void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) +{ + if ((a_Player.GetGameMode() == eGameMode_Creative) && (m_ParentWindow.GetWindowType() == cWindow::Inventory)) + { + // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it + SetSlot(a_SlotNum, a_Player, a_ClickedItem); + return; + } + + // Survival inventory and all other windows' inventory has the same handling as normal slot areas + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); + return; +} + + + + + +const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + // a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset + return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset); +} + + + + + +void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + a_Player.GetInventory().SetSlot(a_SlotNum + m_SlotOffset, a_Item); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaArmor: + +void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +{ + if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty()) + { + if (a_ShouldApply) + { + SetSlot(0, a_Player, a_ItemStack.CopyOne()); + } + a_ItemStack.m_ItemCount -= 1; + } + else if (ItemCategory::IsChestPlate(a_ItemStack.m_ItemType) && GetSlot(1, a_Player)->IsEmpty()) + { + if (a_ShouldApply) + { + SetSlot(1, a_Player, a_ItemStack.CopyOne()); + } + a_ItemStack.m_ItemCount -= 1; + } + else if (ItemCategory::IsLeggings(a_ItemStack.m_ItemType) && GetSlot(2, a_Player)->IsEmpty()) + { + if (a_ShouldApply) + { + SetSlot(2, a_Player, a_ItemStack.CopyOne()); + } + a_ItemStack.m_ItemCount -= 1; + } + else if (ItemCategory::IsBoots(a_ItemStack.m_ItemType) && GetSlot(3, a_Player)->IsEmpty()) + { + if (a_ShouldApply) + { + SetSlot(3, a_Player, a_ItemStack.CopyOne()); + } + a_ItemStack.m_ItemCount -= 1; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaItemGrid: + +cSlotAreaItemGrid::cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow) : + super(a_ItemGrid.GetNumSlots(), a_ParentWindow), + m_ItemGrid(a_ItemGrid) +{ + m_ItemGrid.AddListener(*this); +} + + + + + +cSlotAreaItemGrid::~cSlotAreaItemGrid() +{ + m_ItemGrid.RemoveListener(*this); +} + + + + + +const cItem * cSlotAreaItemGrid::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + return &m_ItemGrid.GetSlot(a_SlotNum); +} + + + + + +void cSlotAreaItemGrid::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + m_ItemGrid.SetSlot(a_SlotNum, a_Item); +} + + + + + +void cSlotAreaItemGrid::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + ASSERT(a_ItemGrid == &m_ItemGrid); + m_ParentWindow.BroadcastSlot(this, a_SlotNum); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSlotAreaTemporary: + +cSlotAreaTemporary::cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow) : + cSlotArea(a_NumSlots, a_ParentWindow) +{ +} + + + + + +const cItem * cSlotAreaTemporary::GetSlot(int a_SlotNum, cPlayer & a_Player) const +{ + cItemMap::const_iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + LOGERROR("cSlotAreaTemporary: player \"%s\" not found for slot %d!", a_Player.GetName().c_str(), a_SlotNum); + ASSERT(!"cSlotAreaTemporary: player not found!"); + + // Player not found, this should not happen, ever! Return NULL, but things may break by this. + return NULL; + } + + if (a_SlotNum >= (int)(itr->second.size())) + { + LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!"); + ASSERT(!"cSlotAreaTemporary: asking for more slots than actually stored!"); + return NULL; + } + + return &(itr->second[a_SlotNum]); +} + + + + + +void cSlotAreaTemporary::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + // Player not found + LOGWARNING("cSlotAreaTemporary: player not found!"); + return; + } + + if (a_SlotNum >= (int)(itr->second.size())) + { + LOGERROR("cSlotAreaTemporary: asking for more slots than actually stored!"); + return; + } + + itr->second[a_SlotNum] = a_Item; +} + + + + + +void cSlotAreaTemporary::OnPlayerAdded(cPlayer & a_Player) +{ + ASSERT(m_Items.find(a_Player.GetUniqueID()) == m_Items.end()); // The player shouldn't be in the itemmap, otherwise we probably have a leak + m_Items[a_Player.GetUniqueID()].resize(m_NumSlots); // Make the vector the specified size of empty items +} + + + + + +void cSlotAreaTemporary::OnPlayerRemoved(cPlayer & a_Player) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + ASSERT(itr != m_Items.end()); // The player should be in the list, otherwise a call to OnPlayerAdded() was mismatched + m_Items.erase(itr); +} + + + + + +void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + LOGWARNING("Player tossing items (%s) not found in the item map", a_Player.GetName().c_str()); + return; + } + + cItems Drops; + for (int i = a_Begin; i < a_End; i++) + { + cItem & Item = itr->second[i]; + if (!Item.IsEmpty()) + { + Drops.push_back(Item); + } + Item.Empty(); + } // for i - itr->second[] + + double vX = 0, vY = 0, vZ = 0; + EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY); + vY = -vY * 2 + 1.f; + a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 2, vY * 2, vZ * 2); +} + + + + + +cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player) +{ + cItemMap::iterator itr = m_Items.find(a_Player.GetUniqueID()); + if (itr == m_Items.end()) + { + return NULL; + } + return &(itr->second[0]); +} + + + + diff --git a/source/UI/SlotArea.h b/source/UI/SlotArea.h index 516e6d644..943452feb 100644 --- a/source/UI/SlotArea.h +++ b/source/UI/SlotArea.h @@ -1,303 +1,303 @@ - -// SlotArea.h - -// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window - - - - -#pragma once - -#include "../Inventory.h" - - - -class cWindow; -class cPlayer; -class cChestEntity; -class cDropSpenserEntity; -class cFurnaceEntity; -class cCraftingRecipe; - - - - - -class cSlotArea -{ -public: - cSlotArea(int a_NumSlots, cWindow & a_ParentWindow); - virtual ~cSlotArea() {} // force a virtual destructor in all subclasses - - int GetNumSlots(void) const { return m_NumSlots; } - - /// Called to retrieve an item in the specified slot for the specified player. Must return a valid cItem. - virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const = 0; - - /// Called to set an item in the specified slot for the specified player - virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) = 0; - - /// Called when a player clicks in the window. Parameters taken from the click packet. - virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem); - - /// Called from Clicked if it is a valid shiftclick - virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem); - - /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked. - virtual void OnPlayerAdded(cPlayer & a_Player) {} ; - - /// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked. - virtual void OnPlayerRemoved(cPlayer & a_Player) {} ; - - /** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change. - The default implementation searches each slot for available space and distributes the stack there. - if a_ShouldApply is true, the changes are written into the slots; - if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes) - If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled - */ - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots); - -protected: - int m_NumSlots; - cWindow & m_ParentWindow; -} ; - - - - - -/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts -class cSlotAreaInventoryBase : - public cSlotArea -{ - typedef cSlotArea super; - -public: - cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow); - - // Creative inventory's click handling is somewhat different from survival inventory's, handle that here: - virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - - virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; - virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; - -protected: - int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory -} ; - - - - - -/// Handles the main inventory of each player, excluding the armor and hotbar -class cSlotAreaInventory : - public cSlotAreaInventoryBase -{ - typedef cSlotAreaInventoryBase super; - -public: - cSlotAreaInventory(cWindow & a_ParentWindow) : - cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow) - { - } -} ; - - - - - -/// Handles the hotbar of each player -class cSlotAreaHotBar : - public cSlotAreaInventoryBase -{ - typedef cSlotAreaInventoryBase super; - -public: - cSlotAreaHotBar(cWindow & a_ParentWindow) : - cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow) - { - } -} ; - - - - - -/// Handles the armor area of the player's inventory -class cSlotAreaArmor : - public cSlotAreaInventoryBase -{ -public: - cSlotAreaArmor(cWindow & a_ParentWindow) : - cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow) - { - } - - // Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.) - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; -} ; - - - - - -/// Handles any slot area that is representing a cItemGrid; same items for all the players -class cSlotAreaItemGrid : - public cSlotArea, - public cItemGrid::cListener -{ - typedef cSlotArea super; - -public: - cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow); - - virtual ~cSlotAreaItemGrid(); - - virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; - virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; - -protected: - cItemGrid & m_ItemGrid; - - // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; -} ; - - - - - -/** A cSlotArea with items layout that is private to each player and is temporary, such as -a crafting grid or an enchantment table. -This common ancestor stores the items in a per-player map. It also implements tossing items from the map. -*/ -class cSlotAreaTemporary : - public cSlotArea -{ - typedef cSlotArea super; - -public: - cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow); - - // cSlotArea overrides: - virtual const cItem * GetSlot (int a_SlotNum, cPlayer & a_Player) const override; - virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; - virtual void OnPlayerAdded (cPlayer & a_Player) override; - virtual void OnPlayerRemoved(cPlayer & a_Player) override; - - /// Tosses the player's items in slots [a_Begin, a_End) (ie. incl. a_Begin, but excl. a_End) - void TossItems(cPlayer & a_Player, int a_Begin, int a_End); - -protected: - typedef std::map > cItemMap; // Maps EntityID -> items - - cItemMap m_Items; - - /// Returns the pointer to the slot array for the player specified. - cItem * GetPlayerSlots(cPlayer & a_Player); -} ; - - - - - -class cSlotAreaCrafting : - public cSlotAreaTemporary -{ - typedef cSlotAreaTemporary super; - -public: - /// a_GridSize is allowed to be only 2 or 3 - cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow); - - // cSlotAreaTemporary overrides: - virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - virtual void OnPlayerRemoved(cPlayer & a_Player) override; - - // Distributing items into this area is completely disabled - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {} - -protected: - /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params - typedef std::list > cRecipeMap; - - int m_GridSize; - cRecipeMap m_Recipes; - - /// Handles a click in the result slot. Crafts using the current recipe, if possible - void ClickedResult(cPlayer & a_Player); - - /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result. - void ShiftClickedResult(cPlayer & a_Player); - - /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player - void UpdateRecipe(cPlayer & a_Player); - - /// Retrieves the recipe for the specified player from the map, or creates one if not found - cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player); -} ; - - - - - -class cSlotAreaChest : - public cSlotArea -{ -public: - cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow); - - virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; - virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; - -protected: - cChestEntity * m_Chest; -} ; - - - - - -class cSlotAreaDoubleChest : - public cSlotArea -{ -public: - cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow); - - virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; - virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; - -protected: - cChestEntity * m_TopChest; - cChestEntity * m_BottomChest; -} ; - - - - - -class cSlotAreaFurnace : - public cSlotArea, - public cItemGrid::cListener -{ - typedef cSlotArea super; - -public: - cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow); - - virtual ~cSlotAreaFurnace(); - - virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; - virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; - -protected: - cFurnaceEntity * m_Furnace; - - // cItemGrid::cListener overrides: - virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; -} ; - - - - + +// SlotArea.h + +// Interfaces to the cSlotArea class representing a contiguous area of slots in a UI window + + + + +#pragma once + +#include "../Inventory.h" + + + +class cWindow; +class cPlayer; +class cChestEntity; +class cDropSpenserEntity; +class cFurnaceEntity; +class cCraftingRecipe; + + + + + +class cSlotArea +{ +public: + cSlotArea(int a_NumSlots, cWindow & a_ParentWindow); + virtual ~cSlotArea() {} // force a virtual destructor in all subclasses + + int GetNumSlots(void) const { return m_NumSlots; } + + /// Called to retrieve an item in the specified slot for the specified player. Must return a valid cItem. + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const = 0; + + /// Called to set an item in the specified slot for the specified player + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) = 0; + + /// Called when a player clicks in the window. Parameters taken from the click packet. + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem); + + /// Called from Clicked if it is a valid shiftclick + virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem); + + /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked. + virtual void OnPlayerAdded(cPlayer & a_Player) {} ; + + /// Called when one of the players closes the parent window. The window already doesn't track the player. CS-locked. + virtual void OnPlayerRemoved(cPlayer & a_Player) {} ; + + /** Called to store as much of a_ItemStack in the area as possible. a_ItemStack is modified to reflect the change. + The default implementation searches each slot for available space and distributes the stack there. + if a_ShouldApply is true, the changes are written into the slots; + if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes) + If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled + */ + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots); + +protected: + int m_NumSlots; + cWindow & m_ParentWindow; +} ; + + + + + +/// Handles any part of the inventory, using parameters in constructor to distinguish between the parts +class cSlotAreaInventoryBase : + public cSlotArea +{ + typedef cSlotArea super; + +public: + cSlotAreaInventoryBase(int a_NumSlots, int a_SlotOffset, cWindow & a_ParentWindow); + + // Creative inventory's click handling is somewhat different from survival inventory's, handle that here: + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + int m_SlotOffset; // Index that this area's slot 0 has in the underlying cInventory +} ; + + + + + +/// Handles the main inventory of each player, excluding the armor and hotbar +class cSlotAreaInventory : + public cSlotAreaInventoryBase +{ + typedef cSlotAreaInventoryBase super; + +public: + cSlotAreaInventory(cWindow & a_ParentWindow) : + cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow) + { + } +} ; + + + + + +/// Handles the hotbar of each player +class cSlotAreaHotBar : + public cSlotAreaInventoryBase +{ + typedef cSlotAreaInventoryBase super; + +public: + cSlotAreaHotBar(cWindow & a_ParentWindow) : + cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow) + { + } +} ; + + + + + +/// Handles the armor area of the player's inventory +class cSlotAreaArmor : + public cSlotAreaInventoryBase +{ +public: + cSlotAreaArmor(cWindow & a_ParentWindow) : + cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow) + { + } + + // Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.) + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; +} ; + + + + + +/// Handles any slot area that is representing a cItemGrid; same items for all the players +class cSlotAreaItemGrid : + public cSlotArea, + public cItemGrid::cListener +{ + typedef cSlotArea super; + +public: + cSlotAreaItemGrid(cItemGrid & a_ItemGrid, cWindow & a_ParentWindow); + + virtual ~cSlotAreaItemGrid(); + + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + cItemGrid & m_ItemGrid; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +} ; + + + + + +/** A cSlotArea with items layout that is private to each player and is temporary, such as +a crafting grid or an enchantment table. +This common ancestor stores the items in a per-player map. It also implements tossing items from the map. +*/ +class cSlotAreaTemporary : + public cSlotArea +{ + typedef cSlotArea super; + +public: + cSlotAreaTemporary(int a_NumSlots, cWindow & a_ParentWindow); + + // cSlotArea overrides: + virtual const cItem * GetSlot (int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + virtual void OnPlayerAdded (cPlayer & a_Player) override; + virtual void OnPlayerRemoved(cPlayer & a_Player) override; + + /// Tosses the player's items in slots [a_Begin, a_End) (ie. incl. a_Begin, but excl. a_End) + void TossItems(cPlayer & a_Player, int a_Begin, int a_End); + +protected: + typedef std::map > cItemMap; // Maps EntityID -> items + + cItemMap m_Items; + + /// Returns the pointer to the slot array for the player specified. + cItem * GetPlayerSlots(cPlayer & a_Player); +} ; + + + + + +class cSlotAreaCrafting : + public cSlotAreaTemporary +{ + typedef cSlotAreaTemporary super; + +public: + /// a_GridSize is allowed to be only 2 or 3 + cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow); + + // cSlotAreaTemporary overrides: + virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + virtual void OnPlayerRemoved(cPlayer & a_Player) override; + + // Distributing items into this area is completely disabled + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override {} + +protected: + /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params + typedef std::list > cRecipeMap; + + int m_GridSize; + cRecipeMap m_Recipes; + + /// Handles a click in the result slot. Crafts using the current recipe, if possible + void ClickedResult(cPlayer & a_Player); + + /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result. + void ShiftClickedResult(cPlayer & a_Player); + + /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player + void UpdateRecipe(cPlayer & a_Player); + + /// Retrieves the recipe for the specified player from the map, or creates one if not found + cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player); +} ; + + + + + +class cSlotAreaChest : + public cSlotArea +{ +public: + cSlotAreaChest(cChestEntity * a_Chest, cWindow & a_ParentWindow); + + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + cChestEntity * m_Chest; +} ; + + + + + +class cSlotAreaDoubleChest : + public cSlotArea +{ +public: + cSlotAreaDoubleChest(cChestEntity * a_TopChest, cChestEntity * a_BottomChest, cWindow & a_ParentWindow); + + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + cChestEntity * m_TopChest; + cChestEntity * m_BottomChest; +} ; + + + + + +class cSlotAreaFurnace : + public cSlotArea, + public cItemGrid::cListener +{ + typedef cSlotArea super; + +public: + cSlotAreaFurnace(cFurnaceEntity * a_Furnace, cWindow & a_ParentWindow); + + virtual ~cSlotAreaFurnace(); + + virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; + virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; + virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; + +protected: + cFurnaceEntity * m_Furnace; + + // cItemGrid::cListener overrides: + virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; +} ; + + + + diff --git a/source/WorldStorage/NBTChunkSerializer.cpp b/source/WorldStorage/NBTChunkSerializer.cpp index d391325c9..da1b9e1c4 100644 --- a/source/WorldStorage/NBTChunkSerializer.cpp +++ b/source/WorldStorage/NBTChunkSerializer.cpp @@ -1,459 +1,459 @@ - -// NBTChunkSerializer.cpp - - -#include "Globals.h" -#include "NBTChunkSerializer.h" -#include "../BlockID.h" -#include "../BlockEntities/ChestEntity.h" -#include "../BlockEntities/DispenserEntity.h" -#include "../BlockEntities/DropperEntity.h" -#include "../BlockEntities/FurnaceEntity.h" -#include "../BlockEntities/HopperEntity.h" -#include "../BlockEntities/JukeboxEntity.h" -#include "../BlockEntities/NoteEntity.h" -#include "../BlockEntities/SignEntity.h" -#include "../ItemGrid.h" -#include "../StringCompression.h" -#include "../Entity.h" -#include "../OSSupport/MakeDir.h" -#include "FastNBT.h" -#include "../FallingBlock.h" -#include "../Minecart.h" -#include "../Mobs/Monster.h" -#include "../Pickup.h" - - - - -cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) : - m_BiomesAreValid(false), - m_Writer(a_Writer), - m_IsTagOpen(false), - m_HasHadEntity(false), - m_HasHadBlockEntity(false), - m_IsLightValid(false) -{ -} - - - - - -void cNBTChunkSerializer::Finish(void) -{ - if (m_IsTagOpen) - { - m_Writer.EndList(); - } - - // If light not valid, reset it to all zeroes: - if (!m_IsLightValid) - { - memset(m_BlockLight, 0, sizeof(m_BlockLight)); - memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight)); - } -} - - - - - -void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName) -{ - m_Writer.BeginCompound(a_CompoundName); - m_Writer.AddShort("id", (short)(a_Item.m_ItemType)); - m_Writer.AddShort("Damage", a_Item.m_ItemDamage); - m_Writer.AddByte ("Count", a_Item.m_ItemCount); - if (a_Slot >= 0) - { - m_Writer.AddByte ("Slot", (unsigned char)a_Slot); - } - - // Write the enchantments: - if (!a_Item.m_Enchantments.IsEmpty()) - { - const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; - m_Writer.BeginCompound("tag"); - a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName); - m_Writer.EndCompound(); - } - - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum) -{ - int NumSlots = a_Grid.GetNumSlots(); - for (int i = 0; i < NumSlots; i++) - { - const cItem & Item = a_Grid.GetSlot(i); - if (Item.IsEmpty()) - { - continue; - } - AddItem(Item, i + a_BeginSlotNum); - } // for i - chest slots[] -} - - - - - -void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID) -{ - m_Writer.AddInt ("x", a_Entity->GetPosX()); - m_Writer.AddInt ("y", a_Entity->GetPosY()); - m_Writer.AddInt ("z", a_Entity->GetPosZ()); - m_Writer.AddString("id", a_EntityTypeID); -} - - - - - -void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Chest"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Entity->GetContents()); - m_Writer.EndList(); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Trap"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Entity->GetContents()); - m_Writer.EndList(); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Dropper"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Entity->GetContents()); - m_Writer.EndList(); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Furnace, "Furnace"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Furnace->GetContents()); - m_Writer.EndList(); - m_Writer.AddShort("BurnTime", a_Furnace->GetFuelBurnTimeLeft()); - m_Writer.AddShort("CookTime", a_Furnace->GetTimeCooked()); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Hopper"); - m_Writer.BeginList("Items", TAG_Compound); - AddItemGrid(a_Entity->GetContents()); - m_Writer.EndList(); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Jukebox, "RecordPlayer"); - m_Writer.AddInt("Record", a_Jukebox->GetRecord()); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Note, "Music"); - m_Writer.AddByte("note", a_Note->GetPitch()); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign) -{ - m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Sign, "Sign"); - m_Writer.AddString("Text1", a_Sign->GetLine(0)); - m_Writer.AddString("Text2", a_Sign->GetLine(1)); - m_Writer.AddString("Text3", a_Sign->GetLine(2)); - m_Writer.AddString("Text4", a_Sign->GetLine(3)); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName) -{ - m_Writer.AddString("id", a_ClassName); - m_Writer.BeginList("Pos", TAG_Double); - m_Writer.AddDouble("", a_Entity->GetPosX()); - m_Writer.AddDouble("", a_Entity->GetPosY()); - m_Writer.AddDouble("", a_Entity->GetPosZ()); - m_Writer.EndList(); - m_Writer.BeginList("Motion", TAG_Double); - m_Writer.AddDouble("", a_Entity->GetSpeedX()); - m_Writer.AddDouble("", a_Entity->GetSpeedY()); - m_Writer.AddDouble("", a_Entity->GetSpeedZ()); - m_Writer.EndList(); - m_Writer.BeginList("Rotation", TAG_Double); - m_Writer.AddDouble("", a_Entity->GetRotation()); - m_Writer.AddDouble("", a_Entity->GetPitch()); - m_Writer.EndList(); -} - - - - - -void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock) -{ - m_Writer.BeginCompound(""); - AddBasicEntity(a_FallingBlock, "FallingSand"); - m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType()); - m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta()); - m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero - m_Writer.AddByte("DropItem", 1); - m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart) -{ - const char * EntityClass = NULL; - switch (a_Minecart->GetPayload()) - { - case cMinecart::mpNone: EntityClass = "MinecartRideable"; break; - case cMinecart::mpChest: EntityClass = "MinecartChest"; break; - case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break; - default: - { - ASSERT(!"Unhandled minecart payload type"); - return; - } - } // switch (payload) - - m_Writer.BeginCompound(""); - AddBasicEntity(a_Minecart, EntityClass); - switch (a_Minecart->GetPayload()) - { - case cMinecart::mpChest: - { - // Add chest contents into the Items tag: - AddMinecartChestContents((cMinecartWithChest *)a_Minecart); - break; - } - - case cMinecart::mpFurnace: - { - // TODO: Add "Push" and "Fuel" tags - break; - } - } // switch (Payload) - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) -{ - // TODO -} - - - - - -void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup) -{ - m_Writer.BeginCompound(""); - AddBasicEntity(a_Pickup, "Item"); - AddItem(a_Pickup->GetItem(), -1, "Item"); - m_Writer.AddShort("Health", a_Pickup->GetHealth()); - m_Writer.AddShort("Age", a_Pickup->GetAge()); - m_Writer.EndCompound(); -} - - - - - -void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart) -{ - m_Writer.BeginList("Items", TAG_Compound); - for (int i = 0; i < cMinecartWithChest::NumSlots; i++) - { - const cItem & Item = a_Minecart->GetSlot(i); - if (Item.IsEmpty()) - { - continue; - } - AddItem(Item, i); - } - m_Writer.EndList(); -} - - - - - -bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid) -{ - m_IsLightValid = a_IsLightValid; - return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother -} - - - - - -void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) -{ - memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes)); - for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++) - { - if ((*a_BiomeMap)[i] < 255) - { - // Normal MC biome, copy as-is: - m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]); - } - else - { - // TODO: MCS-specific biome, need to map to some basic MC biome: - ASSERT(!"Unimplemented MCS-specific biome"); - return; - } - } // for i - m_BiomeMap[] - m_BiomesAreValid = true; -} - - - - - -void cNBTChunkSerializer::Entity(cEntity * a_Entity) -{ - // Add entity into NBT: - if (m_IsTagOpen) - { - if (!m_HasHadEntity) - { - m_Writer.EndList(); - m_Writer.BeginList("Entities", TAG_Compound); - } - } - else - { - m_Writer.BeginList("Entities", TAG_Compound); - } - m_IsTagOpen = true; - m_HasHadEntity = true; - - switch (a_Entity->GetEntityType()) - { - case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *)a_Entity); break; - case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break; - case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break; - case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break; - case cEntity::etPlayer: return; // Players aren't saved into the world - default: - { - ASSERT(!"Unhandled entity type is being saved"); - break; - } - } -} - - - - - -void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) -{ - if (m_IsTagOpen) - { - if (!m_HasHadBlockEntity) - { - m_Writer.EndList(); - m_Writer.BeginList("TileEntities", TAG_Compound); - } - } - else - { - m_Writer.BeginList("TileEntities", TAG_Compound); - } - m_IsTagOpen = true; - - // Add tile-entity into NBT: - switch (a_Entity->GetBlockType()) - { - case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break; - case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; - case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; - case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; - case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; - case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; - case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break; - default: - { - ASSERT(!"Unhandled block entity saved into Anvil"); - } - } - m_HasHadBlockEntity = true; -} - - - - + +// NBTChunkSerializer.cpp + + +#include "Globals.h" +#include "NBTChunkSerializer.h" +#include "../BlockID.h" +#include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/DispenserEntity.h" +#include "../BlockEntities/DropperEntity.h" +#include "../BlockEntities/FurnaceEntity.h" +#include "../BlockEntities/HopperEntity.h" +#include "../BlockEntities/JukeboxEntity.h" +#include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/SignEntity.h" +#include "../ItemGrid.h" +#include "../StringCompression.h" +#include "../Entity.h" +#include "../OSSupport/MakeDir.h" +#include "FastNBT.h" +#include "../FallingBlock.h" +#include "../Minecart.h" +#include "../Mobs/Monster.h" +#include "../Pickup.h" + + + + +cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) : + m_BiomesAreValid(false), + m_Writer(a_Writer), + m_IsTagOpen(false), + m_HasHadEntity(false), + m_HasHadBlockEntity(false), + m_IsLightValid(false) +{ +} + + + + + +void cNBTChunkSerializer::Finish(void) +{ + if (m_IsTagOpen) + { + m_Writer.EndList(); + } + + // If light not valid, reset it to all zeroes: + if (!m_IsLightValid) + { + memset(m_BlockLight, 0, sizeof(m_BlockLight)); + memset(m_BlockSkyLight, 0, sizeof(m_BlockSkyLight)); + } +} + + + + + +void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName) +{ + m_Writer.BeginCompound(a_CompoundName); + m_Writer.AddShort("id", (short)(a_Item.m_ItemType)); + m_Writer.AddShort("Damage", a_Item.m_ItemDamage); + m_Writer.AddByte ("Count", a_Item.m_ItemCount); + if (a_Slot >= 0) + { + m_Writer.AddByte ("Slot", (unsigned char)a_Slot); + } + + // Write the enchantments: + if (!a_Item.m_Enchantments.IsEmpty()) + { + const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench"; + m_Writer.BeginCompound("tag"); + a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName); + m_Writer.EndCompound(); + } + + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum) +{ + int NumSlots = a_Grid.GetNumSlots(); + for (int i = 0; i < NumSlots; i++) + { + const cItem & Item = a_Grid.GetSlot(i); + if (Item.IsEmpty()) + { + continue; + } + AddItem(Item, i + a_BeginSlotNum); + } // for i - chest slots[] +} + + + + + +void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID) +{ + m_Writer.AddInt ("x", a_Entity->GetPosX()); + m_Writer.AddInt ("y", a_Entity->GetPosY()); + m_Writer.AddInt ("z", a_Entity->GetPosZ()); + m_Writer.AddString("id", a_EntityTypeID); +} + + + + + +void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Chest"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Trap"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Dropper"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Furnace, "Furnace"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Furnace->GetContents()); + m_Writer.EndList(); + m_Writer.AddShort("BurnTime", a_Furnace->GetFuelBurnTimeLeft()); + m_Writer.AddShort("CookTime", a_Furnace->GetTimeCooked()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Entity, "Hopper"); + m_Writer.BeginList("Items", TAG_Compound); + AddItemGrid(a_Entity->GetContents()); + m_Writer.EndList(); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Jukebox, "RecordPlayer"); + m_Writer.AddInt("Record", a_Jukebox->GetRecord()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Note, "Music"); + m_Writer.AddByte("note", a_Note->GetPitch()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_Sign, "Sign"); + m_Writer.AddString("Text1", a_Sign->GetLine(0)); + m_Writer.AddString("Text2", a_Sign->GetLine(1)); + m_Writer.AddString("Text3", a_Sign->GetLine(2)); + m_Writer.AddString("Text4", a_Sign->GetLine(3)); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName) +{ + m_Writer.AddString("id", a_ClassName); + m_Writer.BeginList("Pos", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetPosX()); + m_Writer.AddDouble("", a_Entity->GetPosY()); + m_Writer.AddDouble("", a_Entity->GetPosZ()); + m_Writer.EndList(); + m_Writer.BeginList("Motion", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetSpeedX()); + m_Writer.AddDouble("", a_Entity->GetSpeedY()); + m_Writer.AddDouble("", a_Entity->GetSpeedZ()); + m_Writer.EndList(); + m_Writer.BeginList("Rotation", TAG_Double); + m_Writer.AddDouble("", a_Entity->GetRotation()); + m_Writer.AddDouble("", a_Entity->GetPitch()); + m_Writer.EndList(); +} + + + + + +void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_FallingBlock, "FallingSand"); + m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType()); + m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta()); + m_Writer.AddByte("Time", 1); // Unused in MCServer, Vanilla said to need nonzero + m_Writer.AddByte("DropItem", 1); + m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart) +{ + const char * EntityClass = NULL; + switch (a_Minecart->GetPayload()) + { + case cMinecart::mpNone: EntityClass = "MinecartRideable"; break; + case cMinecart::mpChest: EntityClass = "MinecartChest"; break; + case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break; + default: + { + ASSERT(!"Unhandled minecart payload type"); + return; + } + } // switch (payload) + + m_Writer.BeginCompound(""); + AddBasicEntity(a_Minecart, EntityClass); + switch (a_Minecart->GetPayload()) + { + case cMinecart::mpChest: + { + // Add chest contents into the Items tag: + AddMinecartChestContents((cMinecartWithChest *)a_Minecart); + break; + } + + case cMinecart::mpFurnace: + { + // TODO: Add "Push" and "Fuel" tags + break; + } + } // switch (Payload) + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) +{ + // TODO +} + + + + + +void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Pickup, "Item"); + AddItem(a_Pickup->GetItem(), -1, "Item"); + m_Writer.AddShort("Health", a_Pickup->GetHealth()); + m_Writer.AddShort("Age", a_Pickup->GetAge()); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart) +{ + m_Writer.BeginList("Items", TAG_Compound); + for (int i = 0; i < cMinecartWithChest::NumSlots; i++) + { + const cItem & Item = a_Minecart->GetSlot(i); + if (Item.IsEmpty()) + { + continue; + } + AddItem(Item, i); + } + m_Writer.EndList(); +} + + + + + +bool cNBTChunkSerializer::LightIsValid(bool a_IsLightValid) +{ + m_IsLightValid = a_IsLightValid; + return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother +} + + + + + +void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) +{ + memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes)); + for (int i = 0; i < ARRAYCOUNT(m_Biomes); i++) + { + if ((*a_BiomeMap)[i] < 255) + { + // Normal MC biome, copy as-is: + m_VanillaBiomes[i] = (unsigned char)((*a_BiomeMap)[i]); + } + else + { + // TODO: MCS-specific biome, need to map to some basic MC biome: + ASSERT(!"Unimplemented MCS-specific biome"); + return; + } + } // for i - m_BiomeMap[] + m_BiomesAreValid = true; +} + + + + + +void cNBTChunkSerializer::Entity(cEntity * a_Entity) +{ + // Add entity into NBT: + if (m_IsTagOpen) + { + if (!m_HasHadEntity) + { + m_Writer.EndList(); + m_Writer.BeginList("Entities", TAG_Compound); + } + } + else + { + m_Writer.BeginList("Entities", TAG_Compound); + } + m_IsTagOpen = true; + m_HasHadEntity = true; + + switch (a_Entity->GetEntityType()) + { + case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *)a_Entity); break; + case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break; + case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break; + case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break; + case cEntity::etPlayer: return; // Players aren't saved into the world + default: + { + ASSERT(!"Unhandled entity type is being saved"); + break; + } + } +} + + + + + +void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) +{ + if (m_IsTagOpen) + { + if (!m_HasHadBlockEntity) + { + m_Writer.EndList(); + m_Writer.BeginList("TileEntities", TAG_Compound); + } + } + else + { + m_Writer.BeginList("TileEntities", TAG_Compound); + } + m_IsTagOpen = true; + + // Add tile-entity into NBT: + switch (a_Entity->GetBlockType()) + { + case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break; + case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; + case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; + case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; + case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; + case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; + case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break; + default: + { + ASSERT(!"Unhandled block entity saved into Anvil"); + } + } + m_HasHadBlockEntity = true; +} + + + + diff --git a/source/WorldStorage/NBTChunkSerializer.h b/source/WorldStorage/NBTChunkSerializer.h index c71286797..1ccd356b0 100644 --- a/source/WorldStorage/NBTChunkSerializer.h +++ b/source/WorldStorage/NBTChunkSerializer.h @@ -1,110 +1,110 @@ - -// NBTChunkSerializer.h - -// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil - - - - - -#pragma once - -#include "../ChunkDef.h" - - - - - -// fwd: -class cFastNBTWriter; -class cEntity; -class cBlockEntity; -class cChestEntity; -class cDispenserEntity; -class cDropperEntity; -class cFurnaceEntity; -class cHopperEntity; -class cJukeboxEntity; -class cNoteEntity; -class cSignEntity; -class cFallingBlock; -class cMinecart; -class cMinecartWithChest; -class cMinecartWithFurnace; -class cMonster; -class cPickup; -class cItemGrid; - - - - - -class cNBTChunkSerializer : - public cChunkDataSeparateCollector -{ -public: - cChunkDef::BiomeMap m_Biomes; - unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width]; - bool m_BiomesAreValid; - - - cNBTChunkSerializer(cFastNBTWriter & a_Writer); - - /// Close NBT tags that we've opened - void Finish(void); - - bool IsLightValid(void) const {return m_IsLightValid; } - -protected: - - /* From cChunkDataSeparateCollector we inherit: - - m_BlockTypes[] - - m_BlockMetas[] - - m_BlockLight[] - - m_BlockSkyLight[] - */ - - cFastNBTWriter & m_Writer; - - bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed. - bool m_HasHadEntity; // True if any Entity has already been received and processed - bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed - bool m_IsLightValid; // True if the chunk lighting is valid - - - /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested. - void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = ""); - - /// Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag - void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0); - - // Block entities: - void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID); - void AddChestEntity (cChestEntity * a_Entity); - void AddDispenserEntity(cDispenserEntity * a_Entity); - void AddDropperEntity (cDropperEntity * a_Entity); - void AddFurnaceEntity (cFurnaceEntity * a_Furnace); - void AddHopperEntity (cHopperEntity * a_Entity); - void AddJukeboxEntity (cJukeboxEntity * a_Jukebox); - void AddNoteEntity (cNoteEntity * a_Note); - void AddSignEntity (cSignEntity * a_Sign); - - // Entities: - void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName); - void AddFallingBlockEntity(cFallingBlock * a_FallingBlock); - void AddMinecartEntity (cMinecart * a_Minecart); - void AddMonsterEntity (cMonster * a_Monster); - void AddPickupEntity (cPickup * a_Pickup); - - void AddMinecartChestContents(cMinecartWithChest * a_Minecart); - - // cChunkDataSeparateCollector overrides: - virtual bool LightIsValid(bool a_IsLightValid) override; - virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override; - virtual void Entity(cEntity * a_Entity) override; - virtual void BlockEntity(cBlockEntity * a_Entity) override; -} ; // class cNBTChunkSerializer - - - - + +// NBTChunkSerializer.h + +// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil + + + + + +#pragma once + +#include "../ChunkDef.h" + + + + + +// fwd: +class cFastNBTWriter; +class cEntity; +class cBlockEntity; +class cChestEntity; +class cDispenserEntity; +class cDropperEntity; +class cFurnaceEntity; +class cHopperEntity; +class cJukeboxEntity; +class cNoteEntity; +class cSignEntity; +class cFallingBlock; +class cMinecart; +class cMinecartWithChest; +class cMinecartWithFurnace; +class cMonster; +class cPickup; +class cItemGrid; + + + + + +class cNBTChunkSerializer : + public cChunkDataSeparateCollector +{ +public: + cChunkDef::BiomeMap m_Biomes; + unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width]; + bool m_BiomesAreValid; + + + cNBTChunkSerializer(cFastNBTWriter & a_Writer); + + /// Close NBT tags that we've opened + void Finish(void); + + bool IsLightValid(void) const {return m_IsLightValid; } + +protected: + + /* From cChunkDataSeparateCollector we inherit: + - m_BlockTypes[] + - m_BlockMetas[] + - m_BlockLight[] + - m_BlockSkyLight[] + */ + + cFastNBTWriter & m_Writer; + + bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed. + bool m_HasHadEntity; // True if any Entity has already been received and processed + bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed + bool m_IsLightValid; // True if the chunk lighting is valid + + + /// Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested. + void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = ""); + + /// Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag + void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0); + + // Block entities: + void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID); + void AddChestEntity (cChestEntity * a_Entity); + void AddDispenserEntity(cDispenserEntity * a_Entity); + void AddDropperEntity (cDropperEntity * a_Entity); + void AddFurnaceEntity (cFurnaceEntity * a_Furnace); + void AddHopperEntity (cHopperEntity * a_Entity); + void AddJukeboxEntity (cJukeboxEntity * a_Jukebox); + void AddNoteEntity (cNoteEntity * a_Note); + void AddSignEntity (cSignEntity * a_Sign); + + // Entities: + void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName); + void AddFallingBlockEntity(cFallingBlock * a_FallingBlock); + void AddMinecartEntity (cMinecart * a_Minecart); + void AddMonsterEntity (cMonster * a_Monster); + void AddPickupEntity (cPickup * a_Pickup); + + void AddMinecartChestContents(cMinecartWithChest * a_Minecart); + + // cChunkDataSeparateCollector overrides: + virtual bool LightIsValid(bool a_IsLightValid) override; + virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override; + virtual void Entity(cEntity * a_Entity) override; + virtual void BlockEntity(cBlockEntity * a_Entity) override; +} ; // class cNBTChunkSerializer + + + + diff --git a/source/XMLParser.h b/source/XMLParser.h index a6b571862..f492d1a5d 100644 --- a/source/XMLParser.h +++ b/source/XMLParser.h @@ -1,701 +1,701 @@ - -// XMLParser.h - -// Interfaces to the CXMLParser class representing the base class for XML parsing - -// To use, derive a class from this base and override its OnStartElement(), OnEndElement() and OnCharacters() functions - - - - - -#pragma once - -#include "expat/expat.h" - - - - - -class CXMLParser -{ -public: - CXMLParser(void); - virtual ~CXMLParser(); - - // The actual parsing, may be called several times; the last time needs iIsFinal == true (-> flush) - int Parse(const char * iData, size_t iLength, bool iIsFinal = false); - -private: - // LibExpat stuff: - XML_Parser mParser; - - static void StartElementHandler(void * iContext, const XML_Char * iElement, const XML_Char ** iAttributes) - { - ((CXMLParser *)iContext)->OnStartElement(iElement, iAttributes); - } - - static void EndElementHandler (void * iContext, const XML_Char * iElement) - { - ((CXMLParser *)iContext)->OnEndElement(iElement); - } - - static void CharacterDataHandler (void * iContext, const XML_Char * iData, int iLength) - { - ((CXMLParser *)iContext)->OnCharacters(iData, iLength); - } - -protected: - virtual void OnStartElement(const XML_Char * iElement, const XML_Char ** iAttributes) = 0; - virtual void OnEndElement (const XML_Char * iElement) = 0; - virtual void OnCharacters (const XML_Char * iCharacters, int iLength) = 0; -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// The following template has been modified from code available at -// http://www.codeproject.com/Articles/1847/C-Wrappers-for-the-Expat-XML-Parser -// It uses templates to remove the virtual function call penalty (both size and speed) for each callback - -/* Usage: -1, Declare a subclass: - class CMyParser : public CExpatImpl -2, Declare handlers that you want in that subclass: - void CMyParser::OnEndElement(const XML_Char * iTagName); -3, Create an instance of your class: - CMyParser Parser; -4, Call Create(): - Parser.Create(NULL, NULL); -4, Call Parse(), repeatedly: - Parser.Parse(Buffer, Length); -*/ - -template -class CExpatImpl -{ - -// @access Constructors and destructors -public: - - // @cmember General constructor - - CExpatImpl () - { - m_p = NULL; - } - - // @cmember Destructor - - ~CExpatImpl () - { - Destroy (); - } - -// @access Parser creation and deletion methods -public: - - // @cmember Create a parser - - bool Create (const XML_Char * pszEncoding = NULL, const XML_Char * pszSep = NULL) - { - // Destroy the old parser - Destroy (); - - // If the encoding or seperator are empty, then NULL - if (pszEncoding != NULL && pszEncoding [0] == 0) - { - pszEncoding = NULL; - } - if (pszSep != NULL && pszSep [0] == 0) - { - pszSep = NULL; - } - - // Create the new parser - m_p = XML_ParserCreate_MM (pszEncoding, NULL, pszSep); - if (m_p == NULL) - { - return false; - } - - // Invoke the post create routine - _T * pThis = static_cast <_T *> (this); - pThis ->OnPostCreate (); - - // Set the user data used in callbacks - XML_SetUserData (m_p, (void *) this); - return true; - } - - // @cmember Destroy the parser - - void Destroy (void) - { - if (m_p != NULL) - { - XML_ParserFree (m_p); - } - m_p = NULL; - } - - - // @cmember Parse a block of data - - bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true) - { - assert (m_p != NULL); - return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0; - } - - // @cmember Parse internal buffer - - bool ParseBuffer (int nLength, bool fIsFinal = true) - { - assert (m_p != NULL); - return XML_ParseBuffer (m_p, nLength, fIsFinal) != 0; - } - - // @cmember Get the internal buffer - - void *GetBuffer (int nLength) - { - assert (m_p != NULL); - return XML_GetBuffer (m_p, nLength); - } - - -protected: - // Parser callback enable/disable methods: - - // @cmember Enable/Disable the start element handler - - void EnableStartElementHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : NULL); - } - - // @cmember Enable/Disable the end element handler - - void EnableEndElementHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : NULL); - } - - // @cmember Enable/Disable the element handlers - - void EnableElementHandler (bool fEnable = true) - { - assert (m_p != NULL); - EnableStartElementHandler (fEnable); - EnableEndElementHandler (fEnable); - } - - // @cmember Enable/Disable the character data handler - - void EnableCharacterDataHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : NULL); - } - - // @cmember Enable/Disable the processing instruction handler - - void EnableProcessingInstructionHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : NULL); - } - - // @cmember Enable/Disable the comment handler - - void EnableCommentHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetCommentHandler (m_p, fEnable ? CommentHandler : NULL); - } - - // @cmember Enable/Disable the start CDATA section handler - - void EnableStartCdataSectionHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : NULL); - } - - // @cmember Enable/Disable the end CDATA section handler - - void EnableEndCdataSectionHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : NULL); - } - - // @cmember Enable/Disable the CDATA section handlers - - void EnableCdataSectionHandler (bool fEnable = true) - { - assert (m_p != NULL); - EnableStartCdataSectionHandler (fEnable); - EnableEndCdataSectionHandler (fEnable); - } - - // @cmember Enable/Disable default handler - - void EnableDefaultHandler (bool fEnable = true, bool fExpand = true) - { - assert (m_p != NULL); - if (fExpand) - { - XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : NULL); - } - else - XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : NULL); - } - - // @cmember Enable/Disable external entity ref handler - - void EnableExternalEntityRefHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : NULL); - } - - // @cmember Enable/Disable unknown encoding handler - - void EnableUnknownEncodingHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : NULL); - } - - // @cmember Enable/Disable start namespace handler - - void EnableStartNamespaceDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : NULL); - } - - // @cmember Enable/Disable end namespace handler - - void EnableEndNamespaceDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : NULL); - } - - // @cmember Enable/Disable namespace handlers - - void EnableNamespaceDeclHandler (bool fEnable = true) - { - EnableStartNamespaceDeclHandler (fEnable); - EnableEndNamespaceDeclHandler (fEnable); - } - - // @cmember Enable/Disable the XML declaration handler - - void EnableXmlDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : NULL); - } - - // @cmember Enable/Disable the start DOCTYPE declaration handler - - void EnableStartDoctypeDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : NULL); - } - - // @cmember Enable/Disable the end DOCTYPE declaration handler - - void EnableEndDoctypeDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - XML_SetEndDoctypeDeclHandler (m_p, - fEnable ? EndDoctypeDeclHandler : NULL); - } - - // @cmember Enable/Disable the DOCTYPE declaration handler - - void EnableDoctypeDeclHandler (bool fEnable = true) - { - assert (m_p != NULL); - EnableStartDoctypeDeclHandler (fEnable); - EnableEndDoctypeDeclHandler (fEnable); - } - -public: - // Parser error reporting methods - - // @cmember Get last error - - enum XML_Error GetErrorCode () - { - assert (m_p != NULL); - return XML_GetErrorCode (m_p); - } - - // @cmember Get the current byte index - - long GetCurrentByteIndex () - { - assert (m_p != NULL); - return XML_GetCurrentByteIndex (m_p); - } - - // @cmember Get the current line number - - int GetCurrentLineNumber () - { - assert (m_p != NULL); - return XML_GetCurrentLineNumber (m_p); - } - - // @cmember Get the current column number - - int GetCurrentColumnNumber () - { - assert (m_p != NULL); - return XML_GetCurrentColumnNumber (m_p); - } - - // @cmember Get the current byte count - - int GetCurrentByteCount () - { - assert (m_p != NULL); - return XML_GetCurrentByteCount (m_p); - } - - // @cmember Get the input context - - const char *GetInputContext (int *pnOffset, int *pnSize) - { - assert (m_p != NULL); - return XML_GetInputContext (m_p, pnOffset, pnSize); - } - - // @cmember Get last error string - - const XML_LChar *GetErrorString () - { - return XML_ErrorString (GetErrorCode ()); - } - - // @cmember Return the version string - - static const XML_LChar *GetExpatVersion () - { - return XML_ExpatVersion (); - } - - // @cmember Get the version information - - static void GetExpatVersion (int *pnMajor, int *pnMinor, int *pnMicro) - { - XML_expat_version v = XML_ExpatVersionInfo (); - if (pnMajor) - *pnMajor = v .major; - if (pnMinor) - *pnMinor = v .minor; - if (pnMicro) - *pnMicro = v .micro; - } - - // @cmember Get last error string - - static const XML_LChar *GetErrorString (enum XML_Error nError) - { - return XML_ErrorString (nError); - } - - - // Public handler methods: - // The template parameter should provide their own implementation for those handlers that they want - - // @cmember Start element handler - - void OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs) - { - return; - } - - // @cmember End element handler - - void OnEndElement (const XML_Char *pszName) - { - return; - } - - // @cmember Character data handler - - void OnCharacterData (const XML_Char *pszData, int nLength) - { - return; - } - - // @cmember Processing instruction handler - - void OnProcessingInstruction (const XML_Char *pszTarget, - const XML_Char *pszData) - { - return; - } - - // @cmember Comment handler - - void OnComment (const XML_Char *pszData) - { - return; - } - - // @cmember Start CDATA section handler - - void OnStartCdataSection () - { - return; - } - - // @cmember End CDATA section handler - - void OnEndCdataSection () - { - return; - } - - // @cmember Default handler - - void OnDefault (const XML_Char *pszData, int nLength) - { - return; - } - - // @cmember External entity ref handler - - bool OnExternalEntityRef (const XML_Char *pszContext, - const XML_Char *pszBase, const XML_Char *pszSystemID, - const XML_Char *pszPublicID) - { - return false; - } - - // @cmember Unknown encoding handler - - bool OnUnknownEncoding (const XML_Char *pszName, XML_Encoding *pInfo) - { - return false; - } - - // @cmember Start namespace declaration handler - - void OnStartNamespaceDecl (const XML_Char *pszPrefix, - const XML_Char *pszURI) - { - return; - } - - // @cmember End namespace declaration handler - - void OnEndNamespaceDecl (const XML_Char *pszPrefix) - { - return; - } - - // @cmember XML declaration handler - - void OnXmlDecl (const XML_Char *pszVersion, const XML_Char *pszEncoding, - bool fStandalone) - { - return; - } - - // @cmember Start DOCTYPE declaration handler - - void OnStartDoctypeDecl (const XML_Char *pszDoctypeName, - const XML_Char *pszSysID, const XML_Char *pszPubID, - bool fHasInternalSubset) - { - return; - } - - // @cmember End DOCTYPE declaration handler - - void OnEndDoctypeDecl () - { - return; - } - -// @access Protected methods -protected: - - // @cmember Handle any post creation - - void OnPostCreate () - { - } - -// @access Protected static methods -protected: - - // @cmember Start element handler wrapper - - static void __cdecl StartElementHandler (void *pUserData, - const XML_Char *pszName, const XML_Char **papszAttrs) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnStartElement (pszName, papszAttrs); - } - - // @cmember End element handler wrapper - - static void __cdecl EndElementHandler (void *pUserData, - const XML_Char *pszName) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnEndElement (pszName); - } - - // @cmember Character data handler wrapper - - static void __cdecl CharacterDataHandler (void *pUserData, - const XML_Char *pszData, int nLength) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnCharacterData (pszData, nLength); - } - - // @cmember Processing instruction handler wrapper - - static void __cdecl ProcessingInstructionHandler (void *pUserData, - const XML_Char *pszTarget, const XML_Char *pszData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnProcessingInstruction (pszTarget, pszData); - } - - // @cmember Comment handler wrapper - - static void __cdecl CommentHandler (void *pUserData, - const XML_Char *pszData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnComment (pszData); - } - - // @cmember Start CDATA section wrapper - - static void __cdecl StartCdataSectionHandler (void *pUserData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnStartCdataSection (); - } - - // @cmember End CDATA section wrapper - - static void __cdecl EndCdataSectionHandler (void *pUserData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnEndCdataSection (); - } - - // @cmember Default wrapper - - static void __cdecl DefaultHandler (void *pUserData, - const XML_Char *pszData, int nLength) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnDefault (pszData, nLength); - } - - // @cmember External entity ref wrapper - - static int __cdecl ExternalEntityRefHandler (void *pUserData, - const XML_Char *pszContext, const XML_Char *pszBase, - const XML_Char *pszSystemID, const XML_Char *pszPublicID) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - return pThis ->OnExternalEntityRef (pszContext, - pszBase, pszSystemID, pszPublicID) ? 1 : 0; - } - - // @cmember Unknown encoding wrapper - - static int __cdecl UnknownEncodingHandler (void * pUserData, const XML_Char * pszName, XML_Encoding * pInfo) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - return pThis ->OnUnknownEncoding (pszName, pInfo) ? 1 : 0; - } - - // @cmember Start namespace decl wrapper - - static void __cdecl StartNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix, const XML_Char * pszURI) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnStartNamespaceDecl (pszPrefix, pszURI); - } - - // @cmember End namespace decl wrapper - - static void __cdecl EndNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnEndNamespaceDecl (pszPrefix); - } - - // @cmember XML declaration wrapper - - static void __cdecl XmlDeclHandler (void *pUserData, const XML_Char *pszVersion, const XML_Char *pszEncoding, int nStandalone) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnXmlDecl (pszVersion, pszEncoding, nStandalone != 0); - } - - // @cmember Start Doctype declaration wrapper - - static void __cdecl StartDoctypeDeclHandler ( - void *pUserData, const XML_Char *pszDoctypeName, const XML_Char *pszSysID, - const XML_Char *pszPubID, int nHasInternalSubset - ) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnStartDoctypeDecl (pszDoctypeName, pszSysID, - pszPubID, nHasInternalSubset != 0); - } - - // @cmember End Doctype declaration wrapper - - static void __cdecl EndDoctypeDeclHandler (void *pUserData) - { - _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); - pThis ->OnEndDoctypeDecl (); - } - - -protected: - - XML_Parser m_p; - - /// Returns the value of the specified attribute, if found; NULL otherwise - static const XML_Char * FindAttr(const XML_Char ** iAttrs, const XML_Char * iAttrToFind) - { - for (const XML_Char ** Attr = iAttrs; *Attr != NULL; Attr += 2) - { - if (strcmp(*Attr, iAttrToFind) == 0) - { - return *(Attr + 1); - } - } // for Attr - iAttrs[] - return NULL; - } -} ; - - - - + +// XMLParser.h + +// Interfaces to the CXMLParser class representing the base class for XML parsing + +// To use, derive a class from this base and override its OnStartElement(), OnEndElement() and OnCharacters() functions + + + + + +#pragma once + +#include "expat/expat.h" + + + + + +class CXMLParser +{ +public: + CXMLParser(void); + virtual ~CXMLParser(); + + // The actual parsing, may be called several times; the last time needs iIsFinal == true (-> flush) + int Parse(const char * iData, size_t iLength, bool iIsFinal = false); + +private: + // LibExpat stuff: + XML_Parser mParser; + + static void StartElementHandler(void * iContext, const XML_Char * iElement, const XML_Char ** iAttributes) + { + ((CXMLParser *)iContext)->OnStartElement(iElement, iAttributes); + } + + static void EndElementHandler (void * iContext, const XML_Char * iElement) + { + ((CXMLParser *)iContext)->OnEndElement(iElement); + } + + static void CharacterDataHandler (void * iContext, const XML_Char * iData, int iLength) + { + ((CXMLParser *)iContext)->OnCharacters(iData, iLength); + } + +protected: + virtual void OnStartElement(const XML_Char * iElement, const XML_Char ** iAttributes) = 0; + virtual void OnEndElement (const XML_Char * iElement) = 0; + virtual void OnCharacters (const XML_Char * iCharacters, int iLength) = 0; +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// The following template has been modified from code available at +// http://www.codeproject.com/Articles/1847/C-Wrappers-for-the-Expat-XML-Parser +// It uses templates to remove the virtual function call penalty (both size and speed) for each callback + +/* Usage: +1, Declare a subclass: + class CMyParser : public CExpatImpl +2, Declare handlers that you want in that subclass: + void CMyParser::OnEndElement(const XML_Char * iTagName); +3, Create an instance of your class: + CMyParser Parser; +4, Call Create(): + Parser.Create(NULL, NULL); +4, Call Parse(), repeatedly: + Parser.Parse(Buffer, Length); +*/ + +template +class CExpatImpl +{ + +// @access Constructors and destructors +public: + + // @cmember General constructor + + CExpatImpl () + { + m_p = NULL; + } + + // @cmember Destructor + + ~CExpatImpl () + { + Destroy (); + } + +// @access Parser creation and deletion methods +public: + + // @cmember Create a parser + + bool Create (const XML_Char * pszEncoding = NULL, const XML_Char * pszSep = NULL) + { + // Destroy the old parser + Destroy (); + + // If the encoding or seperator are empty, then NULL + if (pszEncoding != NULL && pszEncoding [0] == 0) + { + pszEncoding = NULL; + } + if (pszSep != NULL && pszSep [0] == 0) + { + pszSep = NULL; + } + + // Create the new parser + m_p = XML_ParserCreate_MM (pszEncoding, NULL, pszSep); + if (m_p == NULL) + { + return false; + } + + // Invoke the post create routine + _T * pThis = static_cast <_T *> (this); + pThis ->OnPostCreate (); + + // Set the user data used in callbacks + XML_SetUserData (m_p, (void *) this); + return true; + } + + // @cmember Destroy the parser + + void Destroy (void) + { + if (m_p != NULL) + { + XML_ParserFree (m_p); + } + m_p = NULL; + } + + + // @cmember Parse a block of data + + bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true) + { + assert (m_p != NULL); + return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0; + } + + // @cmember Parse internal buffer + + bool ParseBuffer (int nLength, bool fIsFinal = true) + { + assert (m_p != NULL); + return XML_ParseBuffer (m_p, nLength, fIsFinal) != 0; + } + + // @cmember Get the internal buffer + + void *GetBuffer (int nLength) + { + assert (m_p != NULL); + return XML_GetBuffer (m_p, nLength); + } + + +protected: + // Parser callback enable/disable methods: + + // @cmember Enable/Disable the start element handler + + void EnableStartElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartElementHandler (m_p, fEnable ? StartElementHandler : NULL); + } + + // @cmember Enable/Disable the end element handler + + void EnableEndElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndElementHandler (m_p, fEnable ? EndElementHandler : NULL); + } + + // @cmember Enable/Disable the element handlers + + void EnableElementHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartElementHandler (fEnable); + EnableEndElementHandler (fEnable); + } + + // @cmember Enable/Disable the character data handler + + void EnableCharacterDataHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetCharacterDataHandler (m_p, fEnable ? CharacterDataHandler : NULL); + } + + // @cmember Enable/Disable the processing instruction handler + + void EnableProcessingInstructionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetProcessingInstructionHandler (m_p, fEnable ? ProcessingInstructionHandler : NULL); + } + + // @cmember Enable/Disable the comment handler + + void EnableCommentHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetCommentHandler (m_p, fEnable ? CommentHandler : NULL); + } + + // @cmember Enable/Disable the start CDATA section handler + + void EnableStartCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartCdataSectionHandler (m_p, fEnable ? StartCdataSectionHandler : NULL); + } + + // @cmember Enable/Disable the end CDATA section handler + + void EnableEndCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndCdataSectionHandler (m_p, fEnable ? EndCdataSectionHandler : NULL); + } + + // @cmember Enable/Disable the CDATA section handlers + + void EnableCdataSectionHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartCdataSectionHandler (fEnable); + EnableEndCdataSectionHandler (fEnable); + } + + // @cmember Enable/Disable default handler + + void EnableDefaultHandler (bool fEnable = true, bool fExpand = true) + { + assert (m_p != NULL); + if (fExpand) + { + XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : NULL); + } + else + XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : NULL); + } + + // @cmember Enable/Disable external entity ref handler + + void EnableExternalEntityRefHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetExternalEntityRefHandler (m_p, fEnable ? ExternalEntityRefHandler : NULL); + } + + // @cmember Enable/Disable unknown encoding handler + + void EnableUnknownEncodingHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetUnknownEncodingHandler (m_p, fEnable ? UnknownEncodingHandler : NULL); + } + + // @cmember Enable/Disable start namespace handler + + void EnableStartNamespaceDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartNamespaceDeclHandler (m_p, fEnable ? StartNamespaceDeclHandler : NULL); + } + + // @cmember Enable/Disable end namespace handler + + void EnableEndNamespaceDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndNamespaceDeclHandler (m_p, fEnable ? EndNamespaceDeclHandler : NULL); + } + + // @cmember Enable/Disable namespace handlers + + void EnableNamespaceDeclHandler (bool fEnable = true) + { + EnableStartNamespaceDeclHandler (fEnable); + EnableEndNamespaceDeclHandler (fEnable); + } + + // @cmember Enable/Disable the XML declaration handler + + void EnableXmlDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetXmlDeclHandler (m_p, fEnable ? XmlDeclHandler : NULL); + } + + // @cmember Enable/Disable the start DOCTYPE declaration handler + + void EnableStartDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetStartDoctypeDeclHandler (m_p, fEnable ? StartDoctypeDeclHandler : NULL); + } + + // @cmember Enable/Disable the end DOCTYPE declaration handler + + void EnableEndDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + XML_SetEndDoctypeDeclHandler (m_p, + fEnable ? EndDoctypeDeclHandler : NULL); + } + + // @cmember Enable/Disable the DOCTYPE declaration handler + + void EnableDoctypeDeclHandler (bool fEnable = true) + { + assert (m_p != NULL); + EnableStartDoctypeDeclHandler (fEnable); + EnableEndDoctypeDeclHandler (fEnable); + } + +public: + // Parser error reporting methods + + // @cmember Get last error + + enum XML_Error GetErrorCode () + { + assert (m_p != NULL); + return XML_GetErrorCode (m_p); + } + + // @cmember Get the current byte index + + long GetCurrentByteIndex () + { + assert (m_p != NULL); + return XML_GetCurrentByteIndex (m_p); + } + + // @cmember Get the current line number + + int GetCurrentLineNumber () + { + assert (m_p != NULL); + return XML_GetCurrentLineNumber (m_p); + } + + // @cmember Get the current column number + + int GetCurrentColumnNumber () + { + assert (m_p != NULL); + return XML_GetCurrentColumnNumber (m_p); + } + + // @cmember Get the current byte count + + int GetCurrentByteCount () + { + assert (m_p != NULL); + return XML_GetCurrentByteCount (m_p); + } + + // @cmember Get the input context + + const char *GetInputContext (int *pnOffset, int *pnSize) + { + assert (m_p != NULL); + return XML_GetInputContext (m_p, pnOffset, pnSize); + } + + // @cmember Get last error string + + const XML_LChar *GetErrorString () + { + return XML_ErrorString (GetErrorCode ()); + } + + // @cmember Return the version string + + static const XML_LChar *GetExpatVersion () + { + return XML_ExpatVersion (); + } + + // @cmember Get the version information + + static void GetExpatVersion (int *pnMajor, int *pnMinor, int *pnMicro) + { + XML_expat_version v = XML_ExpatVersionInfo (); + if (pnMajor) + *pnMajor = v .major; + if (pnMinor) + *pnMinor = v .minor; + if (pnMicro) + *pnMicro = v .micro; + } + + // @cmember Get last error string + + static const XML_LChar *GetErrorString (enum XML_Error nError) + { + return XML_ErrorString (nError); + } + + + // Public handler methods: + // The template parameter should provide their own implementation for those handlers that they want + + // @cmember Start element handler + + void OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs) + { + return; + } + + // @cmember End element handler + + void OnEndElement (const XML_Char *pszName) + { + return; + } + + // @cmember Character data handler + + void OnCharacterData (const XML_Char *pszData, int nLength) + { + return; + } + + // @cmember Processing instruction handler + + void OnProcessingInstruction (const XML_Char *pszTarget, + const XML_Char *pszData) + { + return; + } + + // @cmember Comment handler + + void OnComment (const XML_Char *pszData) + { + return; + } + + // @cmember Start CDATA section handler + + void OnStartCdataSection () + { + return; + } + + // @cmember End CDATA section handler + + void OnEndCdataSection () + { + return; + } + + // @cmember Default handler + + void OnDefault (const XML_Char *pszData, int nLength) + { + return; + } + + // @cmember External entity ref handler + + bool OnExternalEntityRef (const XML_Char *pszContext, + const XML_Char *pszBase, const XML_Char *pszSystemID, + const XML_Char *pszPublicID) + { + return false; + } + + // @cmember Unknown encoding handler + + bool OnUnknownEncoding (const XML_Char *pszName, XML_Encoding *pInfo) + { + return false; + } + + // @cmember Start namespace declaration handler + + void OnStartNamespaceDecl (const XML_Char *pszPrefix, + const XML_Char *pszURI) + { + return; + } + + // @cmember End namespace declaration handler + + void OnEndNamespaceDecl (const XML_Char *pszPrefix) + { + return; + } + + // @cmember XML declaration handler + + void OnXmlDecl (const XML_Char *pszVersion, const XML_Char *pszEncoding, + bool fStandalone) + { + return; + } + + // @cmember Start DOCTYPE declaration handler + + void OnStartDoctypeDecl (const XML_Char *pszDoctypeName, + const XML_Char *pszSysID, const XML_Char *pszPubID, + bool fHasInternalSubset) + { + return; + } + + // @cmember End DOCTYPE declaration handler + + void OnEndDoctypeDecl () + { + return; + } + +// @access Protected methods +protected: + + // @cmember Handle any post creation + + void OnPostCreate () + { + } + +// @access Protected static methods +protected: + + // @cmember Start element handler wrapper + + static void __cdecl StartElementHandler (void *pUserData, + const XML_Char *pszName, const XML_Char **papszAttrs) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartElement (pszName, papszAttrs); + } + + // @cmember End element handler wrapper + + static void __cdecl EndElementHandler (void *pUserData, + const XML_Char *pszName) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndElement (pszName); + } + + // @cmember Character data handler wrapper + + static void __cdecl CharacterDataHandler (void *pUserData, + const XML_Char *pszData, int nLength) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnCharacterData (pszData, nLength); + } + + // @cmember Processing instruction handler wrapper + + static void __cdecl ProcessingInstructionHandler (void *pUserData, + const XML_Char *pszTarget, const XML_Char *pszData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnProcessingInstruction (pszTarget, pszData); + } + + // @cmember Comment handler wrapper + + static void __cdecl CommentHandler (void *pUserData, + const XML_Char *pszData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnComment (pszData); + } + + // @cmember Start CDATA section wrapper + + static void __cdecl StartCdataSectionHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartCdataSection (); + } + + // @cmember End CDATA section wrapper + + static void __cdecl EndCdataSectionHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndCdataSection (); + } + + // @cmember Default wrapper + + static void __cdecl DefaultHandler (void *pUserData, + const XML_Char *pszData, int nLength) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnDefault (pszData, nLength); + } + + // @cmember External entity ref wrapper + + static int __cdecl ExternalEntityRefHandler (void *pUserData, + const XML_Char *pszContext, const XML_Char *pszBase, + const XML_Char *pszSystemID, const XML_Char *pszPublicID) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + return pThis ->OnExternalEntityRef (pszContext, + pszBase, pszSystemID, pszPublicID) ? 1 : 0; + } + + // @cmember Unknown encoding wrapper + + static int __cdecl UnknownEncodingHandler (void * pUserData, const XML_Char * pszName, XML_Encoding * pInfo) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + return pThis ->OnUnknownEncoding (pszName, pInfo) ? 1 : 0; + } + + // @cmember Start namespace decl wrapper + + static void __cdecl StartNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix, const XML_Char * pszURI) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartNamespaceDecl (pszPrefix, pszURI); + } + + // @cmember End namespace decl wrapper + + static void __cdecl EndNamespaceDeclHandler (void * pUserData, const XML_Char * pszPrefix) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndNamespaceDecl (pszPrefix); + } + + // @cmember XML declaration wrapper + + static void __cdecl XmlDeclHandler (void *pUserData, const XML_Char *pszVersion, const XML_Char *pszEncoding, int nStandalone) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnXmlDecl (pszVersion, pszEncoding, nStandalone != 0); + } + + // @cmember Start Doctype declaration wrapper + + static void __cdecl StartDoctypeDeclHandler ( + void *pUserData, const XML_Char *pszDoctypeName, const XML_Char *pszSysID, + const XML_Char *pszPubID, int nHasInternalSubset + ) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnStartDoctypeDecl (pszDoctypeName, pszSysID, + pszPubID, nHasInternalSubset != 0); + } + + // @cmember End Doctype declaration wrapper + + static void __cdecl EndDoctypeDeclHandler (void *pUserData) + { + _T *pThis = static_cast <_T *> ((CExpatImpl <_T> *) pUserData); + pThis ->OnEndDoctypeDecl (); + } + + +protected: + + XML_Parser m_p; + + /// Returns the value of the specified attribute, if found; NULL otherwise + static const XML_Char * FindAttr(const XML_Char ** iAttrs, const XML_Char * iAttrToFind) + { + for (const XML_Char ** Attr = iAttrs; *Attr != NULL; Attr += 2) + { + if (strcmp(*Attr, iAttrToFind) == 0) + { + return *(Attr + 1); + } + } // for Attr - iAttrs[] + return NULL; + } +} ; + + + + diff --git a/source/squirrelbindings/SquirrelArray.h b/source/squirrelbindings/SquirrelArray.h index c54315aae..de5027f86 100644 --- a/source/squirrelbindings/SquirrelArray.h +++ b/source/squirrelbindings/SquirrelArray.h @@ -1,56 +1,56 @@ - -#pragma once - - - - - -#ifdef USE_SQUIRREL - - - - - -template -class SquirrelArray -{ -public: - SquirrelArray() - { - } - - unsigned int Size() - { - return m_Values.size(); - } - - T Get(unsigned int a_Index) - { - if(m_Values.size() < a_Index) - { - return T(); - } - return m_Values.at(a_Index); - } - - void Add(T a_Value) - { - m_Values.push_back(a_Value); - } - -protected: - std::vector m_Values; - -}; - -class SquirrelStringArray : public SquirrelArray { }; - - - - - -#endif // USE_SQUIRREL - - - - + +#pragma once + + + + + +#ifdef USE_SQUIRREL + + + + + +template +class SquirrelArray +{ +public: + SquirrelArray() + { + } + + unsigned int Size() + { + return m_Values.size(); + } + + T Get(unsigned int a_Index) + { + if(m_Values.size() < a_Index) + { + return T(); + } + return m_Values.at(a_Index); + } + + void Add(T a_Value) + { + m_Values.push_back(a_Value); + } + +protected: + std::vector m_Values; + +}; + +class SquirrelStringArray : public SquirrelArray { }; + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/squirrelbindings/SquirrelBaseClass.h b/source/squirrelbindings/SquirrelBaseClass.h index 36f532611..fb5e95c05 100644 --- a/source/squirrelbindings/SquirrelBaseClass.h +++ b/source/squirrelbindings/SquirrelBaseClass.h @@ -1,66 +1,66 @@ - -#pragma once - - - - -#ifdef USE_SQUIRREL - - - - - -#include "SquirrelBindings.h" -#include "../Plugin_Squirrel.h" -#include "../PluginManager.h" -#include "../Root.h" -#include "../SquirrelCommandBinder.h" - - - - - -// The baseclass for squirrel plugins -class cSquirrelBaseClass -{ -public: - cSquirrelBaseClass() - : m_Instance(0) - { - } - - void setInstance(cPlugin_Squirrel *a_Instance) - { - m_Instance = a_Instance; - } - - void AddHook(short a_Hook) - { - if(m_Instance) - cRoot::Get()->GetPluginManager()->AddHook(m_Instance, (cPluginManager::PluginHook) a_Hook); - } - - void AddCommand( std::string a_Command, std::string a_Description, std::string a_Permission ) - { - if(m_Instance) m_Instance->AddCommand(a_Command, a_Description, a_Permission); - } - - bool BindCommand( const std::string a_Command, const std::string a_Permission, Sqrat::Function a_Callback) - { - if(!m_Instance) return false; - return cRoot::Get()->GetPluginManager()->GetSquirrelCommandBinder()->BindCommand(a_Command, a_Permission, m_Instance, a_Callback); - } - -protected: - cPlugin_Squirrel *m_Instance; -}; - - - - - -#endif // USE_SQUIRREL - - - - + +#pragma once + + + + +#ifdef USE_SQUIRREL + + + + + +#include "SquirrelBindings.h" +#include "../Plugin_Squirrel.h" +#include "../PluginManager.h" +#include "../Root.h" +#include "../SquirrelCommandBinder.h" + + + + + +// The baseclass for squirrel plugins +class cSquirrelBaseClass +{ +public: + cSquirrelBaseClass() + : m_Instance(0) + { + } + + void setInstance(cPlugin_Squirrel *a_Instance) + { + m_Instance = a_Instance; + } + + void AddHook(short a_Hook) + { + if(m_Instance) + cRoot::Get()->GetPluginManager()->AddHook(m_Instance, (cPluginManager::PluginHook) a_Hook); + } + + void AddCommand( std::string a_Command, std::string a_Description, std::string a_Permission ) + { + if(m_Instance) m_Instance->AddCommand(a_Command, a_Description, a_Permission); + } + + bool BindCommand( const std::string a_Command, const std::string a_Permission, Sqrat::Function a_Callback) + { + if(!m_Instance) return false; + return cRoot::Get()->GetPluginManager()->GetSquirrelCommandBinder()->BindCommand(a_Command, a_Permission, m_Instance, a_Callback); + } + +protected: + cPlugin_Squirrel *m_Instance; +}; + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/squirrelbindings/SquirrelBindings.cpp b/source/squirrelbindings/SquirrelBindings.cpp index ac212b101..51acc184f 100644 --- a/source/squirrelbindings/SquirrelBindings.cpp +++ b/source/squirrelbindings/SquirrelBindings.cpp @@ -1,195 +1,195 @@ - -#include "Globals.h" - - - - - -#ifdef USE_SQUIRREL - - - - -#include "SquirrelBindings.h" -#include "SquirrelFunctions.h" - -#include "SquirrelBaseClass.h" -#include "SquirrelArray.h" - -#include "../Player.h" - - - - - -using namespace Sqrat; - - - - - -void BindSquirrel(HSQUIRRELVM vm) -{ - RootTable() - .Bind("Plugin", Class() - .Func("AddHook", &cSquirrelBaseClass::AddHook) - .Func("AddCommand", &cSquirrelBaseClass::AddCommand) - .Func("BindCommand", &cSquirrelBaseClass::BindCommand) - ); - - RootTable().Bind("Vector3f", Class() - .Func("Set", &Vector3f::Set) - .Func("Normalize", &Vector3f::Normalize) - .Func("Length", &Vector3f::Length) - .Func("SqrLength", &Vector3f::SqrLength) - .Var("x", &Vector3f::x) - .Var("y", &Vector3f::y) - .Var("z", &Vector3f::z) - ); - - RootTable().Bind("Vector3d", Class() - .Func("Set", &Vector3d::Set) - .Func("Normalize", &Vector3d::Normalize) - .Func("Length", &Vector3d::Length) - .Func("SqrLength", &Vector3d::SqrLength) - .Var("x", &Vector3d::x) - .Var("y", &Vector3d::y) - .Var("z", &Vector3d::z) - ); - - RootTable().Bind("cEntity", Class() - .Func("GetEntityType", &cEntity::GetEntityType) - .Func("IsA", &cEntity::IsA) - .Func("GetWorld", &cEntity::GetWorld) - .Func("GetPosition", &cEntity::GetPosition) - .Func("GetPosX", &cEntity::GetPosX) - .Func("GetPosY", &cEntity::GetPosY) - .Func("GetPosZ", &cEntity::GetPosZ) - .Func("GetRot", &cEntity::GetRot) - .Func("GetRotation", &cEntity::GetRotation) - .Func("GetPitch", &cEntity::GetPitch) - .Func("GetRoll", &cEntity::GetRoll) - .Func("SetRotation", &cEntity::SetRotation) - .Func("SetPitch", &cEntity::SetPitch) - .Func("SetRoll", &cEntity::SetRoll) - .Func("GetLookVector", &cEntity::GetLookVector) - .Func("GetChunkX", &cEntity::GetChunkX) - .Func("GetChunkY", &cEntity::GetChunkY) - .Func("GetChunkZ", &cEntity::GetChunkZ) - .Func("SetPosX", &cEntity::SetPosX) - .Func("SetPosY", &cEntity::SetPosY) - .Func("SetPosZ", &cEntity::SetPosZ) - .Func("SetPosition", &cEntity::SetPosition) - .Func("GetUniqueID", &cEntity::GetUniqueID) - .Func("IsDestroyed", &cEntity::IsDestroyed) - .Func("Destroy", &cEntity::Destroy) - ); - - ConstTable().Enum("MetaData", Enumeration() - .Const("Normal", cPawn::NORMAL) - .Const("Burning", cPawn::BURNING) - .Const("Crouched", cPawn::CROUCHED) - .Const("Riding", cPawn::RIDING) - .Const("Sprinting", cPawn::SPRINTING) - .Const("Eating", cPawn::EATING) - .Const("Blocking", cPawn::BLOCKING) - ); - - RootTable().Bind("cPawn", DerivedClass() - .Func("TeleportToEntity", &cPawn::TeleportToEntity) - .Func("TeleportTo", &cPawn::TeleportTo) - .Func("Heal", &cPawn::Heal) - .Func("TakeDamage", &cPawn::TakeDamage) - .Func("KilledBy", &cPawn::KilledBy) - .Func("GetHealth", &cPawn::GetHealth) - .Func("SetMetaData", &cPawn::SetMetaData) - .Func("GetMetaData", &cPawn::GetMetaData) - .Func("SetMaxHealth", &cPawn::SetMaxHealth) - .Func("GetMaxHealth", &cPawn::GetMaxHealth) - ); - - RootTable().Bind("cPlayer", DerivedClass() - .Func("GetName", &cPlayer::GetName) - .Func("SetTouchGround", &cPlayer::SetTouchGround) - .Func("SetStance", &cPlayer::SetStance) - .Func("GetEyeHeight", &cPlayer::GetEyeHeight) - .Func("GetEyePosition", &cPlayer::GetEyePosition) - .Func("GetFlying", &cPlayer::GetFlying) - .Func("GetStance", &cPlayer::GetStance) - //TODO .Func("GetInventory", &cPlayer::GetInventory) - .Func("TeleportTo", &cPlayer::TeleportTo) - .Func("GetGameMode", &cPlayer::GetGameMode) - .Func("GetIP", &cPlayer::GetIP) - .Func("GetLastBlockActionTime", &cPlayer::GetLastBlockActionTime) - .Func("GetLastBlockActionCnt", &cPlayer::GetLastBlockActionCnt) - .Func("SetLastBlockActionCnt", &cPlayer::SetLastBlockActionCnt) - .Func("SetLastBlockActionTime", &cPlayer::SetLastBlockActionTime) - .Func("SetGameMode", &cPlayer::SetGameMode) - .Func("LoginSetGameMode", &cPlayer::LoginSetGameMode) - .Func("SetIP", &cPlayer::SetIP) - .Func("MoveTo", &cPlayer::MoveTo) - .Func("GetClientHandle", &cPlayer::GetClientHandle) - .Func("SendMessage", &cPlayer::SendMessage) - .Func("AddToGroup", &cPlayer::AddToGroup) - .Func("CanUseCommand", &cPlayer::CanUseCommand) - .Func("HasPermission", &cPlayer::HasPermission) - .Func("IsInGroup", &cPlayer::IsInGroup) - .Func("GetColor", &cPlayer::GetColor) - .Func("TossItem", &cPlayer::TossItem) - .Func("Heal", &cPlayer::Heal) - .Func("Feed", &cPlayer::Feed) - .Func("TakeDamage", &cPlayer::TakeDamage) - .Func("KilledBy", &cPlayer::KilledBy) - .Func("Respawn", &cPlayer::Respawn) - .Func("SetVisible", &cPlayer::SetVisible) - .Func("IsVisible", &cPlayer::IsVisible) - .Func("MoveToWorld", &cPlayer::MoveToWorld) - .Func("GetLoadedWorldName", &cPlayer::GetLoadedWorldName) - .Func("UseEquippedItem", &cPlayer::UseEquippedItem) - ); - - RootTable().Bind("StringArray", Class() - .Func("Get", &SquirrelStringArray::Get) - .Func("Add", &SquirrelStringArray::Add) - .Func("Size", &SquirrelStringArray::Size) - ); - - - RootTable().Func("print", &sqPrint); - - - ConstTable().Enum("Hook", Enumeration() - .Const("Tick", cPluginManager::HOOK_TICK) - .Const("Chat", cPluginManager::HOOK_CHAT) - .Const("CollectPickup", cPluginManager::HOOK_COLLECT_PICKUP) - .Const("BlockDig", cPluginManager::HOOK_BLOCK_DIG) - .Const("BlockPlace", cPluginManager::HOOK_BLOCK_PLACE) - .Const("Disconnect", cPluginManager::HOOK_DISCONNECT) - .Const("Handshake", cPluginManager::HOOK_HANDSHAKE) - .Const("Login", cPluginManager::HOOK_LOGIN) - .Const("PlayerSpawn", cPluginManager::HOOK_PLAYER_SPAWN) - .Const("PlayerJoin", cPluginManager::HOOK_PLAYER_JOIN) - .Const("PlayerMove", cPluginManager::HOOK_PLAYER_MOVE) - .Const("TakeDamage", cPluginManager::HOOK_TAKE_DAMAGE) - .Const("Killed", cPluginManager::HOOK_KILLED) - .Const("ChunkGenerated", cPluginManager::HOOK_CHUNK_GENERATED) - .Const("ChunkGenerating", cPluginManager::HOOK_CHUNK_GENERATING) - .Const("BlockToDrops", cPluginManager::HOOK_BLOCK_TO_DROPS) - .Const("PreCrafting", cPluginManager::HOOK_PRE_CRAFTING) - .Const("CraftingNoRecipe", cPluginManager::HOOK_CRAFTING_NO_RECIPE) - .Const("PostCrafting", cPluginManager::HOOK_POST_CRAFTING) - .Const("BlockToPickup", cPluginManager::HOOK_BLOCK_TO_PICKUP) - .Const("WeatherChanged", cPluginManager::HOOK_WEATHER_CHANGED) - .Const("UpdatingSign", cPluginManager::HOOK_UPDATING_SIGN) - .Const("UpdatedSign", cPluginManager::HOOK_UPDATED_SIGN)); -} - - - - - -#endif // USE_SQUIRREL - - - - + +#include "Globals.h" + + + + + +#ifdef USE_SQUIRREL + + + + +#include "SquirrelBindings.h" +#include "SquirrelFunctions.h" + +#include "SquirrelBaseClass.h" +#include "SquirrelArray.h" + +#include "../Player.h" + + + + + +using namespace Sqrat; + + + + + +void BindSquirrel(HSQUIRRELVM vm) +{ + RootTable() + .Bind("Plugin", Class() + .Func("AddHook", &cSquirrelBaseClass::AddHook) + .Func("AddCommand", &cSquirrelBaseClass::AddCommand) + .Func("BindCommand", &cSquirrelBaseClass::BindCommand) + ); + + RootTable().Bind("Vector3f", Class() + .Func("Set", &Vector3f::Set) + .Func("Normalize", &Vector3f::Normalize) + .Func("Length", &Vector3f::Length) + .Func("SqrLength", &Vector3f::SqrLength) + .Var("x", &Vector3f::x) + .Var("y", &Vector3f::y) + .Var("z", &Vector3f::z) + ); + + RootTable().Bind("Vector3d", Class() + .Func("Set", &Vector3d::Set) + .Func("Normalize", &Vector3d::Normalize) + .Func("Length", &Vector3d::Length) + .Func("SqrLength", &Vector3d::SqrLength) + .Var("x", &Vector3d::x) + .Var("y", &Vector3d::y) + .Var("z", &Vector3d::z) + ); + + RootTable().Bind("cEntity", Class() + .Func("GetEntityType", &cEntity::GetEntityType) + .Func("IsA", &cEntity::IsA) + .Func("GetWorld", &cEntity::GetWorld) + .Func("GetPosition", &cEntity::GetPosition) + .Func("GetPosX", &cEntity::GetPosX) + .Func("GetPosY", &cEntity::GetPosY) + .Func("GetPosZ", &cEntity::GetPosZ) + .Func("GetRot", &cEntity::GetRot) + .Func("GetRotation", &cEntity::GetRotation) + .Func("GetPitch", &cEntity::GetPitch) + .Func("GetRoll", &cEntity::GetRoll) + .Func("SetRotation", &cEntity::SetRotation) + .Func("SetPitch", &cEntity::SetPitch) + .Func("SetRoll", &cEntity::SetRoll) + .Func("GetLookVector", &cEntity::GetLookVector) + .Func("GetChunkX", &cEntity::GetChunkX) + .Func("GetChunkY", &cEntity::GetChunkY) + .Func("GetChunkZ", &cEntity::GetChunkZ) + .Func("SetPosX", &cEntity::SetPosX) + .Func("SetPosY", &cEntity::SetPosY) + .Func("SetPosZ", &cEntity::SetPosZ) + .Func("SetPosition", &cEntity::SetPosition) + .Func("GetUniqueID", &cEntity::GetUniqueID) + .Func("IsDestroyed", &cEntity::IsDestroyed) + .Func("Destroy", &cEntity::Destroy) + ); + + ConstTable().Enum("MetaData", Enumeration() + .Const("Normal", cPawn::NORMAL) + .Const("Burning", cPawn::BURNING) + .Const("Crouched", cPawn::CROUCHED) + .Const("Riding", cPawn::RIDING) + .Const("Sprinting", cPawn::SPRINTING) + .Const("Eating", cPawn::EATING) + .Const("Blocking", cPawn::BLOCKING) + ); + + RootTable().Bind("cPawn", DerivedClass() + .Func("TeleportToEntity", &cPawn::TeleportToEntity) + .Func("TeleportTo", &cPawn::TeleportTo) + .Func("Heal", &cPawn::Heal) + .Func("TakeDamage", &cPawn::TakeDamage) + .Func("KilledBy", &cPawn::KilledBy) + .Func("GetHealth", &cPawn::GetHealth) + .Func("SetMetaData", &cPawn::SetMetaData) + .Func("GetMetaData", &cPawn::GetMetaData) + .Func("SetMaxHealth", &cPawn::SetMaxHealth) + .Func("GetMaxHealth", &cPawn::GetMaxHealth) + ); + + RootTable().Bind("cPlayer", DerivedClass() + .Func("GetName", &cPlayer::GetName) + .Func("SetTouchGround", &cPlayer::SetTouchGround) + .Func("SetStance", &cPlayer::SetStance) + .Func("GetEyeHeight", &cPlayer::GetEyeHeight) + .Func("GetEyePosition", &cPlayer::GetEyePosition) + .Func("GetFlying", &cPlayer::GetFlying) + .Func("GetStance", &cPlayer::GetStance) + //TODO .Func("GetInventory", &cPlayer::GetInventory) + .Func("TeleportTo", &cPlayer::TeleportTo) + .Func("GetGameMode", &cPlayer::GetGameMode) + .Func("GetIP", &cPlayer::GetIP) + .Func("GetLastBlockActionTime", &cPlayer::GetLastBlockActionTime) + .Func("GetLastBlockActionCnt", &cPlayer::GetLastBlockActionCnt) + .Func("SetLastBlockActionCnt", &cPlayer::SetLastBlockActionCnt) + .Func("SetLastBlockActionTime", &cPlayer::SetLastBlockActionTime) + .Func("SetGameMode", &cPlayer::SetGameMode) + .Func("LoginSetGameMode", &cPlayer::LoginSetGameMode) + .Func("SetIP", &cPlayer::SetIP) + .Func("MoveTo", &cPlayer::MoveTo) + .Func("GetClientHandle", &cPlayer::GetClientHandle) + .Func("SendMessage", &cPlayer::SendMessage) + .Func("AddToGroup", &cPlayer::AddToGroup) + .Func("CanUseCommand", &cPlayer::CanUseCommand) + .Func("HasPermission", &cPlayer::HasPermission) + .Func("IsInGroup", &cPlayer::IsInGroup) + .Func("GetColor", &cPlayer::GetColor) + .Func("TossItem", &cPlayer::TossItem) + .Func("Heal", &cPlayer::Heal) + .Func("Feed", &cPlayer::Feed) + .Func("TakeDamage", &cPlayer::TakeDamage) + .Func("KilledBy", &cPlayer::KilledBy) + .Func("Respawn", &cPlayer::Respawn) + .Func("SetVisible", &cPlayer::SetVisible) + .Func("IsVisible", &cPlayer::IsVisible) + .Func("MoveToWorld", &cPlayer::MoveToWorld) + .Func("GetLoadedWorldName", &cPlayer::GetLoadedWorldName) + .Func("UseEquippedItem", &cPlayer::UseEquippedItem) + ); + + RootTable().Bind("StringArray", Class() + .Func("Get", &SquirrelStringArray::Get) + .Func("Add", &SquirrelStringArray::Add) + .Func("Size", &SquirrelStringArray::Size) + ); + + + RootTable().Func("print", &sqPrint); + + + ConstTable().Enum("Hook", Enumeration() + .Const("Tick", cPluginManager::HOOK_TICK) + .Const("Chat", cPluginManager::HOOK_CHAT) + .Const("CollectPickup", cPluginManager::HOOK_COLLECT_PICKUP) + .Const("BlockDig", cPluginManager::HOOK_BLOCK_DIG) + .Const("BlockPlace", cPluginManager::HOOK_BLOCK_PLACE) + .Const("Disconnect", cPluginManager::HOOK_DISCONNECT) + .Const("Handshake", cPluginManager::HOOK_HANDSHAKE) + .Const("Login", cPluginManager::HOOK_LOGIN) + .Const("PlayerSpawn", cPluginManager::HOOK_PLAYER_SPAWN) + .Const("PlayerJoin", cPluginManager::HOOK_PLAYER_JOIN) + .Const("PlayerMove", cPluginManager::HOOK_PLAYER_MOVE) + .Const("TakeDamage", cPluginManager::HOOK_TAKE_DAMAGE) + .Const("Killed", cPluginManager::HOOK_KILLED) + .Const("ChunkGenerated", cPluginManager::HOOK_CHUNK_GENERATED) + .Const("ChunkGenerating", cPluginManager::HOOK_CHUNK_GENERATING) + .Const("BlockToDrops", cPluginManager::HOOK_BLOCK_TO_DROPS) + .Const("PreCrafting", cPluginManager::HOOK_PRE_CRAFTING) + .Const("CraftingNoRecipe", cPluginManager::HOOK_CRAFTING_NO_RECIPE) + .Const("PostCrafting", cPluginManager::HOOK_POST_CRAFTING) + .Const("BlockToPickup", cPluginManager::HOOK_BLOCK_TO_PICKUP) + .Const("WeatherChanged", cPluginManager::HOOK_WEATHER_CHANGED) + .Const("UpdatingSign", cPluginManager::HOOK_UPDATING_SIGN) + .Const("UpdatedSign", cPluginManager::HOOK_UPDATED_SIGN)); +} + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/squirrelbindings/SquirrelBindings.h b/source/squirrelbindings/SquirrelBindings.h index fef563f4a..dd2affda0 100644 --- a/source/squirrelbindings/SquirrelBindings.h +++ b/source/squirrelbindings/SquirrelBindings.h @@ -1,32 +1,32 @@ - -#pragma once - - - - - -#ifdef USE_SQUIRREL - - - - - -#include -#include - - - - - -void BindSquirrel(HSQUIRRELVM vm); - - - - - -#endif // USE_SQUIRREL - - - - - + +#pragma once + + + + + +#ifdef USE_SQUIRREL + + + + + +#include +#include + + + + + +void BindSquirrel(HSQUIRRELVM vm); + + + + + +#endif // USE_SQUIRREL + + + + + diff --git a/source/squirrelbindings/SquirrelFunctions.cpp b/source/squirrelbindings/SquirrelFunctions.cpp index 0e35f7361..9407670d4 100644 --- a/source/squirrelbindings/SquirrelFunctions.cpp +++ b/source/squirrelbindings/SquirrelFunctions.cpp @@ -1,94 +1,94 @@ - -#include "Globals.h" - - - - - -#ifdef USE_SQUIRREL - - - - - -#include "SquirrelFunctions.h" -#include "SquirrelBindings.h" - - - - - -static HSQUIRRELVM squirrelvm = NULL; - - - - - -SQInteger runtimeErrorHandler(HSQUIRRELVM a_VM) -{ - const SQChar *sErr = 0; - if(sq_gettop(a_VM) >= 1) - { - if(SQ_SUCCEEDED(sq_getstring(a_VM, 2, &sErr))) - { - LOGERROR("Squirrel Error: %s", sErr); - } - else - { - LOGERROR("Squirrel Error: Unknown Error"); - } - } - return 0; -} - -void compilerErrorHandler(HSQUIRRELVM v, - const SQChar* a_Desc, - const SQChar* a_Source, - SQInteger a_Line, - SQInteger a_Column) -{ - - LOGERROR("Squirrel Error: %s (%d:%d) %s", a_Source, a_Line, a_Column, a_Desc); -} - -HSQUIRRELVM OpenSquirrelVM() -{ - if(!squirrelvm) - { - squirrelvm = sq_open(1024); - Sqrat::DefaultVM::Set(squirrelvm); - - sq_newclosure(squirrelvm, runtimeErrorHandler, 0); - sq_seterrorhandler(squirrelvm); - - sq_setcompilererrorhandler(squirrelvm, compilerErrorHandler); - - BindSquirrel(squirrelvm); - } - return squirrelvm; -} - -void CloseSquirrelVM() -{ - if(squirrelvm) - { - sq_close(squirrelvm); - squirrelvm = NULL; - } -} - - -void sqPrint(SQChar * text) -{ - LOGINFO("%s", text); -} - - - - - -#endif // USE_SQUIRREL - - - - + +#include "Globals.h" + + + + + +#ifdef USE_SQUIRREL + + + + + +#include "SquirrelFunctions.h" +#include "SquirrelBindings.h" + + + + + +static HSQUIRRELVM squirrelvm = NULL; + + + + + +SQInteger runtimeErrorHandler(HSQUIRRELVM a_VM) +{ + const SQChar *sErr = 0; + if(sq_gettop(a_VM) >= 1) + { + if(SQ_SUCCEEDED(sq_getstring(a_VM, 2, &sErr))) + { + LOGERROR("Squirrel Error: %s", sErr); + } + else + { + LOGERROR("Squirrel Error: Unknown Error"); + } + } + return 0; +} + +void compilerErrorHandler(HSQUIRRELVM v, + const SQChar* a_Desc, + const SQChar* a_Source, + SQInteger a_Line, + SQInteger a_Column) +{ + + LOGERROR("Squirrel Error: %s (%d:%d) %s", a_Source, a_Line, a_Column, a_Desc); +} + +HSQUIRRELVM OpenSquirrelVM() +{ + if(!squirrelvm) + { + squirrelvm = sq_open(1024); + Sqrat::DefaultVM::Set(squirrelvm); + + sq_newclosure(squirrelvm, runtimeErrorHandler, 0); + sq_seterrorhandler(squirrelvm); + + sq_setcompilererrorhandler(squirrelvm, compilerErrorHandler); + + BindSquirrel(squirrelvm); + } + return squirrelvm; +} + +void CloseSquirrelVM() +{ + if(squirrelvm) + { + sq_close(squirrelvm); + squirrelvm = NULL; + } +} + + +void sqPrint(SQChar * text) +{ + LOGINFO("%s", text); +} + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/squirrelbindings/SquirrelFunctions.h b/source/squirrelbindings/SquirrelFunctions.h index 2b74fb52d..272b79c3e 100644 --- a/source/squirrelbindings/SquirrelFunctions.h +++ b/source/squirrelbindings/SquirrelFunctions.h @@ -1,28 +1,28 @@ - -#pragma once - -#ifdef USE_SQUIRREL - - - - -#include - - - - - -HSQUIRRELVM OpenSquirrelVM(); -void CloseSquirrelVM(); - -void sqPrint(SQChar * text); - - - - - -#endif // USE_SQUIRREL - - - - + +#pragma once + +#ifdef USE_SQUIRREL + + + + +#include + + + + + +HSQUIRRELVM OpenSquirrelVM(); +void CloseSquirrelVM(); + +void sqPrint(SQChar * text); + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/squirrelbindings/SquirrelObject.h b/source/squirrelbindings/SquirrelObject.h index a7a44d531..248a5e02c 100644 --- a/source/squirrelbindings/SquirrelObject.h +++ b/source/squirrelbindings/SquirrelObject.h @@ -1,55 +1,55 @@ - -#pragma once - - - - - -#ifdef USE_SQUIRREL - - - - - -#include - - - - - -class SquirrelObject -{ -public: - SquirrelObject(Sqrat::Object a_Obj) - { - m_SquirrelObject = a_Obj; - } - - Sqrat::Function GetFunction(const char *a_MethodName) - { - if(m_SquirrelObject.IsNull()) - return Sqrat::Function(); - - Sqrat::Function method(m_SquirrelObject, a_MethodName); - return method; - } - - bool HasFunction(const char *a_MethodName) - { - return !this->GetFunction(a_MethodName).IsNull(); - } - -protected: - Sqrat::Object m_SquirrelObject; - -}; - - - - - -#endif // USE_SQUIRREL - - - - + +#pragma once + + + + + +#ifdef USE_SQUIRREL + + + + + +#include + + + + + +class SquirrelObject +{ +public: + SquirrelObject(Sqrat::Object a_Obj) + { + m_SquirrelObject = a_Obj; + } + + Sqrat::Function GetFunction(const char *a_MethodName) + { + if(m_SquirrelObject.IsNull()) + return Sqrat::Function(); + + Sqrat::Function method(m_SquirrelObject, a_MethodName); + return method; + } + + bool HasFunction(const char *a_MethodName) + { + return !this->GetFunction(a_MethodName).IsNull(); + } + +protected: + Sqrat::Object m_SquirrelObject; + +}; + + + + + +#endif // USE_SQUIRREL + + + + diff --git a/source/virtual_method_hooks.lua b/source/virtual_method_hooks.lua index 407b61627..b6cbe8268 100644 --- a/source/virtual_method_hooks.lua +++ b/source/virtual_method_hooks.lua @@ -1,506 +1,506 @@ --- flags -local disable_virtual_hooks = false -local enable_pure_virtual = true -local default_private_access = false - -local access = {public = 0, protected = 1, private = 2} - -function preparse_hook(p) - - if default_private_access then - -- we need to make all structs 'public' by default - p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n") - end -end - - -function parser_hook(s) - - local container = classContainer.curr -- get the current container - - if default_private_access then - if not container.curr_member_access and container.classtype == 'class' then - -- default access for classes is private - container.curr_member_access = access.private - end - end - - -- try labels (public, private, etc) - do - local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type' - if b then - - -- found a label, get the new access value from the global 'access' table - if access[label] then - container.curr_member_access = access[label] - end -- else ? - - return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:] - end - end - - - local ret = nil - - if disable_virtual_hooks then - - return ret - end - - local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())") - local const - if b then - local ret = string.sub(s, e+1) - if string.find(ret, "^%s*const") then - const = "const" - ret = string.gsub(ret, "^%s*const", "") - end - local purev = false - if string.find(ret, "^%s*=%s*0") then - purev = true - ret = string.gsub(ret, "^%s*=%s*0", "") - end - ret = string.gsub(ret, "^%s*%b{}", "") - - local func = Function(decl, arg, const) - func.pure_virtual = purev - --func.access = access - func.original_sig = decl - - local curflags = classContainer.curr.flags - if not curflags.virtual_class then - - curflags.virtual_class = VirtualClass() - end - curflags.virtual_class:add(func) - curflags.pure_virtual = curflags.pure_virtual or purev - - return ret - end - - return ret -end - - --- class VirtualClass -classVirtualClass = { - classtype = 'class', - name = '', - base = '', - type = '', - btype = '', - ctype = '', -} -classVirtualClass.__index = classVirtualClass -setmetatable(classVirtualClass,classClass) - -function classVirtualClass:add(f) - - local parent = classContainer.curr - pop() - - table.insert(self.methods, {f=f}) - - local name,sig - - -- doble negative means positive - if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then - - name = self.original_name - elseif f.name == 'delete' then - name = '~'..self.original_name - else - if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then - name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name - end - end - - if name then - sig = name..self:get_arg_list(f, true)..";\n" - push(self) - sig = preprocess(sig) - self:parse(sig) - pop() - end - - push(parent) -end - -function preprocess(sig) - - sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' - sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' - sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' - sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*' - - return sig -end - -function classVirtualClass:get_arg_list(f, decl) - - local ret = "" - local sep = "" - local i=1 - while f.args[i] do - - local arg = f.args[i] - if decl then - local ptr - if arg.ret ~= '' then - ptr = arg.ret - else - ptr = arg.ptr - end - local def = "" - if arg.def and arg.def ~= "" then - - def = " = "..arg.def - end - ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def - else - ret = ret..sep..arg.name - end - - sep = "," - i = i+1 - end - - return "("..ret..")" -end - -function classVirtualClass:add_parent_virtual_methods(parent) - - parent = parent or _global_classes[self.flags.parent_object.btype] - - if not parent then return end - - if parent.flags.virtual_class then - - local vclass = parent.flags.virtual_class - for k,v in ipairs(vclass.methods) do - if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then - table.insert(self.methods, {f=v.f}) - end - end - end - - parent = _global_classes[parent.btype] - if parent then - self:add_parent_virtual_methods(parent) - end -end - -function classVirtualClass:has_method(f) - - for k,v in pairs(self.methods) do - -- just match name for now - if v.f.name == f.name then - return true - end - end - - return false -end - -function classVirtualClass:add_constructors() - - local i=1 - while self.flags.parent_object[i] do - - local v = self.flags.parent_object[i] - if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then - - self:add(v) - end - - i = i+1 - end - -end - ---[[ -function classVirtualClass:requirecollection(t) - - self:add_constructors() - local req = classClass.requirecollection(self, t) - if req then - output('class ',self.name,";") - end - return req -end ---]] - -function classVirtualClass:supcode() - - -- pure virtual classes can have no default constructors on gcc 4 - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n') - end - - local ns - if self.prox.classtype == 'namespace' then - output('namespace ',self.prox.name, " {") - ns = true - end - - output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {") - - output("public:\n") - - self:add_parent_virtual_methods() - - self:output_methods(self.btype) - self:output_parent_methods() - - self:add_constructors() - - -- no constructor for pure virtual classes - if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then - - self:output_constructors() - end - - output("};\n\n") - - if ns then - output("};") - end - - classClass.supcode(self) - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#endif // __GNUC__ >= 4\n') - end - - -- output collector for custom class if required - if self:requirecollection(_collect) and _collect[self.type] then - - output('\n') - output('/* function to release collected object via destructor */') - output('#ifdef __cplusplus\n') - --for i,v in pairs(collect) do - i,v = self.type, _collect[self.type] - output('\nstatic int '..v..' (lua_State* tolua_S)') - output('{') - output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);') - output(' delete self;') - output(' return 0;') - output('}') - --end - output('#endif\n\n') - end - -end - -function classVirtualClass:register(pre) - - -- pure virtual classes can have no default constructors on gcc 4 - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n') - end - - classClass.register(self, pre) - - if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then - output('#endif // __GNUC__ >= 4\n') - end -end - - ---function classVirtualClass:requirecollection(_c) --- if self.flags.parent_object.flags.pure_virtual then --- return false --- end --- return classClass.requirecollection(self, _c) ---end - -function classVirtualClass:output_parent_methods() - - for k,v in ipairs(self.methods) do - - if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then - - local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." " - local parent_name = rettype..self.btype.."__"..v.f.name - - local par_list = self:get_arg_list(v.f, true) - local var_list = self:get_arg_list(v.f, false) - - -- the parent's virtual function - output("\t"..parent_name..par_list.." {") - - output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";") - output("\t};") - end - end -end - -function classVirtualClass:output_methods(btype) - - for k,v in ipairs(self.methods) do - - if v.f.name ~= 'new' and v.f.name ~= 'delete' then - - self:output_method(v.f, btype) - end - end - output("\n") -end - -function classVirtualClass:output_constructors() - - for k,v in ipairs(self.methods) do - - if v.f.name == 'new' then - - local par_list = self:get_arg_list(v.f, true) - local var_list = self:get_arg_list(v.f, false) - - output("\t",self.original_name,par_list,":",self.btype,var_list,"{};") - end - end -end - -function classVirtualClass:output_method(f, btype) - - if f.access == 2 then -- private - return - end - - local ptr - if f.ret ~= '' then - ptr = f.ret - else - ptr = f.ptr - end - - local rettype = f.mod.." "..f.type..f.ptr.." " - local par_list = self:get_arg_list(f, true) - local var_list = self:get_arg_list(f, false) - - if string.find(rettype, "%s*LuaQtGenericFlags%s*") then - - _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+") - end - - -- the caller of the lua method - output("\t"..rettype.." "..f.name..par_list..f.const.." {") - local fn = f.cname - if f.access == 1 then - fn = "NULL" - end - output('\t\tif (push_method("',f.lname,'", ',fn,')) {') - - --if f.type ~= 'void' then - -- output("\t\t\tint top = lua_gettop(lua_state)-1;") - --end - - -- push the parameters - local argn = 0 - for i,arg in ipairs(f.args) do - if arg.type ~= 'void' then - local t,ct = isbasic(arg.type) - if t and t ~= '' then - if arg.ret == "*" then - t = 'userdata' - ct = 'void*' - end - output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");"); - else - local m = arg.ptr - if m and m~= "" then - if m == "*" then m = "" end - output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");") - else - output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n") - output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n') - end - end - argn = argn+1 - end - end - - -- call the function - output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ") - - -- return value - if f.type ~= 'void' then - output("1);") - - local t,ct = isbasic(f.type) - if t and t ~= '' then - --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);") - output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);") - else - - local mod = "" - if f.ptr ~= "*" then - mod = "*("..f.type.."*)" - end - - --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);") - output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);") - end - output("\t\t\tlua_pop(lua_state, 1);") - output("\t\t\treturn tolua_ret;") - else - output("0);") - end - - -- handle non-implemeted function - output("\t\t} else {") - - if f.pure_virtual then - - output('\t\t\tif (lua_state)') - --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);') - output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");') - output('\t\t\telse {') - output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");') - output('\t\t\t\t::abort();') - output('\t\t\t};') - if( rettype == " std::string " ) then - output('\t\t\treturn "";') - else - output('\t\t\treturn (',rettype,')0;') - end - else - - output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';') - end - - output("\t\t};") - - output("\t};") -end - -function VirtualClass() - - local parent = classContainer.curr - pop() - - local name = "Lua__"..parent.original_name - - local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil}) - setmetatable(c, classVirtualClass) - - local ft = getnamespace(c.parent)..c.original_name - append_global_type(ft, c) - - push(parent) - - c.flags.parent_object = parent - c.methods = {} - - push(c) - c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n") - pop() - - return c -end - - - - - +-- flags +local disable_virtual_hooks = false +local enable_pure_virtual = true +local default_private_access = false + +local access = {public = 0, protected = 1, private = 2} + +function preparse_hook(p) + + if default_private_access then + -- we need to make all structs 'public' by default + p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n") + end +end + + +function parser_hook(s) + + local container = classContainer.curr -- get the current container + + if default_private_access then + if not container.curr_member_access and container.classtype == 'class' then + -- default access for classes is private + container.curr_member_access = access.private + end + end + + -- try labels (public, private, etc) + do + local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type' + if b then + + -- found a label, get the new access value from the global 'access' table + if access[label] then + container.curr_member_access = access[label] + end -- else ? + + return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:] + end + end + + + local ret = nil + + if disable_virtual_hooks then + + return ret + end + + local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())") + local const + if b then + local ret = string.sub(s, e+1) + if string.find(ret, "^%s*const") then + const = "const" + ret = string.gsub(ret, "^%s*const", "") + end + local purev = false + if string.find(ret, "^%s*=%s*0") then + purev = true + ret = string.gsub(ret, "^%s*=%s*0", "") + end + ret = string.gsub(ret, "^%s*%b{}", "") + + local func = Function(decl, arg, const) + func.pure_virtual = purev + --func.access = access + func.original_sig = decl + + local curflags = classContainer.curr.flags + if not curflags.virtual_class then + + curflags.virtual_class = VirtualClass() + end + curflags.virtual_class:add(func) + curflags.pure_virtual = curflags.pure_virtual or purev + + return ret + end + + return ret +end + + +-- class VirtualClass +classVirtualClass = { + classtype = 'class', + name = '', + base = '', + type = '', + btype = '', + ctype = '', +} +classVirtualClass.__index = classVirtualClass +setmetatable(classVirtualClass,classClass) + +function classVirtualClass:add(f) + + local parent = classContainer.curr + pop() + + table.insert(self.methods, {f=f}) + + local name,sig + + -- doble negative means positive + if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then + + name = self.original_name + elseif f.name == 'delete' then + name = '~'..self.original_name + else + if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then + name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name + end + end + + if name then + sig = name..self:get_arg_list(f, true)..";\n" + push(self) + sig = preprocess(sig) + self:parse(sig) + pop() + end + + push(parent) +end + +function preprocess(sig) + + sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' + sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' + sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' + sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*' + + return sig +end + +function classVirtualClass:get_arg_list(f, decl) + + local ret = "" + local sep = "" + local i=1 + while f.args[i] do + + local arg = f.args[i] + if decl then + local ptr + if arg.ret ~= '' then + ptr = arg.ret + else + ptr = arg.ptr + end + local def = "" + if arg.def and arg.def ~= "" then + + def = " = "..arg.def + end + ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def + else + ret = ret..sep..arg.name + end + + sep = "," + i = i+1 + end + + return "("..ret..")" +end + +function classVirtualClass:add_parent_virtual_methods(parent) + + parent = parent or _global_classes[self.flags.parent_object.btype] + + if not parent then return end + + if parent.flags.virtual_class then + + local vclass = parent.flags.virtual_class + for k,v in ipairs(vclass.methods) do + if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then + table.insert(self.methods, {f=v.f}) + end + end + end + + parent = _global_classes[parent.btype] + if parent then + self:add_parent_virtual_methods(parent) + end +end + +function classVirtualClass:has_method(f) + + for k,v in pairs(self.methods) do + -- just match name for now + if v.f.name == f.name then + return true + end + end + + return false +end + +function classVirtualClass:add_constructors() + + local i=1 + while self.flags.parent_object[i] do + + local v = self.flags.parent_object[i] + if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then + + self:add(v) + end + + i = i+1 + end + +end + +--[[ +function classVirtualClass:requirecollection(t) + + self:add_constructors() + local req = classClass.requirecollection(self, t) + if req then + output('class ',self.name,";") + end + return req +end +--]] + +function classVirtualClass:supcode() + + -- pure virtual classes can have no default constructors on gcc 4 + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n') + end + + local ns + if self.prox.classtype == 'namespace' then + output('namespace ',self.prox.name, " {") + ns = true + end + + output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {") + + output("public:\n") + + self:add_parent_virtual_methods() + + self:output_methods(self.btype) + self:output_parent_methods() + + self:add_constructors() + + -- no constructor for pure virtual classes + if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then + + self:output_constructors() + end + + output("};\n\n") + + if ns then + output("};") + end + + classClass.supcode(self) + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#endif // __GNUC__ >= 4\n') + end + + -- output collector for custom class if required + if self:requirecollection(_collect) and _collect[self.type] then + + output('\n') + output('/* function to release collected object via destructor */') + output('#ifdef __cplusplus\n') + --for i,v in pairs(collect) do + i,v = self.type, _collect[self.type] + output('\nstatic int '..v..' (lua_State* tolua_S)') + output('{') + output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);') + output(' delete self;') + output(' return 0;') + output('}') + --end + output('#endif\n\n') + end + +end + +function classVirtualClass:register(pre) + + -- pure virtual classes can have no default constructors on gcc 4 + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n') + end + + classClass.register(self, pre) + + if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then + output('#endif // __GNUC__ >= 4\n') + end +end + + +--function classVirtualClass:requirecollection(_c) +-- if self.flags.parent_object.flags.pure_virtual then +-- return false +-- end +-- return classClass.requirecollection(self, _c) +--end + +function classVirtualClass:output_parent_methods() + + for k,v in ipairs(self.methods) do + + if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then + + local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." " + local parent_name = rettype..self.btype.."__"..v.f.name + + local par_list = self:get_arg_list(v.f, true) + local var_list = self:get_arg_list(v.f, false) + + -- the parent's virtual function + output("\t"..parent_name..par_list.." {") + + output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";") + output("\t};") + end + end +end + +function classVirtualClass:output_methods(btype) + + for k,v in ipairs(self.methods) do + + if v.f.name ~= 'new' and v.f.name ~= 'delete' then + + self:output_method(v.f, btype) + end + end + output("\n") +end + +function classVirtualClass:output_constructors() + + for k,v in ipairs(self.methods) do + + if v.f.name == 'new' then + + local par_list = self:get_arg_list(v.f, true) + local var_list = self:get_arg_list(v.f, false) + + output("\t",self.original_name,par_list,":",self.btype,var_list,"{};") + end + end +end + +function classVirtualClass:output_method(f, btype) + + if f.access == 2 then -- private + return + end + + local ptr + if f.ret ~= '' then + ptr = f.ret + else + ptr = f.ptr + end + + local rettype = f.mod.." "..f.type..f.ptr.." " + local par_list = self:get_arg_list(f, true) + local var_list = self:get_arg_list(f, false) + + if string.find(rettype, "%s*LuaQtGenericFlags%s*") then + + _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+") + end + + -- the caller of the lua method + output("\t"..rettype.." "..f.name..par_list..f.const.." {") + local fn = f.cname + if f.access == 1 then + fn = "NULL" + end + output('\t\tif (push_method("',f.lname,'", ',fn,')) {') + + --if f.type ~= 'void' then + -- output("\t\t\tint top = lua_gettop(lua_state)-1;") + --end + + -- push the parameters + local argn = 0 + for i,arg in ipairs(f.args) do + if arg.type ~= 'void' then + local t,ct = isbasic(arg.type) + if t and t ~= '' then + if arg.ret == "*" then + t = 'userdata' + ct = 'void*' + end + output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");"); + else + local m = arg.ptr + if m and m~= "" then + if m == "*" then m = "" end + output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");") + else + output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n") + output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n') + end + end + argn = argn+1 + end + end + + -- call the function + output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ") + + -- return value + if f.type ~= 'void' then + output("1);") + + local t,ct = isbasic(f.type) + if t and t ~= '' then + --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);") + output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);") + else + + local mod = "" + if f.ptr ~= "*" then + mod = "*("..f.type.."*)" + end + + --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);") + output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);") + end + output("\t\t\tlua_pop(lua_state, 1);") + output("\t\t\treturn tolua_ret;") + else + output("0);") + end + + -- handle non-implemeted function + output("\t\t} else {") + + if f.pure_virtual then + + output('\t\t\tif (lua_state)') + --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);') + output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");') + output('\t\t\telse {') + output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");') + output('\t\t\t\t::abort();') + output('\t\t\t};') + if( rettype == " std::string " ) then + output('\t\t\treturn "";') + else + output('\t\t\treturn (',rettype,')0;') + end + else + + output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';') + end + + output("\t\t};") + + output("\t};") +end + +function VirtualClass() + + local parent = classContainer.curr + pop() + + local name = "Lua__"..parent.original_name + + local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil}) + setmetatable(c, classVirtualClass) + + local ft = getnamespace(c.parent)..c.original_name + append_global_type(ft, c) + + push(parent) + + c.flags.parent_object = parent + c.methods = {} + + push(c) + c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n") + pop() + + return c +end + + + + + -- cgit v1.2.3