From 3889b2cac26410650d7ec6f296e0bb19c3debb4b Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 21 Jun 2015 19:49:22 +0200 Subject: Refactored block placement workflow. Multi-blocks can now use the default OnPlayerPlaced() callback in cItemHandler. --- src/Items/ItemBed.h | 15 ++++------ src/Items/ItemBigFlower.h | 15 +++++----- src/Items/ItemChest.h | 75 +++++++++++++++++++++++++++++++---------------- src/Items/ItemDoor.h | 12 ++++---- src/Items/ItemHandler.cpp | 71 ++++++++++++++++++++++++++++++++------------ src/Items/ItemHandler.h | 20 +++++++++++-- src/Items/ItemMobHead.h | 33 +++++++++++---------- 7 files changed, 156 insertions(+), 85 deletions(-) diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h index 77d51d744..15b924a08 100644 --- a/src/Items/ItemBed.h +++ b/src/Items/ItemBed.h @@ -25,10 +25,11 @@ public: } - virtual bool OnPlayerPlace( + virtual bool GetBlocksToPlace( cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ + int a_CursorX, int a_CursorY, int a_CursorZ, + sSetBlockVector & a_BlocksToPlace ) override { // Can only be placed on the floor: @@ -36,12 +37,10 @@ public: { return false; } - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // The "foot" block: - sSetBlockVector blks; NIBBLETYPE BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player.GetYaw()); - blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta); + a_BlocksToPlace.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta); // Check if there is empty space for the "head" block: // (Vanilla only allows beds to be placed into air) @@ -50,10 +49,8 @@ public: { return false; } - blks.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08); - - // Place both bed blocks: - return a_Player.PlaceBlocks(blks); + a_BlocksToPlace.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08); + return true; } } ; diff --git a/src/Items/ItemBigFlower.h b/src/Items/ItemBigFlower.h index 4341a1a17..a052485e4 100644 --- a/src/Items/ItemBigFlower.h +++ b/src/Items/ItemBigFlower.h @@ -27,10 +27,11 @@ public: } - virtual bool OnPlayerPlace( + virtual bool GetBlocksToPlace( cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ + int a_CursorX, int a_CursorY, int a_CursorZ, + sSetBlockVector & a_BlocksToSet ) override { // Can only be placed on the floor: @@ -38,16 +39,14 @@ public: { return false; } - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - // Place both blocks atomically: - sSetBlockVector blks; - blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07); + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07); if (a_BlockY < cChunkDef::Height - 1) { - blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08); + a_BlocksToSet.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08); } - return a_Player.PlaceBlocks(blks); + return true; } }; diff --git a/src/Items/ItemChest.h b/src/Items/ItemChest.h index 1d23975cd..3dd112c91 100644 --- a/src/Items/ItemChest.h +++ b/src/Items/ItemChest.h @@ -27,6 +27,8 @@ public: } + /** 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, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, @@ -118,34 +120,32 @@ public: } // for j } // for i - // If there's no chest neighbor, place the single block chest and bail out: + // Get the meta of the placed chest; take existing neighbors into account: BLOCKTYPE ChestBlockType = static_cast(m_ItemType); - if (NeighborIdx < 0) + NIBBLETYPE Meta; + auto yaw = a_Player.GetYaw(); + switch (NeighborIdx) { - NIBBLETYPE Meta = cBlockChestHandler::PlayerYawToMetaData(a_Player.GetYaw()); - return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta); - } - - // There is a neighbor to which we need to adjust - double yaw = a_Player.GetYaw(); - if ((NeighborIdx == 0) || (NeighborIdx == 2)) - { - // The neighbor is in the X axis, form a X-axis-aligned dblchest: - NIBBLETYPE Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP; - - // Place the new chest: - if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta)) + case 0: + case 2: { - return false; + // The neighbor is in the X axis, form a X-axis-aligned dblchest: + Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP; + break; } - - // Adjust the existing chest: - a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta); - return true; - } - - // The neighbor is in the Z axis, form a Z-axis-aligned dblchest: - NIBBLETYPE Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP; + case 1: + case 3: + { + // The neighbor is in the Z axis, form a Z-axis-aligned dblchest: + Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP; + break; + } + default: + { + Meta = cBlockChestHandler::PlayerYawToMetaData(yaw); + break; + } + } // switch (NeighborIdx) // Place the new chest: if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta)) @@ -153,8 +153,31 @@ public: return false; } - // Adjust the existing chest: - a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta); + // Adjust the existing chest, if any: + if (NeighborIdx > 0) + { + a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta); + } + + // Play the placement sound: + AString PlaceSound = cBlockInfo::GetPlaceSound(ChestBlockType); + float Volume = 1.0f, Pitch = 0.8f; + if (PlaceSound == "dig.metal") + { + Pitch = 1.2f; + PlaceSound = "dig.stone"; + } + else if (PlaceSound == "random.anvil_land") + { + Volume = 0.65f; + } + a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); + + // Remove the "placed" item: + if (a_Player.IsGameModeSurvival()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } return true; } diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h index ddd3d4e20..2e4767349 100644 --- a/src/Items/ItemDoor.h +++ b/src/Items/ItemDoor.h @@ -20,10 +20,11 @@ public: } - virtual bool OnPlayerPlace( + virtual bool GetBlocksToPlace( cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ + int a_CursorX, int a_CursorY, int a_CursorZ, + sSetBlockVector & a_BlocksToSet ) override { // Vanilla only allows door placement while clicking on the top face of the block below the door: @@ -107,10 +108,9 @@ public: } // Set the blocks: - sSetBlockVector blks; - blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta); - blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta); - return a_Player.PlaceBlocks(blks); + a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta); + a_BlocksToSet.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta); + return true; } diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index b7f89809e..a1fa67152 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -367,37 +367,51 @@ bool cItemHandler::OnPlayerPlace( return false; } } - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + + // Get all the blocks to place: + sSetBlockVector blocks; + if (!GetBlocksToPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, blocks)) { // Handler refused the placement, send that information back to the client: + for (const auto & blk: blocks) + { + a_World.SendBlockTo(blk.GetX(), blk.GetY(), blk.GetZ(), &a_Player); + } a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player); a_Player.GetInventory().SendEquippedSlot(); return false; } - if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta)) + // Try to place the blocks: + if (!a_Player.PlaceBlocks(blocks)) { - // The placement failed, the block has already been re-sent, re-send inventory: + // The placement failed, the blocks have already been re-sent, re-send inventory: a_Player.GetInventory().SendEquippedSlot(); return false; } - AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType); - float Volume = 1.0f, Pitch = 0.8f; - if (PlaceSound == "dig.metal") - { - Pitch = 1.2f; - PlaceSound = "dig.stone"; - } - else if (PlaceSound == "random.anvil_land") + // Play the placement sound for the main block: + for (const auto & blk: blocks) { - Volume = 0.65f; - } - - a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); + // Find the main block by comparing the coords: + if ((blk.GetX() != a_BlockX) || (blk.GetY() != a_BlockY) || (blk.GetZ() != a_BlockZ)) + { + continue; + } + AString PlaceSound = cBlockInfo::GetPlaceSound(blk.m_BlockType); + float Volume = 1.0f, Pitch = 0.8f; + if (PlaceSound == "dig.metal") + { + Pitch = 1.2f; + PlaceSound = "dig.stone"; + } + else if (PlaceSound == "random.anvil_land") + { + Volume = 0.65f; + } + a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); + break; + } // for blk - blocks[] // Remove the "placed" item: if (a_Player.IsGameModeSurvival()) @@ -411,6 +425,27 @@ bool cItemHandler::OnPlayerPlace( +bool cItemHandler::GetBlocksToPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + sSetBlockVector & a_BlocksToSet +) +{ + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + return false; + } + a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + return true; +} + + + + + bool cItemHandler::OnItemUse( cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h index ec88aeb99..684b55788 100644 --- a/src/Items/ItemHandler.h +++ b/src/Items/ItemHandler.h @@ -35,8 +35,9 @@ public: /** Called when the player tries to place the item (right mouse button, IsPlaceable() == true). - The default handler uses GetPlacementBlockTypeMeta and places the returned block. - Override this function for advanced behavior such as placing multiple blocks. + The block coords are for the block that has been clicked. + 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( @@ -46,7 +47,20 @@ public: ); - /** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlace() is not overridden. + /** Called from OnPlayerPlace() to determine the blocks that the current placement operation should set. + The block coords are where the new (main) block should be placed. + 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, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + sSetBlockVector & a_BlocksToSet + ); + + + /** 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( diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h index 8780f7e4b..9a4044bc0 100644 --- a/src/Items/ItemMobHead.h +++ b/src/Items/ItemMobHead.h @@ -12,9 +12,11 @@ class cItemMobHeadHandler : public cItemHandler { + typedef cItemHandler super; + public: cItemMobHeadHandler(int a_ItemType) : - cItemHandler(a_ItemType) + super(a_ItemType) { } @@ -30,34 +32,36 @@ public: { return true; } - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + auto placedX = a_BlockX, placedY = a_BlockY, placedZ = a_BlockZ; + AddFaceDirection(placedY, placedY, placedZ, a_BlockFace); // If the placed head is a wither, try to spawn the wither first: if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER) { - if (TrySpawnWitherAround(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ)) + if (TrySpawnWitherAround(a_World, a_Player, placedX, placedY, placedZ)) { return true; } // Wither not created, proceed with regular head placement } - return PlaceRegularHead(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + 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_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return false; + } + RegularHeadPlaced(a_World, a_Player, itemCopy, placedX, placedY, placedZ, a_BlockFace); + return true; } - /** Places a regular head block with no mob spawning checking. */ - bool PlaceRegularHead( + /** 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, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace ) { - // Place the block: - if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_BlockFace))) - { - return false; - } - // Use a callback to set the properties of the mob head block entity: class cCallback : public cBlockEntityCallback { @@ -71,7 +75,7 @@ public: { return false; } - cMobHeadEntity * MobHeadEntity = static_cast(a_BlockEntity); + auto MobHeadEntity = static_cast(a_BlockEntity); int Rotation = 0; if (m_BlockMeta == 1) @@ -94,7 +98,6 @@ public: }; cCallback Callback(a_Player, static_cast(a_EquippedItem.m_ItemDamage), static_cast(a_BlockFace)); a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback); - return true; } @@ -340,7 +343,7 @@ public: ) override { a_BlockType = E_BLOCK_HEAD; - a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); + a_BlockMeta = BlockFaceToBlockMeta(a_BlockFace); return true; } } ; -- cgit v1.2.3