From a62b2b1be2103d7de2fd66c7304b7473e369be3c Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Wed, 5 May 2021 14:25:10 +0100 Subject: Move item placement into item handlers (#5184) * Move item placement into item handlers + Add appropriate CanBeAt checks in cPlayer::PlaceBlocks, into which all placement handlers call. * Partly addresses #5157 * Fixes #4878 * Fixes #2919 * Fixes #4629 * Fixes #4239 * Fixes #4849 Co-authored-by: changyong guo Co-authored-by: Xotheus Co-authored-by: Krist Pregracke * Review fixes * Update APIDesc.lua * Rename Co-authored-by: changyong guo Co-authored-by: Xotheus Co-authored-by: Krist Pregracke --- src/Items/CMakeLists.txt | 24 +++ src/Items/ItemAnvil.h | 30 +++ src/Items/ItemBanner.h | 160 ++++++--------- src/Items/ItemBed.h | 60 +++--- src/Items/ItemBigFlower.h | 36 ++-- src/Items/ItemBucket.h | 6 +- src/Items/ItemButton.h | 45 +++++ src/Items/ItemChest.h | 83 ++------ src/Items/ItemComparator.h | 14 +- src/Items/ItemDoor.h | 67 +++---- src/Items/ItemDropSpenser.h | 26 +++ src/Items/ItemDye.h | 2 +- src/Items/ItemEnchantingTable.h | 32 ++- src/Items/ItemEndPortalFrame.h | 26 +++ src/Items/ItemEnderchest.h | 26 +++ src/Items/ItemFenceGate.h | 26 +++ src/Items/ItemFurnace.h | 26 +++ src/Items/ItemGlazedTerracotta.h | 26 +++ src/Items/ItemHandler.cpp | 344 +++++++++++++++++++-------------- src/Items/ItemHandler.h | 54 +----- src/Items/ItemHopper.h | 40 ++++ src/Items/ItemJackOLantern.h | 27 +++ src/Items/ItemLadder.h | 77 ++++++++ src/Items/ItemLeaves.h | 20 +- src/Items/ItemLever.h | 43 +++++ src/Items/ItemMobHead.h | 117 +++++------ src/Items/ItemNetherWart.h | 32 +-- src/Items/ItemObserver.h | 26 +++ src/Items/ItemPiston.h | 26 +++ src/Items/ItemPlanks.h | 25 +++ src/Items/ItemPumpkin.h | 70 ++++--- src/Items/ItemQuartz.h | 60 ++++++ src/Items/ItemRail.h | 28 +++ src/Items/ItemRedstoneDust.h | 56 +----- src/Items/ItemRedstoneRepeater.h | 14 +- src/Items/ItemSapling.h | 28 +-- src/Items/ItemSeeds.h | 51 ++--- src/Items/ItemSideways.h | 53 +++++ src/Items/ItemSign.h | 89 ++++----- src/Items/ItemSlab.h | 134 ++++++------- src/Items/ItemSnow.h | 41 ++++ src/Items/ItemStairs.h | 47 +++++ src/Items/ItemTorch.h | 82 ++++++++ src/Items/ItemTrapdoor.h | 66 +++++++ src/Items/ItemTripwireHook.h | 43 +++++ src/Items/ItemVine.h | 44 +++++ src/Items/SimplePlaceableItemHandler.h | 9 +- 47 files changed, 1597 insertions(+), 864 deletions(-) create mode 100644 src/Items/ItemAnvil.h create mode 100644 src/Items/ItemButton.h create mode 100644 src/Items/ItemDropSpenser.h create mode 100644 src/Items/ItemEndPortalFrame.h create mode 100644 src/Items/ItemEnderchest.h create mode 100644 src/Items/ItemFenceGate.h create mode 100644 src/Items/ItemFurnace.h create mode 100644 src/Items/ItemGlazedTerracotta.h create mode 100644 src/Items/ItemHopper.h create mode 100644 src/Items/ItemJackOLantern.h create mode 100644 src/Items/ItemLadder.h create mode 100644 src/Items/ItemLever.h create mode 100644 src/Items/ItemObserver.h create mode 100644 src/Items/ItemPiston.h create mode 100644 src/Items/ItemPlanks.h create mode 100644 src/Items/ItemQuartz.h create mode 100644 src/Items/ItemRail.h create mode 100644 src/Items/ItemSideways.h create mode 100644 src/Items/ItemSnow.h create mode 100644 src/Items/ItemStairs.h create mode 100644 src/Items/ItemTorch.h create mode 100644 src/Items/ItemTrapdoor.h create mode 100644 src/Items/ItemTripwireHook.h create mode 100644 src/Items/ItemVine.h (limited to 'src/Items') 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(a_HeldItem.m_ItemType), + cBlockAnvilHandler::YawToMetaData(a_Player.GetYaw()) | static_cast(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(a_BlockEntity).SetBaseColor(static_cast(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(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(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(BlockMeta | 0x08) } + }) + ) + { + return false; + } + + auto SetColor = [&a_HeldItem](cBlockEntity & a_BlockEntity) + { + ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_BED); + + static_cast(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(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(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(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(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(a_Entity).SetCustomName(a_EquippedItem.m_CustomName); + static_cast(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(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(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(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(m_ItemType), + static_cast(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(m_ItemType), + static_cast(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(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(a_EquippedItem.m_ItemDamage); - auto BlockMeta = static_cast(a_ClickedBlockFace); + const auto HeadType = static_cast(a_HeldItem.m_ItemDamage); + const auto BlockMeta = static_cast(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 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(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(a_HeldItem.m_ItemType), static_cast(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(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(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(m_ItemType), + static_cast(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(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace, static_cast(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(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(a_HeldItem.m_ItemType)), static_cast(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(a_HeldItem.m_ItemType), FaceToMetaData(static_cast(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(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(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(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: -- cgit v1.2.3