#pragma once
#include "ItemHandler.h"
#include "../Entities/Player.h"
#include "../LineBlockTracer.h"
class cItemLilypadHandler final:
public cItemHandler
{
using Super = cItemHandler;
public:
constexpr cItemLilypadHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool IsPlaceable(void) const override
{
return false; // Set as not placeable so OnItemUse is called
}
virtual bool OnItemUse(
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) const override
{
// 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)
{
// 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;
}
const auto Below = PlacePos.addedY(-1);
if (Below.y < 0)
{
return false;
}
// Lilypad should be placed only if there is a water block below:
if (
const auto BlockBelow = a_World->GetBlock(Below);
(BlockBelow != E_BLOCK_WATER) &&
(BlockBelow != E_BLOCK_STATIONARY_WATER)
)
{
return false;
}
a_World->SetBlock(PlacePos, E_BLOCK_LILY_PAD, 0);
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
return true;
}
class cCallbacks:
public cBlockTracer::cCallbacks
{
public:
virtual bool OnNextBlock(Vector3i a_CBBlockPos, BLOCKTYPE a_CBBlockType, NIBBLETYPE a_CBBlockMeta, eBlockFace a_CBEntryFace) override
{
if (
!IsBlockWater(a_CBBlockType) ||
(a_CBBlockMeta != 0) // The hit block should be a source
)
{
// 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;
}
Position = AddFaceDirection(a_CBBlockPos, BLOCK_FACE_YP); // Always place pad at top of water block
return true;
}
Vector3i Position;
} Callbacks;
const auto EyePosition = a_Player->GetEyePosition();
const auto End = EyePosition + a_Player->GetLookVector() * 5;
if (cLineBlockTracer::Trace(*a_Player->GetWorld(), Callbacks, EyePosition, End))
{
// 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 true;
}
};