summaryrefslogtreecommitdiffstats
path: root/src/Items
diff options
context:
space:
mode:
Diffstat (limited to 'src/Items')
-rw-r--r--src/Items/CMakeLists.txt24
-rw-r--r--src/Items/ItemAnvil.h30
-rw-r--r--src/Items/ItemBanner.h160
-rw-r--r--src/Items/ItemBed.h60
-rw-r--r--src/Items/ItemBigFlower.h36
-rw-r--r--src/Items/ItemBucket.h6
-rw-r--r--src/Items/ItemButton.h45
-rw-r--r--src/Items/ItemChest.h83
-rw-r--r--src/Items/ItemComparator.h14
-rw-r--r--src/Items/ItemDoor.h67
-rw-r--r--src/Items/ItemDropSpenser.h26
-rw-r--r--src/Items/ItemDye.h2
-rw-r--r--src/Items/ItemEnchantingTable.h32
-rw-r--r--src/Items/ItemEndPortalFrame.h26
-rw-r--r--src/Items/ItemEnderchest.h26
-rw-r--r--src/Items/ItemFenceGate.h26
-rw-r--r--src/Items/ItemFurnace.h26
-rw-r--r--src/Items/ItemGlazedTerracotta.h26
-rw-r--r--src/Items/ItemHandler.cpp344
-rw-r--r--src/Items/ItemHandler.h54
-rw-r--r--src/Items/ItemHopper.h40
-rw-r--r--src/Items/ItemJackOLantern.h27
-rw-r--r--src/Items/ItemLadder.h77
-rw-r--r--src/Items/ItemLeaves.h20
-rw-r--r--src/Items/ItemLever.h43
-rw-r--r--src/Items/ItemMobHead.h117
-rw-r--r--src/Items/ItemNetherWart.h32
-rw-r--r--src/Items/ItemObserver.h26
-rw-r--r--src/Items/ItemPiston.h26
-rw-r--r--src/Items/ItemPlanks.h25
-rw-r--r--src/Items/ItemPumpkin.h70
-rw-r--r--src/Items/ItemQuartz.h60
-rw-r--r--src/Items/ItemRail.h28
-rw-r--r--src/Items/ItemRedstoneDust.h56
-rw-r--r--src/Items/ItemRedstoneRepeater.h14
-rw-r--r--src/Items/ItemSapling.h28
-rw-r--r--src/Items/ItemSeeds.h51
-rw-r--r--src/Items/ItemSideways.h53
-rw-r--r--src/Items/ItemSign.h89
-rw-r--r--src/Items/ItemSlab.h134
-rw-r--r--src/Items/ItemSnow.h41
-rw-r--r--src/Items/ItemStairs.h47
-rw-r--r--src/Items/ItemTorch.h82
-rw-r--r--src/Items/ItemTrapdoor.h66
-rw-r--r--src/Items/ItemTripwireHook.h43
-rw-r--r--src/Items/ItemVine.h44
-rw-r--r--src/Items/SimplePlaceableItemHandler.h9
47 files changed, 1597 insertions, 864 deletions
diff --git a/src/Items/CMakeLists.txt b/src/Items/CMakeLists.txt
index 0046386d0..3f182dd76 100644
--- a/src/Items/CMakeLists.txt
+++ b/src/Items/CMakeLists.txt
@@ -3,6 +3,7 @@ target_sources(
ItemHandler.cpp
+ ItemAnvil.h
ItemArmor.h
ItemAxe.h
ItemBanner.h
@@ -12,24 +13,35 @@ target_sources(
ItemBottle.h
ItemBow.h
ItemBucket.h
+ ItemButton.h
ItemChest.h
ItemCloth.h
ItemComparator.h
ItemCookedFish.h
ItemDoor.h
+ ItemDropSpenser.h
ItemDye.h
ItemEmptyMap.h
ItemEnchantingTable.h
ItemEndCrystal.h
+ ItemEndPortalFrame.h
+ ItemEnderchest.h
ItemEyeOfEnder.h
+ ItemFenceGate.h
ItemFishingRod.h
ItemFood.h
ItemFoodSeeds.h
+ ItemFurnace.h
+ ItemGlazedTerracotta.h
ItemGoldenApple.h
ItemHandler.h
ItemHoe.h
+ ItemHopper.h
ItemItemFrame.h
+ ItemJackOLantern.h
+ ItemLadder.h
ItemLeaves.h
+ ItemLever.h
ItemLighter.h
ItemLilypad.h
ItemMap.h
@@ -37,11 +49,16 @@ target_sources(
ItemMinecart.h
ItemMobHead.h
ItemNetherWart.h
+ ItemObserver.h
ItemPainting.h
ItemPickaxe.h
+ ItemPiston.h
+ ItemPlanks.h
ItemPoisonousPotato.h
ItemPotion.h
ItemPumpkin.h
+ ItemQuartz.h
+ ItemRail.h
ItemRawChicken.h
ItemRawFish.h
ItemRedstoneDust.h
@@ -51,12 +68,19 @@ target_sources(
ItemSeeds.h
ItemShears.h
ItemShovel.h
+ ItemSideways.h
ItemSign.h
ItemSlab.h
+ ItemSnow.h
ItemSoup.h
ItemSpawnEgg.h
ItemSpiderEye.h
+ ItemStairs.h
ItemSword.h
ItemThrowable.h
+ ItemTorch.h
+ ItemTrapdoor.h
+ ItemTripwireHook.h
+ ItemVine.h
SimplePlaceableItemHandler.h
)
diff --git a/src/Items/ItemAnvil.h b/src/Items/ItemAnvil.h
new file mode 100644
index 000000000..e52f28e65
--- /dev/null
+++ b/src/Items/ItemAnvil.h
@@ -0,0 +1,30 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockAnvil.h"
+
+
+
+
+
+class cItemAnvilHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(
+ a_PlacePosition,
+ static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType),
+ cBlockAnvilHandler::YawToMetaData(a_Player.GetYaw()) | static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage << 2)
+ );
+ }
+};
diff --git a/src/Items/ItemBanner.h b/src/Items/ItemBanner.h
index 3f082c5a5..2ebfca44a 100644
--- a/src/Items/ItemBanner.h
+++ b/src/Items/ItemBanner.h
@@ -24,6 +24,32 @@ public:
{
}
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ // Cannot place a banner at "no face" and from the bottom:
+ if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
+ {
+ return false;
+ }
+
+ if (!TryPlaceBanner(a_Player, a_PlacePosition, a_ClickedBlockFace))
+ {
+ return false;
+ }
+
+ a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_HeldItem](cBlockEntity & a_BlockEntity)
+ {
+ ASSERT((a_BlockEntity.GetBlockType() == E_BLOCK_STANDING_BANNER) || (a_BlockEntity.GetBlockType() == E_BLOCK_WALL_BANNER));
+
+ static_cast<cBannerEntity &>(a_BlockEntity).SetBaseColor(static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage));
+ return false;
+ });
+
+ return true;
+ }
+
@@ -36,182 +62,120 @@ public:
-
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ static bool TryPlaceBanner(cPlayer & a_Player, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace)
{
- a_BlockMeta = 0x00;
- const double Rotation = a_Player->GetYaw();
+ const auto Rotation = a_Player.GetYaw();
- // Placing on the floor
+ // Placing on the floor:
if (a_ClickedBlockFace == BLOCK_FACE_TOP)
{
- if ((Rotation >= - 11.25f) && (Rotation < 11.25f))
+ NIBBLETYPE Meta;
+
+ if ((Rotation >= -11.25f) && (Rotation < 11.25f))
{
// South
- a_BlockMeta |= 0x08;
+ Meta = 0x08;
}
else if ((Rotation >= 11.25f) && (Rotation < 33.75f))
{
// SouthSouthWest
- a_BlockMeta |= 0x09;
+ Meta = 0x09;
}
else if ((Rotation >= 23.75f) && (Rotation < 56.25f))
{
// SouthWest
- a_BlockMeta |= 0x0a;
+ Meta = 0x0a;
}
else if ((Rotation >= 56.25f) && (Rotation < 78.75f))
{
// WestSouthWest
- a_BlockMeta |= 0x0b;
+ Meta = 0x0b;
}
else if ((Rotation >= 78.75f) && (Rotation < 101.25f))
{
// West
- a_BlockMeta |= 0x0c;
+ Meta = 0x0c;
}
else if ((Rotation >= 101.25f) && (Rotation < 123.75f))
{
// WestNorthWest
- a_BlockMeta |= 0x0d;
+ Meta = 0x0d;
}
else if ((Rotation >= 123.75f) && (Rotation < 146.25f))
{
// NorthWest
- a_BlockMeta |= 0x0e;
+ Meta = 0x0e;
}
else if ((Rotation >= 146.25f) && (Rotation < 168.75f))
{
// NorthNorthWest
- a_BlockMeta |= 0x0f;
+ Meta = 0x0f;
}
else if ((Rotation >= -168.75f) && (Rotation < -146.25f))
{
// NorthNorthEast
- a_BlockMeta |= 0x01;
+ Meta = 0x01;
}
else if ((Rotation >= -146.25f) && (Rotation < -123.75f))
{
// NorthEast
- a_BlockMeta |= 0x02;
+ Meta = 0x02;
}
else if ((Rotation >= -123.75f) && (Rotation < -101.25f))
{
// EastNorthEast
- a_BlockMeta |= 0x03;
+ Meta = 0x03;
}
else if ((Rotation >= -101.25) && (Rotation < -78.75f))
{
// East
- a_BlockMeta |= 0x04;
+ Meta = 0x04;
}
else if ((Rotation >= -78.75) && (Rotation < -56.25f))
{
// EastSouthEast
- a_BlockMeta |= 0x05;
+ Meta = 0x05;
}
else if ((Rotation >= -56.25f) && (Rotation < -33.75f))
{
// SouthEast
- a_BlockMeta |= 0x06;
+ Meta = 0x06;
}
else if ((Rotation >= -33.75f) && (Rotation < -11.25f))
{
// SouthSouthEast
- a_BlockMeta |= 0x07;
+ Meta = 0x07;
}
else // degrees jumping from 180 to -180
{
// North
- a_BlockMeta |= 0x00;
- }
- a_BlockType = E_BLOCK_STANDING_BANNER;
- }
- // placing on the sides
- else if (a_ClickedBlockFace != BLOCK_FACE_NONE)
- {
- if (a_ClickedBlockFace == BLOCK_FACE_EAST)
- {
- a_BlockMeta |= 0x05;
- }
- else if (a_ClickedBlockFace == BLOCK_FACE_WEST)
- {
- a_BlockMeta |= 0x04;
+ Meta = 0x00;
}
- else if (a_ClickedBlockFace == BLOCK_FACE_NORTH)
- {
- a_BlockMeta |= 0x02;
- }
- else // degrees jumping from 180 to -180
- {
- a_BlockMeta |= 0x03;
- }
- a_BlockType = E_BLOCK_WALL_BANNER;
- }
- else
- {
- return false;
- }
-
- return true;
- }
-
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_STANDING_BANNER, Meta);
+ }
+ // We must be placing on the side of a block.
+ NIBBLETYPE Meta;
- virtual bool OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
- ) override
- {
- // Cannot place a banner at "no face" and from the bottom:
- if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
+ if (a_ClickedBlockFace == BLOCK_FACE_EAST)
{
- return true;
+ Meta = 0x05;
}
-
- // Checks if the banner replaced the block
- BLOCKTYPE ClickedBlockType;
- NIBBLETYPE ClickedBlockMeta;
- a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
- cChunkInterface ChunkInterface(a_World.GetChunkMap());
- bool IsReplacingClickedBlock = cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta);
- if (IsReplacingClickedBlock)
+ else if (a_ClickedBlockFace == BLOCK_FACE_WEST)
{
- // TODO: There is a bug in the network which prevents the client from receiving the new block entity on placement
- // For now the replaced blocks are disabled
- return false;
+ Meta = 0x04;
}
-
- // saving the color of the banner in case it's the players last one
- NIBBLETYPE Color = static_cast<NIBBLETYPE>(a_EquippedItem.m_ItemDamage);
-
- if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_ClickedBlockPos))
+ else if (a_ClickedBlockFace == BLOCK_FACE_NORTH)
{
- return false;
+ Meta = 0x02;
}
-
- const auto BannerPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
- a_World.DoWithBlockEntityAt(BannerPos, [Color](cBlockEntity & a_BlockEntity)
+ else // degrees jumping from 180 to -180
{
- ASSERT((a_BlockEntity.GetBlockType() == E_BLOCK_STANDING_BANNER) || (a_BlockEntity.GetBlockType() == E_BLOCK_WALL_BANNER));
-
- auto & Banner = static_cast<cBannerEntity &>(a_BlockEntity);
- Banner.SetBaseColor(Color);
- return false;
- });
+ Meta = 0x03;
+ }
- return true;
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_WALL_BANNER, Meta);
}
};
diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h
index 98f9f614f..7b8dde637 100644
--- a/src/Items/ItemBed.h
+++ b/src/Items/ItemBed.h
@@ -2,8 +2,8 @@
#pragma once
#include "ItemHandler.h"
-#include "../World.h"
-#include "../Blocks/BlockBed.h"
+#include "Blocks/BlockBed.h"
+#include "BlockEntities/BedEntity.h"
@@ -22,35 +22,51 @@ public:
}
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
-
- virtual bool GetBlocksToPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- sSetBlockVector & a_BlocksToPlace
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
const auto BlockMeta = cBlockBedHandler::YawToMetaData(a_Player.GetYaw());
- const auto HeadPosition = a_PlacedBlockPos + cBlockBedHandler::MetaDataToDirection(BlockMeta);
+ const auto HeadPosition = a_PlacePosition + cBlockBedHandler::MetaDataToDirection(BlockMeta);
- // Vanilla only allows beds to be placed into air
+ auto & World = *a_Player.GetWorld();
+ BLOCKTYPE HeadType;
+ NIBBLETYPE HeadMeta;
+ World.GetBlockTypeMeta(HeadPosition, HeadType, HeadMeta);
+
+ // Vanilla only allows beds to be placed into air.
// Check if there is empty space for the "head" block:
- if (a_World.GetBlock(HeadPosition) != E_BLOCK_AIR)
+ if (!cBlockHandler::For(HeadType).DoesIgnoreBuildCollision(World, a_HeldItem, HeadPosition, HeadMeta, a_ClickedBlockFace, false))
{
return false;
}
// The "foot", and the "head" block:
- a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BED, BlockMeta);
- a_BlocksToPlace.emplace_back(HeadPosition, E_BLOCK_BED, BlockMeta | 0x08);
+ if (
+ !a_Player.PlaceBlocks(
+ {
+ { a_PlacePosition, E_BLOCK_BED, BlockMeta },
+ { HeadPosition, E_BLOCK_BED, static_cast<NIBBLETYPE>(BlockMeta | 0x08) }
+ })
+ )
+ {
+ return false;
+ }
+
+ auto SetColor = [&a_HeldItem](cBlockEntity & a_BlockEntity)
+ {
+ ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_BED);
+
+ static_cast<cBedEntity &>(a_BlockEntity).SetColor(a_HeldItem.m_ItemDamage);
+ return false;
+ };
+ World.DoWithBlockEntityAt(a_PlacePosition, SetColor);
+ World.DoWithBlockEntityAt(HeadPosition, SetColor);
+
+ return true;
+ }
+
+
+ virtual bool IsPlaceable(void) override
+ {
return true;
}
};
diff --git a/src/Items/ItemBigFlower.h b/src/Items/ItemBigFlower.h
index a67ca8d0a..12ebc2188 100644
--- a/src/Items/ItemBigFlower.h
+++ b/src/Items/ItemBigFlower.h
@@ -24,41 +24,29 @@ public:
- virtual bool GetBlocksToPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- sSetBlockVector & a_BlocksToPlace
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- // Can only be placed on dirt:
- if ((a_PlacedBlockPos.y <= 0) || !IsBlockTypeOfDirt(a_World.GetBlock(a_PlacedBlockPos.addedY(-1))))
+ // Needs at least two free blocks to build in:
+ if (a_PlacePosition.y >= (cChunkDef::Height - 1))
{
return false;
}
- // Needs at least two free blocks to build in
- if (a_PlacedBlockPos.y >= cChunkDef::Height - 1)
- {
- return false;
- }
-
- auto TopPos = a_PlacedBlockPos.addedY(1);
+ const auto & World = *a_Player.GetWorld();
+ const auto TopPos = a_PlacePosition.addedY(1);
BLOCKTYPE TopType;
NIBBLETYPE TopMeta;
- a_World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
- cChunkInterface ChunkInterface(a_World.GetChunkMap());
+ World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
- if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(ChunkInterface, TopPos, a_Player, TopMeta))
+ if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(World, a_HeldItem, TopPos, TopMeta, a_ClickedBlockFace, false))
{
return false;
}
- a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
- a_BlocksToPlace.emplace_back(TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP);
- return true;
+ return a_Player.PlaceBlocks(
+ {
+ { a_PlacePosition, E_BLOCK_BIG_FLOWER, static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x07) },
+ { TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP }
+ });
}
};
diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h
index a5b1085ba..6d7a33ccf 100644
--- a/src/Items/ItemBucket.h
+++ b/src/Items/ItemBucket.h
@@ -105,8 +105,8 @@ public:
return false;
}
- // Remove water / lava block (unless plugins disagree)
- if (!a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0))
+ // Remove water / lava block (unless plugins disagree):
+ if (!a_Player->PlaceBlock(BlockPos, E_BLOCK_AIR, 0))
{
return false;
}
@@ -175,7 +175,7 @@ public:
}
// Place the actual fluid block:
- return a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, a_FluidBlock, 0);
+ return a_Player->PlaceBlock(BlockPos, a_FluidBlock, 0);
}
diff --git a/src/Items/ItemButton.h b/src/Items/ItemButton.h
new file mode 100644
index 000000000..0f6e5c6f5
--- /dev/null
+++ b/src/Items/ItemButton.h
@@ -0,0 +1,45 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemButtonHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ /** Converts the block face of the neighbor to which the button is attached, to the block meta for this button. */
+ static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_YP: return 0x5;
+ case BLOCK_FACE_ZM: return 0x4;
+ case BLOCK_FACE_ZP: return 0x3;
+ case BLOCK_FACE_XM: return 0x2;
+ case BLOCK_FACE_XP: return 0x1;
+ case BLOCK_FACE_YM: return 0x0;
+ case BLOCK_FACE_NONE:
+ {
+ break;
+ }
+ }
+ UNREACHABLE("Unsupported block face");
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace));
+ }
+};
diff --git a/src/Items/ItemChest.h b/src/Items/ItemChest.h
index 8548f8d25..a714045ee 100644
--- a/src/Items/ItemChest.h
+++ b/src/Items/ItemChest.h
@@ -2,8 +2,7 @@
#pragma once
#include "ItemHandler.h"
-#include "../BlockInfo.h"
-#include "../Blocks/BlockChest.h"
+#include "Blocks/BlockChest.h"
@@ -21,59 +20,12 @@ public:
{
}
+ cItemChestHandler(const cItemChestHandler &) = delete;
- /** We need an OnPlayerPlace override because we're processing neighbor chests and changing their metas,
- the parent class cannot do that. */
- virtual bool OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
- ) override
- {
- if (a_ClickedBlockFace < 0)
- {
- // Clicked in air
- return false;
- }
-
- if (!cChunkDef::IsValidHeight(a_ClickedBlockPos.y))
- {
- // The clicked block is outside the world, ignore this call altogether (#128)
- return false;
- }
-
- // Check if the block ignores build collision (water, grass etc.):
- BLOCKTYPE ClickedBlockType;
- NIBBLETYPE ClickedBlockMeta;
- a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
- cChunkInterface ChunkInterface(a_World.GetChunkMap());
- Vector3i PlacePos;
- if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
- {
- PlacePos = a_ClickedBlockPos;
- }
- else
- {
- PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
- if (!cChunkDef::IsValidHeight(PlacePos.y))
- {
- // The block is being placed outside the world, ignore this packet altogether (#128)
- return false;
- }
-
- // Check if the chest can overwrite the block at PlacePos:
- BLOCKTYPE PlaceBlock;
- NIBBLETYPE PlaceMeta;
- a_World.GetBlockTypeMeta(PlacePos, PlaceBlock, PlaceMeta);
- if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(ChunkInterface, PlacePos, a_Player, PlaceMeta))
- {
- return false;
- }
- }
+private:
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
// Check that there is at most one single neighbor of the same chest type:
static const Vector3i CrossCoords[] =
{
@@ -82,11 +34,14 @@ public:
{ 1, 0, 0},
{ 0, 0, 1},
};
+
+ auto & World = *a_Player.GetWorld();
int NeighborIdx = -1;
+
for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
{
- auto NeighborPos = PlacePos + CrossCoords[i];
- if (a_World.GetBlock(NeighborPos) != m_ItemType)
+ const auto NeighborPos = a_PlacePosition + CrossCoords[i];
+ if (World.GetBlock(NeighborPos) != m_ItemType)
{
continue;
}
@@ -100,7 +55,7 @@ public:
// Check that this neighbor is a single chest:
for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++)
{
- if (a_World.GetBlock(NeighborPos + CrossCoords[j]) == m_ItemType)
+ if (World.GetBlock(NeighborPos + CrossCoords[j]) == m_ItemType)
{
// Trying to place next to a dblchest
return false;
@@ -111,7 +66,7 @@ public:
// Get the meta of the placed chest; take existing neighbors into account:
BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
NIBBLETYPE Meta;
- auto yaw = a_Player.GetYaw();
+ const auto yaw = a_Player.GetYaw();
switch (NeighborIdx)
{
case 0:
@@ -131,13 +86,13 @@ public:
default:
{
// No neighbor, place based on yaw:
- Meta = cBlockChestHandler::PlayerYawToMetaData(yaw);
+ Meta = cBlockChestHandler::YawToMetaData(yaw);
break;
}
} // switch (NeighborIdx)
// Place the new chest:
- if (!a_Player.PlaceBlock(PlacePos.x, PlacePos.y, PlacePos.z, ChestBlockType, Meta))
+ if (!a_Player.PlaceBlock(a_PlacePosition, ChestBlockType, Meta))
{
return false;
}
@@ -145,17 +100,9 @@ public:
// Adjust the existing chest, if any:
if (NeighborIdx != -1)
{
- a_World.FastSetBlock(PlacePos + CrossCoords[NeighborIdx], ChestBlockType, Meta);
+ World.FastSetBlock(a_PlacePosition + CrossCoords[NeighborIdx], ChestBlockType, Meta);
}
- // Remove the "placed" item from inventory:
- if (a_Player.IsGameModeSurvival())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
return true;
}
-
-private:
- cItemChestHandler(const cItemChestHandler &) = delete;
};
diff --git a/src/Items/ItemComparator.h b/src/Items/ItemComparator.h
index fbb61b317..681ce0b54 100644
--- a/src/Items/ItemComparator.h
+++ b/src/Items/ItemComparator.h
@@ -24,25 +24,17 @@ public:
- virtual bool IsPlaceable(void) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- return true;
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_INACTIVE_COMPARATOR, cBlockComparatorHandler::YawToMetaData(a_Player.GetYaw()));
}
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ virtual bool IsPlaceable(void) override
{
- a_BlockType = E_BLOCK_INACTIVE_COMPARATOR;
- a_BlockMeta = cBlockComparatorHandler::YawToMetaData(a_Player->GetYaw());
return true;
}
} ;
diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h
index aec6bc0fe..b85f018bc 100644
--- a/src/Items/ItemDoor.h
+++ b/src/Items/ItemDoor.h
@@ -25,13 +25,7 @@ public:
- virtual bool GetBlocksToPlace(
- cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- sSetBlockVector & a_BlocksToSet
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
// Vanilla only allows door placement while clicking on the top face of the block below the door:
if (a_ClickedBlockFace != BLOCK_FACE_TOP)
@@ -39,19 +33,6 @@ public:
return false;
}
- // Door (bottom block) can be placed in Y range of [1, 254]:
- if ((a_PlacedBlockPos.y < 1) || (a_PlacedBlockPos.y >= cChunkDef::Height - 2))
- {
- return false;
- }
-
- // The door needs a compatible block below it:
- auto BelowPos = a_PlacedBlockPos.addedY(-1);
- if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(BelowPos), a_World.GetBlockMeta(BelowPos)))
- {
- return false;
- }
-
// Get the block type of the door to place:
BLOCKTYPE BlockType;
switch (m_ItemType)
@@ -63,22 +44,22 @@ public:
case E_ITEM_JUNGLE_DOOR: BlockType = E_BLOCK_JUNGLE_DOOR; break;
case E_ITEM_DARK_OAK_DOOR: BlockType = E_BLOCK_DARK_OAK_DOOR; break;
case E_ITEM_ACACIA_DOOR: BlockType = E_BLOCK_ACACIA_DOOR; break;
- default:
- {
- ASSERT(!"Unhandled door type");
- return false;
- }
+ default: UNREACHABLE("Unhandled door type");
}
- // Check the two blocks that will get replaced by the door:
- auto UpperBlockPos = a_PlacedBlockPos.addedY(1);
- BLOCKTYPE LowerBlockType = a_World.GetBlock(a_PlacedBlockPos);
- BLOCKTYPE UpperBlockType = a_World.GetBlock(UpperBlockPos);
- if (
- !cBlockDoorHandler::CanReplaceBlock(LowerBlockType) ||
- !cBlockDoorHandler::CanReplaceBlock(UpperBlockType))
+ const auto & World = *a_Player.GetWorld();
+ const auto UpperBlockPosition = a_PlacePosition.addedY(1);
+
+ // Check the block that will get replaced by the door:
{
- return false;
+ BLOCKTYPE TopType;
+ NIBBLETYPE TopMeta;
+ World.GetBlockTypeMeta(UpperBlockPosition, TopType, TopMeta);
+
+ if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(World, a_HeldItem, UpperBlockPosition, TopMeta, a_ClickedBlockFace, false))
+ {
+ return false;
+ }
}
// Get the coords of the neighboring blocks:
@@ -86,22 +67,24 @@ public:
Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta);
Vector3i LeftNeighborPos = RelDirToOutside;
LeftNeighborPos.TurnCW();
- LeftNeighborPos.Move(a_PlacedBlockPos);
+ LeftNeighborPos.Move(a_PlacePosition);
Vector3i RightNeighborPos = RelDirToOutside;
RightNeighborPos.TurnCCW();
- RightNeighborPos.Move(a_PlacedBlockPos);
+ RightNeighborPos.Move(a_PlacePosition);
// Decide whether the hinge is on the left (default) or on the right:
NIBBLETYPE UpperBlockMeta = 0x08;
- BLOCKTYPE LeftNeighborBlock = a_World.GetBlock(LeftNeighborPos);
- BLOCKTYPE RightNeighborBlock = a_World.GetBlock(RightNeighborPos);
+ BLOCKTYPE LeftNeighborBlock = World.GetBlock(LeftNeighborPos);
+ BLOCKTYPE RightNeighborBlock = World.GetBlock(RightNeighborPos);
+
/*
// DEBUG:
- FLOGD("Door being placed at {0}", a_PlacedBlockPos);
+ FLOGD("Door being placed at {0}", a_PlacePosition);
FLOGD("RelDirToOutside: {0}", RelDirToOutside);
FLOGD("Left neighbor at {0}: {1} ({2})", LeftNeighborPos, LeftNeighborBlock, ItemTypeToString(LeftNeighborBlock));
FLOGD("Right neighbor at {0}: {1} ({2})", RightNeighborPos, RightNeighborBlock, ItemTypeToString(RightNeighborBlock));
*/
+
if (
cBlockDoorHandler::IsDoorBlockType(LeftNeighborBlock) || // The block to the left is a door block
(
@@ -116,9 +99,11 @@ public:
}
// Set the blocks:
- a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, LowerBlockMeta);
- a_BlocksToSet.emplace_back(UpperBlockPos, BlockType, UpperBlockMeta);
- return true;
+ return a_Player.PlaceBlocks(
+ {
+ { a_PlacePosition, BlockType, LowerBlockMeta },
+ { UpperBlockPosition, BlockType, UpperBlockMeta }
+ });
}
diff --git a/src/Items/ItemDropSpenser.h b/src/Items/ItemDropSpenser.h
new file mode 100644
index 000000000..d226436db
--- /dev/null
+++ b/src/Items/ItemDropSpenser.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockDropSpenser.h"
+
+
+
+
+
+class cItemDropSpenserHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), cBlockDropSpenserHandler::PitchYawToMetaData(a_Player.GetYaw(), a_Player.GetPitch()));
+ }
+};
diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h
index 631e28e7d..683e8a73f 100644
--- a/src/Items/ItemDye.h
+++ b/src/Items/ItemDye.h
@@ -74,7 +74,7 @@ public:
{
return false;
}
- if (a_Player->PlaceBlock(CocoaPos.x, CocoaPos.y, CocoaPos.z, E_BLOCK_COCOA_POD, BlockMeta))
+ if (a_Player->PlaceBlock(CocoaPos, E_BLOCK_COCOA_POD, BlockMeta))
{
if (a_Player->IsGameModeSurvival())
{
diff --git a/src/Items/ItemEnchantingTable.h b/src/Items/ItemEnchantingTable.h
index 12835cb4a..29a6eeef6 100644
--- a/src/Items/ItemEnchantingTable.h
+++ b/src/Items/ItemEnchantingTable.h
@@ -20,40 +20,32 @@ public:
private:
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
-
- virtual bool OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
+ if (!Super::CommitPlacement(a_Player, a_HeldItem, a_PlacePosition, a_ClickedBlockFace, a_CursorPosition))
{
return false;
}
- if (a_EquippedItem.IsCustomNameEmpty())
+ if (a_HeldItem.IsCustomNameEmpty())
{
return true;
}
- const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
- a_World.DoWithBlockEntityAt(PlacePos, [&a_EquippedItem](cBlockEntity & a_Entity)
+ a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_HeldItem](cBlockEntity & a_BlockEntity)
{
- ASSERT(a_Entity.GetBlockType() == E_BLOCK_ENCHANTMENT_TABLE);
+ ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_ENCHANTMENT_TABLE);
- static_cast<cEnchantingTableEntity &>(a_Entity).SetCustomName(a_EquippedItem.m_CustomName);
+ static_cast<cEnchantingTableEntity &>(a_BlockEntity).SetCustomName(a_HeldItem.m_CustomName);
return false;
});
return true;
}
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
+ }
} ;
diff --git a/src/Items/ItemEndPortalFrame.h b/src/Items/ItemEndPortalFrame.h
new file mode 100644
index 000000000..08bcdf9fa
--- /dev/null
+++ b/src/Items/ItemEndPortalFrame.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockEndPortalFrame.h"
+
+
+
+
+
+class cItemEndPortalFrameHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_END_PORTAL_FRAME, cBlockEndPortalFrameHandler::YawToMetaData(a_Player.GetYaw()));
+ }
+};
diff --git a/src/Items/ItemEnderchest.h b/src/Items/ItemEnderchest.h
new file mode 100644
index 000000000..4feba1f13
--- /dev/null
+++ b/src/Items/ItemEnderchest.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockEnderchest.h"
+
+
+
+
+
+class cItemEnderchestHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_ENDER_CHEST, cBlockEnderchestHandler::YawToMetaData(a_Player.GetYaw()));
+ }
+};
diff --git a/src/Items/ItemFenceGate.h b/src/Items/ItemFenceGate.h
new file mode 100644
index 000000000..cd06340b0
--- /dev/null
+++ b/src/Items/ItemFenceGate.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockFenceGate.h"
+
+
+
+
+
+class cItemFenceGateHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), cBlockFenceGateHandler::YawToMetaData(a_Player.GetYaw()));
+ }
+};
diff --git a/src/Items/ItemFurnace.h b/src/Items/ItemFurnace.h
new file mode 100644
index 000000000..24bcd5e06
--- /dev/null
+++ b/src/Items/ItemFurnace.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockFurnace.h"
+
+
+
+
+
+class cItemFurnaceHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_FURNACE, cBlockFurnaceHandler::YawToMetaData(a_Player.GetYaw()));
+ }
+};
diff --git a/src/Items/ItemGlazedTerracotta.h b/src/Items/ItemGlazedTerracotta.h
new file mode 100644
index 000000000..d08d6308e
--- /dev/null
+++ b/src/Items/ItemGlazedTerracotta.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockGlazedTerracotta.h"
+
+
+
+
+
+class cItemGlazedTerracottaHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), cBlockGlazedTerracottaHandler::YawToMetaData(a_Player.GetYaw()));
+ }
+};
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index bfeba1373..1c6300353 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -8,6 +8,7 @@
#include "../Chunk.h"
// Handlers:
+#include "ItemAnvil.h"
#include "ItemArmor.h"
#include "ItemAxe.h"
#include "ItemBanner.h"
@@ -17,23 +18,34 @@
#include "ItemBottle.h"
#include "ItemBow.h"
#include "ItemBucket.h"
+#include "ItemButton.h"
#include "ItemChest.h"
#include "ItemCloth.h"
#include "ItemComparator.h"
#include "ItemCookedFish.h"
#include "ItemDoor.h"
+#include "ItemDropSpenser.h"
#include "ItemDye.h"
#include "ItemEmptyMap.h"
#include "ItemEnchantingTable.h"
#include "ItemEndCrystal.h"
+#include "ItemEnderchest.h"
+#include "ItemEndPortalFrame.h"
#include "ItemEyeOfEnder.h"
+#include "ItemFenceGate.h"
#include "ItemFishingRod.h"
#include "ItemFood.h"
#include "ItemFoodSeeds.h"
+#include "ItemFurnace.h"
+#include "ItemGlazedTerracotta.h"
#include "ItemGoldenApple.h"
#include "ItemHoe.h"
+#include "ItemHopper.h"
#include "ItemItemFrame.h"
+#include "ItemJackOLantern.h"
+#include "ItemLadder.h"
#include "ItemLeaves.h"
+#include "ItemLever.h"
#include "ItemLighter.h"
#include "ItemLilypad.h"
#include "ItemMap.h"
@@ -41,11 +53,16 @@
#include "ItemMinecart.h"
#include "ItemMobHead.h"
#include "ItemNetherWart.h"
+#include "ItemObserver.h"
#include "ItemPainting.h"
#include "ItemPickaxe.h"
+#include "ItemPiston.h"
+#include "ItemPlanks.h"
#include "ItemPoisonousPotato.h"
#include "ItemPotion.h"
#include "ItemPumpkin.h"
+#include "ItemQuartz.h"
+#include "ItemRail.h"
#include "ItemRawChicken.h"
#include "ItemRawFish.h"
#include "ItemRedstoneDust.h"
@@ -55,13 +72,20 @@
#include "ItemSeeds.h"
#include "ItemShears.h"
#include "ItemShovel.h"
+#include "ItemSideways.h"
#include "ItemSign.h"
#include "ItemSlab.h"
+#include "ItemSnow.h"
#include "ItemSoup.h"
#include "ItemSpawnEgg.h"
#include "ItemSpiderEye.h"
+#include "ItemStairs.h"
#include "ItemSword.h"
#include "ItemThrowable.h"
+#include "ItemTorch.h"
+#include "ItemTrapdoor.h"
+#include "ItemTripwireHook.h"
+#include "ItemVine.h"
#include "../Blocks/BlockHandler.h"
#include "SimplePlaceableItemHandler.h"
@@ -113,56 +137,87 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
default: return new cItemHandler(a_ItemType);
// Single item per handler, alphabetically sorted:
- case E_ITEM_BANNER: return new cItemBannerHandler(a_ItemType);
- case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler;
- case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
- case E_BLOCK_ENCHANTMENT_TABLE: return new cItemEnchantingTableHandler(a_ItemType);
- case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
- case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
- case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
- case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
- case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler;
- case E_BLOCK_PURPUR_SLAB: return new cItemSlabHandler(E_BLOCK_PURPUR_SLAB, E_BLOCK_PURPUR_DOUBLE_SLAB);
- case E_BLOCK_RED_SANDSTONE_SLAB: return new cItemSlabHandler(E_BLOCK_RED_SANDSTONE_SLAB, E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB);
- case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
- case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB);
- case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
- case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB);
- case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
- case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
- case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
- case E_ITEM_BOW: return new cItemBowHandler();
- case E_ITEM_BREWING_STAND: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_BREWING_STAND);
- case E_ITEM_CAKE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAKE);
- case E_ITEM_CAULDRON: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAULDRON);
- case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
- case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
- case E_ITEM_EGG: return new cItemEggHandler();
- case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
- case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
- case E_ITEM_END_CRYSTAL: return new cItemEndCrystalHandler(a_ItemType);
- case E_ITEM_EYE_OF_ENDER: return new cItemEyeOfEnderHandler();
- case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
- case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
- case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
- case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
- case E_ITEM_FLOWER_POT: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_FLOWER_POT);
- case E_ITEM_GLASS_BOTTLE: return new cItemBottleHandler();
- case E_ITEM_MAP: return new cItemMapHandler();
- case E_ITEM_MILK: return new cItemMilkHandler();
- case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
- case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
- case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
- case E_ITEM_POTIONS: return new cItemPotionHandler();
- case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
- case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
- case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
- case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
- case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
- case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
- case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
- case E_ITEM_STRING: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_TRIPWIRE);
- case E_ITEM_SUGARCANE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_SUGARCANE);
+ case E_BLOCK_ACTIVATOR_RAIL: return new cItemRailHandler(a_ItemType);
+ case E_BLOCK_ANVIL: return new cItemAnvilHandler(a_ItemType);
+ case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler;
+ case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
+ case E_BLOCK_DETECTOR_RAIL: return new cItemRailHandler(a_ItemType);
+ case E_BLOCK_DISPENSER: return new cItemDropSpenserHandler(a_ItemType);
+ case E_BLOCK_DROPPER: return new cItemDropSpenserHandler(a_ItemType);
+ case E_BLOCK_ENCHANTMENT_TABLE: return new cItemEnchantingTableHandler(a_ItemType);
+ case E_BLOCK_ENDER_CHEST: return new cItemEnderchestHandler(a_ItemType);
+ case E_BLOCK_END_PORTAL_FRAME: return new cItemEndPortalFrameHandler(a_ItemType);
+ case E_BLOCK_FURNACE: return new cItemFurnaceHandler(a_ItemType);
+ case E_BLOCK_HAY_BALE: return new cItemSidewaysHandler(a_ItemType);
+ case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
+ case E_BLOCK_HOPPER: return new cItemHopperHandler(a_ItemType);
+ case E_BLOCK_IRON_TRAPDOOR: return new cItemTrapdoorHandler(a_ItemType);
+ case E_BLOCK_JACK_O_LANTERN: return new cItemJackOLanternHandler(a_ItemType);
+ case E_BLOCK_LADDER: return new cItemLadderHandler(a_ItemType);
+ case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
+ case E_BLOCK_LEVER: return new cItemLeverHandler(a_ItemType);
+ case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
+ case E_BLOCK_LOG: return new cItemSidewaysHandler(a_ItemType);
+ case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
+ case E_BLOCK_NEW_LOG: return new cItemSidewaysHandler(a_ItemType);
+ case E_BLOCK_OBSERVER: return new cItemObserverHandler(a_ItemType);
+ case E_BLOCK_PISTON: return new cItemPistonHandler(a_ItemType);
+ case E_BLOCK_PLANKS: return new cItemPlanksHandler(a_ItemType);
+ case E_BLOCK_POWERED_RAIL: return new cItemRailHandler(a_ItemType);
+ case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler(a_ItemType);
+ case E_BLOCK_PURPUR_SLAB: return new cItemSlabHandler(a_ItemType);
+ case E_BLOCK_QUARTZ_BLOCK: return new cItemQuartzHandler(a_ItemType);
+ case E_BLOCK_RAIL: return new cItemRailHandler(a_ItemType);
+ case E_BLOCK_REDSTONE_TORCH_ON: return new cItemTorchHandler(a_ItemType);
+ case E_BLOCK_RED_SANDSTONE_SLAB: return new cItemSlabHandler(a_ItemType);
+ case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
+ case E_BLOCK_SNOW: return new cItemSnowHandler(a_ItemType);
+ case E_BLOCK_STICKY_PISTON: return new cItemPistonHandler(a_ItemType);
+ case E_BLOCK_STONE_BUTTON: return new cItemButtonHandler(a_ItemType);
+ case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(a_ItemType);
+ case E_BLOCK_TORCH: return new cItemTorchHandler(a_ItemType);
+ case E_BLOCK_TRAPDOOR: return new cItemTrapdoorHandler(a_ItemType);
+ case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
+ case E_BLOCK_TRIPWIRE_HOOK: return new cItemTripwireHookHandler(a_ItemType);
+ case E_BLOCK_VINES: return new cItemVineHandler(a_ItemType);
+ case E_BLOCK_WOODEN_BUTTON: return new cItemButtonHandler(a_ItemType);
+ case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(a_ItemType);
+ case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
+ case E_ITEM_BANNER: return new cItemBannerHandler(a_ItemType);
+ case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
+ case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
+ case E_ITEM_BOW: return new cItemBowHandler();
+ case E_ITEM_BREWING_STAND: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_BREWING_STAND);
+ case E_ITEM_CAKE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAKE);
+ case E_ITEM_CAULDRON: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAULDRON);
+ case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
+ case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
+ case E_ITEM_EGG: return new cItemEggHandler();
+ case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
+ case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
+ case E_ITEM_END_CRYSTAL: return new cItemEndCrystalHandler(a_ItemType);
+ case E_ITEM_EYE_OF_ENDER: return new cItemEyeOfEnderHandler();
+ case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
+ case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
+ case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
+ case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
+ case E_ITEM_FLOWER_POT: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_FLOWER_POT);
+ case E_ITEM_GLASS_BOTTLE: return new cItemBottleHandler();
+ case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
+ case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
+ case E_ITEM_MAP: return new cItemMapHandler();
+ case E_ITEM_MILK: return new cItemMilkHandler();
+ case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
+ case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
+ case E_ITEM_POTIONS: return new cItemPotionHandler();
+ case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
+ case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
+ case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
+ case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
+ case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
+ case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
+ case E_ITEM_STRING: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_TRIPWIRE);
+ case E_ITEM_SUGARCANE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_SUGARCANE);
case E_ITEM_WOODEN_HOE:
case E_ITEM_STONE_HOE:
@@ -247,6 +302,54 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
return new cItemMinecartHandler(a_ItemType);
}
+ case E_BLOCK_ACACIA_FENCE_GATE:
+ case E_BLOCK_BIRCH_FENCE_GATE:
+ case E_BLOCK_DARK_OAK_FENCE_GATE:
+ case E_BLOCK_JUNGLE_FENCE_GATE:
+ case E_BLOCK_OAK_FENCE_GATE:
+ case E_BLOCK_SPRUCE_FENCE_GATE:
+ {
+ return new cItemFenceGateHandler(a_ItemType);
+ }
+
+ case E_BLOCK_ACACIA_WOOD_STAIRS:
+ case E_BLOCK_BIRCH_WOOD_STAIRS:
+ case E_BLOCK_BRICK_STAIRS:
+ case E_BLOCK_COBBLESTONE_STAIRS:
+ case E_BLOCK_DARK_OAK_WOOD_STAIRS:
+ case E_BLOCK_JUNGLE_WOOD_STAIRS:
+ case E_BLOCK_NETHER_BRICK_STAIRS:
+ case E_BLOCK_OAK_WOOD_STAIRS:
+ case E_BLOCK_PURPUR_STAIRS:
+ case E_BLOCK_QUARTZ_STAIRS:
+ case E_BLOCK_RED_SANDSTONE_STAIRS:
+ case E_BLOCK_SANDSTONE_STAIRS:
+ case E_BLOCK_SPRUCE_WOOD_STAIRS:
+ case E_BLOCK_STONE_BRICK_STAIRS:
+ {
+ return new cItemStairsHandler(a_ItemType);
+ }
+
+ case E_BLOCK_WHITE_GLAZED_TERRACOTTA:
+ case E_BLOCK_ORANGE_GLAZED_TERRACOTTA:
+ case E_BLOCK_MAGENTA_GLAZED_TERRACOTTA:
+ case E_BLOCK_LIGHT_BLUE_GLAZED_TERRACOTTA:
+ case E_BLOCK_YELLOW_GLAZED_TERRACOTTA:
+ case E_BLOCK_LIME_GLAZED_TERRACOTTA:
+ case E_BLOCK_PINK_GLAZED_TERRACOTTA:
+ case E_BLOCK_GRAY_GLAZED_TERRACOTTA:
+ case E_BLOCK_LIGHT_GRAY_GLAZED_TERRACOTTA:
+ case E_BLOCK_CYAN_GLAZED_TERRACOTTA:
+ case E_BLOCK_PURPLE_GLAZED_TERRACOTTA:
+ case E_BLOCK_BLUE_GLAZED_TERRACOTTA:
+ case E_BLOCK_BROWN_GLAZED_TERRACOTTA:
+ case E_BLOCK_GREEN_GLAZED_TERRACOTTA:
+ case E_BLOCK_RED_GLAZED_TERRACOTTA:
+ case E_BLOCK_BLACK_GLAZED_TERRACOTTA:
+ {
+ return new cItemGlazedTerracottaHandler(a_ItemType);
+ }
+
// Food (please keep alpha-sorted):
case E_ITEM_BAKED_POTATO: return new cItemFoodHandler(a_ItemType, FoodInfo(5, 6));
case E_ITEM_BEETROOT: return new cItemFoodHandler(a_ItemType, FoodInfo(1, 1.2));
@@ -347,84 +450,66 @@ cItemHandler::cItemHandler(int a_ItemType)
-bool cItemHandler::OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
-)
+void cItemHandler::OnPlayerPlace(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_ClickedBlockPosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
{
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{
// Clicked in the air, no placement possible
- return false;
+ return;
}
- if (!cChunkDef::IsValidHeight(a_ClickedBlockPos.y))
+ if (!cChunkDef::IsValidHeight(a_ClickedBlockPosition.y))
{
- // The clicked block is outside the world, ignore this call altogether (#128)
- return false;
+ // The clicked block is outside the world, ignore this call altogether (GH #128):
+ return;
}
+ const auto & World = *a_Player.GetWorld();
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
-
- a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
- cChunkInterface ChunkInterface(a_World.GetChunkMap());
+ World.GetBlockTypeMeta(a_ClickedBlockPosition, ClickedBlockType, ClickedBlockMeta);
// Check if the block ignores build collision (water, grass etc.):
- auto PlacedBlockPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
- if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
+ if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(World, a_HeldItem, a_ClickedBlockPosition, ClickedBlockMeta, a_ClickedBlockFace, true))
{
- // Replace the clicked block:
- a_World.DropBlockAsPickups(a_ClickedBlockPos, &a_Player, nullptr);
- PlacedBlockPos = a_ClickedBlockPos;
+ // Try to place the block at the clicked position:
+ if (!CommitPlacement(a_Player, a_HeldItem, a_ClickedBlockPosition, a_ClickedBlockFace, a_CursorPosition))
+ {
+ // The placement failed, the blocks have already been re-sent, re-send inventory:
+ a_Player.GetInventory().SendEquippedSlot();
+ return;
+ }
}
else
{
- if (!cChunkDef::IsValidHeight(PlacedBlockPos.y))
+ const auto PlacedPosition = AddFaceDirection(a_ClickedBlockPosition, a_ClickedBlockFace);
+
+ if (!cChunkDef::IsValidHeight(PlacedPosition.y))
{
- // The block is being placed outside the world, ignore this packet altogether (#128)
- return false;
+ // The block is being placed outside the world, ignore this packet altogether (GH #128):
+ return;
}
NIBBLETYPE PlaceMeta;
BLOCKTYPE PlaceBlock;
- a_World.GetBlockTypeMeta(PlacedBlockPos, PlaceBlock, PlaceMeta);
+ World.GetBlockTypeMeta(PlacedPosition, PlaceBlock, PlaceMeta);
// Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
// No need to do combinability (dblslab) checks, client will do that here.
- if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(ChunkInterface, PlacedBlockPos, a_Player, PlaceMeta))
+ if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(World, a_HeldItem, PlacedPosition, PlaceMeta, a_ClickedBlockFace, false))
{
// Tried to place a block into another?
// Happens when you place a block aiming at side of block with a torch on it or stem beside it
- return false;
+ return;
}
- }
- // Get all the blocks to place:
- sSetBlockVector blocks;
- if (!GetBlocksToPlace(a_World, a_Player, a_EquippedItem, PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, blocks))
- {
- // Handler refused the placement, send that information back to the client:
- for (const auto & blk: blocks)
+ // Try to place the block:
+ if (!CommitPlacement(a_Player, a_HeldItem, PlacedPosition, a_ClickedBlockFace, a_CursorPosition))
{
- const auto & AbsPos = blk.GetAbsolutePos();
- a_World.SendBlockTo(AbsPos, a_Player);
+ // The placement failed, the blocks have already been re-sent, re-send inventory:
+ a_Player.GetInventory().SendEquippedSlot();
+ return;
}
- a_World.SendBlockTo(PlacedBlockPos, a_Player);
- a_Player.GetInventory().SendEquippedSlot();
- return false;
- }
-
- // Try to place the blocks:
- if (!a_Player.PlaceBlocks(blocks))
- {
- // The placement failed, the blocks have already been re-sent, re-send inventory:
- a_Player.GetInventory().SendEquippedSlot();
- return false;
}
// Remove the "placed" item:
@@ -432,29 +517,6 @@ bool cItemHandler::OnPlayerPlace(
{
a_Player.GetInventory().RemoveOneEquippedItem();
}
- return true;
-}
-
-
-
-
-
-bool cItemHandler::GetBlocksToPlace(
- cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- sSetBlockVector & a_BlocksToSet
-)
-{
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, BlockType, BlockMeta))
- {
- return false;
- }
- a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, BlockMeta);
- return true;
}
@@ -821,34 +883,6 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
-bool cItemHandler::GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos, eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
-)
-{
- ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers
-
- if (m_ItemType >= 256)
- {
- LOGERROR("%s: Item %d is not eligible for direct block placement!", __FUNCTION__, m_ItemType);
- return false;
- }
-
- cChunkInterface ChunkInterface(a_World->GetChunkMap());
- return cBlockHandler::For(static_cast<BLOCKTYPE>(m_ItemType)).GetPlacementBlockTypeMeta(
- ChunkInterface, *a_Player,
- a_PlacedBlockPos, a_ClickedBlockFace,
- a_CursorPos,
- a_BlockType, a_BlockMeta
- );
-}
-
-
-
-
-
bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
{
auto FoodInfo = GetFoodInfo(a_Item);
@@ -873,3 +907,19 @@ float cItemHandler::GetBlockBreakingStrength(BLOCKTYPE a_Block)
{
return 1.0f;
}
+
+
+
+
+
+bool cItemHandler::CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
+{
+ ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers.
+
+ // By default, all blocks can be placed and the meta is copied over from the item's damage value:
+ return a_Player.PlaceBlock(
+ a_PlacePosition,
+ static_cast<BLOCKTYPE>(m_ItemType),
+ static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x0f)
+ );
+}
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 74de4eaa5..415f7f24f 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -9,6 +9,7 @@
// fwd:
+class cChunk;
class cWorld;
class cPlayer;
class cBlockPluginInterface;
@@ -36,55 +37,14 @@ public:
/** Force virtual destructor */
virtual ~cItemHandler() {}
-
/** Called when the player tries to place the item (right mouse button, IsPlaceable() == true).
a_ClickedBlockPos is the (neighbor) block that has been clicked to place this item.
a_ClickedBlockFace is the face of the neighbor that has been clicked to place this item.
a_CursorPos is the position of the player's cursor within a_ClickedBlockFace.
The default handler uses GetBlocksToPlace() and places the returned blocks.
Override if the item needs advanced processing, such as spawning a mob based on the blocks being placed.
- If the block placement is refused inside this call, it will automatically revert the client-side changes.
- Returns true if the placement succeeded, false if the placement was aborted for any reason. */
- virtual bool OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
- );
-
-
- /** Called from OnPlayerPlace() to determine the blocks that the current placement operation should set.
- a_PlacedBlockPos points to the location where the new block should be set.
- a_ClickedBlockFace is the block face of the neighbor that was clicked to place this block.
- a_CursorPos is the position of the mouse cursor within the clicked (neighbor's) block face.
- The blocks in a_BlocksToPlace will be sent through cPlayer::PlaceBlocks() after returning from this function.
- The default handler uses GetPlacementBlockTypeMeta() and provides that as the single block at the specified coords.
- Returns true if the placement succeeded, false if the placement was aborted for any reason.
- If aborted, the server then sends all original blocks in the coords provided in a_BlocksToSet to the client. */
- virtual bool GetBlocksToPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- sSetBlockVector & a_BlocksToPlace
- );
-
-
- /** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlayerPlace() is not overridden.
- This function should provide the block type and meta for the placed block, or refuse the placement.
- Returns true to allow placement, false to refuse. */
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- );
-
+ If the block placement is refused inside this call, it will automatically revert the client-side changes. */
+ void OnPlayerPlace(cPlayer & a_Player, const cItem & a_HeldItem, Vector3i a_ClickedBlockPosition, eBlockFace a_ClickedBlockFace, Vector3i a_CursorPosition);
/** Called when the player tries to use the item (right mouse button).
Descendants can return false to abort the usage (default behavior). */
@@ -97,7 +57,6 @@ public:
eBlockFace a_ClickedBlockFace
);
-
/** Called when the client sends the SHOOT status in the lclk packet (releasing the bow). */
virtual void OnItemShoot(cPlayer *, const Vector3i a_BlockPos, eBlockFace a_BlockFace)
{
@@ -181,9 +140,16 @@ public:
static void Deinit();
protected:
+
int m_ItemType;
static cItemHandler * CreateItemHandler(int m_ItemType);
+ /** Performs the actual placement of this placeable item.
+ The descendant handler should call a_Player.PlaceBlock(s) supplying correct values for the newly placed block.
+ The default handler uses the stored block type and meta copied from the lowest 4 bits of the player's equipped item's damage value.
+ Handlers return what a_Player.PlaceBlock(s) returns, indicating whether the placement was successful. */
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, Vector3i a_PlacePosition, eBlockFace a_ClickedBlockFace, Vector3i a_CursorPosition);
+
static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1];
static bool m_HandlerInitialized; // used to detect if the itemhandlers are initialized
};
diff --git a/src/Items/ItemHopper.h b/src/Items/ItemHopper.h
new file mode 100644
index 000000000..2f0651a43
--- /dev/null
+++ b/src/Items/ItemHopper.h
@@ -0,0 +1,40 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemHopperHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_BOTTOM: return E_META_HOPPER_FACING_YM;
+ case BLOCK_FACE_TOP: return E_META_HOPPER_FACING_YM;
+ case BLOCK_FACE_EAST: return E_META_HOPPER_FACING_XM;
+ case BLOCK_FACE_NORTH: return E_META_HOPPER_FACING_ZP;
+ case BLOCK_FACE_SOUTH: return E_META_HOPPER_FACING_ZM;
+ case BLOCK_FACE_WEST: return E_META_HOPPER_FACING_XP;
+ default: UNREACHABLE("Unsupported block face");
+ }
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_HOPPER, BlockFaceToMetaData(a_ClickedBlockFace));
+ }
+};
diff --git a/src/Items/ItemJackOLantern.h b/src/Items/ItemJackOLantern.h
new file mode 100644
index 000000000..703088013
--- /dev/null
+++ b/src/Items/ItemJackOLantern.h
@@ -0,0 +1,27 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockPumpkin.h"
+
+
+
+
+
+class cItemJackOLanternHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ // Re-use the pumpkin converter for lanterns:
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_JACK_O_LANTERN, cBlockPumpkinHandler::YawToMetaData(a_Player.GetYaw()));
+ }
+};
diff --git a/src/Items/ItemLadder.h b/src/Items/ItemLadder.h
new file mode 100644
index 000000000..67de799c5
--- /dev/null
+++ b/src/Items/ItemLadder.h
@@ -0,0 +1,77 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockLadder.h"
+
+
+
+
+
+class cItemLadderHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ /** Converts the block face of the neighbor to which the ladder is attached to the ladder block's meta. */
+ static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
+ {
+ switch (a_NeighborBlockFace)
+ {
+ case BLOCK_FACE_ZM: return 0x2;
+ case BLOCK_FACE_ZP: return 0x3;
+ case BLOCK_FACE_XM: return 0x4;
+ case BLOCK_FACE_XP: return 0x5;
+ case BLOCK_FACE_YM:
+ case BLOCK_FACE_YP: return 0x2;
+ default: UNREACHABLE("Unsupported neighbor block face");
+ }
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ const auto & World = *a_Player.GetWorld();
+ const auto ClickedBlockType = World.GetBlock(AddFaceDirection(a_PlacePosition, a_ClickedBlockFace, true));
+
+ // Try finding a suitable neighbor block face for the ladder; start with the given one:
+ if (!cBlockLadderHandler::CanBePlacedOn(ClickedBlockType, a_ClickedBlockFace))
+ {
+ // Couldn't be placed on whatever face was clicked, last ditch resort - find another face:
+ a_ClickedBlockFace = FindSuitableFace(World, a_PlacePosition);
+ if (a_ClickedBlockFace == BLOCK_FACE_NONE)
+ {
+ // No attachable face found - don't place the ladder:
+ return false;
+ }
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_LADDER, BlockFaceToMetaData(a_ClickedBlockFace));
+ }
+
+
+ /** Returns a suitable neighbor's blockface to place the ladder at the specified position.
+ Returns BLOCK_FACE_NONE on failure. */
+ static eBlockFace FindSuitableFace(const cWorld & a_World, const Vector3i a_Position)
+ {
+ for (const auto Face : { BLOCK_FACE_ZM, BLOCK_FACE_XP, BLOCK_FACE_ZP, BLOCK_FACE_XM }) // Loop through all faces in specific order.
+ {
+ // The direction of Face is relative to the direction the ladder faces.
+ // This is the position, computed inverted, that such a ladder would attach to.
+ const auto NeighborPosition = AddFaceDirection(a_Position, Face, true);
+
+ if (cBlockLadderHandler::CanBePlacedOn(a_World.GetBlock(NeighborPosition), Face))
+ {
+ return Face;
+ }
+ }
+
+ return BLOCK_FACE_NONE;
+ }
+};
diff --git a/src/Items/ItemLeaves.h b/src/Items/ItemLeaves.h
index 0233ed6a6..abe2a76cf 100644
--- a/src/Items/ItemLeaves.h
+++ b/src/Items/ItemLeaves.h
@@ -23,23 +23,13 @@ public:
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- bool res = Super::GetPlacementBlockTypeMeta(
- a_World, a_Player,
- a_PlacedBlockPos,
- a_ClickedBlockFace,
- a_CursorPos,
- a_BlockType, a_BlockMeta
+ return a_Player.PlaceBlock(
+ a_PlacePosition,
+ static_cast<BLOCKTYPE>(m_ItemType),
+ static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage | 0x4) // 0x4 bit set means this is a player-placed leaves block, not to be decayed.
);
- a_BlockMeta = a_BlockMeta | 0x4; // 0x4 bit set means this is a player-placed leaves block, not to be decayed
- return res;
}
} ;
diff --git a/src/Items/ItemLever.h b/src/Items/ItemLever.h
new file mode 100644
index 000000000..66c83f485
--- /dev/null
+++ b/src/Items/ItemLever.h
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemLeverHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ /** Converts the block face of the neighbor to which the lever is attached to the lever block's meta. */
+ static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
+ {
+ // Determine lever direction:
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_YP: return 0x6;
+ case BLOCK_FACE_XP: return 0x1;
+ case BLOCK_FACE_XM: return 0x2;
+ case BLOCK_FACE_ZP: return 0x3;
+ case BLOCK_FACE_ZM: return 0x4;
+ case BLOCK_FACE_YM: return 0x0;
+ case BLOCK_FACE_NONE: break;
+ }
+ UNREACHABLE("Unsupported block face");
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace));
+ }
+};
diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h
index 90d67b02b..3d6f1d7aa 100644
--- a/src/Items/ItemMobHead.h
+++ b/src/Items/ItemMobHead.h
@@ -25,38 +25,30 @@ public:
- virtual bool OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
// Cannot place a head at "no face" and from the bottom:
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
{
- return true;
+ return false;
}
- const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
// If the placed head is a wither, try to spawn the wither first:
- if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER)
+ if (a_HeldItem.m_ItemDamage == E_META_HEAD_WITHER)
{
- if (TrySpawnWitherAround(a_World, a_Player, PlacePos))
+ if (TrySpawnWitherAround(a_Player, a_PlacePosition))
{
return true;
}
// Wither not created, proceed with regular head placement
}
- cItem ItemCopy(a_EquippedItem); // Make a copy in case this is the player's last head item and OnPlayerPlace removes it
- if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
+ if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_ClickedBlockFace)))
{
return false;
}
- RegularHeadPlaced(a_World, a_Player, ItemCopy, PlacePos, a_ClickedBlockFace);
+
+ RegularHeadPlaced(a_Player, a_HeldItem, a_PlacePosition, a_ClickedBlockFace);
return true;
}
@@ -66,16 +58,13 @@ public:
/** Called after placing a regular head block with no mob spawning.
Adjusts the mob head entity based on the equipped item's data. */
- void RegularHeadPlaced(
- cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
- const Vector3i a_PlacePos, eBlockFace a_ClickedBlockFace
- )
+ void RegularHeadPlaced(const cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace)
{
- auto HeadType = static_cast<eMobHeadType>(a_EquippedItem.m_ItemDamage);
- auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace);
+ const auto HeadType = static_cast<eMobHeadType>(a_HeldItem.m_ItemDamage);
+ const auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace);
// Use a callback to set the properties of the mob head block entity:
- a_World.DoWithBlockEntityAt(a_PlacePos, [&](cBlockEntity & a_BlockEntity)
+ a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_Player, HeadType, BlockMeta](cBlockEntity & a_BlockEntity)
{
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_HEAD);
@@ -99,10 +88,7 @@ public:
/** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula.
Returns true if the wither was created. */
- bool TrySpawnWitherAround(
- cWorld & a_World, cPlayer & a_Player,
- const Vector3i a_BlockPos
- )
+ bool TrySpawnWitherAround(cPlayer & a_Player, const Vector3i a_BlockPos)
{
// No wither can be created at Y < 2 - not enough space for the formula:
if (a_BlockPos.y < 2)
@@ -119,10 +105,11 @@ public:
{ 0, 0, 1},
{ 0, 0, -1},
};
+
for (auto & RelCoord : RelCoords)
{
if (TrySpawnWitherAt(
- a_World, a_Player,
+ *a_Player.GetWorld(), a_Player,
a_BlockPos,
RelCoord.x, RelCoord.z
))
@@ -177,12 +164,12 @@ public:
// Try to spawn the wither from each image:
return (
TrySpawnWitherFromImage(
- a_World, a_Player, ImageWitherX, ARRAYCOUNT(ImageWitherX),
+ a_World, a_Player, ImageWitherX,
a_PlacedHeadPos,
a_OffsetX, a_OffsetZ
) ||
TrySpawnWitherFromImage(
- a_World, a_Player, ImageWitherZ, ARRAYCOUNT(ImageWitherZ),
+ a_World, a_Player, ImageWitherZ,
a_PlacedHeadPos,
a_OffsetX, a_OffsetZ
)
@@ -199,35 +186,39 @@ public:
Offset is used to shift the image around the X and Z axis.
Returns true iff the wither was created successfully. */
bool TrySpawnWitherFromImage(
- cWorld & a_World, cPlayer & a_Player, const sSetBlock * a_Image, size_t a_ImageCount,
+ cWorld & a_World, cPlayer & a_Player, const sSetBlock (& a_Image)[9],
Vector3i a_PlacedHeadPos,
int a_OffsetX, int a_OffsetZ
)
{
- // Check each block individually; simultaneously build the SetBlockVector for clearing the blocks:
- sSetBlockVector AirBlocks;
- AirBlocks.reserve(a_ImageCount);
- for (size_t i = 0; i < a_ImageCount; i++)
- {
- // Get the absolute coords of the image:
- int BlockX = a_PlacedHeadPos.x + a_OffsetX + a_Image[i].GetX();
- int BlockY = a_PlacedHeadPos.y + a_Image[i].GetY();
- int BlockZ = a_PlacedHeadPos.z + a_OffsetZ + a_Image[i].GetZ();
+ std::array<Vector3i, 9> PositionsToClear;
- // If the query is for the placed head, short-circuit-evaluate it:
- if ((BlockX == a_PlacedHeadPos.x) && (BlockY == a_PlacedHeadPos.y) && (BlockZ == a_PlacedHeadPos.z))
+ // Check each block individually:
+ for (size_t i = 0; i != std::size(a_Image); i++)
+ {
+ // The absolute coords of the block in the image to check.
+ const Vector3i Block(
+ a_PlacedHeadPos.x + a_OffsetX + a_Image[i].GetX(),
+ a_PlacedHeadPos.y + a_Image[i].GetY(),
+ a_PlacedHeadPos.z + a_OffsetZ + a_Image[i].GetZ()
+ );
+
+ // If the query is for the head the player is about to place (remember, it hasn't been set into the world yet), short-circuit-evaluate it:
+ if (Block == a_PlacedHeadPos)
{
if (a_Image[i].m_BlockType != E_BLOCK_HEAD)
{
- return false; // Didn't match
+ return false; // Didn't match.
}
- continue; // Matched, continue checking the rest of the image
+
+ PositionsToClear[i] = Block;
+ continue; // Matched, continue checking the rest of the image.
}
// Query the world block:
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
- if (!a_World.GetBlockTypeMeta({ BlockX, BlockY, BlockZ }, BlockType, BlockMeta))
+ if (!a_World.GetBlockTypeMeta(Block, BlockType, BlockMeta))
{
// Cannot query block, assume unloaded chunk, fail to spawn the wither
return false;
@@ -242,7 +233,7 @@ public:
// If it is a mob head, check it's a wither skull using the block entity:
if (
(BlockType == E_BLOCK_HEAD) &&
- !a_World.DoWithBlockEntityAt({ BlockX, BlockY, BlockZ }, [&](cBlockEntity & a_BlockEntity)
+ !a_World.DoWithBlockEntityAt(Block, [&](cBlockEntity & a_BlockEntity)
{
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_HEAD);
@@ -253,12 +244,25 @@ public:
return false;
}
- // Matched, continue checking
- AirBlocks.emplace_back(BlockX, BlockY, BlockZ, E_BLOCK_AIR, 0);
+ // Matched, continue checking:
+ PositionsToClear[i] = Block;
} // for i - a_Image
// All image blocks matched, try replace the image with air blocks:
- if (!a_Player.PlaceBlocks(AirBlocks))
+ if (
+ !a_Player.PlaceBlocks(
+ {
+ { PositionsToClear[0], E_BLOCK_AIR, 0 },
+ { PositionsToClear[1], E_BLOCK_AIR, 0 },
+ { PositionsToClear[2], E_BLOCK_AIR, 0 },
+ { PositionsToClear[3], E_BLOCK_AIR, 0 },
+ { PositionsToClear[4], E_BLOCK_AIR, 0 },
+ { PositionsToClear[5], E_BLOCK_AIR, 0 },
+ { PositionsToClear[6], E_BLOCK_AIR, 0 },
+ { PositionsToClear[7], E_BLOCK_AIR, 0 },
+ { PositionsToClear[8], E_BLOCK_AIR, 0 },
+ })
+ )
{
return false;
}
@@ -323,23 +327,6 @@ public:
{
return true;
}
-
-
-
-
-
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
- {
- a_BlockType = E_BLOCK_HEAD;
- a_BlockMeta = BlockFaceToBlockMeta(a_ClickedBlockFace);
- return true;
- }
} ;
diff --git a/src/Items/ItemNetherWart.h b/src/Items/ItemNetherWart.h
index 462ea61f9..61043226d 100644
--- a/src/Items/ItemNetherWart.h
+++ b/src/Items/ItemNetherWart.h
@@ -24,37 +24,23 @@ public:
- virtual bool IsPlaceable(void) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- return true;
+ // Only allow planting nether wart onto the top side of the block:
+ if (a_ClickedBlockFace != BLOCK_FACE_TOP)
+ {
+ return true;
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_NETHER_WART, 0);
}
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ virtual bool IsPlaceable(void) override
{
- // Only allow planting nether wart onto the top side of the block:
- if (a_ClickedBlockFace != BLOCK_FACE_TOP)
- {
- return false;
- }
-
- // Only allow placement on soulsand
- if ((a_PlacedBlockPos.y < 1) || (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_SOULSAND))
- {
- return false;
- }
-
- a_BlockMeta = 0;
- a_BlockType = E_BLOCK_NETHER_WART;
return true;
}
} ;
diff --git a/src/Items/ItemObserver.h b/src/Items/ItemObserver.h
new file mode 100644
index 000000000..b95882ae0
--- /dev/null
+++ b/src/Items/ItemObserver.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockObserver.h"
+
+
+
+
+
+class cItemObserverHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_OBSERVER, cBlockObserverHandler::PitchYawToMetaData(a_Player.GetYaw(), a_Player.GetPitch()));
+ }
+};
diff --git a/src/Items/ItemPiston.h b/src/Items/ItemPiston.h
new file mode 100644
index 000000000..659300924
--- /dev/null
+++ b/src/Items/ItemPiston.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockPiston.h"
+
+
+
+
+
+class cItemPistonHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), cBlockPistonHandler::PitchYawToMetaData(a_Player.GetYaw(), a_Player.GetPitch()));
+ }
+};
diff --git a/src/Items/ItemPlanks.h b/src/Items/ItemPlanks.h
new file mode 100644
index 000000000..537e44460
--- /dev/null
+++ b/src/Items/ItemPlanks.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemPlanksHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage));
+ }
+};
diff --git a/src/Items/ItemPumpkin.h b/src/Items/ItemPumpkin.h
index 897cba279..cd17472b5 100644
--- a/src/Items/ItemPumpkin.h
+++ b/src/Items/ItemPumpkin.h
@@ -2,6 +2,7 @@
#pragma once
#include "ItemHandler.h"
+#include "Blocks/BlockPumpkin.h"
@@ -14,35 +15,22 @@ class cItemPumpkinHandler:
public:
- cItemPumpkinHandler():
- Super(E_BLOCK_PUMPKIN)
- {
- }
-
-
-
+ using Super::Super;
+private:
- virtual bool OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
// First try spawning a snow golem or an iron golem:
- auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
- if (TrySpawnGolem(a_World, a_Player, PlacePos))
+ if (TrySpawnGolem(a_Player, a_PlacePosition))
{
// The client thinks that they placed the pumpkin, let them know it's been replaced:
- a_Player.SendBlocksAround(PlacePos.x, PlacePos.y, PlacePos.z);
+ a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z);
return true;
}
// No golem at these coords, place the block normally:
- return Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos);
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_PUMPKIN, cBlockPumpkinHandler::YawToMetaData(a_Player.GetYaw()));
}
@@ -51,22 +39,24 @@ public:
/** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */
- bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, const Vector3i a_PumpkinPos)
+ bool TrySpawnGolem(cPlayer & a_Player, const Vector3i a_PumpkinPos)
{
- // A golem can't form with a pumpkin below level 2 or above level 255
+ // A golem can't form with a pumpkin below level 2 or above level 255:
if ((a_PumpkinPos.y < 2) || (a_PumpkinPos.y >= cChunkDef::Height))
{
return false;
}
+ auto & World = *a_Player.GetWorld();
+
// Decide which golem to try spawning based on the block below the placed pumpkin:
- switch (a_World.GetBlock(a_PumpkinPos.addedY(-1)))
+ switch (World.GetBlock(a_PumpkinPos.addedY(-1)))
{
- case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_PumpkinPos);
- case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_PumpkinPos);
+ case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(World, a_Player, a_PumpkinPos);
+ case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(World, a_Player, a_PumpkinPos);
default:
{
- // No golem here
+ // No golem here:
return false;
}
}
@@ -91,11 +81,14 @@ public:
}
// Try to place air blocks where the original recipe blocks were:
- sSetBlockVector AirBlocks;
- AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
- AirBlocks.emplace_back(a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0); // Torso
- AirBlocks.emplace_back(a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0); // Legs
- if (!a_Player.PlaceBlocks(AirBlocks))
+ if (
+ !a_Player.PlaceBlocks(
+ {
+ { a_PumpkinPos, E_BLOCK_AIR, 0 }, // Head
+ { a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0 }, // Torso
+ { a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0 } // Legs
+ })
+ )
{
return false;
}
@@ -143,13 +136,16 @@ public:
}
// Try to place air blocks where the original recipe blocks were:
- sSetBlockVector AirBlocks;
- AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
- AirBlocks.emplace_back(BodyPos, E_BLOCK_AIR, 0); // Torso
- AirBlocks.emplace_back(BodyPos.addedY(-1), E_BLOCK_AIR, 0); // Legs
- AirBlocks.emplace_back(BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
- AirBlocks.emplace_back(BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
- if (!a_Player.PlaceBlocks(AirBlocks))
+ if (
+ !a_Player.PlaceBlocks(
+ {
+ { a_PumpkinPos, E_BLOCK_AIR, 0 }, // Head
+ { BodyPos, E_BLOCK_AIR, 0 }, // Torso
+ { BodyPos.addedY(-1), E_BLOCK_AIR, 0 }, // Legs
+ { BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0 }, // Arm
+ { BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0 } // Arm
+ })
+ )
{
return false;
}
diff --git a/src/Items/ItemQuartz.h b/src/Items/ItemQuartz.h
new file mode 100644
index 000000000..a3aaa49e7
--- /dev/null
+++ b/src/Items/ItemQuartz.h
@@ -0,0 +1,60 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemQuartzHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ /** Converts the block face of the pillar block's "base" to the block's metadata. */
+ static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_YM:
+ case BLOCK_FACE_YP:
+ {
+ return E_META_QUARTZ_PILLAR; // Top or bottom.
+ }
+
+ case BLOCK_FACE_ZP:
+ case BLOCK_FACE_ZM:
+ {
+ return 0x4; // North or south.
+ }
+
+ case BLOCK_FACE_XP:
+ case BLOCK_FACE_XM:
+ {
+ return 0x3; // East or west.
+ }
+ default: UNREACHABLE("Unsupported block face");
+ }
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ const auto Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
+
+ // Pillar block needs additional direction in the metadata:
+ if (Meta == E_META_QUARTZ_PILLAR)
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_QUARTZ_BLOCK, BlockFaceToMetaData(a_ClickedBlockFace));
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_QUARTZ_BLOCK, Meta);
+ }
+};
diff --git a/src/Items/ItemRail.h b/src/Items/ItemRail.h
new file mode 100644
index 000000000..a259a2a6d
--- /dev/null
+++ b/src/Items/ItemRail.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockRail.h"
+
+
+
+
+
+class cItemRailHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ cChunkInterface ChunkInterface(a_Player.GetWorld()->GetChunkMap());
+ const auto RailType = static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType);
+ return a_Player.PlaceBlock(a_PlacePosition, RailType, cBlockRailHandler::FindMeta(ChunkInterface, a_PlacePosition, RailType));
+ }
+};
diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h
index 559be843d..d1bb6556c 100644
--- a/src/Items/ItemRedstoneDust.h
+++ b/src/Items/ItemRedstoneDust.h
@@ -23,69 +23,19 @@ public:
- virtual bool IsPlaceable(void) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- return true;
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_REDSTONE_WIRE, 0);
}
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ virtual bool IsPlaceable(void) override
{
- // Check the block below, if it supports dust on top of it:
- auto UnderPos = a_PlacedBlockPos.addedY(-1);
- if (UnderPos.y < 0)
- {
- return false;
- }
- BLOCKTYPE BlockType;
- NIBBLETYPE BlockMeta;
- if (!a_World->GetBlockTypeMeta(UnderPos, BlockType, BlockMeta))
- {
- return false;
- }
- if (!IsBlockTypeUnderSuitable(BlockType, BlockMeta))
- {
- return false;
- }
-
- a_BlockType = E_BLOCK_REDSTONE_WIRE;
- a_BlockMeta = 0;
return true;
}
-
-
-
-
-
- /** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */
- static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
- {
- if (cBlockInfo::FullyOccupiesVoxel(a_BlockType))
- {
- return true;
- }
-
- switch (a_BlockType)
- {
- case E_BLOCK_RED_SANDSTONE_SLAB:
- case E_BLOCK_WOODEN_SLAB:
- case E_BLOCK_STONE_SLAB:
- {
- // Slabs can support redstone if they're upside down:
- return ((a_BlockMeta & 0x08) != 0);
- }
- }
- return false;
- }
} ;
diff --git a/src/Items/ItemRedstoneRepeater.h b/src/Items/ItemRedstoneRepeater.h
index 6b1635609..6461d3ffd 100644
--- a/src/Items/ItemRedstoneRepeater.h
+++ b/src/Items/ItemRedstoneRepeater.h
@@ -24,25 +24,17 @@ public:
- virtual bool IsPlaceable() override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- return true;
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_REDSTONE_REPEATER_OFF, cBlockRedstoneRepeaterHandler::YawToMetaData(a_Player.GetYaw()));
}
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ virtual bool IsPlaceable() override
{
- a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF;
- a_BlockMeta = cBlockRedstoneRepeaterHandler::YawToMetaData(a_Player->GetYaw());
return true;
}
} ;
diff --git a/src/Items/ItemSapling.h b/src/Items/ItemSapling.h
index 513f542f6..3f62af94f 100644
--- a/src/Items/ItemSapling.h
+++ b/src/Items/ItemSapling.h
@@ -20,30 +20,12 @@ public:
}
-
-
-
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- bool res = Super::GetPlacementBlockTypeMeta(
- a_World, a_Player,
- a_PlacedBlockPos, a_ClickedBlockFace,
- a_CursorPos,
- a_BlockType, a_BlockMeta
+ return a_Player.PlaceBlock(
+ a_PlacePosition,
+ static_cast<BLOCKTYPE>(m_ItemType),
+ static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x07) // Allow only the lowest 3 bits (top bit is for growth).
);
-
- // Allow only the lowest 3 bits (top bit is for growth):
- a_BlockMeta = a_BlockMeta & 0x07;
- return res;
}
} ;
-
-
-
-
diff --git a/src/Items/ItemSeeds.h b/src/Items/ItemSeeds.h
index 67d90362a..0bb9afbb9 100644
--- a/src/Items/ItemSeeds.h
+++ b/src/Items/ItemSeeds.h
@@ -25,47 +25,38 @@ public:
- virtual bool IsPlaceable(void) override
- {
- return true;
- }
-
-
-
-
-
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
// Only allow planting seeds from the top side of the block:
- if ((a_ClickedBlockFace != BLOCK_FACE_TOP) || (a_PlacedBlockPos.y <= 0))
+ if (a_ClickedBlockFace != BLOCK_FACE_TOP)
{
return false;
}
- // Only allow placement on farmland
- if (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_FARMLAND)
- {
- return false;
- }
+ BLOCKTYPE BlockType;
// Get the produce block based on the seed item:
- a_BlockMeta = 0;
switch (m_ItemType)
{
- case E_ITEM_BEETROOT_SEEDS: a_BlockType = E_BLOCK_BEETROOTS; return true;
- case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true;
- case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true;
- case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true;
- case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true;
- case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true;
- default: a_BlockType = E_BLOCK_AIR; return true;
+ case E_ITEM_BEETROOT_SEEDS: BlockType = E_BLOCK_BEETROOTS; break;
+ case E_ITEM_CARROT: BlockType = E_BLOCK_CARROTS; break;
+ case E_ITEM_MELON_SEEDS: BlockType = E_BLOCK_MELON_STEM; break;
+ case E_ITEM_POTATO: BlockType = E_BLOCK_POTATOES; break;
+ case E_ITEM_PUMPKIN_SEEDS: BlockType = E_BLOCK_PUMPKIN_STEM; break;
+ case E_ITEM_SEEDS: BlockType = E_BLOCK_CROPS; break;
+ default: UNREACHABLE("Unsupported seed type");
}
+
+ return a_Player.PlaceBlock(a_PlacePosition, BlockType, 0);
+ }
+
+
+
+
+
+ virtual bool IsPlaceable(void) override
+ {
+ return true;
}
} ;
diff --git a/src/Items/ItemSideways.h b/src/Items/ItemSideways.h
new file mode 100644
index 000000000..cb6403fd6
--- /dev/null
+++ b/src/Items/ItemSideways.h
@@ -0,0 +1,53 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockSideways.h"
+
+
+
+
+
+/** Handler for blocks that have 3 orientations (hay bale, log), specified by the upper 2 bits in meta.
+Handles setting the correct orientation on placement.
+Additionally supports the metadata specifying block sub-type in its lower 2 bits. */
+class cItemSidewaysHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace, NIBBLETYPE a_Meta)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_YM:
+ case BLOCK_FACE_YP:
+ {
+ return a_Meta; // Top or bottom, just return original.
+ }
+ case BLOCK_FACE_ZP:
+ case BLOCK_FACE_ZM:
+ {
+ return a_Meta | 0x8; // North or south.
+ }
+ case BLOCK_FACE_XP:
+ case BLOCK_FACE_XM:
+ {
+ return a_Meta | 0x4; // East or west.
+ }
+ default: UNREACHABLE("Unsupported block face");
+ }
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace, static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage)));
+ }
+};
diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h
index 64d162f8d..fc5174e0b 100644
--- a/src/Items/ItemSign.h
+++ b/src/Items/ItemSign.h
@@ -3,8 +3,6 @@
#include "ItemHandler.h"
#include "../World.h"
-#include "../Blocks/BlockSignPost.h"
-#include "../Blocks/BlockWallSign.h"
#include "../ClientHandle.h"
@@ -18,77 +16,68 @@ class cItemSignHandler:
public:
- cItemSignHandler(int a_ItemType):
- Super(a_ItemType)
- {
- }
-
+ using Super::Super;
+private:
+ /** Converts the block face of the neighbor to which the wallsign is attached to the wallsign block's meta. */
+ static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
+ {
+ switch (a_NeighborBlockFace)
+ {
+ case BLOCK_FACE_ZM: return 0x02;
+ case BLOCK_FACE_ZP: return 0x03;
+ case BLOCK_FACE_XM: return 0x04;
+ case BLOCK_FACE_XP: return 0x05;
+ case BLOCK_FACE_NONE:
+ case BLOCK_FACE_YP:
+ case BLOCK_FACE_YM:
+ {
+ break;
+ }
+ }
+ return 0x02;
+ }
- virtual bool OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
- ) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- // Check if placing on something ignoring build collision to edit the correct sign later on:
- BLOCKTYPE ClickedBlockType;
- NIBBLETYPE ClickedBlockMeta;
- a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
- cChunkInterface ChunkInterface(a_World.GetChunkMap());
- bool IsReplacingClickedBlock = cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta);
-
- // If the regular placement doesn't work, do no further processing:
- if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
+ if (a_ClickedBlockFace == BLOCK_FACE_TOP)
+ {
+ if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_SIGN_POST, RotationToMetaData(a_Player.GetYaw())))
+ {
+ return false;
+ }
+ }
+ else if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_WALLSIGN, BlockFaceToMetaData(a_ClickedBlockFace)))
{
return false;
}
- // Use IsReplacingClickedBlock to make sure we will edit the right sign:
- auto SignPos = IsReplacingClickedBlock ? a_ClickedBlockPos : AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
-
// After successfully placing the sign, open the sign editor for the player:
- a_Player.GetClientHandle()->SendEditSign(SignPos.x, SignPos.y, SignPos.z);
+ a_Player.GetClientHandle()->SendEditSign(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z);
return true;
}
-
-
-
virtual bool IsPlaceable(void) override
{
return true;
}
-
-
-
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player,
- const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
- ) override
+ /** Converts the (player) rotation to placed-signpost block meta. */
+ static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
- if (a_ClickedBlockFace == BLOCK_FACE_TOP)
+ a_Rotation += 180 + (180.f / 16); // So it's not aligned with axis.
+ if (a_Rotation > 360)
{
- a_BlockMeta = cBlockSignPostHandler::RotationToMetaData(a_Player->GetYaw());
- a_BlockType = E_BLOCK_SIGN_POST;
+ a_Rotation -= 360;
}
- else
- {
- a_BlockMeta = cBlockWallSignHandler::BlockFaceToMetaData(a_ClickedBlockFace);
- a_BlockType = E_BLOCK_WALLSIGN;
- }
- return true;
+
+ a_Rotation = (a_Rotation / 360) * 16;
+
+ return static_cast<char>(a_Rotation) % 16;
}
} ;
diff --git a/src/Items/ItemSlab.h b/src/Items/ItemSlab.h
index 944336f7e..467975047 100644
--- a/src/Items/ItemSlab.h
+++ b/src/Items/ItemSlab.h
@@ -14,95 +14,87 @@ class cItemSlabHandler:
public:
- /** Creates a new handler for the specified slab item type.
- Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */
- cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType):
- Super(a_ItemType),
- m_DoubleSlabBlockType(a_DoubleSlabBlockType)
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- }
+ // Confer BlockSlab.h, which we're in cahoots with to make the below logic work.
+
+ // If clicking a slab, combine it into a double-slab:
+ if (cBlockSlabHandler::IsAnySlabType(a_Player.GetWorld()->GetBlock(a_PlacePosition)))
+ {
+ if (!a_Player.PlaceBlock(a_PlacePosition, GetDoubleSlabType(static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType)), static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage)))
+ {
+ return false;
+ }
+ a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z, 2); // (see below)
+ return true;
+ }
+
+ // Set the correct metadata based on player equipped item:
+ if (!a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), FaceToMetaData(static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage), a_ClickedBlockFace, a_CursorPosition)))
+ {
+ return false;
+ }
+ /* This is a workaround for versions < 1.13, where the client combines a slab in the
+ direction of the clicked block face of a block ignoring build collision, rather than replacing said block.
+ Resend blocks to the client to fix the bug.
+ Ref.: https://forum.cuberite.org/thread-434-post-17388.html#pid17388 */
+ a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z, 2);
+ return true;
+ }
- // cItemHandler overrides:
- virtual bool OnPlayerPlace(
- cWorld & a_World,
- cPlayer & a_Player,
- const cItem & a_EquippedItem,
- const Vector3i a_ClickedBlockPos,
- eBlockFace a_ClickedBlockFace,
- const Vector3i a_CursorPos
- ) override
+ static NIBBLETYPE FaceToMetaData(const NIBBLETYPE a_BaseMeta, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
{
- // If clicking a slab, try combining it into a double-slab:
- BLOCKTYPE ClickedBlockType;
- NIBBLETYPE ClickedBlockMeta;
- a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
- if (
- (ClickedBlockType == m_ItemType) && // Placing the same slab material
- ((ClickedBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
- )
+ switch (a_ClickedBlockFace)
{
- if (
- ((a_ClickedBlockFace == BLOCK_FACE_TOP) && ((ClickedBlockMeta & 0x08) == 0)) || // Top side of a bottom-half-slab
- ((a_ClickedBlockFace == BLOCK_FACE_BOTTOM) && ((ClickedBlockMeta & 0x08) != 0)) // Bottom side of a top-half-slab
- )
+ case BLOCK_FACE_TOP:
{
- if (!a_Player.PlaceBlock(a_ClickedBlockPos.x, a_ClickedBlockPos.y, a_ClickedBlockPos.z, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07))
- {
- return false;
- }
- if (a_Player.IsGameModeSurvival())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
- return true;
+ // Bottom half slab block:
+ return a_BaseMeta & 0x07;
}
- }
-
- // If there's already a slab in the destination, combine it into a double-slab:
- auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
- BLOCKTYPE PlaceBlockType;
- NIBBLETYPE PlaceBlockMeta;
- a_World.GetBlockTypeMeta(PlacePos, PlaceBlockType, PlaceBlockMeta);
- if (
- (PlaceBlockType == m_ItemType) && // Placing the same slab material
- ((PlaceBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
- )
- {
- if (!a_Player.PlaceBlock(PlacePos.x, PlacePos.y, PlacePos.z, m_DoubleSlabBlockType, PlaceBlockMeta & 0x07))
+ case BLOCK_FACE_BOTTOM:
{
- return false;
+ // Top half slab block:
+ return a_BaseMeta | 0x08;
}
- if (a_Player.IsGameModeSurvival())
+ case BLOCK_FACE_EAST:
+ case BLOCK_FACE_NORTH:
+ case BLOCK_FACE_SOUTH:
+ case BLOCK_FACE_WEST:
{
- a_Player.GetInventory().RemoveOneEquippedItem();
+ if (a_CursorPosition.y > 7)
+ {
+ // Cursor at top half of block, place top slab:
+ return a_BaseMeta | 0x08;
+ }
+ else
+ {
+ // Cursor at bottom half of block, place bottom slab:
+ return a_BaseMeta & 0x07;
+ }
}
- return true;
+ default: UNREACHABLE("Unhandled block face");
}
+ }
- // The slabs didn't combine, use the default handler to place the slab:
- bool res = Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos);
- /*
- The client has a bug when a slab replaces snow and there's a slab above it.
- The client then combines the slab above, rather than replacing the snow.
- We send the block above the currently placed block back to the client to fix the bug.
- Ref.: https://forum.cuberite.org/thread-434-post-17388.html#pid17388
- */
- if ((a_ClickedBlockFace == BLOCK_FACE_TOP) && (a_ClickedBlockPos.y < cChunkDef::Height - 1))
+ /** Converts the single-slab blocktype to its equivalent double-slab blocktype. */
+ static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType)
+ {
+ switch (a_SingleSlabBlockType)
{
- auto AbovePos = a_ClickedBlockPos.addedY(1);
- a_Player.SendBlocksAround(AbovePos.x, AbovePos.y, AbovePos.z, 1);
+ case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB;
+ case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB;
+ case E_BLOCK_RED_SANDSTONE_SLAB: return E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB;
+ case E_BLOCK_PURPUR_SLAB: return E_BLOCK_PURPUR_DOUBLE_SLAB;
}
- return res;
+ UNREACHABLE("Unhandled slab type");
}
-
-
-protected:
-
- /** The block type to use when the slab combines into a doubleslab block. */
- BLOCKTYPE m_DoubleSlabBlockType;
};
diff --git a/src/Items/ItemSnow.h b/src/Items/ItemSnow.h
new file mode 100644
index 000000000..551b4d922
--- /dev/null
+++ b/src/Items/ItemSnow.h
@@ -0,0 +1,41 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemSnowHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ BLOCKTYPE Block;
+ NIBBLETYPE Meta;
+ a_Player.GetWorld()->GetBlockTypeMeta(a_PlacePosition, Block, Meta);
+
+ // Check if incrementing existing snow height:
+ if (Block == E_BLOCK_SNOW)
+ {
+ ASSERT(Meta < 7); // BlockSnow.h ensures that if we replace a snow layer, it won't be at max height.
+ Meta++;
+ }
+ else
+ {
+ // First time placement:
+ Meta = 0;
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_SNOW, Meta);
+ }
+};
diff --git a/src/Items/ItemStairs.h b/src/Items/ItemStairs.h
new file mode 100644
index 000000000..6fe447be5
--- /dev/null
+++ b/src/Items/ItemStairs.h
@@ -0,0 +1,47 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockStairs.h"
+
+
+
+
+
+class cItemStairsHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ NIBBLETYPE Meta = cBlockStairsHandler::YawToMetaData(a_Player.GetYaw());
+
+ switch (a_ClickedBlockFace)
+ {
+ case BLOCK_FACE_TOP: break;
+ case BLOCK_FACE_BOTTOM: Meta |= 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_CursorPosition.y > 8)
+ {
+ Meta |= 0x4;
+ }
+ break;
+ }
+ default: UNREACHABLE("Unhandled block face");
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), Meta);
+ }
+};
diff --git a/src/Items/ItemTorch.h b/src/Items/ItemTorch.h
new file mode 100644
index 000000000..a0ed86e93
--- /dev/null
+++ b/src/Items/ItemTorch.h
@@ -0,0 +1,82 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockTorch.h"
+
+
+
+
+
+class cItemTorchHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ /** Converts the block face of the neighbor to which the torch is attached, to the torch block's meta. */
+ static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ 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: UNREACHABLE("Unsupported block face");
+ }
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ const auto & World = *a_Player.GetWorld();
+ BLOCKTYPE ClickedBlockType;
+ NIBBLETYPE ClickedBlockMeta;
+ World.GetBlockTypeMeta(AddFaceDirection(a_PlacePosition, a_ClickedBlockFace, true), ClickedBlockType, ClickedBlockMeta);
+
+ // Try finding a suitable neighbor block face for the torch; start with the given one:
+ if (!cBlockTorchHandler::CanBePlacedOn(ClickedBlockType, ClickedBlockMeta, a_ClickedBlockFace))
+ {
+ // Couldn't be placed on whatever face was clicked, last ditch resort - find another face:
+ a_ClickedBlockFace = FindSuitableFace(World, a_PlacePosition);
+ if (a_ClickedBlockFace == BLOCK_FACE_NONE)
+ {
+ // No attachable face found - don't place the torch:
+ return false;
+ }
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace));
+ }
+
+
+ /** Returns a suitable neighbor's blockface to place the torch at the specified position.
+ Returns BLOCK_FACE_NONE on failure. */
+ static eBlockFace FindSuitableFace(const cWorld & a_World, const Vector3i a_Position)
+ {
+ for (const auto Face : { BLOCK_FACE_ZM, BLOCK_FACE_XP, BLOCK_FACE_ZP, BLOCK_FACE_XM, BLOCK_FACE_YP }) // Loop through all faces in specific order.
+ {
+ // The direction of Face is relative to the direction the torch faces.
+ // This is the position, computed inverted, that such a torch would attach to.
+ const auto NeighborPosition = AddFaceDirection(a_Position, Face, true);
+
+ BLOCKTYPE NeighborBlockType;
+ NIBBLETYPE NeighborBlockMeta;
+ a_World.GetBlockTypeMeta(NeighborPosition, NeighborBlockType, NeighborBlockMeta);
+
+ if (cBlockTorchHandler::CanBePlacedOn(NeighborBlockType, NeighborBlockMeta, Face))
+ {
+ return Face;
+ }
+ }
+
+ return BLOCK_FACE_NONE;
+ }
+};
diff --git a/src/Items/ItemTrapdoor.h b/src/Items/ItemTrapdoor.h
new file mode 100644
index 000000000..9cb89b8bf
--- /dev/null
+++ b/src/Items/ItemTrapdoor.h
@@ -0,0 +1,66 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+#include "Blocks/BlockTrapdoor.h"
+
+
+
+
+
+class cItemTrapdoorHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_ZP: return 0x1;
+ case BLOCK_FACE_ZM: return 0x0;
+ case BLOCK_FACE_XP: return 0x3;
+ case BLOCK_FACE_XM: return 0x2;
+ default: UNREACHABLE("Unsupported block face");
+ }
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ NIBBLETYPE Meta;
+
+ if (a_ClickedBlockFace == BLOCK_FACE_YP)
+ {
+ // Trapdoor is placed on top of a block.
+ // Engage yaw rotation to determine hinge direction:
+ Meta = cBlockTrapdoorHandler::YawToMetaData(a_Player.GetYaw());
+ }
+ else if (a_ClickedBlockFace == BLOCK_FACE_YM)
+ {
+ // Trapdoor is placed on bottom of a block.
+ // Engage yaw rotation to determine hinge direction, and toggle 'Move up half-block' bit on:
+ Meta = cBlockTrapdoorHandler::YawToMetaData(a_Player.GetYaw()) | 0x8;
+ }
+ else
+ {
+ // Placement on block sides; hinge direction is determined by which side was clicked:
+ Meta = BlockFaceToMetaData(a_ClickedBlockFace);
+
+ if (a_CursorPosition.y > 7)
+ {
+ // Trapdoor is placed on a higher half of a vertical block.
+ // Toggle 'Move up half-block' bit on:
+ Meta |= 0x8;
+ }
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), Meta);
+ }
+};
diff --git a/src/Items/ItemTripwireHook.h b/src/Items/ItemTripwireHook.h
new file mode 100644
index 000000000..005bd5676
--- /dev/null
+++ b/src/Items/ItemTripwireHook.h
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemTripwireHookHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
+ {
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_XM: return 0x1;
+ case BLOCK_FACE_XP: return 0x3;
+ case BLOCK_FACE_ZM: return 0x2;
+ case BLOCK_FACE_ZP: return 0x0;
+ default: UNREACHABLE("Unsupported block face");
+ }
+ }
+
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ if ((a_ClickedBlockFace == BLOCK_FACE_YP) || (a_ClickedBlockFace == BLOCK_FACE_YM))
+ {
+ return false;
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_TRIPWIRE_HOOK, BlockFaceToMetaData(a_ClickedBlockFace));
+ }
+};
diff --git a/src/Items/ItemVine.h b/src/Items/ItemVine.h
new file mode 100644
index 000000000..1a56a23d0
--- /dev/null
+++ b/src/Items/ItemVine.h
@@ -0,0 +1,44 @@
+
+#pragma once
+
+#include "ItemHandler.h"
+
+
+
+
+
+class cItemVineHandler :
+ public cItemHandler
+{
+ using Super = cItemHandler;
+
+public:
+
+ using Super::Super;
+
+private:
+
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
+ {
+ BLOCKTYPE Block;
+ NIBBLETYPE Meta;
+ a_Player.GetWorld()->GetBlockTypeMeta(a_PlacePosition, Block, Meta);
+
+ NIBBLETYPE PlaceMeta;
+ switch (a_ClickedBlockFace)
+ {
+ case BLOCK_FACE_NORTH: PlaceMeta = 0x1; break;
+ case BLOCK_FACE_SOUTH: PlaceMeta = 0x4; break;
+ case BLOCK_FACE_WEST: PlaceMeta = 0x8; break;
+ case BLOCK_FACE_EAST: PlaceMeta = 0x2; break;
+ default: return false;
+ }
+
+ if (Block == E_BLOCK_VINES)
+ {
+ PlaceMeta |= Meta;
+ }
+
+ return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_VINES, PlaceMeta);
+ }
+};
diff --git a/src/Items/SimplePlaceableItemHandler.h b/src/Items/SimplePlaceableItemHandler.h
index 4400b9d0e..8b61f3198 100644
--- a/src/Items/SimplePlaceableItemHandler.h
+++ b/src/Items/SimplePlaceableItemHandler.h
@@ -26,14 +26,9 @@ public:
}
- virtual bool GetPlacementBlockTypeMeta(
- cWorld * a_World, cPlayer * a_Player, const Vector3i a_PlacedBlockPos,
- eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPos,
- BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) override
+ virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
- a_BlockType = m_BlockType;
- a_BlockMeta = 0;
- return true;
+ return a_Player.PlaceBlock(a_PlacePosition, m_BlockType, 0);
}
private: