summaryrefslogtreecommitdiffstats
path: root/src/Items/ItemLilypad.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/Items/ItemLilypad.h')
-rw-r--r--src/Items/ItemLilypad.h100
1 files 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;
}
};