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/ItemMobHead.h | 117 +++++++++++++++++++++--------------------------- 1 file changed, 52 insertions(+), 65 deletions(-) (limited to 'src/Items/ItemMobHead.h') 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; - } } ; -- cgit v1.2.3