From 7027b2279f8a713eff6169fa14eed1e3a52dcc83 Mon Sep 17 00:00:00 2001 From: Derek Qu Date: Fri, 27 Nov 2020 20:01:47 -0500 Subject: Fix lilypad displacing block above (#5056) * Fix lilypad displacing block above Side fixes: * Fix lilypad displacing half slabs * Fix lilypad being placed on flowing water and non-water blocks in general Co-authored-by: Tiger Wang --- src/Items/ItemLilypad.h | 100 +++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/src/Items/ItemLilypad.h b/src/Items/ItemLilypad.h index 747175811..5760ff830 100644 --- a/src/Items/ItemLilypad.h +++ b/src/Items/ItemLilypad.h @@ -44,10 +44,35 @@ public: eBlockFace a_ClickedBlockFace ) override { - if (a_ClickedBlockFace > BLOCK_FACE_NONE) + // The client sends BLOCK_FACE_NONE when it determines it should do a tracing-based placement. + // Otherwise, a normal block face is sent. + + if (a_ClickedBlockFace != BLOCK_FACE_NONE) { - // Clicked on a face of a submerged block; vanilla allows placement, so should we - auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace); + // The position the client wants the lilypad placed. + const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace); + + // Lilypad should not replace non air and non water blocks: + if ( + const auto BlockToReplace = a_World->GetBlock(PlacePos); + (BlockToReplace != E_BLOCK_AIR) && + (BlockToReplace != E_BLOCK_WATER) && + (BlockToReplace != E_BLOCK_STATIONARY_WATER) + ) + { + return false; + } + + // Lilypad should be placed only if there is a water block below + if ( + const auto BlockBelow = a_World->GetBlock(PlacePos.addedY(-1)); + (BlockBelow != E_BLOCK_WATER) && + (BlockBelow != E_BLOCK_STATIONARY_WATER) + ) + { + return false; + } + a_World->SetBlock(PlacePos, E_BLOCK_LILY_PAD, 0); if (!a_Player->IsGameModeCreative()) { @@ -61,53 +86,50 @@ public: { public: - cCallbacks(): - m_HasHitFluid(false) - { - } - virtual bool OnNextBlock(Vector3i a_CBBlockPos, BLOCKTYPE a_CBBlockType, NIBBLETYPE a_CBBlockMeta, eBlockFace a_CBEntryFace) override { - if (IsBlockWater(a_CBBlockType)) + if ( + !IsBlockWater(a_CBBlockType) || + (a_CBBlockMeta != 0) // The hit block should be a source + ) { - if ((a_CBBlockMeta != 0) || (a_CBEntryFace == BLOCK_FACE_NONE)) // The hit block should be a source. The FACE_NONE check is clicking whilst submerged - { - return false; - } - a_CBBlockPos = AddFaceDirection(a_CBBlockPos, BLOCK_FACE_YP); // Always place pad at top of water block - if ( - !IsBlockWater(a_CBBlockType) && - cBlockInfo::FullyOccupiesVoxel(a_CBBlockType) - ) - { - // Can't place lilypad on air / in another block! - return true; - } - m_HasHitFluid = true; - m_Pos = a_CBBlockPos; - return true; + // TODO: Vanilla stops the trace. However, we need to continue the trace, to work around our lack of block bounding box support + // which would otherwise mean we misbehave when clicking through the voxel a (e.g.) button occupies. Now, however, we misbehave + // when clicking on a block near water... Nonetheless, the former would cause ghost blocks, so continue for now. + + // Ignore and continue trace: + return false; } - return false; + + Position = AddFaceDirection(a_CBBlockPos, BLOCK_FACE_YP); // Always place pad at top of water block + return true; } - Vector3i m_Pos; - bool m_HasHitFluid; + Vector3i Position; } Callbacks; - auto Start = a_Player->GetEyePosition() + a_Player->GetLookVector(); - auto End = a_Player->GetEyePosition() + a_Player->GetLookVector() * 5; - cLineBlockTracer::Trace(*a_Player->GetWorld(), Callbacks, Start, End); - if (Callbacks.m_HasHitFluid) + const auto EyePosition = a_Player->GetEyePosition(); + const auto End = EyePosition + a_Player->GetLookVector() * 5; + if (cLineBlockTracer::Trace(*a_Player->GetWorld(), Callbacks, EyePosition, End)) { - a_World->SetBlock(Callbacks.m_Pos, E_BLOCK_LILY_PAD, 0); - if (!a_Player->IsGameModeCreative()) - { - a_Player->GetInventory().RemoveOneEquippedItem(); - } - return true; + // The line traced to completion; no suitable water was found: + return false; + } + + const auto BlockToReplace = a_World->GetBlock(Callbacks.Position); + if (BlockToReplace != E_BLOCK_AIR) + { + // Lilypad should not replace non air blocks: + return false; + } + + a_World->SetBlock(Callbacks.Position, E_BLOCK_LILY_PAD, 0); + if (!a_Player->IsGameModeCreative()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); } - return false; + return true; } }; -- cgit v1.2.3