summaryrefslogtreecommitdiffstats
path: root/source/Blocks
diff options
context:
space:
mode:
Diffstat (limited to 'source/Blocks')
-rw-r--r--source/Blocks/BlockBed.cpp88
-rw-r--r--source/Blocks/BlockBed.h73
-rw-r--r--source/Blocks/BlockBrewingStand.h32
-rw-r--r--source/Blocks/BlockCactus.h88
-rw-r--r--source/Blocks/BlockCarpet.h54
-rw-r--r--source/Blocks/BlockCauldron.h59
-rw-r--r--source/Blocks/BlockChest.h223
-rw-r--r--source/Blocks/BlockCloth.h34
-rw-r--r--source/Blocks/BlockCobWeb.h30
-rw-r--r--source/Blocks/BlockCrops.h108
-rw-r--r--source/Blocks/BlockDeadBush.h47
-rw-r--r--source/Blocks/BlockDirt.h88
-rw-r--r--source/Blocks/BlockDoor.cpp88
-rw-r--r--source/Blocks/BlockDoor.h105
-rw-r--r--source/Blocks/BlockDropSpenser.h41
-rw-r--r--source/Blocks/BlockEnderchest.h28
-rw-r--r--source/Blocks/BlockEntity.h31
-rw-r--r--source/Blocks/BlockFarmland.h99
-rw-r--r--source/Blocks/BlockFenceGate.h60
-rw-r--r--source/Blocks/BlockFire.h42
-rw-r--r--source/Blocks/BlockFlower.h53
-rw-r--r--source/Blocks/BlockFlowerPot.h105
-rw-r--r--source/Blocks/BlockFluid.h50
-rw-r--r--source/Blocks/BlockFurnace.h47
-rw-r--r--source/Blocks/BlockGlass.h26
-rw-r--r--source/Blocks/BlockGlowstone.h30
-rw-r--r--source/Blocks/BlockGravel.h27
-rw-r--r--source/Blocks/BlockHandler.cpp456
-rw-r--r--source/Blocks/BlockHandler.h158
-rw-r--r--source/Blocks/BlockHopper.h46
-rw-r--r--source/Blocks/BlockIce.h37
-rw-r--r--source/Blocks/BlockLadder.h115
-rw-r--r--source/Blocks/BlockLeaves.h184
-rw-r--r--source/Blocks/BlockLever.cpp48
-rw-r--r--source/Blocks/BlockLever.h61
-rw-r--r--source/Blocks/BlockMelon.h35
-rw-r--r--source/Blocks/BlockMushroom.h71
-rw-r--r--source/Blocks/BlockMycelium.h27
-rw-r--r--source/Blocks/BlockNote.h13
-rw-r--r--source/Blocks/BlockOre.h80
-rw-r--r--source/Blocks/BlockPiston.cpp102
-rw-r--r--source/Blocks/BlockPiston.h43
-rw-r--r--source/Blocks/BlockRail.h421
-rw-r--r--source/Blocks/BlockRedstone.cpp27
-rw-r--r--source/Blocks/BlockRedstone.h46
-rw-r--r--source/Blocks/BlockRedstoneRepeater.cpp47
-rw-r--r--source/Blocks/BlockRedstoneRepeater.h60
-rw-r--r--source/Blocks/BlockRedstoneTorch.h36
-rw-r--r--source/Blocks/BlockSand.h28
-rw-r--r--source/Blocks/BlockSapling.h69
-rw-r--r--source/Blocks/BlockSign.h84
-rw-r--r--source/Blocks/BlockSlab.h70
-rw-r--r--source/Blocks/BlockSnow.h52
-rw-r--r--source/Blocks/BlockStairs.h153
-rw-r--r--source/Blocks/BlockStems.h58
-rw-r--r--source/Blocks/BlockStone.h29
-rw-r--r--source/Blocks/BlockSugarcane.h96
-rw-r--r--source/Blocks/BlockTallGrass.h51
-rw-r--r--source/Blocks/BlockTorch.h251
-rw-r--r--source/Blocks/BlockVine.h200
-rw-r--r--source/Blocks/BlockWood.h27
-rw-r--r--source/Blocks/BlockWorkbench.h43
62 files changed, 5080 insertions, 0 deletions
diff --git a/source/Blocks/BlockBed.cpp b/source/Blocks/BlockBed.cpp
new file mode 100644
index 000000000..f5e9db88d
--- /dev/null
+++ b/source/Blocks/BlockBed.cpp
@@ -0,0 +1,88 @@
+#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() != dimOverworld)
+ {
+ Vector3i Coords(a_BlockX, a_BlockY, a_BlockZ);
+ a_World->DoExplosiontAt(5, a_BlockX, a_BlockY, a_BlockZ, true, esBed, &Coords);
+ }
+ 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
new file mode 100644
index 000000000..8286ceb11
--- /dev/null
+++ b/source/Blocks/BlockBed.h
@@ -0,0 +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();
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockBrewingStand.h b/source/Blocks/BlockBrewingStand.h
new file mode 100644
index 000000000..57642bcb6
--- /dev/null
+++ b/source/Blocks/BlockBrewingStand.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCactus.h b/source/Blocks/BlockCactus.h
new file mode 100644
index 000000000..1d123bc0a
--- /dev/null
+++ b/source/Blocks/BlockCactus.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCarpet.h b/source/Blocks/BlockCarpet.h
new file mode 100644
index 000000000..f70ff45b6
--- /dev/null
+++ b/source/Blocks/BlockCarpet.h
@@ -0,0 +1,54 @@
+
+// BlockCarpet.h
+
+// Declares the cBlockCarpetHandler class representing the handler for the carpet block
+
+
+
+
+#pragma once
+
+
+
+
+
+class cBlockCarpetHandler :
+ public cBlockHandler
+{
+public:
+ cBlockCarpetHandler(BLOCKTYPE a_BlockType);
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.cloth";
+ }
+
+
+ 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 = a_Player->GetEquippedItem().m_ItemDamage & 0x0f;
+ return true;
+ }
+
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_CARPET, a_BlockMeta));
+ }
+
+
+ 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));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCauldron.h b/source/Blocks/BlockCauldron.h
new file mode 100644
index 000000000..b0e00f869
--- /dev/null
+++ b/source/Blocks/BlockCauldron.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockChest.h b/source/Blocks/BlockChest.h
new file mode 100644
index 000000000..8164942a3
--- /dev/null
+++ b/source/Blocks/BlockChest.h
@@ -0,0 +1,223 @@
+
+#pragma once
+
+#include "BlockEntity.h"
+#include "../World.h"
+#include "../BlockArea.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
new file mode 100644
index 000000000..a136d3b9d
--- /dev/null
+++ b/source/Blocks/BlockCloth.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCobWeb.h b/source/Blocks/BlockCobWeb.h
new file mode 100644
index 000000000..982bfaa30
--- /dev/null
+++ b/source/Blocks/BlockCobWeb.h
@@ -0,0 +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));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockCrops.h b/source/Blocks/BlockCrops.h
new file mode 100644
index 000000000..4bc76fd50
--- /dev/null
+++ b/source/Blocks/BlockCrops.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockDeadBush.h b/source/Blocks/BlockDeadBush.h
new file mode 100644
index 000000000..379e8e5df
--- /dev/null
+++ b/source/Blocks/BlockDeadBush.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockDirt.h b/source/Blocks/BlockDirt.h
new file mode 100644
index 000000000..b2bc4756c
--- /dev/null
+++ b/source/Blocks/BlockDirt.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockDoor.cpp b/source/Blocks/BlockDoor.cpp
new file mode 100644
index 000000000..b2fe273c5
--- /dev/null
+++ b/source/Blocks/BlockDoor.cpp
@@ -0,0 +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";
+}
+
+
+
+
diff --git a/source/Blocks/BlockDoor.h b/source/Blocks/BlockDoor.h
new file mode 100644
index 000000000..19701ffc9
--- /dev/null
+++ b/source/Blocks/BlockDoor.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockDropSpenser.h b/source/Blocks/BlockDropSpenser.h
new file mode 100644
index 000000000..e5572da8a
--- /dev/null
+++ b/source/Blocks/BlockDropSpenser.h
@@ -0,0 +1,41 @@
+
+// BlockDropSpenser.h
+
+// Declares the cBlockDropSpenserHandler class representing the BlockHandler for Dropper and Dispenser blocks
+
+#pragma once
+
+#include "../Piston.h"
+
+
+
+
+
+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
new file mode 100644
index 000000000..0ce813f1c
--- /dev/null
+++ b/source/Blocks/BlockEnderchest.h
@@ -0,0 +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));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockEntity.h b/source/Blocks/BlockEntity.h
new file mode 100644
index 000000000..9c6b23665
--- /dev/null
+++ b/source/Blocks/BlockEntity.h
@@ -0,0 +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;
+ }
+};
+
+
+
+
diff --git a/source/Blocks/BlockFarmland.h b/source/Blocks/BlockFarmland.h
new file mode 100644
index 000000000..6cab1fa38
--- /dev/null
+++ b/source/Blocks/BlockFarmland.h
@@ -0,0 +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;
+ }
+ }
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFenceGate.h b/source/Blocks/BlockFenceGate.h
new file mode 100644
index 000000000..d6f8aa85f
--- /dev/null
+++ b/source/Blocks/BlockFenceGate.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFire.h b/source/Blocks/BlockFire.h
new file mode 100644
index 000000000..d3ba499b1
--- /dev/null
+++ b/source/Blocks/BlockFire.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFlower.h b/source/Blocks/BlockFlower.h
new file mode 100644
index 000000000..952901ba5
--- /dev/null
+++ b/source/Blocks/BlockFlower.h
@@ -0,0 +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 true;
+ }
+
+
+ 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
new file mode 100644
index 000000000..b0faf5218
--- /dev/null
+++ b/source/Blocks/BlockFlowerPot.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFluid.h b/source/Blocks/BlockFluid.h
new file mode 100644
index 000000000..696bfb3ce
--- /dev/null
+++ b/source/Blocks/BlockFluid.h
@@ -0,0 +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);
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockFurnace.h b/source/Blocks/BlockFurnace.h
new file mode 100644
index 000000000..b358a1d71
--- /dev/null
+++ b/source/Blocks/BlockFurnace.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockGlass.h b/source/Blocks/BlockGlass.h
new file mode 100644
index 000000000..f6958bbb6
--- /dev/null
+++ b/source/Blocks/BlockGlass.h
@@ -0,0 +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
+ {
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockGlowstone.h b/source/Blocks/BlockGlowstone.h
new file mode 100644
index 000000000..5f0d95dee
--- /dev/null
+++ b/source/Blocks/BlockGlowstone.h
@@ -0,0 +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));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockGravel.h b/source/Blocks/BlockGravel.h
new file mode 100644
index 000000000..e1c9ff390
--- /dev/null
+++ b/source/Blocks/BlockGravel.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockHandler.cpp b/source/Blocks/BlockHandler.cpp
new file mode 100644
index 000000000..cbca5a4ec
--- /dev/null
+++ b/source/Blocks/BlockHandler.cpp
@@ -0,0 +1,456 @@
+
+#include "Globals.h"
+#include "BlockHandler.h"
+#include "../Item.h"
+#include "../World.h"
+#include "../Root.h"
+#include "../PluginManager.h"
+#include "BlockBed.h"
+#include "BlockBrewingStand.h"
+#include "BlockCactus.h"
+#include "BlockCarpet.h"
+#include "BlockCauldron.h"
+#include "BlockChest.h"
+#include "BlockCloth.h"
+#include "BlockCobWeb.h"
+#include "BlockCrops.h"
+#include "BlockDeadBush.h"
+#include "BlockDirt.h"
+#include "BlockDoor.h"
+#include "BlockDropSpenser.h"
+#include "BlockEnderchest.h"
+#include "BlockEntity.h"
+#include "BlockFarmland.h"
+#include "BlockFenceGate.h"
+#include "BlockFire.h"
+#include "BlockFlower.h"
+#include "BlockFlowerPot.h"
+#include "BlockFluid.h"
+#include "BlockFurnace.h"
+#include "BlockGlass.h"
+#include "BlockGlowstone.h"
+#include "BlockGravel.h"
+#include "BlockHopper.h"
+#include "BlockIce.h"
+#include "BlockLadder.h"
+#include "BlockLeaves.h"
+#include "BlockLever.h"
+#include "BlockMelon.h"
+#include "BlockMushroom.h"
+#include "BlockMycelium.h"
+#include "BlockNote.h"
+#include "BlockOre.h"
+#include "BlockPiston.h"
+#include "BlockRail.h"
+#include "BlockRedstone.h"
+#include "BlockRedstoneRepeater.h"
+#include "BlockRedstoneTorch.h"
+#include "BlockSand.h"
+#include "BlockSapling.h"
+#include "BlockSign.h"
+#include "BlockSlab.h"
+#include "BlockSnow.h"
+#include "BlockStairs.h"
+#include "BlockStems.h"
+#include "BlockStone.h"
+#include "BlockSugarcane.h"
+#include "BlockTallGrass.h"
+#include "BlockTorch.h"
+#include "BlockVine.h"
+#include "BlockWood.h"
+#include "BlockWorkbench.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_PISTON_EXTENSION: return new cBlockPistonHeadHandler ();
+ case E_BLOCK_PLANKS: return new cBlockWoodHandler (a_BlockType);
+ case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType);
+ case E_BLOCK_QUARTZ_STAIR: return new cBlockStairsHandler (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
new file mode 100644
index 000000000..228ce174b
--- /dev/null
+++ b/source/Blocks/BlockHandler.h
@@ -0,0 +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);
+}
+
+
+
+
diff --git a/source/Blocks/BlockHopper.h b/source/Blocks/BlockHopper.h
new file mode 100644
index 000000000..3998276d7
--- /dev/null
+++ b/source/Blocks/BlockHopper.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockIce.h b/source/Blocks/BlockIce.h
new file mode 100644
index 000000000..11fe425f3
--- /dev/null
+++ b/source/Blocks/BlockIce.h
@@ -0,0 +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
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockLadder.h b/source/Blocks/BlockLadder.h
new file mode 100644
index 000000000..c0aa25f60
--- /dev/null
+++ b/source/Blocks/BlockLadder.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockLeaves.h b/source/Blocks/BlockLeaves.h
new file mode 100644
index 000000000..6e015b8fa
--- /dev/null
+++ b/source/Blocks/BlockLeaves.h
@@ -0,0 +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;
+}
+
+
+
+
diff --git a/source/Blocks/BlockLever.cpp b/source/Blocks/BlockLever.cpp
new file mode 100644
index 000000000..6dbff10c1
--- /dev/null
+++ b/source/Blocks/BlockLever.cpp
@@ -0,0 +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);
+}
+
+
+
+
diff --git a/source/Blocks/BlockLever.h b/source/Blocks/BlockLever.h
new file mode 100644
index 000000000..362cf563e
--- /dev/null
+++ b/source/Blocks/BlockLever.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockMelon.h b/source/Blocks/BlockMelon.h
new file mode 100644
index 000000000..2f7d9a461
--- /dev/null
+++ b/source/Blocks/BlockMelon.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockMushroom.h b/source/Blocks/BlockMushroom.h
new file mode 100644
index 000000000..b3b23e2ba
--- /dev/null
+++ b/source/Blocks/BlockMushroom.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockMycelium.h b/source/Blocks/BlockMycelium.h
new file mode 100644
index 000000000..0ed7162ac
--- /dev/null
+++ b/source/Blocks/BlockMycelium.h
@@ -0,0 +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));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockNote.h b/source/Blocks/BlockNote.h
new file mode 100644
index 000000000..fef38d845
--- /dev/null
+++ b/source/Blocks/BlockNote.h
@@ -0,0 +1,13 @@
+#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
new file mode 100644
index 000000000..9684dbb19
--- /dev/null
+++ b/source/Blocks/BlockOre.h
@@ -0,0 +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));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockPiston.cpp b/source/Blocks/BlockPiston.cpp
new file mode 100644
index 000000000..1bfac3ca1
--- /dev/null
+++ b/source/Blocks/BlockPiston.cpp
@@ -0,0 +1,102 @@
+
+#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)
+{
+ NIBBLETYPE 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;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBlockPistonHeadHandler:
+
+cBlockPistonHeadHandler::cBlockPistonHeadHandler(void) :
+ super(E_BLOCK_PISTON_EXTENSION)
+{
+}
+
+
+
+
+
+void cBlockPistonHeadHandler::OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ NIBBLETYPE 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);
+
+ BLOCKTYPE Block = a_World->GetBlock(newX, newY, newZ);
+ if ((Block == E_BLOCK_STICKY_PISTON) || (Block == E_BLOCK_PISTON))
+ {
+ a_World->DigBlock(newX, newY, newZ);
+ }
+}
+
+
+
+
+
diff --git a/source/Blocks/BlockPiston.h b/source/Blocks/BlockPiston.h
new file mode 100644
index 000000000..109f5ea8b
--- /dev/null
+++ b/source/Blocks/BlockPiston.h
@@ -0,0 +1,43 @@
+
+#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;
+} ;
+
+
+
+
+
+class cBlockPistonHeadHandler :
+ public cBlockHandler
+{
+ typedef cBlockHandler super;
+
+public:
+ cBlockPistonHeadHandler(void);
+
+ virtual void OnDestroyedByPlayer(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockRail.h b/source/Blocks/BlockRail.h
new file mode 100644
index 000000000..65d120923
--- /dev/null
+++ b/source/Blocks/BlockRail.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockRedstone.cpp b/source/Blocks/BlockRedstone.cpp
new file mode 100644
index 000000000..35cdc34cf
--- /dev/null
+++ b/source/Blocks/BlockRedstone.cpp
@@ -0,0 +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
+}
+
+
+
+
diff --git a/source/Blocks/BlockRedstone.h b/source/Blocks/BlockRedstone.h
new file mode 100644
index 000000000..ae0466937
--- /dev/null
+++ b/source/Blocks/BlockRedstone.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockRedstoneRepeater.cpp b/source/Blocks/BlockRedstoneRepeater.cpp
new file mode 100644
index 000000000..9857dd223
--- /dev/null
+++ b/source/Blocks/BlockRedstoneRepeater.cpp
@@ -0,0 +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);
+}
+
+
+
+
diff --git a/source/Blocks/BlockRedstoneRepeater.h b/source/Blocks/BlockRedstoneRepeater.h
new file mode 100644
index 000000000..f3e250963
--- /dev/null
+++ b/source/Blocks/BlockRedstoneRepeater.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockRedstoneTorch.h b/source/Blocks/BlockRedstoneTorch.h
new file mode 100644
index 000000000..cb897ba3f
--- /dev/null
+++ b/source/Blocks/BlockRedstoneTorch.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSand.h b/source/Blocks/BlockSand.h
new file mode 100644
index 000000000..3fc271483
--- /dev/null
+++ b/source/Blocks/BlockSand.h
@@ -0,0 +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";
+ }
+
+};
+
+
+
+
diff --git a/source/Blocks/BlockSapling.h b/source/Blocks/BlockSapling.h
new file mode 100644
index 000000000..17ef4984f
--- /dev/null
+++ b/source/Blocks/BlockSapling.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSign.h b/source/Blocks/BlockSign.h
new file mode 100644
index 000000000..09fa721b6
--- /dev/null
+++ b/source/Blocks/BlockSign.h
@@ -0,0 +1,84 @@
+
+#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;
+ }
+
+
+ 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
+ {
+ a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSlab.h b/source/Blocks/BlockSlab.h
new file mode 100644
index 000000000..f34f42bae
--- /dev/null
+++ b/source/Blocks/BlockSlab.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSnow.h b/source/Blocks/BlockSnow.h
new file mode 100644
index 000000000..bdd9f0b87
--- /dev/null
+++ b/source/Blocks/BlockSnow.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockStairs.h b/source/Blocks/BlockStairs.h
new file mode 100644
index 000000000..485ebda1a
--- /dev/null
+++ b/source/Blocks/BlockStairs.h
@@ -0,0 +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;
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockStems.h b/source/Blocks/BlockStems.h
new file mode 100644
index 000000000..ce02d9cb8
--- /dev/null
+++ b/source/Blocks/BlockStems.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockStone.h b/source/Blocks/BlockStone.h
new file mode 100644
index 000000000..af4c6509a
--- /dev/null
+++ b/source/Blocks/BlockStone.h
@@ -0,0 +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));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockSugarcane.h b/source/Blocks/BlockSugarcane.h
new file mode 100644
index 000000000..9d66d6be6
--- /dev/null
+++ b/source/Blocks/BlockSugarcane.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockTallGrass.h b/source/Blocks/BlockTallGrass.h
new file mode 100644
index 000000000..cd27ab7e6
--- /dev/null
+++ b/source/Blocks/BlockTallGrass.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockTorch.h b/source/Blocks/BlockTorch.h
new file mode 100644
index 000000000..99c1a7b75
--- /dev/null
+++ b/source/Blocks/BlockTorch.h
@@ -0,0 +1,251 @@
+#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 true;
+ //was false
+ }
+
+
+ static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_Direction)
+ {
+ if ( g_BlockIsSolid[a_BlockType] ) {
+ return (a_Direction == 0x1); // allow only direction "standing on floor"
+ }
+ else {
+ 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
new file mode 100644
index 000000000..0bc935272
--- /dev/null
+++ b/source/Blocks/BlockVine.h
@@ -0,0 +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));
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockWood.h b/source/Blocks/BlockWood.h
new file mode 100644
index 000000000..4e2246506
--- /dev/null
+++ b/source/Blocks/BlockWood.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+
diff --git a/source/Blocks/BlockWorkbench.h b/source/Blocks/BlockWorkbench.h
new file mode 100644
index 000000000..60aa1791b
--- /dev/null
+++ b/source/Blocks/BlockWorkbench.h
@@ -0,0 +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";
+ }
+} ;
+
+
+
+