1
0

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 <guo1487@163.com>
Co-authored-by: Xotheus <shady3300@outlook.com>
Co-authored-by: Krist Pregracke <krist@tiger-scm.com>

* Review fixes

* Update APIDesc.lua

* Rename

Co-authored-by: changyong guo <guo1487@163.com>
Co-authored-by: Xotheus <shady3300@outlook.com>
Co-authored-by: Krist Pregracke <krist@tiger-scm.com>
This commit is contained in:
Tiger Wang 2021-05-05 14:25:10 +01:00 committed by GitHub
parent 34bf5c0d9d
commit a62b2b1be2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
109 changed files with 2096 additions and 2686 deletions

View File

@ -222,6 +222,24 @@ return
},
Notes = "Returns how much light the specified block type consumes.",
},
IsClickedThrough =
{
IsStatic = true,
Params =
{
{
Name = "BlockType",
Type = "number",
},
},
Returns =
{
{
Type = "boolean",
},
},
Notes = "Returns true if the specified block type is ignored by the client on left and right clicks, that is, treated as if it were air.",
},
IsOneHitDig =
{
IsStatic = true,

View File

@ -715,6 +715,17 @@ bool cBlockInfo::FullyOccupiesVoxel(const BLOCKTYPE Block)
bool cBlockInfo::IsClickedThrough(const BLOCKTYPE a_Block)
{
// TODO: Nether Fire too.
return a_Block == E_BLOCK_FIRE;
}
bool cBlockInfo::IsOneHitDig(const BLOCKTYPE Block)
{
#ifdef __clang__

View File

@ -24,6 +24,10 @@ public:
/** Does this block fully occupy its voxel - is it a 'full' block? */
static bool FullyOccupiesVoxel(BLOCKTYPE Block);
/** Does the client pretend the block doesn't exist when clicking?
For example, digging a fire will hit the block below the fire, so fire is "clicked through". */
static bool IsClickedThrough(BLOCKTYPE a_Block);
/** Is a block destroyed after a single hit?
Warning: IsOneHitDig does not take into account enchantments / status effects / swim state / floating state
and therefore may be incorrect. Only use to check if hardness is 0.

View File

@ -48,28 +48,6 @@ private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta))
{
return false;
}
a_BlockMeta = a_BlockMeta | static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage << 2);
return true;
}
virtual bool IsUseable() const override
{
return true;

View File

@ -29,14 +29,14 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y < 1)
if (a_Position.y < 1)
{
return false;
}
return cBlockInfo::IsSolid(a_Chunk.GetBlock(a_RelPos.addedY(-1)));
return cBlockInfo::IsSolid(a_Chunk.GetBlock(a_Position.addedY(-1)));
}

View File

@ -9,7 +9,6 @@
#include "../World.h"
#include "../BoundingBox.h"
#include "../Mobs/Monster.h"
#include "../BlockEntities/BedEntity.h"
@ -158,21 +157,6 @@ bool cBlockBedHandler::OnUse(
void cBlockBedHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange) const
{
a_Player.GetWorld()->DoWithBlockEntityAt(a_BlockChange.GetAbsolutePos(), [&a_Player](cBlockEntity & a_BlockEntity)
{
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_BED);
static_cast<cBedEntity &>(a_BlockEntity).SetColor(a_Player.GetEquippedItem().m_ItemDamage);
return false;
});
}
cItems cBlockBedHandler::ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const
{
// Drops handled by the block entity:

View File

@ -91,11 +91,6 @@ private:
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, const cItem * a_Tool) const override;
virtual void OnPlacedByPlayer(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player,
const sSetBlock & a_BlockChange
) const override;

View File

@ -20,14 +20,14 @@ public:
private:
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
{
if (IsMetaTopPart(a_Meta))
{
BLOCKTYPE BottomType;
if (
(a_Pos.y < 1) ||
!a_ChunkInterface.GetBlockTypeMeta(a_Pos - Vector3i(0, 1, 0), BottomType, a_Meta) ||
(a_Position.y < 1) ||
!a_World.GetBlockTypeMeta(a_Position - Vector3i(0, 1, 0), BottomType, a_Meta) ||
(BottomType != E_BLOCK_BIG_FLOWER)
)
{
@ -98,17 +98,13 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
{
return false;
}
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BlockType, BlockMeta);
// CanBeAt is also called on placement, so the top part can't check for the bottom part.
// Both parts can only that they're rooted in grass.
return IsBlockTypeOfDirt(BlockType) || ((BlockType == E_BLOCK_BIG_FLOWER) && !IsMetaTopPart(BlockMeta));
const auto RootPosition = a_Position.addedY(IsMetaTopPart(a_Meta) ? -2 : -1);
return (RootPosition.y >= 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(RootPosition));
}

View File

@ -101,47 +101,6 @@ private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
a_BlockType = m_BlockType;
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
return true;
}
/** Converts the block face of the neighbor to which the button is attached, to the block meta for this button. */
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_YP: return 0x5;
case BLOCK_FACE_ZM: return 0x4;
case BLOCK_FACE_ZP: return 0x3;
case BLOCK_FACE_XM: return 0x2;
case BLOCK_FACE_XP: return 0x1;
case BLOCK_FACE_YM: return 0x0;
case BLOCK_FACE_NONE:
{
break;
}
}
UNREACHABLE("Unsupported block face");
}
/** Converts the block meta of this button into a block face of the neighbor to which the button is attached. */
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
{
@ -165,10 +124,9 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
auto Meta = a_Chunk.GetMeta(a_RelPos);
auto SupportRelPos = AddFaceDirection(a_RelPos, BlockMetaDataToBlockFace(Meta), true);
auto SupportRelPos = AddFaceDirection(a_Position, BlockMetaDataToBlockFace(a_Meta), true);
if (!cChunkDef::IsValidHeight(SupportRelPos.y))
{
return false;

View File

@ -18,47 +18,13 @@ public:
private:
/** Called before a cactus block is placed by a player, overrides cItemHandler::GetPlacementBlockTypeMeta().
Calls CanBeAt function to determine if a cactus block can be placed on a given block. */
bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (
a_Player.GetWorld()->DoWithChunkAt(a_PlacedBlockPos,
[this, a_PlacedBlockPos, &a_ChunkInterface](cChunk & a_Chunk)
{
auto RelPos = cChunkDef::AbsoluteToRelative(a_PlacedBlockPos);
return CanBeAt(a_ChunkInterface, RelPos, a_Chunk);
}
))
{
a_BlockType = m_BlockType;
// Setting a_BlockMeta to meta copied from the lowest 4 bits of the player's equipped item's damage value.
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
a_BlockMeta = Meta & 0x0f;
return true;
}
return false;
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelPos.addedY(-1));
BLOCKTYPE Surface = a_Chunk.GetBlock(a_Position.addedY(-1));
if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS))
{
// Cactus can only be placed on sand and itself
@ -78,7 +44,7 @@ private:
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
if (
a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[i], BlockType, BlockMeta) &&
a_Chunk.UnboundedRelGetBlock(a_Position + Coords[i], BlockType, BlockMeta) &&
(
cBlockInfo::IsSolid(BlockType) ||
(BlockType == E_BLOCK_LAVA) ||

View File

@ -25,27 +25,9 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
a_BlockType = m_BlockType;
a_BlockMeta = a_Player.GetEquippedItem().m_ItemDamage & 0x0f;
return true;
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
{
return (a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) != E_BLOCK_AIR);
return (a_Position.y > 0) && (a_Chunk.GetBlock(a_Position.addedY(-1)) != E_BLOCK_AIR);
}

View File

@ -18,184 +18,6 @@ public:
using Super::Super;
/** Translates player yaw when placing a chest into the chest block metadata. Valid for single chests only */
static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
{
a_Yaw += 90 + 45; // So its not aligned with axis
if (a_Yaw > 360.f)
{
a_Yaw -= 360.f;
}
if ((a_Yaw >= 0.f) && (a_Yaw < 90.f))
{
return 0x04;
}
else if ((a_Yaw >= 180) && (a_Yaw < 270))
{
return 0x05;
}
else if ((a_Yaw >= 90) && (a_Yaw < 180))
{
return 0x02;
}
else
{
return 0x03;
}
}
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
// Cannot place right next to double-chest:
if (!CanBeAt(a_ChunkInterface, a_PlacedBlockPos))
{
// Yup, cannot form a triple-chest, refuse:
return false;
}
// Try to read double-chest information:
cBlockArea Area;
if (!Area.Read(a_ChunkInterface, a_PlacedBlockPos - Vector3i(1, 0, 1), a_PlacedBlockPos + Vector3i(1, 0, 1)))
{
return false;
}
// Get meta as if this was a single-chest:
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta))
{
return false;
}
// Check if this forms a doublechest, if so, need to adjust the meta:
double yaw = a_Player.GetYaw();
if (
(Area.GetRelBlockType(0, 0, 1) == m_BlockType) ||
(Area.GetRelBlockType(2, 0, 1) == m_BlockType)
)
{
a_BlockMeta = ((yaw >= -90) && (yaw < 90)) ? 2 : 3;
return true;
}
if (
(Area.GetRelBlockType(1, 0, 0) == m_BlockType) ||
(Area.GetRelBlockType(1, 0, 2) == m_BlockType)
)
{
a_BlockMeta = (yaw < 0) ? 4 : 5;
return true;
}
return true;
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
{
auto BlockPos = a_Chunk.RelativeToAbsolute(a_RelPos);
return CanBeAt(a_ChunkInterface, BlockPos);
}
bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos) const
{
cBlockArea Area;
if (!Area.Read(a_ChunkInterface, a_BlockPos - Vector3i(2, 0, 2), a_BlockPos + Vector3i(2, 0, 2)))
{
// Cannot read the surroundings, probably at the edge of loaded chunks. Disallow.
return false;
}
int NumChestNeighbors = 0;
if (Area.GetRelBlockType(1, 0, 2) == m_BlockType)
{
if (
(Area.GetRelBlockType(0, 0, 2) == m_BlockType) ||
(Area.GetRelBlockType(1, 0, 1) == m_BlockType) ||
(Area.GetRelBlockType(1, 0, 3) == m_BlockType)
)
{
// Already a doublechest neighbor, disallow:
return false;
}
NumChestNeighbors += 1;
}
if (Area.GetRelBlockType(3, 0, 2) == m_BlockType)
{
if (
(Area.GetRelBlockType(4, 0, 2) == m_BlockType) ||
(Area.GetRelBlockType(3, 0, 1) == m_BlockType) ||
(Area.GetRelBlockType(3, 0, 3) == m_BlockType)
)
{
// Already a doublechest neighbor, disallow:
return false;
}
NumChestNeighbors += 1;
}
if (Area.GetRelBlockType(2, 0, 1) == m_BlockType)
{
if (
(Area.GetRelBlockType(2, 0, 0) == m_BlockType) ||
(Area.GetRelBlockType(1, 0, 1) == m_BlockType) ||
(Area.GetRelBlockType(3, 0, 1) == m_BlockType)
)
{
// Already a doublechest neighbor, disallow:
return false;
}
NumChestNeighbors += 1;
}
if (Area.GetRelBlockType(2, 0, 3) == m_BlockType)
{
if (
(Area.GetRelBlockType(2, 0, 4) == m_BlockType) ||
(Area.GetRelBlockType(1, 0, 3) == m_BlockType) ||
(Area.GetRelBlockType(3, 0, 3) == m_BlockType)
)
{
// Already a doublechest neighbor, disallow:
return false;
}
NumChestNeighbors += 1;
}
return (NumChestNeighbors < 2);
}
/** If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true. */
bool CheckAndAdjustNeighbor(cChunkInterface & a_ChunkInterface, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta) const
{
if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != m_BlockType)
{
return false;
}
a_ChunkInterface.SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta);
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{
@ -203,7 +25,3 @@ private:
return 13;
}
} ;

View File

@ -36,11 +36,11 @@ public:
private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
// Check that we're attached to a jungle log block:
eBlockFace BlockFace = MetaToBlockFace(a_Chunk.GetMeta(a_RelPos));
auto LogPos = AddFaceDirection(a_RelPos, BlockFace, true);
eBlockFace BlockFace = MetaToBlockFace(a_Meta);
auto LogPos = AddFaceDirection(a_Position, BlockFace, true);
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
a_Chunk.UnboundedRelGetBlock(LogPos, BlockType, BlockMeta);

View File

@ -152,16 +152,16 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
{

View File

@ -116,9 +116,9 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
return ((a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) == E_BLOCK_FARMLAND));
return (a_Position.y > 0) && (a_Chunk.GetBlock(a_Position.addedY(-1)) == E_BLOCK_FARMLAND);
}

View File

@ -18,7 +18,7 @@ public:
private:
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
{
return true;
}
@ -27,14 +27,14 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1));
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_Position.addedY(-1));
switch (BelowBlock)
{
case E_BLOCK_CLAY:

View File

@ -11,6 +11,8 @@
class cBlockDoorHandler final :
public cYawRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x01, 0x02>
{
@ -43,25 +45,6 @@ public:
}
}
static bool CanReplaceBlock(BLOCKTYPE a_BlockType)
{
switch (a_BlockType)
{
case E_BLOCK_AIR:
case E_BLOCK_TALL_GRASS:
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
case E_BLOCK_LAVA:
case E_BLOCK_STATIONARY_LAVA:
case E_BLOCK_SNOW:
case E_BLOCK_FIRE:
{
return true;
}
}
return false;
}
/** Returns a vector pointing one block in the direction the door is facing (where the outside is). */
inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta)
{
@ -167,38 +150,6 @@ private:
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) const override;
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) const override;
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) const override;
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
// If clicking a bottom face, place the door one block lower:
auto PlacedPos = a_PlacedBlockPos;
if (a_ClickedBlockFace == BLOCK_FACE_BOTTOM)
{
PlacedPos.y--;
}
if (
!CanReplaceBlock(a_ChunkInterface.GetBlock(PlacedPos)) ||
!CanReplaceBlock(a_ChunkInterface.GetBlock(PlacedPos.addedY(1)))
)
{
return false;
}
return Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, PlacedPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta);
}
virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP) const override;
@ -237,9 +188,17 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
return ((a_RelPos.y > 0) && CanBeOn(a_Chunk.GetBlock(a_RelPos.addedY(-1)), a_Chunk.GetMeta(a_RelPos.addedY(-1))));
// CanBeAt is also called on placement, so the top part can't check for the bottom part.
// Both parts can only that their base is a valid block.
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
const auto BasePosition = a_Position.addedY(((a_Meta & 0x8) == 0x8) ? -2 : -1);
a_Chunk.GetBlockTypeMeta(BasePosition, BlockType, BlockMeta);
return (BasePosition.y >= 0) && CanBeOn(BlockType, BlockMeta);
}

View File

@ -8,19 +8,19 @@
class cBlockEndPortalFrameHandler final :
public cMetaRotator<cBlockHandler, 0x03,
E_META_END_PORTAL_FRAME_ZM,
E_META_END_PORTAL_FRAME_XP,
public cYawRotator<cBlockHandler, 0x03,
E_META_END_PORTAL_FRAME_ZP,
E_META_END_PORTAL_FRAME_XM
E_META_END_PORTAL_FRAME_XM,
E_META_END_PORTAL_FRAME_ZM,
E_META_END_PORTAL_FRAME_XP
>
{
using Super = cMetaRotator<
using Super = cYawRotator<
cBlockHandler, 0x03,
E_META_END_PORTAL_FRAME_ZM,
E_META_END_PORTAL_FRAME_XP,
E_META_END_PORTAL_FRAME_ZP,
E_META_END_PORTAL_FRAME_XM
E_META_END_PORTAL_FRAME_XM,
E_META_END_PORTAL_FRAME_ZM,
E_META_END_PORTAL_FRAME_XP
>;
public:
@ -29,54 +29,6 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
a_BlockType = m_BlockType;
a_BlockMeta = YawToMetaData(a_Player.GetYaw());
return true;
}
inline static NIBBLETYPE YawToMetaData(double a_Rotation)
{
a_Rotation += 90 + 45; // So its not aligned with axis
if (a_Rotation > 360)
{
a_Rotation -= 360;
}
if ((a_Rotation >= 0) && (a_Rotation < 90))
{
return E_META_END_PORTAL_FRAME_XM;
}
else if ((a_Rotation >= 180) && (a_Rotation < 270))
{
return E_META_END_PORTAL_FRAME_XP;
}
else if ((a_Rotation >= 90) && (a_Rotation < 180))
{
return E_META_END_PORTAL_FRAME_ZM;
}
else
{
return E_META_END_PORTAL_FRAME_ZP;
}
}
virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) const override
{
// E_META_END_PORTAL_FRAME_EYE is the bit which signifies the eye of ender is in it.
@ -237,16 +189,6 @@ private:
virtual bool IsClickedThrough(void) const override
{
// TODO: Colision
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{
UNUSED(a_Meta);

View File

@ -65,14 +65,6 @@ private:
}
virtual bool IsClickedThrough(void) const override
{
return true;
}
/** Traces along YP until it finds an obsidian block, returns Y difference or 0 if no portal, and -1 for border
Takes the X, Y, and Z of the base block; with an optional MaxY for portal border finding */
static int FindObsidianCeiling(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, int MaxY = 0)
@ -256,7 +248,7 @@ private:
return (FoundFrameZP && FoundFrameZM);
}
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
{
return true;
}

View File

@ -28,9 +28,9 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
return ((a_RelPos.y > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelPos.addedY(-1))));
return (a_Position.y > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_Position.addedY(-1)));
}

View File

@ -32,7 +32,7 @@ private:
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
{
return true;
}

View File

@ -456,24 +456,6 @@ namespace
////////////////////////////////////////////////////////////////////////////////
// cBlockHandler:
bool cBlockHandler::GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const
{
// By default, all blocks can be placed and the meta is copied over from the item's damage value:
a_BlockType = m_BlockType;
a_BlockMeta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage & 0x0f);
return true;
}
void cBlockHandler::OnUpdate(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
@ -490,7 +472,7 @@ void cBlockHandler::OnUpdate(
void cBlockHandler::OnNeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const
{
if (a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & a_Chunk) { return CanBeAt(a_ChunkInterface, cChunkDef::AbsoluteToRelative(a_BlockPos), a_Chunk); }))
if (a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & a_Chunk) { return CanBeAt(a_Chunk, cChunkDef::AbsoluteToRelative(a_BlockPos), a_Chunk.GetMeta(cChunkDef::AbsoluteToRelative(a_BlockPos))); }))
{
return;
}
@ -528,7 +510,7 @@ cItems cBlockHandler::ConvertToPickups(NIBBLETYPE a_BlockMeta, const cItem * con
bool cBlockHandler::CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const
bool cBlockHandler::CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const
{
return true;
}
@ -546,18 +528,9 @@ bool cBlockHandler::IsUseable() const
bool cBlockHandler::IsClickedThrough(void) const
bool cBlockHandler::DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const
{
return false;
}
bool cBlockHandler::DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const
{
return (m_BlockType == E_BLOCK_AIR);
return m_BlockType == E_BLOCK_AIR;
}

View File

@ -14,6 +14,7 @@ class cPlayer;
class cChunk;
class cBlockPluginInterface;
class cChunkInterface;
class cWorld;
class cWorldInterface;
class cItems;
@ -45,22 +46,6 @@ public:
blocktype of the minus-X neighbor, the positive-X neighbor, etc. */
virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP) const;
/** Called before a block is placed into a world by player, by cItemHandler::GetPlacementBlockTypeMeta().
The handler should return true to allow placement, false to refuse.
a_PlacedBlockPos is the coords of the block being placed
a_ClickedBlockFace is the face of the neighbor block clicked by the client to place this block.
a_CursorPos is the position of the cursor within the neighbor's face
The descendant handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
The default handler uses the stored block type and meta copied from the lowest 4 bits of the player's equipped item's damage value. */
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const;
/** Called by cWorld::SetBlock() after the block has been set */
virtual void OnPlaced(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
@ -68,11 +53,6 @@ public:
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
) const {}
/** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */
virtual void OnPlacedByPlayer(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange
) const {}
/** Called after a block gets broken (replaced with air), by natural means.
The block is already dug up in the world, the original block type and meta is passed in a_OldBlockType and a_OldBlockMeta.
By default notifies all direct neighbors via their OnNeighborChanged() callbacks.
@ -142,11 +122,7 @@ public:
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, const cItem * a_Tool = nullptr) const;
/** Checks if the block can stay at the specified relative coords in the chunk */
virtual bool CanBeAt(
cChunkInterface & a_ChunkInterface,
const Vector3i a_RelPos,
const cChunk & a_Chunk
) const;
virtual bool CanBeAt(const cChunk & a_Chunk, Vector3i a_Position, NIBBLETYPE a_Meta) const;
/** Checks whether the block has an effect on growing the plant */
virtual bool CanSustainPlant(BLOCKTYPE a_Plant) const { return false; }
@ -155,16 +131,12 @@ public:
If it returns true, OnUse() is called */
virtual bool IsUseable(void) const;
/** Indicates whether the client will click through this block.
For example digging a fire will hit the block below the fire so fire is clicked through. */
virtual bool IsClickedThrough(void) const;
/** Checks if the player can build "inside" this block.
For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision
@param a_Pos Position of the block
@param a_Player Player trying to build on the block
@param a_Meta Meta value of the block currently at a_Pos */
virtual bool DoesIgnoreBuildCollision(cChunkInterface & ChunkInterface, const Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const;
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, Vector3i a_Position, NIBBLETYPE a_Meta, eBlockFace a_ClickedBlockFace, bool a_ClickedDirectly) const;
/** Tests if a_RelPosition is inside the block, where a_RelPosition is relative to the origin of the block.
Coords in a_RelPosition are guaranteed to be in the [0..1] range. */

View File

@ -8,9 +8,9 @@
class cBlockHopperHandler final :
public cPitchYawRotator<cClearMetaOnDrop<cBlockEntityHandler>>
public cClearMetaOnDrop<cMetaRotator<cBlockEntityHandler, 0x7, E_META_HOPPER_FACING_ZP, E_META_HOPPER_FACING_XM, E_META_HOPPER_FACING_ZM, E_META_HOPPER_FACING_XP>>
{
using Super = cPitchYawRotator<cClearMetaOnDrop<cBlockEntityHandler>>;
using Super = cClearMetaOnDrop<cMetaRotator<cBlockEntityHandler, 0x7, E_META_HOPPER_FACING_ZP, E_META_HOPPER_FACING_XM, E_META_HOPPER_FACING_ZM, E_META_HOPPER_FACING_XP>>;
public:
@ -18,42 +18,9 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
a_BlockType = m_BlockType;
// Convert the blockface into meta:
switch (a_ClickedBlockFace)
{
case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break;
case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break;
case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break;
case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break;
case BLOCK_FACE_NONE: a_BlockMeta = E_META_HOPPER_UNATTACHED; break;
}
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{
UNUSED(a_Meta);
return 11;
}
} ;

View File

@ -2,6 +2,7 @@
#pragma once
#include "BlockHandler.h"
#include "BlockInfo.h"
#include "Mixins.h"
@ -17,59 +18,24 @@ public:
using Super::Super;
/** Returns true if the ladder will be supported by the block through the given blockface. */
static bool CanBePlacedOn(const BLOCKTYPE a_BlockType, const eBlockFace a_BlockFace)
{
if (
(a_BlockFace == BLOCK_FACE_NONE) ||
(a_BlockFace == BLOCK_FACE_BOTTOM) ||
(a_BlockFace == BLOCK_FACE_TOP)
)
{
return false;
}
return cBlockInfo::IsSolid(a_BlockType);
}
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
// Try finding a suitable neighbor block face for the ladder; start with the given one.
if (!LadderCanBePlacedAt(a_ChunkInterface, a_PlacedBlockPos, a_ClickedBlockFace))
{
a_ClickedBlockFace = FindSuitableBlockFace(a_ChunkInterface, a_PlacedBlockPos);
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{
return false;
}
}
a_BlockType = m_BlockType;
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
return true;
}
/** Converts the block face of the neighbor to which the ladder is attached to the ladder block's meta. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
{
switch (a_NeighborBlockFace)
{
case BLOCK_FACE_ZM: return 0x2;
case BLOCK_FACE_ZP: return 0x3;
case BLOCK_FACE_XM: return 0x4;
case BLOCK_FACE_XP: return 0x5;
case BLOCK_FACE_NONE:
case BLOCK_FACE_YM:
case BLOCK_FACE_YP:
{
return 0x2;
}
}
UNREACHABLE("Unsupported neighbor block face");
}
/** Converts the ladder block's meta to the block face of the neighbor to which the ladder is attached. */
static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
{
@ -87,51 +53,13 @@ private:
/** Finds a suitable block face value for the Ladder.
The returned value is the block face of the neighbor of the specified block to which a ladder at a_LadderPos can be attached.
Returns BLOCK_FACE_NONE if no valid location is found */
static eBlockFace FindSuitableBlockFace(cChunkInterface & a_ChunkInterface, const Vector3i a_LadderPos)
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
for (int FaceInt = BLOCK_FACE_ZM; FaceInt <= BLOCK_FACE_XP; FaceInt++)
{
eBlockFace Face = static_cast<eBlockFace>(FaceInt);
if (LadderCanBePlacedAt(a_ChunkInterface, a_LadderPos, Face))
{
return Face;
}
}
return BLOCK_FACE_NONE;
}
/** Returns true if the ladder in the specified position will be supported by its neghbor through the specified neighbor's blockface. */
static bool LadderCanBePlacedAt(cChunkInterface & a_ChunkInterface, const Vector3i a_LadderPos, eBlockFace a_NeighborBlockFace)
{
if (
(a_NeighborBlockFace == BLOCK_FACE_NONE) ||
(a_NeighborBlockFace == BLOCK_FACE_BOTTOM) ||
(a_NeighborBlockFace == BLOCK_FACE_TOP)
)
{
return false;
}
auto NeighborPos = AddFaceDirection(a_LadderPos, a_NeighborBlockFace, true);
return cBlockInfo::IsSolid(a_ChunkInterface.GetBlock(NeighborPos));
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
{
auto NeighborBlockFace = MetaDataToBlockFace(a_Chunk.GetMeta(a_RelPos));
auto LadderAbsPos = a_Chunk.RelativeToAbsolute(a_RelPos);
return LadderCanBePlacedAt(a_ChunkInterface, LadderAbsPos, NeighborBlockFace);
auto Face = MetaDataToBlockFace(a_Meta);
auto NeighborRelPos = AddFaceDirection(a_Position, Face, true);
BLOCKTYPE NeighborBlockType;
a_Chunk.UnboundedRelGetBlockType(NeighborRelPos, NeighborBlockType);
return CanBePlacedOn(NeighborBlockType, Face);
}

View File

@ -64,45 +64,6 @@ private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
a_BlockType = m_BlockType;
a_BlockMeta = LeverDirectionToMetaData(a_ClickedBlockFace);
return true;
}
/** Converts the block face of the neighbor to which the lever is attached to the lever block's meta. */
inline static NIBBLETYPE LeverDirectionToMetaData(eBlockFace a_Dir)
{
// Determine lever direction:
switch (a_Dir)
{
case BLOCK_FACE_YP: return 0x6;
case BLOCK_FACE_XP: return 0x1;
case BLOCK_FACE_XM: return 0x2;
case BLOCK_FACE_ZP: return 0x3;
case BLOCK_FACE_ZM: return 0x4;
case BLOCK_FACE_YM: return 0x0;
case BLOCK_FACE_NONE: return 0x6;
}
UNREACHABLE("Unsupported block face");
}
/** Converts the leve block's meta to the block face of the neighbor to which the lever is attached. */
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
{
@ -128,18 +89,18 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
// Find the type of block the lever is attached to:
auto Meta = a_Chunk.GetMeta(a_RelPos);
auto NeighborFace = BlockMetaDataToBlockFace(Meta);
auto NeighborPos = AddFaceDirection(a_RelPos, NeighborFace, true);
auto NeighborFace = BlockMetaDataToBlockFace(a_Meta);
auto NeighborPos = AddFaceDirection(a_Position, NeighborFace, true);
if (!cChunkDef::IsValidHeight(NeighborPos.y))
{
return false;
}
BLOCKTYPE NeighborBlockType;
if (!a_Chunk.UnboundedRelGetBlock(NeighborPos, NeighborBlockType, Meta))
NIBBLETYPE NeighborMeta;
if (!a_Chunk.UnboundedRelGetBlock(NeighborPos, NeighborBlockType, NeighborMeta))
{
return false;
}
@ -152,8 +113,8 @@ private:
else if (cBlockSlabHandler::IsAnySlabType(NeighborBlockType))
{
return (
(((Meta & 0x08) == 0x08) && (NeighborFace == BLOCK_FACE_TOP)) ||
(((Meta & 0x08) == 0) && (NeighborFace == BLOCK_FACE_BOTTOM))
(((NeighborMeta & 0x08) == 0x08) && (NeighborFace == BLOCK_FACE_TOP)) ||
(((NeighborMeta & 0x08) == 0) && (NeighborFace == BLOCK_FACE_BOTTOM))
);
}

View File

@ -28,9 +28,9 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
auto UnderPos = a_RelPos.addedY(-1);
auto UnderPos = a_Position.addedY(-1);
if (!cChunkDef::IsValidHeight(UnderPos.y))
{
return false;

View File

@ -21,16 +21,16 @@ private:
// TODO: Add Mushroom Spread
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
// TODO: Cannot be at too much daylight
switch (a_Chunk.GetBlock(a_RelPos.addedY(-1)))
switch (a_Chunk.GetBlock(a_Position.addedY(-1)))
{
case E_BLOCK_GLASS:
case E_BLOCK_CACTUS:

View File

@ -57,10 +57,10 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
// Needs to be placed on top of a Soulsand block:
return ((a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) == E_BLOCK_SOULSAND));
return (a_Position.y > 0) && (a_Chunk.GetBlock(a_Position.addedY(-1)) == E_BLOCK_SOULSAND);
}

View File

@ -18,24 +18,6 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
a_BlockType = m_BlockType;
a_BlockMeta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{
switch (a_Meta)

View File

@ -18,28 +18,6 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
// We set meta to zero so Cuberite doesn't stop a Creative-mode player from building custom portal shapes
// CanBeAt doesn't do anything if meta is zero
// We set to zero because the client sends meta = 1 or 2 to the server (it calculates rotation itself)
a_BlockType = m_BlockType;
a_BlockMeta = 0;
return true;
}
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
{
// No pickups
@ -71,14 +49,14 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if ((a_RelPos.y <= 0) || (a_RelPos.y >= cChunkDef::Height - 1))
if ((a_Position.y <= 0) || (a_Position.y >= cChunkDef::Height - 1))
{
return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1
return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1.
}
switch (a_Chunk.GetMeta(a_RelPos))
switch (a_Meta)
{
case 0x1:
{
@ -95,7 +73,7 @@ private:
for (const auto & Direction : PortalCheck)
{
BLOCKTYPE Block;
a_Chunk.UnboundedRelGetBlockType(a_RelPos + Direction, Block);
a_Chunk.UnboundedRelGetBlockType(a_Position + Direction, Block);
if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
{
return false;
@ -118,7 +96,7 @@ private:
for (const auto & Direction : PortalCheck)
{
BLOCKTYPE Block;
a_Chunk.UnboundedRelGetBlockType(a_RelPos + Direction, Block);
a_Chunk.UnboundedRelGetBlockType(a_Position + Direction, Block);
if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
{
return false;

View File

@ -17,15 +17,16 @@ public:
private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 1)
if (a_Position.y <= 0)
{
return false;
}
// TODO: check if the block is upside-down slab or upside-down stairs
auto Block = a_Chunk.GetBlock(a_RelPos.addedY(-1));
const auto Block = a_Chunk.GetBlock(a_Position.addedY(-1));
switch (Block)
{
case E_BLOCK_ACACIA_FENCE:
@ -41,7 +42,7 @@ private:
}
default:
{
return (!cBlockInfo::IsTransparent(Block));
return !cBlockInfo::IsTransparent(Block);
}
}
}

View File

@ -18,66 +18,6 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
a_BlockType = m_BlockType;
auto Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
// Pillar block needs additional direction in the metadata:
if (Meta != E_META_QUARTZ_PILLAR)
{
a_BlockMeta = Meta;
return true;
}
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
return true;
}
/** Converts the block face of the pillar block's "base" to the block's metadata. */
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_YM:
case BLOCK_FACE_YP:
{
return E_META_QUARTZ_PILLAR; // Top or bottom
}
case BLOCK_FACE_ZP:
case BLOCK_FACE_ZM:
{
return 0x4; // North or south
}
case BLOCK_FACE_XP:
case BLOCK_FACE_XM:
{
return 0x3; // East or west
}
default:
{
return E_META_QUARTZ_PILLAR;
}
}
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{
UNUSED(a_Meta);

View File

@ -25,141 +25,8 @@ public:
using Super::Super;
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
a_BlockType = m_BlockType;
a_BlockMeta = FindMeta(a_ChunkInterface, a_PlacedBlockPos);
return a_Player.GetWorld()->DoWithChunkAt(a_PlacedBlockPos,
[this, a_PlacedBlockPos, &a_ChunkInterface](cChunk & a_Chunk)
{
auto RelPos = cChunkDef::AbsoluteToRelative(a_PlacedBlockPos);
return CanBeAt(a_ChunkInterface, RelPos, a_Chunk);
}
);
}
virtual void OnPlaced(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
Vector3i a_BlockPos,
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
) const override
{
Super::OnPlaced(a_ChunkInterface, a_WorldInterface, a_BlockPos, a_BlockType, a_BlockMeta);
// Alert diagonal rails:
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, 1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, 1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, 1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, -1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, -1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, -1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, 1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, -1), BLOCK_FACE_NONE);
}
virtual void OnBroken(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
Vector3i a_BlockPos,
BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta,
const cEntity * a_Digger
) const override
{
Super::OnBroken(a_ChunkInterface, a_WorldInterface, a_BlockPos, a_OldBlockType, a_OldBlockMeta, a_Digger);
// Alert diagonal rails:
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, 1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, 1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, 1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, -1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, -1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, -1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, 1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, -1), BLOCK_FACE_NONE);
}
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const override
{
const auto Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
const auto NewMeta = FindMeta(a_ChunkInterface, a_BlockPos);
if ((Meta != NewMeta) && IsUnstable(a_ChunkInterface, a_BlockPos))
{
a_ChunkInterface.FastSetBlock(a_BlockPos, m_BlockType, (m_BlockType == E_BLOCK_RAIL) ? NewMeta : NewMeta | (Meta & 0x08));
}
Super::OnNeighborChanged(a_ChunkInterface, a_BlockPos, a_WhichNeighbor);
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
{
if (a_RelPos.y <= 0)
{
return false;
}
if (!cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(a_RelPos.addedY(-1))))
{
return false;
}
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelPos);
switch (Meta)
{
case E_META_RAIL_ASCEND_XP:
case E_META_RAIL_ASCEND_XM:
case E_META_RAIL_ASCEND_ZM:
case E_META_RAIL_ASCEND_ZP:
{
// Mapping between the meta and the neighbors that need checking
Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero
static const Vector3i Coords[] =
{
{ 1, 0, 0}, // east, XP
{-1, 0, 0}, // west, XM
{ 0, 0, -1}, // north, ZM
{ 0, 0, 1}, // south, ZP
} ;
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
if (!a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[Meta], BlockType, BlockMeta))
{
// Too close to the edge, cannot simulate
return true;
}
return cBlockInfo::FullyOccupiesVoxel(BlockType);
}
}
return true;
}
NIBBLETYPE FindMeta(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos) const
static NIBBLETYPE FindMeta(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, BLOCKTYPE a_RailType)
{
NIBBLETYPE Meta = 0;
char RailsCnt = 0;
@ -234,19 +101,21 @@ private:
}
if (RailsCnt > 1)
{
if (Neighbors[3] && Neighbors[0] && CanThisRailCurve())
const bool CanCurve = a_RailType == E_BLOCK_RAIL;
if (Neighbors[3] && Neighbors[0] && CanCurve)
{
return E_META_RAIL_CURVED_ZP_XP;
}
else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve())
else if (Neighbors[3] && Neighbors[1] && CanCurve)
{
return E_META_RAIL_CURVED_ZP_XM;
}
else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve())
else if (Neighbors[2] && Neighbors[0] && CanCurve)
{
return E_META_RAIL_CURVED_ZM_XP;
}
else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve())
else if (Neighbors[2] && Neighbors[1] && CanCurve)
{
return E_META_RAIL_CURVED_ZM_XM;
}
@ -275,7 +144,7 @@ private:
return E_META_RAIL_ZM_ZP;
}
if (CanThisRailCurve())
if (CanCurve)
{
ASSERT(!"Weird neighbor count");
}
@ -283,10 +152,42 @@ private:
return Meta;
}
private:
bool CanThisRailCurve(void) const
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, NIBBLETYPE a_Meta) const override
{
return m_BlockType == E_BLOCK_RAIL;
if ((a_Position.y <= 0) || !cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(a_Position.addedY(-1))))
{
return false;
}
switch (a_Meta)
{
case E_META_RAIL_ASCEND_XP:
case E_META_RAIL_ASCEND_XM:
case E_META_RAIL_ASCEND_ZM:
case E_META_RAIL_ASCEND_ZP:
{
// Mapping between the meta and the neighbors that need checking
a_Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero
static const Vector3i Coords[] =
{
{ 1, 0, 0}, // east, XP
{-1, 0, 0}, // west, XM
{ 0, 0, -1}, // north, ZM
{ 0, 0, 1}, // south, ZP
} ;
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
if (!a_Chunk.UnboundedRelGetBlock(a_Position + Coords[a_Meta], BlockType, BlockMeta))
{
// Too close to the edge, cannot simulate
return true;
}
return cBlockInfo::FullyOccupiesVoxel(BlockType);
}
}
return true;
}
@ -521,6 +422,60 @@ private:
}
virtual void OnBroken(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
Vector3i a_BlockPos,
BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta,
const cEntity * a_Digger
) const override
{
Super::OnBroken(a_ChunkInterface, a_WorldInterface, a_BlockPos, a_OldBlockType, a_OldBlockMeta, a_Digger);
// Alert diagonal rails:
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, 1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, 1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, 1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, -1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, -1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, -1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, 1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, -1), BLOCK_FACE_NONE);
}
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const override
{
const auto Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
const auto NewMeta = FindMeta(a_ChunkInterface, a_BlockPos, m_BlockType);
if ((Meta != NewMeta) && IsUnstable(a_ChunkInterface, a_BlockPos))
{
a_ChunkInterface.FastSetBlock(a_BlockPos, m_BlockType, (m_BlockType == E_BLOCK_RAIL) ? NewMeta : NewMeta | (Meta & 0x08));
}
Super::OnNeighborChanged(a_ChunkInterface, a_BlockPos, a_WhichNeighbor);
}
virtual void OnPlaced(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
Vector3i a_BlockPos,
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
) const override
{
Super::OnPlaced(a_ChunkInterface, a_WorldInterface, a_BlockPos, a_BlockType, a_BlockMeta);
// Alert diagonal rails:
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, 1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, 1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, 1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, -1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, -1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, -1, 0), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, 1), BLOCK_FACE_NONE);
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, -1), BLOCK_FACE_NONE);
}
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) const override
{
// Bit 0x08 is a flag when a_Meta is in the range 0x00--0x05 and 0x0A--0x0F.

View File

@ -19,16 +19,16 @@ public:
private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
{

View File

@ -105,16 +105,16 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
{

View File

@ -29,9 +29,9 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
return (a_RelPos.y > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelPos.addedY(-1)));
return (a_Position.y > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_Position.addedY(-1)));
}

View File

@ -8,7 +8,6 @@
/** Handler for blocks that have 3 orientations (hay bale, log), specified by the upper 2 bits in meta.
Handles setting the correct orientation on placement.
Additionally supports the metadata specifying block sub-type in its lower 2 bits. */
class cBlockSidewaysHandler final :
public cBlockHandler
@ -21,66 +20,9 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
a_BlockType = m_BlockType;
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace, Meta);
return true;
}
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
{
// Reset the orientation part of meta, keep the sub-type part of meta
// Reset the orientation part of meta, keep the sub-type part of meta:
return cItem(m_BlockType, 1, a_BlockMeta & 0x03);
}
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace, NIBBLETYPE a_Meta)
{
switch (a_BlockFace)
{
case BLOCK_FACE_YM:
case BLOCK_FACE_YP:
{
return a_Meta; // Top or bottom, just return original
}
case BLOCK_FACE_ZP:
case BLOCK_FACE_ZM:
{
return a_Meta | 0x8; // North or south
}
case BLOCK_FACE_XP:
case BLOCK_FACE_XM:
{
return a_Meta | 0x4; // East or west
}
case BLOCK_FACE_NONE:
{
break;
}
}
UNREACHABLE("Unsupported block face");
}
} ;

View File

@ -17,20 +17,6 @@ public:
using Super::Super;
/** Converts the (player) rotation to placed-signpost block meta. */
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
if (a_Rotation > 360)
{
a_Rotation -= 360;
}
a_Rotation = (a_Rotation / 360) * 16;
return (static_cast<char>(a_Rotation)) % 16;
}
private:
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
@ -42,14 +28,15 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
BLOCKTYPE Type = a_Chunk.GetBlock(a_RelPos.addedY(-1));
return ((Type == E_BLOCK_SIGN_POST) || (Type == E_BLOCK_WALLSIGN) || cBlockInfo::IsSolid(Type));
BLOCKTYPE Type = a_Chunk.GetBlock(a_Position.addedY(-1));
return (Type == E_BLOCK_SIGN_POST) || (Type == E_BLOCK_WALLSIGN) || cBlockInfo::IsSolid(Type);
}

View File

@ -46,64 +46,25 @@ private:
}
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
{
a_BlockType = m_BlockType;
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
// Set the correct metadata based on player equipped item (i.e. a_BlockMeta not initialised yet)
switch (a_ClickedBlockFace)
/* Double slab combining uses build collision checks to replace single slabs with double slabs in the right conditions.
For us to be replaced, the player must be:
1. Placing the same slab material.
2. Placing the same slab sub-kind (and existing slab is single). */
if ((m_BlockType != a_HeldItem.m_ItemType) || ((a_Meta & 0x07) != a_HeldItem.m_ItemDamage))
{
case BLOCK_FACE_TOP:
{
// Bottom half slab block
a_BlockMeta = Meta & 0x07;
break;
}
case BLOCK_FACE_BOTTOM:
{
// Top half slab block
a_BlockMeta = Meta | 0x08;
break;
}
case BLOCK_FACE_EAST:
case BLOCK_FACE_NORTH:
case BLOCK_FACE_SOUTH:
case BLOCK_FACE_WEST:
{
if (a_CursorPos.y > 7)
{
// Cursor at top half of block, place top slab
a_BlockMeta = Meta | 0x08; break;
}
else
{
// Cursor at bottom half of block, place bottom slab
a_BlockMeta = Meta & 0x07; break;
}
}
case BLOCK_FACE_NONE: return false;
} // switch (a_BlockFace)
// Check if the block at the coordinates is a single slab. Eligibility for combining has already been processed in ClientHandle
// Changed to-be-placed to a double slab if we are clicking on a single slab, as opposed to placing one for the first time
if (IsAnySlabType(a_ChunkInterface.GetBlock(a_PlacedBlockPos)))
{
a_BlockType = GetDoubleSlabType(m_BlockType);
a_BlockMeta = a_BlockMeta & 0x07;
return false;
}
return true;
const bool IsTopSlab = (a_Meta & 0x08) == 0x08;
const auto CanClickCombine = ((a_ClickedBlockFace == BLOCK_FACE_TOP) && !IsTopSlab) || ((a_ClickedBlockFace == BLOCK_FACE_BOTTOM) && IsTopSlab);
/* When the player clicks on us directly, we'll combine if we're
a bottom slab and he clicked the top, or vice versa. Clicking on the sides will not combine.
However, when indirectly clicked (on the side of another block, that caused placement to go to us)
the conditions are exactly the opposite. */
return a_ClickedDirectly ? CanClickCombine : !CanClickCombine;
}
@ -131,24 +92,6 @@ private:
/** Converts the single-slab blocktype to its equivalent double-slab blocktype */
static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType)
{
switch (a_SingleSlabBlockType)
{
case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB;
case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB;
case E_BLOCK_RED_SANDSTONE_SLAB: return E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB;
case E_BLOCK_PURPUR_SLAB: return E_BLOCK_PURPUR_DOUBLE_SLAB;
}
ASSERT(!"Unhandled slab type!");
return E_BLOCK_AIR;
}
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
{
// Toggle the 4th bit - up / down:

View File

@ -20,58 +20,21 @@ private:
enum
{
FullBlockMeta = 7 // Meta value of a full-height snow block
FullBlockMeta = 7 // Meta value of a full-height snow block.
};
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
{
a_BlockType = m_BlockType;
// Check if incrementing existing snow height:
BLOCKTYPE BlockBeforePlacement;
NIBBLETYPE MetaBeforePlacement;
a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos, BlockBeforePlacement, MetaBeforePlacement);
if ((BlockBeforePlacement == E_BLOCK_SNOW) && (MetaBeforePlacement < FullBlockMeta))
{
// Only increment if:
// - A snow block was already there (not first time placement) AND
// - Height is smaller than the maximum possible
a_BlockMeta = MetaBeforePlacement + 1;
return true;
}
// First time placement, check placement is valid
a_BlockMeta = 0;
BLOCKTYPE BlockBelow;
NIBBLETYPE MetaBelow;
return (
(a_PlacedBlockPos.y > 0) &&
a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos.addedY(-1), BlockBelow, MetaBelow) &&
CanBeOn(BlockBelow, MetaBelow)
);
}
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
{
if ((a_Player.GetEquippedItem().m_ItemType == E_BLOCK_SNOW) && (a_Meta < FullBlockMeta))
{
return true; // If a player is holding a (thin) snow block and it's size can be increased, return collision ignored
}
if (a_Meta == 0)
{
return true; // If at normal snowfall height (lowest), we ignore collision
return true; // If at normal snowfall height (lowest), we ignore collision.
}
// Special case if a player is holding a (thin) snow block and its size can be increased:
if ((a_HeldItem.m_ItemType == E_BLOCK_SNOW) && (a_Meta < FullBlockMeta))
{
return !a_ClickedDirectly || (a_ClickedBlockFace == BLOCK_FACE_YP); // If clicked an adjacent block, or clicked YP directly, we ignore collision.
}
return false;
@ -83,7 +46,7 @@ private:
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
{
// No drop unless dug up with a shovel
// No drop unless dug up with a shovel:
if ((a_Tool == nullptr) || !ItemCategory::IsShovel(a_Tool->m_ItemType))
{
return {};
@ -104,13 +67,13 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
auto BelowPos = a_RelPos.addedY(-1);
auto BelowPos = a_Position.addedY(-1);
auto BlockBelow = a_Chunk.GetBlock(BelowPos);
auto MetaBelow = a_Chunk.GetMeta(BelowPos);
return CanBeOn(BlockBelow, MetaBelow);
@ -141,7 +104,7 @@ private:
/** Returns true if snow can be placed on top of a block with the given type and meta. */
static bool CanBeOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
// If block below is snowable, or it is a thin slow block and is a full thin snow block, say yay
// If block below is snowable, or it is a thin snow block and is a full thin snow block, say yay:
return (
cBlockInfo::IsSnowable(a_BlockType) ||
(

View File

@ -8,9 +8,9 @@
class cBlockStairsHandler final :
public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>>
public cClearMetaOnDrop<cYawRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>>
{
using Super = cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>>;
using Super = cClearMetaOnDrop<cYawRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>>;
public:
@ -18,74 +18,6 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
UNUSED(a_ChunkInterface);
UNUSED(a_PlacedBlockPos);
UNUSED(a_CursorPos);
a_BlockType = m_BlockType;
a_BlockMeta = RotationToMetaData(a_Player.GetYaw());
switch (a_ClickedBlockFace)
{
case BLOCK_FACE_TOP: break;
case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block
case BLOCK_FACE_EAST:
case BLOCK_FACE_NORTH:
case BLOCK_FACE_SOUTH:
case BLOCK_FACE_WEST:
{
// When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block
if (a_CursorPos.y > 8)
{
a_BlockMeta |= 0x4;
}
break;
}
case BLOCK_FACE_NONE: return false;
}
return true;
}
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 90 + 45; // So its not aligned with axis
if (a_Rotation > 360)
{
a_Rotation -= 360;
}
if ((a_Rotation >= 0) && (a_Rotation < 90))
{
return 0x0;
}
else if ((a_Rotation >= 180) && (a_Rotation < 270))
{
return 0x1;
}
else if ((a_Rotation >= 90) && (a_Rotation < 180))
{
return 0x2;
}
else
{
return 0x3;
}
}
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
{
// Toggle bit 3:

View File

@ -56,9 +56,9 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
return ((a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) == E_BLOCK_FARMLAND));
return (a_Position.y > 0) && (a_Chunk.GetBlock(a_Position.addedY(-1)) == E_BLOCK_FARMLAND);
}

View File

@ -27,14 +27,14 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
switch (a_Chunk.GetBlock(a_RelPos.addedY(-1)))
switch (a_Chunk.GetBlock(a_Position.addedY(-1)))
{
case E_BLOCK_DIRT:
case E_BLOCK_GRASS:
@ -52,7 +52,7 @@ private:
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
if (!a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[i], BlockType, BlockMeta))
if (!a_Chunk.UnboundedRelGetBlock(a_Position + Coords[i], BlockType, BlockMeta))
{
// Too close to the edge, cannot simulate
return true;

View File

@ -20,7 +20,7 @@ public:
private:
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
{
return true;
}
@ -52,14 +52,14 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
if (a_RelPos.y <= 0)
if (a_Position.y <= 0)
{
return false;
}
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1));
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_Position.addedY(-1));
return IsBlockTypeOfDirt(BelowBlock);
}

View File

@ -18,91 +18,6 @@ public:
using Super::Super;
protected:
~cBlockTorchBaseHandler() = default;
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
auto ClickedBlockPos = AddFaceDirection(a_PlacedBlockPos, a_ClickedBlockFace, true);
a_ChunkInterface.GetBlockTypeMeta(ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
if (!CanBePlacedOn(ClickedBlockType, ClickedBlockMeta, a_ClickedBlockFace))
{
// Couldn't be placed on whatever face was clicked, last ditch resort - find another face
a_ClickedBlockFace = FindSuitableFace(a_ChunkInterface, a_PlacedBlockPos); // Set a_BlockFace to a valid direction which will be converted later to a metadata
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{
// No attachable face found - don't place the torch
return false;
}
}
a_BlockType = m_BlockType;
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
return true;
}
/** Converts the block face of the neighbor to which the torch is attached, to the torch block's meta. */
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
{
switch (a_NeighborBlockFace)
{
case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0;
case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR;
case BLOCK_FACE_EAST: return E_META_TORCH_EAST;
case BLOCK_FACE_WEST: return E_META_TORCH_WEST;
case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH;
case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH;
case BLOCK_FACE_NONE:
{
ASSERT(!"Unhandled torch direction!");
break;
}
}
return 0x0;
}
/** Converts the torch block's meta to the block face of the neighbor to which the torch is attached. */
inline static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
{
switch (a_MetaData)
{
case 0: return BLOCK_FACE_TOP; // By default, the torches stand on the ground
case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP;
case E_META_TORCH_EAST: return BLOCK_FACE_EAST;
case E_META_TORCH_WEST: return BLOCK_FACE_WEST;
case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH;
case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH;
default:
{
ASSERT(!"Unhandled torch metadata");
break;
}
}
return BLOCK_FACE_TOP;
}
/** Returns true if the torch can be placed on the specified block's face. */
static bool CanBePlacedOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_BlockFace)
@ -163,37 +78,40 @@ private:
}
}
protected:
~cBlockTorchBaseHandler() = default;
private:
/** Returns a suitable neighbor's blockface to place the torch at the specified pos
Returns BLOCK_FACE_NONE on failure */
static eBlockFace FindSuitableFace(cChunkInterface & a_ChunkInterface, const Vector3i a_TorchPos)
/** Converts the torch block's meta to the block face of the neighbor to which the torch is attached. */
inline static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
{
for (int i = BLOCK_FACE_YM; i <= BLOCK_FACE_XP; i++) // Loop through all faces
switch (a_MetaData)
{
auto Face = static_cast<eBlockFace>(i);
auto NeighborPos = AddFaceDirection(a_TorchPos, Face, true);
BLOCKTYPE NeighborBlockType;
NIBBLETYPE NeighborBlockMeta;
a_ChunkInterface.GetBlockTypeMeta(NeighborPos, NeighborBlockType, NeighborBlockMeta);
if (CanBePlacedOn(NeighborBlockType, NeighborBlockMeta, Face))
case 0: return BLOCK_FACE_TOP; // By default, the torches stand on the ground
case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP;
case E_META_TORCH_EAST: return BLOCK_FACE_EAST;
case E_META_TORCH_WEST: return BLOCK_FACE_WEST;
case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH;
case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH;
default:
{
return Face;
ASSERT(!"Unhandled torch metadata");
break;
}
}
return BLOCK_FACE_NONE;
return BLOCK_FACE_TOP;
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
auto Face = MetaDataToBlockFace(a_Chunk.GetMeta(a_RelPos));
auto NeighborRelPos = AddFaceDirection(a_RelPos, Face, true);
auto Face = MetaDataToBlockFace(a_Meta);
auto NeighborRelPos = AddFaceDirection(a_Position, Face, true);
BLOCKTYPE NeighborBlockType;
NIBBLETYPE NeighborBlockMeta;
if (!a_Chunk.UnboundedRelGetBlock(NeighborRelPos, NeighborBlockType, NeighborBlockMeta))

View File

@ -71,74 +71,6 @@ private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
if (a_ClickedBlockFace == BLOCK_FACE_YP)
{
// Trapdoor is placed on top of a block.
// Engage yaw rotation to determine hinge direction:
return Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta);
}
else if (a_ClickedBlockFace == BLOCK_FACE_YM)
{
// Trapdoor is placed on bottom of a block.
// Engage yaw rotation to determine hinge direction:
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta))
{
return false;
}
// Toggle 'Move up half-block' bit on:
a_BlockMeta |= 0x8;
return true;
}
// Placement on block sides; hinge direction is determined by which side was clicked:
a_BlockType = m_BlockType;
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
if (a_CursorPos.y > 7)
{
// Trapdoor is placed on a higher half of a vertical block.
// Toggle 'Move up half-block' bit on:
a_BlockMeta |= 0x8;
}
return true;
}
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_ZP: return 0x1;
case BLOCK_FACE_ZM: return 0x0;
case BLOCK_FACE_XP: return 0x3;
case BLOCK_FACE_XM: return 0x2;
default:
{
ASSERT(!"Unhandled block face!");
return 0;
}
}
}
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
{
switch (a_Meta & 0x3)

View File

@ -30,57 +30,9 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
a_BlockType = m_BlockType;
switch (a_ClickedBlockFace)
{
case BLOCK_FACE_XM:
{
a_BlockMeta = 0x1;
return true;
}
case BLOCK_FACE_XP:
{
a_BlockMeta = 0x3;
return true;
}
case BLOCK_FACE_ZM:
{
a_BlockMeta = 0x2;
return true;
}
case BLOCK_FACE_ZP:
{
a_BlockMeta = 0x0;
return true;
}
case BLOCK_FACE_NONE:
case BLOCK_FACE_YM:
case BLOCK_FACE_YP:
{
return false;
}
}
UNREACHABLE("Unsupported block face");
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
{
const auto Meta = a_Chunk.GetMeta(a_RelPos);
const auto RearPosition = AddFaceDirection(a_RelPos, MetadataToDirection(Meta), true);
const auto RearPosition = AddFaceDirection(a_Position, MetadataToDirection(a_Meta), true);
BLOCKTYPE NeighborBlockType;
if (!a_Chunk.UnboundedRelGetBlockType(RearPosition, NeighborBlockType))

View File

@ -17,35 +17,16 @@ public:
private:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
static const NIBBLETYPE VINE_LOST_SUPPORT = 16;
static const NIBBLETYPE VINE_UNCHANGED = 17;
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
// TODO: Disallow placement where the vine doesn't attach to something properly
BLOCKTYPE BlockType = 0;
NIBBLETYPE BlockMeta;
a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos, BlockType, BlockMeta);
if (BlockType == m_BlockType)
{
a_BlockMeta = BlockMeta | DirectionToMetaData(a_ClickedBlockFace);
}
else
{
a_BlockMeta = DirectionToMetaData(a_ClickedBlockFace);
}
a_BlockType = m_BlockType;
return true;
return GetMaxMeta(a_Chunk, a_Position, a_Meta) != VINE_LOST_SUPPORT;
}
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
{
// Only drops self when using shears, otherwise drops nothing:
@ -60,22 +41,6 @@ private:
static NIBBLETYPE DirectionToMetaData(char a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_NORTH: return 0x1;
case BLOCK_FACE_SOUTH: return 0x4;
case BLOCK_FACE_WEST: return 0x8;
case BLOCK_FACE_EAST: return 0x2;
default: return 0x0;
}
}
static char MetaDataToDirection(NIBBLETYPE a_MetaData)
{
switch (a_MetaData)
@ -122,8 +87,9 @@ private:
/** Returns the meta that has the maximum allowable sides of the vine, given the surroundings */
static NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, Vector3i a_RelPos)
/** Returns the meta that has the maximum allowable sides of the vine, given the surroundings and current vine meta.
Returns special values for a vine that can continue to exist unchanged, or must die completely. */
static NIBBLETYPE GetMaxMeta(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_CurrentMeta)
{
static const struct
{
@ -136,21 +102,41 @@ private:
{ 0, -1, 4}, // north, ZM
{ 1, 0, 8}, // east, XP
} ;
NIBBLETYPE res = 0;
NIBBLETYPE MaxMeta = 0;
for (auto & Coord : Coords)
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
auto checkPos = a_RelPos.addedXZ(Coord.x, Coord.z);
auto checkPos = a_Position.addedXZ(Coord.x, Coord.z);
if (
a_Chunk.UnboundedRelGetBlock(checkPos.x, checkPos.y, checkPos.z, BlockType, BlockMeta) &&
IsBlockAttachable(BlockType)
)
{
res |= Coord.Bit;
MaxMeta |= Coord.Bit;
}
}
return res;
// Check if vine above us, add its meta to MaxMeta:
if ((a_Position.y < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_Position.addedY(1)) == E_BLOCK_VINES))
{
MaxMeta |= a_Chunk.GetMeta(a_Position.addedY(1));
}
NIBBLETYPE Common = a_CurrentMeta & MaxMeta; // Neighbors that we have and are legal.
if (Common != a_CurrentMeta)
{
bool HasTop = (a_Position.y < (cChunkDef::Height - 1)) && IsBlockAttachable(a_Chunk.GetBlock(a_Position.addedY(1)));
if ((Common == 0) && !HasTop) // Meta equals 0 also means top. Make a last-ditch attempt to save the vine.
{
return VINE_LOST_SUPPORT;
}
return Common;
}
return VINE_UNCHANGED;
}
@ -162,28 +148,25 @@ private:
a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & a_Chunk)
{
const auto a_RelPos = cChunkDef::AbsoluteToRelative(a_BlockPos);
NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelPos);
NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelPos);
const auto Position = cChunkDef::AbsoluteToRelative(a_BlockPos);
const auto MaxMeta = GetMaxMeta(a_Chunk, Position, a_Chunk.GetMeta(Position));
// Check if vine above us, add its meta to MaxMeta
if ((a_RelPos.y < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelPos.addedY(1)) == m_BlockType))
if (MaxMeta == VINE_UNCHANGED)
{
MaxMeta |= a_Chunk.GetMeta(a_RelPos.addedY(1));
return false;
}
NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal
if (Common != CurMeta)
// There is a neighbor missing, need to update the meta or even destroy the block.
if (MaxMeta == VINE_LOST_SUPPORT)
{
// There is a neighbor missing, need to update the meta or even destroy the block
bool HasTop = (a_RelPos.y < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelPos.addedY(1)));
if ((Common == 0) && !HasTop)
{
// The vine just lost all its support, destroy the block:
a_Chunk.SetBlock(a_RelPos, E_BLOCK_AIR, 0);
return false;
}
a_Chunk.SetBlock(a_RelPos, m_BlockType, Common);
// The vine just lost all its support, destroy the block:
a_Chunk.SetBlock(Position, E_BLOCK_AIR, 0);
}
else
{
// It lost some of its support, set it to what remains (SetBlock to notify neighbors):
a_Chunk.SetBlock(Position, E_BLOCK_VINES, MaxMeta);
}
return false;
@ -194,9 +177,9 @@ private:
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
{
return true;
return !a_ClickedDirectly || (a_HeldItem.m_ItemType != m_BlockType);
}

View File

@ -17,25 +17,6 @@ public:
using Super::Super;
/** Converts the block face of the neighbor to which the wallsign is attached to the wallsign block's meta. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
{
switch (a_NeighborBlockFace)
{
case BLOCK_FACE_ZM: return 0x02;
case BLOCK_FACE_ZP: return 0x03;
case BLOCK_FACE_XM: return 0x04;
case BLOCK_FACE_XP: return 0x05;
case BLOCK_FACE_NONE:
case BLOCK_FACE_YP:
case BLOCK_FACE_YM:
{
break;
}
}
return 0x02;
}
private:
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
@ -47,16 +28,16 @@ private:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
{
auto NeighborPos = a_RelPos + GetOffsetBehindTheSign(a_Chunk.GetMeta(a_RelPos));
auto NeighborPos = a_Position + GetOffsetBehindTheSign(a_Meta);
BLOCKTYPE NeighborType;
if (!a_Chunk.UnboundedRelGetBlockType(NeighborPos, NeighborType))
{
// The neighbor is not accessible (unloaded chunk), bail out without changing this
return true;
}
return ((NeighborType == E_BLOCK_WALLSIGN) || (NeighborType == E_BLOCK_SIGN_POST) || cBlockInfo::IsSolid(NeighborType));
return (NeighborType == E_BLOCK_WALLSIGN) || (NeighborType == E_BLOCK_SIGN_POST) || cBlockInfo::IsSolid(NeighborType);
}

View File

@ -167,27 +167,6 @@ public:
using Super::Super;
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
NIBBLETYPE BaseMeta;
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_BlockPos, a_BlockFace, a_CursorPos, a_BlockType, BaseMeta))
{
return false;
}
a_BlockMeta = (BaseMeta & ~BitMask) | YawToMetaData(a_Player.GetYaw());
return true;
}
/** Converts the rotation value as returned by cPlayer::GetYaw() to the appropriate metadata
value for a block placed by a player facing that way */
@ -241,48 +220,6 @@ public:
using Super::Super;
protected:
~cPitchYawRotator() = default;
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{
NIBBLETYPE BaseMeta;
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, BaseMeta))
{
return false;
}
a_BlockMeta = (BaseMeta & ~BitMask) | PitchYawToMetaData(a_Player.GetYaw(), a_Player.GetPitch());
return true;
}
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case Down: return Up | OtherMeta; // Down -> Up
case Up: return Down | OtherMeta; // Up -> Down
}
// Not Facing Up or Down; No change.
return a_Meta;
}
/** Converts the rotation and pitch values as returned by cPlayer::GetYaw() and cPlayer::GetPitch()
respectively to the appropriate metadata value for a block placed by a player facing that way */
@ -299,4 +236,21 @@ protected:
return Super::YawToMetaData(a_Rotation);
}
protected:
~cPitchYawRotator() = default;
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
switch (a_Meta & BitMask)
{
case Down: return Up | OtherMeta; // Down -> Up
case Up: return Down | OtherMeta; // Up -> Down
}
// Not Facing Up or Down; No change.
return a_Meta;
}
};

View File

@ -54,10 +54,10 @@ public:
virtual std::vector<UInt32> SpawnSplitExperienceOrbs(Vector3d a_Pos, int a_Reward) = 0;
/** Sends the block on those coords to the player */
virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer & a_Player) = 0;
virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, const cPlayer & a_Player) = 0;
/** Sends the block on those coords to the player */
inline void SendBlockTo(const Vector3i a_BlockPos, cPlayer & a_Player)
inline void SendBlockTo(const Vector3i a_BlockPos, const cPlayer & a_Player)
{
SendBlockTo(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Player);
}

View File

@ -748,6 +748,10 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
res = false;
continue;
}
if (!cChunkDef::IsValidHeight(itr->m_RelY))
{
continue;
}
itr->m_BlockType = Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
itr->m_BlockMeta = Chunk->GetMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
}
@ -798,7 +802,7 @@ cItems cChunkMap::PickupsFromBlock(Vector3i a_BlockPos, const cEntity * a_Digger
void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer & a_Player)
void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, const cPlayer & a_Player)
{
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ);

View File

@ -153,7 +153,7 @@ public:
/** Sends the block at the specified coords to the specified player.
Uses a blockchange packet to send the block.
If the relevant chunk isn't loaded, doesn't do anything. */
void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer & a_Player);
void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, const cPlayer & a_Player);
/** Compares clients of two chunks, calls the callback accordingly */
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);

View File

@ -987,15 +987,13 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
/* Check for clickthrough-blocks:
When the user breaks a fire block, the client send the wrong block location.
We must find the right block with the face direction. */
int BlockX = a_BlockX;
int BlockY = a_BlockY;
int BlockZ = a_BlockZ;
AddFaceDirection(BlockX, BlockY, BlockZ, a_BlockFace);
if (
cChunkDef::IsValidHeight(BlockY) &&
cBlockHandler::For(m_Player->GetWorld()->GetBlock({ BlockX, BlockY, BlockZ })).IsClickedThrough()
)
if (cChunkDef::IsValidHeight(BlockY) && cBlockInfo::IsClickedThrough(m_Player->GetWorld()->GetBlock({ BlockX, BlockY, BlockZ })))
{
a_BlockX = BlockX;
a_BlockY = BlockY;
@ -1338,7 +1336,6 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
cWorld * World = m_Player->GetWorld();
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
bool Success = false;
if (
!PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ) &&
IsWithinReach && !m_Player->IsFrozen()
@ -1354,29 +1351,24 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (BlockUsable && !(m_Player->IsCrouched() && !HeldItem.IsEmpty()))
{
// use a block
cChunkInterface ChunkInterface(World->GetChunkMap());
if (!PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
{
// Use a block:
if (BlockHandler.OnUse(ChunkInterface, *World, *m_Player, ClickedBlockPos, a_BlockFace, CursorPos))
{
// block use was successful, we're done
PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
Success = true;
return; // Block use was successful, we're done.
}
// Check if the item is place able, for example a torch on a fence
if (!Success && Placeable)
// Check if the item is place able, for example a torch on a fence:
if (Placeable)
{
// place a block
Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
// Place a block:
ItemHandler->OnPlayerPlace(*m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
}
}
else
{
// TODO: OnCancelRightClick seems to do the same thing with updating blocks at the end of this function. Need to double check
// A plugin doesn't agree with the action, replace the block on the client and quit:
BlockHandler.OnCancelRightClick(ChunkInterface, *World, *m_Player, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
return;
}
}
else if (Placeable)
@ -1388,36 +1380,30 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
return;
}
// place a block
Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
// Place a block:
ItemHandler->OnPlayerPlace(*m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
return;
}
else
else if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
{
// Use an item in hand with a target block
if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
{
// All plugins agree with using the item
cBlockInServerPluginInterface PluginInterface(*World);
ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
Success = true;
}
// All plugins agree with using the item.
// Use an item in hand with a target block.
cBlockInServerPluginInterface PluginInterface(*World);
ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
return;
}
}
if (!Success)
{
// Update the target block including the block above and below for 2 block high things
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
for (int y = a_BlockY - 1; y <= a_BlockY + 1; y++)
{
if (cChunkDef::IsValidHeight(y))
{
World->SendBlockTo(a_BlockX, y, a_BlockZ, *m_Player);
}
}
// TODO: Send corresponding slot based on hand
m_Player->GetInventory().SendEquippedSlot();
}
// TODO: delete OnItemUse bool return, delete onCancelRightClick
// Update the target block including the block above and below for 2 block high things:
m_Player->SendBlocksAround(a_BlockX, a_BlockY, a_BlockZ, 2);
// TODO: Send corresponding slot based on hand
m_Player->GetInventory().SendEquippedSlot();
}

View File

@ -2391,10 +2391,9 @@ void cPlayer::LoadRank(void)
bool cPlayer::PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
bool cPlayer::PlaceBlock(const Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
sSetBlockVector blk{{a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta}};
return PlaceBlocks(blk);
return PlaceBlocks({ { a_Position, a_BlockType, a_BlockMeta } });
}
@ -2442,7 +2441,7 @@ void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_R
bool cPlayer::DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks)
bool cPlayer::DoesPlacingBlocksIntersectEntity(const std::initializer_list<sSetBlock> a_Blocks) const
{
// Compute the bounding box for each block to be placed
std::vector<cBoundingBox> PlacementBoxes;
@ -2516,44 +2515,52 @@ const cUUID & cPlayer::GetUUID(void) const
bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks)
bool cPlayer::PlaceBlocks(const std::initializer_list<sSetBlock> a_Blocks)
{
if (DoesPlacingBlocksIntersectEntity(a_Blocks))
{
// Abort - re-send all the current blocks in the a_Blocks' coords to the client:
for (auto blk2: a_Blocks)
for (const auto & ResendBlock : a_Blocks)
{
m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), *this);
m_World->SendBlockTo(ResendBlock.GetX(), ResendBlock.GetY(), ResendBlock.GetZ(), *this);
}
return false;
}
// Call the "placing" hooks; if any fail, abort:
cPluginManager * pm = cPluginManager::Get();
for (auto blk: a_Blocks)
// Check the blocks CanBeAt, and call the "placing" hooks; if any fail, abort:
for (const auto & Block : a_Blocks)
{
if (pm->CallHookPlayerPlacingBlock(*this, blk))
if (
!m_World->DoWithChunkAt(Block.GetAbsolutePos(), [&Block](cChunk & a_Chunk)
{
return cBlockHandler::For(Block.m_BlockType).CanBeAt(a_Chunk, Block.GetRelativePos(), Block.m_BlockMeta);
})
)
{
return false;
}
if (pm->CallHookPlayerPlacingBlock(*this, Block))
{
// Abort - re-send all the current blocks in the a_Blocks' coords to the client:
for (auto blk2: a_Blocks)
for (const auto & ResendBlock : a_Blocks)
{
m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), *this);
m_World->SendBlockTo(ResendBlock.GetX(), ResendBlock.GetY(), ResendBlock.GetZ(), *this);
}
return false;
}
} // for blk - a_Blocks[]
}
cChunkInterface ChunkInterface(m_World->GetChunkMap());
for (auto blk: a_Blocks)
for (const auto & Block : a_Blocks)
{
// Set the blocks:
m_World->PlaceBlock(blk.GetAbsolutePos(), blk.m_BlockType, blk.m_BlockMeta);
// Notify the blockhandlers:
cBlockHandler::For(blk.m_BlockType).OnPlacedByPlayer(ChunkInterface, *m_World, *this, blk);
m_World->PlaceBlock(Block.GetAbsolutePos(), Block.m_BlockType, Block.m_BlockMeta);
// Call the "placed" hooks:
pm->CallHookPlayerPlacedBlock(*this, blk);
pm->CallHookPlayerPlacedBlock(*this, Block);
}
return true;

View File

@ -534,7 +534,7 @@ public:
void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround);
/** Whether placing the given blocks would intersect any entitiy */
bool DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks);
bool DoesPlacingBlocksIntersectEntity(std::initializer_list<sSetBlock> a_Blocks) const;
/** Returns the UUID that has been read from the client, or nil if not available. */
const cUUID & GetUUID(void) const; // Exported in ManualBindings.cpp
@ -552,7 +552,7 @@ public:
If the hook prevents the placement, sends the current block at the specified coords back to the client.
Assumes that the block is in a currently loaded chunk.
Returns true if the block is successfully placed. */
bool PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool PlaceBlock(Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
/** Sends the block in the specified range around the specified coord to the client
as a block change packet.
@ -571,7 +571,7 @@ public:
If the any of the "placing" hooks aborts, none of the blocks are placed and the function returns false.
Returns true if all the blocks are placed.
Assumes that all the blocks are in currently loaded chunks. */
bool PlaceBlocks(const sSetBlockVector & a_Blocks);
bool PlaceBlocks(std::initializer_list<sSetBlock> a_Blocks);
/** Notify nearby wolves that the player or one of the player's wolves took damage or did damage to an entity
@param a_Opponent the opponent we're fighting.

View File

@ -3,6 +3,7 @@ target_sources(
ItemHandler.cpp
ItemAnvil.h
ItemArmor.h
ItemAxe.h
ItemBanner.h
@ -12,24 +13,35 @@ target_sources(
ItemBottle.h
ItemBow.h
ItemBucket.h
ItemButton.h
ItemChest.h
ItemCloth.h
ItemComparator.h
ItemCookedFish.h
ItemDoor.h
ItemDropSpenser.h
ItemDye.h
ItemEmptyMap.h
ItemEnchantingTable.h
ItemEndCrystal.h
ItemEndPortalFrame.h
ItemEnderchest.h
ItemEyeOfEnder.h
ItemFenceGate.h
ItemFishingRod.h
ItemFood.h
ItemFoodSeeds.h
ItemFurnace.h
ItemGlazedTerracotta.h
ItemGoldenApple.h
ItemHandler.h
ItemHoe.h
ItemHopper.h
ItemItemFrame.h
ItemJackOLantern.h
ItemLadder.h
ItemLeaves.h
ItemLever.h
ItemLighter.h
ItemLilypad.h
ItemMap.h
@ -37,11 +49,16 @@ target_sources(
ItemMinecart.h
ItemMobHead.h
ItemNetherWart.h
ItemObserver.h
ItemPainting.h
ItemPickaxe.h
ItemPiston.h
ItemPlanks.h
ItemPoisonousPotato.h
ItemPotion.h
ItemPumpkin.h
ItemQuartz.h
ItemRail.h
ItemRawChicken.h
ItemRawFish.h
ItemRedstoneDust.h
@ -51,12 +68,19 @@ target_sources(
ItemSeeds.h
ItemShears.h
ItemShovel.h
ItemSideways.h
ItemSign.h
ItemSlab.h
ItemSnow.h
ItemSoup.h
ItemSpawnEgg.h
ItemSpiderEye.h
ItemStairs.h
ItemSword.h
ItemThrowable.h
ItemTorch.h
ItemTrapdoor.h
ItemTripwireHook.h
ItemVine.h
SimplePlaceableItemHandler.h
)

30
src/Items/ItemAnvil.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockAnvil.h"
class cItemAnvilHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(
a_PlacePosition,
static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType),
cBlockAnvilHandler::YawToMetaData(a_Player.GetYaw()) | static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage << 2)
);
}
};

View File

@ -24,6 +24,32 @@ public:
{
}
private:
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 banner at "no face" and from the bottom:
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
{
return false;
}
if (!TryPlaceBanner(a_Player, a_PlacePosition, a_ClickedBlockFace))
{
return false;
}
a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_HeldItem](cBlockEntity & a_BlockEntity)
{
ASSERT((a_BlockEntity.GetBlockType() == E_BLOCK_STANDING_BANNER) || (a_BlockEntity.GetBlockType() == E_BLOCK_WALL_BANNER));
static_cast<cBannerEntity &>(a_BlockEntity).SetBaseColor(static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage));
return false;
});
return true;
}
@ -36,182 +62,120 @@ public:
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
static bool TryPlaceBanner(cPlayer & a_Player, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace)
{
a_BlockMeta = 0x00;
const double Rotation = a_Player->GetYaw();
const auto Rotation = a_Player.GetYaw();
// Placing on the floor
// Placing on the floor:
if (a_ClickedBlockFace == BLOCK_FACE_TOP)
{
if ((Rotation >= - 11.25f) && (Rotation < 11.25f))
NIBBLETYPE Meta;
if ((Rotation >= -11.25f) && (Rotation < 11.25f))
{
// South
a_BlockMeta |= 0x08;
Meta = 0x08;
}
else if ((Rotation >= 11.25f) && (Rotation < 33.75f))
{
// SouthSouthWest
a_BlockMeta |= 0x09;
Meta = 0x09;
}
else if ((Rotation >= 23.75f) && (Rotation < 56.25f))
{
// SouthWest
a_BlockMeta |= 0x0a;
Meta = 0x0a;
}
else if ((Rotation >= 56.25f) && (Rotation < 78.75f))
{
// WestSouthWest
a_BlockMeta |= 0x0b;
Meta = 0x0b;
}
else if ((Rotation >= 78.75f) && (Rotation < 101.25f))
{
// West
a_BlockMeta |= 0x0c;
Meta = 0x0c;
}
else if ((Rotation >= 101.25f) && (Rotation < 123.75f))
{
// WestNorthWest
a_BlockMeta |= 0x0d;
Meta = 0x0d;
}
else if ((Rotation >= 123.75f) && (Rotation < 146.25f))
{
// NorthWest
a_BlockMeta |= 0x0e;
Meta = 0x0e;
}
else if ((Rotation >= 146.25f) && (Rotation < 168.75f))
{
// NorthNorthWest
a_BlockMeta |= 0x0f;
Meta = 0x0f;
}
else if ((Rotation >= -168.75f) && (Rotation < -146.25f))
{
// NorthNorthEast
a_BlockMeta |= 0x01;
Meta = 0x01;
}
else if ((Rotation >= -146.25f) && (Rotation < -123.75f))
{
// NorthEast
a_BlockMeta |= 0x02;
Meta = 0x02;
}
else if ((Rotation >= -123.75f) && (Rotation < -101.25f))
{
// EastNorthEast
a_BlockMeta |= 0x03;
Meta = 0x03;
}
else if ((Rotation >= -101.25) && (Rotation < -78.75f))
{
// East
a_BlockMeta |= 0x04;
Meta = 0x04;
}
else if ((Rotation >= -78.75) && (Rotation < -56.25f))
{
// EastSouthEast
a_BlockMeta |= 0x05;
Meta = 0x05;
}
else if ((Rotation >= -56.25f) && (Rotation < -33.75f))
{
// SouthEast
a_BlockMeta |= 0x06;
Meta = 0x06;
}
else if ((Rotation >= -33.75f) && (Rotation < -11.25f))
{
// SouthSouthEast
a_BlockMeta |= 0x07;
Meta = 0x07;
}
else // degrees jumping from 180 to -180
{
// North
a_BlockMeta |= 0x00;
Meta = 0x00;
}
a_BlockType = E_BLOCK_STANDING_BANNER;
}
// placing on the sides
else if (a_ClickedBlockFace != BLOCK_FACE_NONE)
{
if (a_ClickedBlockFace == BLOCK_FACE_EAST)
{
a_BlockMeta |= 0x05;
}
else if (a_ClickedBlockFace == BLOCK_FACE_WEST)
{
a_BlockMeta |= 0x04;
}
else if (a_ClickedBlockFace == BLOCK_FACE_NORTH)
{
a_BlockMeta |= 0x02;
}
else // degrees jumping from 180 to -180
{
a_BlockMeta |= 0x03;
}
a_BlockType = E_BLOCK_WALL_BANNER;
}
else
{
return false;
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_STANDING_BANNER, Meta);
}
return true;
}
// We must be placing on the side of a block.
NIBBLETYPE Meta;
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
{
// Cannot place a banner at "no face" and from the bottom:
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
if (a_ClickedBlockFace == BLOCK_FACE_EAST)
{
return true;
Meta = 0x05;
}
else if (a_ClickedBlockFace == BLOCK_FACE_WEST)
{
Meta = 0x04;
}
else if (a_ClickedBlockFace == BLOCK_FACE_NORTH)
{
Meta = 0x02;
}
else // degrees jumping from 180 to -180
{
Meta = 0x03;
}
// Checks if the banner replaced the block
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap());
bool IsReplacingClickedBlock = cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta);
if (IsReplacingClickedBlock)
{
// TODO: There is a bug in the network which prevents the client from receiving the new block entity on placement
// For now the replaced blocks are disabled
return false;
}
// saving the color of the banner in case it's the players last one
NIBBLETYPE Color = static_cast<NIBBLETYPE>(a_EquippedItem.m_ItemDamage);
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_ClickedBlockPos))
{
return false;
}
const auto BannerPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
a_World.DoWithBlockEntityAt(BannerPos, [Color](cBlockEntity & a_BlockEntity)
{
ASSERT((a_BlockEntity.GetBlockType() == E_BLOCK_STANDING_BANNER) || (a_BlockEntity.GetBlockType() == E_BLOCK_WALL_BANNER));
auto & Banner = static_cast<cBannerEntity &>(a_BlockEntity);
Banner.SetBaseColor(Color);
return false;
});
return true;
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_WALL_BANNER, Meta);
}
};

View File

@ -2,8 +2,8 @@
#pragma once
#include "ItemHandler.h"
#include "../World.h"
#include "../Blocks/BlockBed.h"
#include "Blocks/BlockBed.h"
#include "BlockEntities/BedEntity.h"
@ -22,35 +22,51 @@ public:
}
virtual bool IsPlaceable(void) override
{
return true;
}
virtual bool GetBlocksToPlace(
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToPlace
) override
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
const auto BlockMeta = cBlockBedHandler::YawToMetaData(a_Player.GetYaw());
const auto HeadPosition = a_PlacedBlockPos + cBlockBedHandler::MetaDataToDirection(BlockMeta);
const auto HeadPosition = a_PlacePosition + cBlockBedHandler::MetaDataToDirection(BlockMeta);
// Vanilla only allows beds to be placed into air
auto & World = *a_Player.GetWorld();
BLOCKTYPE HeadType;
NIBBLETYPE HeadMeta;
World.GetBlockTypeMeta(HeadPosition, HeadType, HeadMeta);
// Vanilla only allows beds to be placed into air.
// Check if there is empty space for the "head" block:
if (a_World.GetBlock(HeadPosition) != E_BLOCK_AIR)
if (!cBlockHandler::For(HeadType).DoesIgnoreBuildCollision(World, a_HeldItem, HeadPosition, HeadMeta, a_ClickedBlockFace, false))
{
return false;
}
// The "foot", and the "head" block:
a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BED, BlockMeta);
a_BlocksToPlace.emplace_back(HeadPosition, E_BLOCK_BED, BlockMeta | 0x08);
if (
!a_Player.PlaceBlocks(
{
{ a_PlacePosition, E_BLOCK_BED, BlockMeta },
{ HeadPosition, E_BLOCK_BED, static_cast<NIBBLETYPE>(BlockMeta | 0x08) }
})
)
{
return false;
}
auto SetColor = [&a_HeldItem](cBlockEntity & a_BlockEntity)
{
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_BED);
static_cast<cBedEntity &>(a_BlockEntity).SetColor(a_HeldItem.m_ItemDamage);
return false;
};
World.DoWithBlockEntityAt(a_PlacePosition, SetColor);
World.DoWithBlockEntityAt(HeadPosition, SetColor);
return true;
}
virtual bool IsPlaceable(void) override
{
return true;
}
};

View File

@ -24,41 +24,29 @@ public:
virtual bool GetBlocksToPlace(
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToPlace
) override
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
// Can only be placed on dirt:
if ((a_PlacedBlockPos.y <= 0) || !IsBlockTypeOfDirt(a_World.GetBlock(a_PlacedBlockPos.addedY(-1))))
// Needs at least two free blocks to build in:
if (a_PlacePosition.y >= (cChunkDef::Height - 1))
{
return false;
}
// Needs at least two free blocks to build in
if (a_PlacedBlockPos.y >= cChunkDef::Height - 1)
{
return false;
}
auto TopPos = a_PlacedBlockPos.addedY(1);
const auto & World = *a_Player.GetWorld();
const auto TopPos = a_PlacePosition.addedY(1);
BLOCKTYPE TopType;
NIBBLETYPE TopMeta;
a_World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap());
World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(ChunkInterface, TopPos, a_Player, TopMeta))
if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(World, a_HeldItem, TopPos, TopMeta, a_ClickedBlockFace, false))
{
return false;
}
a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
a_BlocksToPlace.emplace_back(TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP);
return true;
return a_Player.PlaceBlocks(
{
{ a_PlacePosition, E_BLOCK_BIG_FLOWER, static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x07) },
{ TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP }
});
}
};

View File

@ -105,8 +105,8 @@ public:
return false;
}
// Remove water / lava block (unless plugins disagree)
if (!a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0))
// Remove water / lava block (unless plugins disagree):
if (!a_Player->PlaceBlock(BlockPos, E_BLOCK_AIR, 0))
{
return false;
}
@ -175,7 +175,7 @@ public:
}
// Place the actual fluid block:
return a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, a_FluidBlock, 0);
return a_Player->PlaceBlock(BlockPos, a_FluidBlock, 0);
}

45
src/Items/ItemButton.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include "ItemHandler.h"
class cItemButtonHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
/** Converts the block face of the neighbor to which the button is attached, to the block meta for this button. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_YP: return 0x5;
case BLOCK_FACE_ZM: return 0x4;
case BLOCK_FACE_ZP: return 0x3;
case BLOCK_FACE_XM: return 0x2;
case BLOCK_FACE_XP: return 0x1;
case BLOCK_FACE_YM: return 0x0;
case BLOCK_FACE_NONE:
{
break;
}
}
UNREACHABLE("Unsupported block face");
}
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace));
}
};

View File

@ -2,8 +2,7 @@
#pragma once
#include "ItemHandler.h"
#include "../BlockInfo.h"
#include "../Blocks/BlockChest.h"
#include "Blocks/BlockChest.h"
@ -21,59 +20,12 @@ public:
{
}
cItemChestHandler(const cItemChestHandler &) = delete;
/** 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,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
if (a_ClickedBlockFace < 0)
{
// Clicked in air
return false;
}
if (!cChunkDef::IsValidHeight(a_ClickedBlockPos.y))
{
// The clicked block is outside the world, ignore this call altogether (#128)
return false;
}
// Check if the block ignores build collision (water, grass etc.):
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap());
Vector3i PlacePos;
if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
{
PlacePos = a_ClickedBlockPos;
}
else
{
PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if (!cChunkDef::IsValidHeight(PlacePos.y))
{
// The block is being placed outside the world, ignore this packet altogether (#128)
return false;
}
// Check if the chest can overwrite the block at PlacePos:
BLOCKTYPE PlaceBlock;
NIBBLETYPE PlaceMeta;
a_World.GetBlockTypeMeta(PlacePos, PlaceBlock, PlaceMeta);
if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(ChunkInterface, PlacePos, a_Player, PlaceMeta))
{
return false;
}
}
// Check that there is at most one single neighbor of the same chest type:
static const Vector3i CrossCoords[] =
{
@ -82,11 +34,14 @@ public:
{ 1, 0, 0},
{ 0, 0, 1},
};
auto & World = *a_Player.GetWorld();
int NeighborIdx = -1;
for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
{
auto NeighborPos = PlacePos + CrossCoords[i];
if (a_World.GetBlock(NeighborPos) != m_ItemType)
const auto NeighborPos = a_PlacePosition + CrossCoords[i];
if (World.GetBlock(NeighborPos) != m_ItemType)
{
continue;
}
@ -100,7 +55,7 @@ public:
// Check that this neighbor is a single chest:
for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++)
{
if (a_World.GetBlock(NeighborPos + CrossCoords[j]) == m_ItemType)
if (World.GetBlock(NeighborPos + CrossCoords[j]) == m_ItemType)
{
// Trying to place next to a dblchest
return false;
@ -111,7 +66,7 @@ public:
// Get the meta of the placed chest; take existing neighbors into account:
BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
NIBBLETYPE Meta;
auto yaw = a_Player.GetYaw();
const auto yaw = a_Player.GetYaw();
switch (NeighborIdx)
{
case 0:
@ -131,13 +86,13 @@ public:
default:
{
// No neighbor, place based on yaw:
Meta = cBlockChestHandler::PlayerYawToMetaData(yaw);
Meta = cBlockChestHandler::YawToMetaData(yaw);
break;
}
} // switch (NeighborIdx)
// Place the new chest:
if (!a_Player.PlaceBlock(PlacePos.x, PlacePos.y, PlacePos.z, ChestBlockType, Meta))
if (!a_Player.PlaceBlock(a_PlacePosition, ChestBlockType, Meta))
{
return false;
}
@ -145,17 +100,9 @@ public:
// Adjust the existing chest, if any:
if (NeighborIdx != -1)
{
a_World.FastSetBlock(PlacePos + CrossCoords[NeighborIdx], ChestBlockType, Meta);
World.FastSetBlock(a_PlacePosition + CrossCoords[NeighborIdx], ChestBlockType, Meta);
}
// Remove the "placed" item from inventory:
if (a_Player.IsGameModeSurvival())
{
a_Player.GetInventory().RemoveOneEquippedItem();
}
return true;
}
private:
cItemChestHandler(const cItemChestHandler &) = delete;
};

View File

@ -24,25 +24,17 @@ public:
virtual bool IsPlaceable(void) override
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return true;
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_INACTIVE_COMPARATOR, cBlockComparatorHandler::YawToMetaData(a_Player.GetYaw()));
}
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
virtual bool IsPlaceable(void) override
{
a_BlockType = E_BLOCK_INACTIVE_COMPARATOR;
a_BlockMeta = cBlockComparatorHandler::YawToMetaData(a_Player->GetYaw());
return true;
}
} ;

View File

@ -25,13 +25,7 @@ public:
virtual bool GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToSet
) override
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
// Vanilla only allows door placement while clicking on the top face of the block below the door:
if (a_ClickedBlockFace != BLOCK_FACE_TOP)
@ -39,19 +33,6 @@ public:
return false;
}
// Door (bottom block) can be placed in Y range of [1, 254]:
if ((a_PlacedBlockPos.y < 1) || (a_PlacedBlockPos.y >= cChunkDef::Height - 2))
{
return false;
}
// The door needs a compatible block below it:
auto BelowPos = a_PlacedBlockPos.addedY(-1);
if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(BelowPos), a_World.GetBlockMeta(BelowPos)))
{
return false;
}
// Get the block type of the door to place:
BLOCKTYPE BlockType;
switch (m_ItemType)
@ -63,22 +44,22 @@ public:
case E_ITEM_JUNGLE_DOOR: BlockType = E_BLOCK_JUNGLE_DOOR; break;
case E_ITEM_DARK_OAK_DOOR: BlockType = E_BLOCK_DARK_OAK_DOOR; break;
case E_ITEM_ACACIA_DOOR: BlockType = E_BLOCK_ACACIA_DOOR; break;
default:
{
ASSERT(!"Unhandled door type");
return false;
}
default: UNREACHABLE("Unhandled door type");
}
// Check the two blocks that will get replaced by the door:
auto UpperBlockPos = a_PlacedBlockPos.addedY(1);
BLOCKTYPE LowerBlockType = a_World.GetBlock(a_PlacedBlockPos);
BLOCKTYPE UpperBlockType = a_World.GetBlock(UpperBlockPos);
if (
!cBlockDoorHandler::CanReplaceBlock(LowerBlockType) ||
!cBlockDoorHandler::CanReplaceBlock(UpperBlockType))
const auto & World = *a_Player.GetWorld();
const auto UpperBlockPosition = a_PlacePosition.addedY(1);
// Check the block that will get replaced by the door:
{
return false;
BLOCKTYPE TopType;
NIBBLETYPE TopMeta;
World.GetBlockTypeMeta(UpperBlockPosition, TopType, TopMeta);
if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(World, a_HeldItem, UpperBlockPosition, TopMeta, a_ClickedBlockFace, false))
{
return false;
}
}
// Get the coords of the neighboring blocks:
@ -86,22 +67,24 @@ public:
Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta);
Vector3i LeftNeighborPos = RelDirToOutside;
LeftNeighborPos.TurnCW();
LeftNeighborPos.Move(a_PlacedBlockPos);
LeftNeighborPos.Move(a_PlacePosition);
Vector3i RightNeighborPos = RelDirToOutside;
RightNeighborPos.TurnCCW();
RightNeighborPos.Move(a_PlacedBlockPos);
RightNeighborPos.Move(a_PlacePosition);
// Decide whether the hinge is on the left (default) or on the right:
NIBBLETYPE UpperBlockMeta = 0x08;
BLOCKTYPE LeftNeighborBlock = a_World.GetBlock(LeftNeighborPos);
BLOCKTYPE RightNeighborBlock = a_World.GetBlock(RightNeighborPos);
BLOCKTYPE LeftNeighborBlock = World.GetBlock(LeftNeighborPos);
BLOCKTYPE RightNeighborBlock = World.GetBlock(RightNeighborPos);
/*
// DEBUG:
FLOGD("Door being placed at {0}", a_PlacedBlockPos);
FLOGD("Door being placed at {0}", a_PlacePosition);
FLOGD("RelDirToOutside: {0}", RelDirToOutside);
FLOGD("Left neighbor at {0}: {1} ({2})", LeftNeighborPos, LeftNeighborBlock, ItemTypeToString(LeftNeighborBlock));
FLOGD("Right neighbor at {0}: {1} ({2})", RightNeighborPos, RightNeighborBlock, ItemTypeToString(RightNeighborBlock));
*/
if (
cBlockDoorHandler::IsDoorBlockType(LeftNeighborBlock) || // The block to the left is a door block
(
@ -116,9 +99,11 @@ public:
}
// Set the blocks:
a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, LowerBlockMeta);
a_BlocksToSet.emplace_back(UpperBlockPos, BlockType, UpperBlockMeta);
return true;
return a_Player.PlaceBlocks(
{
{ a_PlacePosition, BlockType, LowerBlockMeta },
{ UpperBlockPosition, BlockType, UpperBlockMeta }
});
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockDropSpenser.h"
class cItemDropSpenserHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), cBlockDropSpenserHandler::PitchYawToMetaData(a_Player.GetYaw(), a_Player.GetPitch()));
}
};

View File

@ -74,7 +74,7 @@ public:
{
return false;
}
if (a_Player->PlaceBlock(CocoaPos.x, CocoaPos.y, CocoaPos.z, E_BLOCK_COCOA_POD, BlockMeta))
if (a_Player->PlaceBlock(CocoaPos, E_BLOCK_COCOA_POD, BlockMeta))
{
if (a_Player->IsGameModeSurvival())
{

View File

@ -20,40 +20,32 @@ public:
private:
virtual bool IsPlaceable(void) override
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return true;
}
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
{
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
if (!Super::CommitPlacement(a_Player, a_HeldItem, a_PlacePosition, a_ClickedBlockFace, a_CursorPosition))
{
return false;
}
if (a_EquippedItem.IsCustomNameEmpty())
if (a_HeldItem.IsCustomNameEmpty())
{
return true;
}
const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
a_World.DoWithBlockEntityAt(PlacePos, [&a_EquippedItem](cBlockEntity & a_Entity)
a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_HeldItem](cBlockEntity & a_BlockEntity)
{
ASSERT(a_Entity.GetBlockType() == E_BLOCK_ENCHANTMENT_TABLE);
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_ENCHANTMENT_TABLE);
static_cast<cEnchantingTableEntity &>(a_Entity).SetCustomName(a_EquippedItem.m_CustomName);
static_cast<cEnchantingTableEntity &>(a_BlockEntity).SetCustomName(a_HeldItem.m_CustomName);
return false;
});
return true;
}
virtual bool IsPlaceable(void) override
{
return true;
}
} ;

View File

@ -0,0 +1,26 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockEndPortalFrame.h"
class cItemEndPortalFrameHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_END_PORTAL_FRAME, cBlockEndPortalFrameHandler::YawToMetaData(a_Player.GetYaw()));
}
};

View File

@ -0,0 +1,26 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockEnderchest.h"
class cItemEnderchestHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_ENDER_CHEST, cBlockEnderchestHandler::YawToMetaData(a_Player.GetYaw()));
}
};

26
src/Items/ItemFenceGate.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockFenceGate.h"
class cItemFenceGateHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), cBlockFenceGateHandler::YawToMetaData(a_Player.GetYaw()));
}
};

26
src/Items/ItemFurnace.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockFurnace.h"
class cItemFurnaceHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_FURNACE, cBlockFurnaceHandler::YawToMetaData(a_Player.GetYaw()));
}
};

View File

@ -0,0 +1,26 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockGlazedTerracotta.h"
class cItemGlazedTerracottaHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), cBlockGlazedTerracottaHandler::YawToMetaData(a_Player.GetYaw()));
}
};

View File

@ -8,6 +8,7 @@
#include "../Chunk.h"
// Handlers:
#include "ItemAnvil.h"
#include "ItemArmor.h"
#include "ItemAxe.h"
#include "ItemBanner.h"
@ -17,23 +18,34 @@
#include "ItemBottle.h"
#include "ItemBow.h"
#include "ItemBucket.h"
#include "ItemButton.h"
#include "ItemChest.h"
#include "ItemCloth.h"
#include "ItemComparator.h"
#include "ItemCookedFish.h"
#include "ItemDoor.h"
#include "ItemDropSpenser.h"
#include "ItemDye.h"
#include "ItemEmptyMap.h"
#include "ItemEnchantingTable.h"
#include "ItemEndCrystal.h"
#include "ItemEnderchest.h"
#include "ItemEndPortalFrame.h"
#include "ItemEyeOfEnder.h"
#include "ItemFenceGate.h"
#include "ItemFishingRod.h"
#include "ItemFood.h"
#include "ItemFoodSeeds.h"
#include "ItemFurnace.h"
#include "ItemGlazedTerracotta.h"
#include "ItemGoldenApple.h"
#include "ItemHoe.h"
#include "ItemHopper.h"
#include "ItemItemFrame.h"
#include "ItemJackOLantern.h"
#include "ItemLadder.h"
#include "ItemLeaves.h"
#include "ItemLever.h"
#include "ItemLighter.h"
#include "ItemLilypad.h"
#include "ItemMap.h"
@ -41,11 +53,16 @@
#include "ItemMinecart.h"
#include "ItemMobHead.h"
#include "ItemNetherWart.h"
#include "ItemObserver.h"
#include "ItemPainting.h"
#include "ItemPickaxe.h"
#include "ItemPiston.h"
#include "ItemPlanks.h"
#include "ItemPoisonousPotato.h"
#include "ItemPotion.h"
#include "ItemPumpkin.h"
#include "ItemQuartz.h"
#include "ItemRail.h"
#include "ItemRawChicken.h"
#include "ItemRawFish.h"
#include "ItemRedstoneDust.h"
@ -55,13 +72,20 @@
#include "ItemSeeds.h"
#include "ItemShears.h"
#include "ItemShovel.h"
#include "ItemSideways.h"
#include "ItemSign.h"
#include "ItemSlab.h"
#include "ItemSnow.h"
#include "ItemSoup.h"
#include "ItemSpawnEgg.h"
#include "ItemSpiderEye.h"
#include "ItemStairs.h"
#include "ItemSword.h"
#include "ItemThrowable.h"
#include "ItemTorch.h"
#include "ItemTrapdoor.h"
#include "ItemTripwireHook.h"
#include "ItemVine.h"
#include "../Blocks/BlockHandler.h"
#include "SimplePlaceableItemHandler.h"
@ -113,56 +137,87 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
default: return new cItemHandler(a_ItemType);
// Single item per handler, alphabetically sorted:
case E_ITEM_BANNER: return new cItemBannerHandler(a_ItemType);
case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler;
case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
case E_BLOCK_ENCHANTMENT_TABLE: return new cItemEnchantingTableHandler(a_ItemType);
case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler;
case E_BLOCK_PURPUR_SLAB: return new cItemSlabHandler(E_BLOCK_PURPUR_SLAB, E_BLOCK_PURPUR_DOUBLE_SLAB);
case E_BLOCK_RED_SANDSTONE_SLAB: return new cItemSlabHandler(E_BLOCK_RED_SANDSTONE_SLAB, E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB);
case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB);
case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB);
case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
case E_ITEM_BOW: return new cItemBowHandler();
case E_ITEM_BREWING_STAND: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_BREWING_STAND);
case E_ITEM_CAKE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAKE);
case E_ITEM_CAULDRON: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAULDRON);
case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
case E_ITEM_EGG: return new cItemEggHandler();
case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
case E_ITEM_END_CRYSTAL: return new cItemEndCrystalHandler(a_ItemType);
case E_ITEM_EYE_OF_ENDER: return new cItemEyeOfEnderHandler();
case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FLOWER_POT: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_FLOWER_POT);
case E_ITEM_GLASS_BOTTLE: return new cItemBottleHandler();
case E_ITEM_MAP: return new cItemMapHandler();
case E_ITEM_MILK: return new cItemMilkHandler();
case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
case E_ITEM_POTIONS: return new cItemPotionHandler();
case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
case E_ITEM_STRING: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_TRIPWIRE);
case E_ITEM_SUGARCANE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_SUGARCANE);
case E_BLOCK_ACTIVATOR_RAIL: return new cItemRailHandler(a_ItemType);
case E_BLOCK_ANVIL: return new cItemAnvilHandler(a_ItemType);
case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler;
case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
case E_BLOCK_DETECTOR_RAIL: return new cItemRailHandler(a_ItemType);
case E_BLOCK_DISPENSER: return new cItemDropSpenserHandler(a_ItemType);
case E_BLOCK_DROPPER: return new cItemDropSpenserHandler(a_ItemType);
case E_BLOCK_ENCHANTMENT_TABLE: return new cItemEnchantingTableHandler(a_ItemType);
case E_BLOCK_ENDER_CHEST: return new cItemEnderchestHandler(a_ItemType);
case E_BLOCK_END_PORTAL_FRAME: return new cItemEndPortalFrameHandler(a_ItemType);
case E_BLOCK_FURNACE: return new cItemFurnaceHandler(a_ItemType);
case E_BLOCK_HAY_BALE: return new cItemSidewaysHandler(a_ItemType);
case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
case E_BLOCK_HOPPER: return new cItemHopperHandler(a_ItemType);
case E_BLOCK_IRON_TRAPDOOR: return new cItemTrapdoorHandler(a_ItemType);
case E_BLOCK_JACK_O_LANTERN: return new cItemJackOLanternHandler(a_ItemType);
case E_BLOCK_LADDER: return new cItemLadderHandler(a_ItemType);
case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
case E_BLOCK_LEVER: return new cItemLeverHandler(a_ItemType);
case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
case E_BLOCK_LOG: return new cItemSidewaysHandler(a_ItemType);
case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
case E_BLOCK_NEW_LOG: return new cItemSidewaysHandler(a_ItemType);
case E_BLOCK_OBSERVER: return new cItemObserverHandler(a_ItemType);
case E_BLOCK_PISTON: return new cItemPistonHandler(a_ItemType);
case E_BLOCK_PLANKS: return new cItemPlanksHandler(a_ItemType);
case E_BLOCK_POWERED_RAIL: return new cItemRailHandler(a_ItemType);
case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler(a_ItemType);
case E_BLOCK_PURPUR_SLAB: return new cItemSlabHandler(a_ItemType);
case E_BLOCK_QUARTZ_BLOCK: return new cItemQuartzHandler(a_ItemType);
case E_BLOCK_RAIL: return new cItemRailHandler(a_ItemType);
case E_BLOCK_REDSTONE_TORCH_ON: return new cItemTorchHandler(a_ItemType);
case E_BLOCK_RED_SANDSTONE_SLAB: return new cItemSlabHandler(a_ItemType);
case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
case E_BLOCK_SNOW: return new cItemSnowHandler(a_ItemType);
case E_BLOCK_STICKY_PISTON: return new cItemPistonHandler(a_ItemType);
case E_BLOCK_STONE_BUTTON: return new cItemButtonHandler(a_ItemType);
case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(a_ItemType);
case E_BLOCK_TORCH: return new cItemTorchHandler(a_ItemType);
case E_BLOCK_TRAPDOOR: return new cItemTrapdoorHandler(a_ItemType);
case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
case E_BLOCK_TRIPWIRE_HOOK: return new cItemTripwireHookHandler(a_ItemType);
case E_BLOCK_VINES: return new cItemVineHandler(a_ItemType);
case E_BLOCK_WOODEN_BUTTON: return new cItemButtonHandler(a_ItemType);
case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(a_ItemType);
case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
case E_ITEM_BANNER: return new cItemBannerHandler(a_ItemType);
case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
case E_ITEM_BOW: return new cItemBowHandler();
case E_ITEM_BREWING_STAND: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_BREWING_STAND);
case E_ITEM_CAKE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAKE);
case E_ITEM_CAULDRON: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAULDRON);
case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
case E_ITEM_EGG: return new cItemEggHandler();
case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
case E_ITEM_END_CRYSTAL: return new cItemEndCrystalHandler(a_ItemType);
case E_ITEM_EYE_OF_ENDER: return new cItemEyeOfEnderHandler();
case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FLOWER_POT: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_FLOWER_POT);
case E_ITEM_GLASS_BOTTLE: return new cItemBottleHandler();
case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
case E_ITEM_MAP: return new cItemMapHandler();
case E_ITEM_MILK: return new cItemMilkHandler();
case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
case E_ITEM_POTIONS: return new cItemPotionHandler();
case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
case E_ITEM_STRING: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_TRIPWIRE);
case E_ITEM_SUGARCANE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_SUGARCANE);
case E_ITEM_WOODEN_HOE:
case E_ITEM_STONE_HOE:
@ -247,6 +302,54 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
return new cItemMinecartHandler(a_ItemType);
}
case E_BLOCK_ACACIA_FENCE_GATE:
case E_BLOCK_BIRCH_FENCE_GATE:
case E_BLOCK_DARK_OAK_FENCE_GATE:
case E_BLOCK_JUNGLE_FENCE_GATE:
case E_BLOCK_OAK_FENCE_GATE:
case E_BLOCK_SPRUCE_FENCE_GATE:
{
return new cItemFenceGateHandler(a_ItemType);
}
case E_BLOCK_ACACIA_WOOD_STAIRS:
case E_BLOCK_BIRCH_WOOD_STAIRS:
case E_BLOCK_BRICK_STAIRS:
case E_BLOCK_COBBLESTONE_STAIRS:
case E_BLOCK_DARK_OAK_WOOD_STAIRS:
case E_BLOCK_JUNGLE_WOOD_STAIRS:
case E_BLOCK_NETHER_BRICK_STAIRS:
case E_BLOCK_OAK_WOOD_STAIRS:
case E_BLOCK_PURPUR_STAIRS:
case E_BLOCK_QUARTZ_STAIRS:
case E_BLOCK_RED_SANDSTONE_STAIRS:
case E_BLOCK_SANDSTONE_STAIRS:
case E_BLOCK_SPRUCE_WOOD_STAIRS:
case E_BLOCK_STONE_BRICK_STAIRS:
{
return new cItemStairsHandler(a_ItemType);
}
case E_BLOCK_WHITE_GLAZED_TERRACOTTA:
case E_BLOCK_ORANGE_GLAZED_TERRACOTTA:
case E_BLOCK_MAGENTA_GLAZED_TERRACOTTA:
case E_BLOCK_LIGHT_BLUE_GLAZED_TERRACOTTA:
case E_BLOCK_YELLOW_GLAZED_TERRACOTTA:
case E_BLOCK_LIME_GLAZED_TERRACOTTA:
case E_BLOCK_PINK_GLAZED_TERRACOTTA:
case E_BLOCK_GRAY_GLAZED_TERRACOTTA:
case E_BLOCK_LIGHT_GRAY_GLAZED_TERRACOTTA:
case E_BLOCK_CYAN_GLAZED_TERRACOTTA:
case E_BLOCK_PURPLE_GLAZED_TERRACOTTA:
case E_BLOCK_BLUE_GLAZED_TERRACOTTA:
case E_BLOCK_BROWN_GLAZED_TERRACOTTA:
case E_BLOCK_GREEN_GLAZED_TERRACOTTA:
case E_BLOCK_RED_GLAZED_TERRACOTTA:
case E_BLOCK_BLACK_GLAZED_TERRACOTTA:
{
return new cItemGlazedTerracottaHandler(a_ItemType);
}
// Food (please keep alpha-sorted):
case E_ITEM_BAKED_POTATO: return new cItemFoodHandler(a_ItemType, FoodInfo(5, 6));
case E_ITEM_BEETROOT: return new cItemFoodHandler(a_ItemType, FoodInfo(1, 1.2));
@ -347,84 +450,66 @@ cItemHandler::cItemHandler(int a_ItemType)
bool cItemHandler::OnPlayerPlace(
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
)
void cItemHandler::OnPlayerPlace(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_ClickedBlockPosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
{
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{
// Clicked in the air, no placement possible
return false;
return;
}
if (!cChunkDef::IsValidHeight(a_ClickedBlockPos.y))
if (!cChunkDef::IsValidHeight(a_ClickedBlockPosition.y))
{
// The clicked block is outside the world, ignore this call altogether (#128)
return false;
// The clicked block is outside the world, ignore this call altogether (GH #128):
return;
}
const auto & World = *a_Player.GetWorld();
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap());
World.GetBlockTypeMeta(a_ClickedBlockPosition, ClickedBlockType, ClickedBlockMeta);
// Check if the block ignores build collision (water, grass etc.):
auto PlacedBlockPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(World, a_HeldItem, a_ClickedBlockPosition, ClickedBlockMeta, a_ClickedBlockFace, true))
{
// Replace the clicked block:
a_World.DropBlockAsPickups(a_ClickedBlockPos, &a_Player, nullptr);
PlacedBlockPos = a_ClickedBlockPos;
// Try to place the block at the clicked position:
if (!CommitPlacement(a_Player, a_HeldItem, a_ClickedBlockPosition, a_ClickedBlockFace, a_CursorPosition))
{
// The placement failed, the blocks have already been re-sent, re-send inventory:
a_Player.GetInventory().SendEquippedSlot();
return;
}
}
else
{
if (!cChunkDef::IsValidHeight(PlacedBlockPos.y))
const auto PlacedPosition = AddFaceDirection(a_ClickedBlockPosition, a_ClickedBlockFace);
if (!cChunkDef::IsValidHeight(PlacedPosition.y))
{
// The block is being placed outside the world, ignore this packet altogether (#128)
return false;
// The block is being placed outside the world, ignore this packet altogether (GH #128):
return;
}
NIBBLETYPE PlaceMeta;
BLOCKTYPE PlaceBlock;
a_World.GetBlockTypeMeta(PlacedBlockPos, PlaceBlock, PlaceMeta);
World.GetBlockTypeMeta(PlacedPosition, PlaceBlock, PlaceMeta);
// Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
// No need to do combinability (dblslab) checks, client will do that here.
if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(ChunkInterface, PlacedBlockPos, a_Player, PlaceMeta))
if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(World, a_HeldItem, PlacedPosition, PlaceMeta, a_ClickedBlockFace, false))
{
// Tried to place a block into another?
// Happens when you place a block aiming at side of block with a torch on it or stem beside it
return false;
return;
}
}
// Get all the blocks to place:
sSetBlockVector blocks;
if (!GetBlocksToPlace(a_World, a_Player, a_EquippedItem, PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, blocks))
{
// Handler refused the placement, send that information back to the client:
for (const auto & blk: blocks)
// Try to place the block:
if (!CommitPlacement(a_Player, a_HeldItem, PlacedPosition, a_ClickedBlockFace, a_CursorPosition))
{
const auto & AbsPos = blk.GetAbsolutePos();
a_World.SendBlockTo(AbsPos, a_Player);
// The placement failed, the blocks have already been re-sent, re-send inventory:
a_Player.GetInventory().SendEquippedSlot();
return;
}
a_World.SendBlockTo(PlacedBlockPos, a_Player);
a_Player.GetInventory().SendEquippedSlot();
return false;
}
// Try to place the blocks:
if (!a_Player.PlaceBlocks(blocks))
{
// The placement failed, the blocks have already been re-sent, re-send inventory:
a_Player.GetInventory().SendEquippedSlot();
return false;
}
// Remove the "placed" item:
@ -432,29 +517,6 @@ bool cItemHandler::OnPlayerPlace(
{
a_Player.GetInventory().RemoveOneEquippedItem();
}
return true;
}
bool cItemHandler::GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToSet
)
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, BlockType, BlockMeta))
{
return false;
}
a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, BlockMeta);
return true;
}
@ -821,34 +883,6 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
bool cItemHandler::GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
const Vector3i a_PlacedBlockPos, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
)
{
ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers
if (m_ItemType >= 256)
{
LOGERROR("%s: Item %d is not eligible for direct block placement!", __FUNCTION__, m_ItemType);
return false;
}
cChunkInterface ChunkInterface(a_World->GetChunkMap());
return cBlockHandler::For(static_cast<BLOCKTYPE>(m_ItemType)).GetPlacementBlockTypeMeta(
ChunkInterface, *a_Player,
a_PlacedBlockPos, a_ClickedBlockFace,
a_CursorPos,
a_BlockType, a_BlockMeta
);
}
bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
{
auto FoodInfo = GetFoodInfo(a_Item);
@ -873,3 +907,19 @@ float cItemHandler::GetBlockBreakingStrength(BLOCKTYPE a_Block)
{
return 1.0f;
}
bool cItemHandler::CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
{
ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers.
// By default, all blocks can be placed and the meta is copied over from the item's damage value:
return a_Player.PlaceBlock(
a_PlacePosition,
static_cast<BLOCKTYPE>(m_ItemType),
static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x0f)
);
}

View File

@ -9,6 +9,7 @@
// fwd:
class cChunk;
class cWorld;
class cPlayer;
class cBlockPluginInterface;
@ -36,55 +37,14 @@ public:
/** Force virtual destructor */
virtual ~cItemHandler() {}
/** Called when the player tries to place the item (right mouse button, IsPlaceable() == true).
a_ClickedBlockPos is the (neighbor) block that has been clicked to place this item.
a_ClickedBlockFace is the face of the neighbor that has been clicked to place this item.
a_CursorPos is the position of the player's cursor within a_ClickedBlockFace.
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(
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
);
/** Called from OnPlayerPlace() to determine the blocks that the current placement operation should set.
a_PlacedBlockPos points to the location where the new block should be set.
a_ClickedBlockFace is the block face of the neighbor that was clicked to place this block.
a_CursorPos is the position of the mouse cursor within the clicked (neighbor's) block face.
The blocks in a_BlocksToPlace will be sent through cPlayer::PlaceBlocks() after returning from this function.
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,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToPlace
);
/** 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(
cWorld * a_World, cPlayer * a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
);
If the block placement is refused inside this call, it will automatically revert the client-side changes. */
void OnPlayerPlace(cPlayer & a_Player, const cItem & a_HeldItem, Vector3i a_ClickedBlockPosition, eBlockFace a_ClickedBlockFace, Vector3i a_CursorPosition);
/** Called when the player tries to use the item (right mouse button).
Descendants can return false to abort the usage (default behavior). */
@ -97,7 +57,6 @@ public:
eBlockFace a_ClickedBlockFace
);
/** Called when the client sends the SHOOT status in the lclk packet (releasing the bow). */
virtual void OnItemShoot(cPlayer *, const Vector3i a_BlockPos, eBlockFace a_BlockFace)
{
@ -181,9 +140,16 @@ public:
static void Deinit();
protected:
int m_ItemType;
static cItemHandler * CreateItemHandler(int m_ItemType);
/** Performs the actual placement of this placeable item.
The descendant handler should call a_Player.PlaceBlock(s) supplying correct values for the newly placed block.
The default handler uses the stored block type and meta copied from the lowest 4 bits of the player's equipped item's damage value.
Handlers return what a_Player.PlaceBlock(s) returns, indicating whether the placement was successful. */
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, Vector3i a_PlacePosition, eBlockFace a_ClickedBlockFace, Vector3i a_CursorPosition);
static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1];
static bool m_HandlerInitialized; // used to detect if the itemhandlers are initialized
};

40
src/Items/ItemHopper.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "ItemHandler.h"
class cItemHopperHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_BOTTOM: return E_META_HOPPER_FACING_YM;
case BLOCK_FACE_TOP: return E_META_HOPPER_FACING_YM;
case BLOCK_FACE_EAST: return E_META_HOPPER_FACING_XM;
case BLOCK_FACE_NORTH: return E_META_HOPPER_FACING_ZP;
case BLOCK_FACE_SOUTH: return E_META_HOPPER_FACING_ZM;
case BLOCK_FACE_WEST: return E_META_HOPPER_FACING_XP;
default: UNREACHABLE("Unsupported block face");
}
}
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_HOPPER, BlockFaceToMetaData(a_ClickedBlockFace));
}
};

View File

@ -0,0 +1,27 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockPumpkin.h"
class cItemJackOLanternHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
// Re-use the pumpkin converter for lanterns:
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_JACK_O_LANTERN, cBlockPumpkinHandler::YawToMetaData(a_Player.GetYaw()));
}
};

77
src/Items/ItemLadder.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockLadder.h"
class cItemLadderHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
/** Converts the block face of the neighbor to which the ladder is attached to the ladder block's meta. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
{
switch (a_NeighborBlockFace)
{
case BLOCK_FACE_ZM: return 0x2;
case BLOCK_FACE_ZP: return 0x3;
case BLOCK_FACE_XM: return 0x4;
case BLOCK_FACE_XP: return 0x5;
case BLOCK_FACE_YM:
case BLOCK_FACE_YP: return 0x2;
default: UNREACHABLE("Unsupported neighbor block face");
}
}
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
const auto & World = *a_Player.GetWorld();
const auto ClickedBlockType = World.GetBlock(AddFaceDirection(a_PlacePosition, a_ClickedBlockFace, true));
// Try finding a suitable neighbor block face for the ladder; start with the given one:
if (!cBlockLadderHandler::CanBePlacedOn(ClickedBlockType, a_ClickedBlockFace))
{
// Couldn't be placed on whatever face was clicked, last ditch resort - find another face:
a_ClickedBlockFace = FindSuitableFace(World, a_PlacePosition);
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{
// No attachable face found - don't place the ladder:
return false;
}
}
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_LADDER, BlockFaceToMetaData(a_ClickedBlockFace));
}
/** Returns a suitable neighbor's blockface to place the ladder at the specified position.
Returns BLOCK_FACE_NONE on failure. */
static eBlockFace FindSuitableFace(const cWorld & a_World, const Vector3i a_Position)
{
for (const auto Face : { BLOCK_FACE_ZM, BLOCK_FACE_XP, BLOCK_FACE_ZP, BLOCK_FACE_XM }) // Loop through all faces in specific order.
{
// The direction of Face is relative to the direction the ladder faces.
// This is the position, computed inverted, that such a ladder would attach to.
const auto NeighborPosition = AddFaceDirection(a_Position, Face, true);
if (cBlockLadderHandler::CanBePlacedOn(a_World.GetBlock(NeighborPosition), Face))
{
return Face;
}
}
return BLOCK_FACE_NONE;
}
};

View File

@ -23,23 +23,13 @@ public:
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
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
bool res = Super::GetPlacementBlockTypeMeta(
a_World, a_Player,
a_PlacedBlockPos,
a_ClickedBlockFace,
a_CursorPos,
a_BlockType, a_BlockMeta
return a_Player.PlaceBlock(
a_PlacePosition,
static_cast<BLOCKTYPE>(m_ItemType),
static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage | 0x4) // 0x4 bit set means this is a player-placed leaves block, not to be decayed.
);
a_BlockMeta = a_BlockMeta | 0x4; // 0x4 bit set means this is a player-placed leaves block, not to be decayed
return res;
}
} ;

43
src/Items/ItemLever.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "ItemHandler.h"
class cItemLeverHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
/** Converts the block face of the neighbor to which the lever is attached to the lever block's meta. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
// Determine lever direction:
switch (a_BlockFace)
{
case BLOCK_FACE_YP: return 0x6;
case BLOCK_FACE_XP: return 0x1;
case BLOCK_FACE_XM: return 0x2;
case BLOCK_FACE_ZP: return 0x3;
case BLOCK_FACE_ZM: return 0x4;
case BLOCK_FACE_YM: return 0x0;
case BLOCK_FACE_NONE: break;
}
UNREACHABLE("Unsupported block face");
}
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace));
}
};

View File

@ -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<eMobHeadType>(a_EquippedItem.m_ItemDamage);
auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace);
const auto HeadType = static_cast<eMobHeadType>(a_HeldItem.m_ItemDamage);
const auto BlockMeta = static_cast<NIBBLETYPE>(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<Vector3i, 9> 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;
}
} ;

View File

@ -24,37 +24,23 @@ public:
virtual bool IsPlaceable(void) override
{
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
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
// Only allow planting nether wart onto the top side of the block:
if (a_ClickedBlockFace != BLOCK_FACE_TOP)
{
return false;
return true;
}
// Only allow placement on soulsand
if ((a_PlacedBlockPos.y < 1) || (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_SOULSAND))
{
return false;
}
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_NETHER_WART, 0);
}
a_BlockMeta = 0;
a_BlockType = E_BLOCK_NETHER_WART;
virtual bool IsPlaceable(void) override
{
return true;
}
} ;

26
src/Items/ItemObserver.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockObserver.h"
class cItemObserverHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_OBSERVER, cBlockObserverHandler::PitchYawToMetaData(a_Player.GetYaw(), a_Player.GetPitch()));
}
};

26
src/Items/ItemPiston.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockPiston.h"
class cItemPistonHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), cBlockPistonHandler::PitchYawToMetaData(a_Player.GetYaw(), a_Player.GetPitch()));
}
};

25
src/Items/ItemPlanks.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "ItemHandler.h"
class cItemPlanksHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage));
}
};

View File

@ -2,6 +2,7 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockPumpkin.h"
@ -14,35 +15,22 @@ class cItemPumpkinHandler:
public:
cItemPumpkinHandler():
Super(E_BLOCK_PUMPKIN)
{
}
using Super::Super;
private:
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
{
// First try spawning a snow golem or an iron golem:
auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if (TrySpawnGolem(a_World, a_Player, PlacePos))
if (TrySpawnGolem(a_Player, a_PlacePosition))
{
// The client thinks that they placed the pumpkin, let them know it's been replaced:
a_Player.SendBlocksAround(PlacePos.x, PlacePos.y, PlacePos.z);
a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z);
return true;
}
// No golem at these coords, place the block normally:
return Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos);
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_PUMPKIN, cBlockPumpkinHandler::YawToMetaData(a_Player.GetYaw()));
}
@ -51,22 +39,24 @@ public:
/** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */
bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, const Vector3i a_PumpkinPos)
bool TrySpawnGolem(cPlayer & a_Player, const Vector3i a_PumpkinPos)
{
// A golem can't form with a pumpkin below level 2 or above level 255
// A golem can't form with a pumpkin below level 2 or above level 255:
if ((a_PumpkinPos.y < 2) || (a_PumpkinPos.y >= cChunkDef::Height))
{
return false;
}
auto & World = *a_Player.GetWorld();
// Decide which golem to try spawning based on the block below the placed pumpkin:
switch (a_World.GetBlock(a_PumpkinPos.addedY(-1)))
switch (World.GetBlock(a_PumpkinPos.addedY(-1)))
{
case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_PumpkinPos);
case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_PumpkinPos);
case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(World, a_Player, a_PumpkinPos);
case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(World, a_Player, a_PumpkinPos);
default:
{
// No golem here
// No golem here:
return false;
}
}
@ -91,11 +81,14 @@ public:
}
// Try to place air blocks where the original recipe blocks were:
sSetBlockVector AirBlocks;
AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
AirBlocks.emplace_back(a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0); // Torso
AirBlocks.emplace_back(a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0); // Legs
if (!a_Player.PlaceBlocks(AirBlocks))
if (
!a_Player.PlaceBlocks(
{
{ a_PumpkinPos, E_BLOCK_AIR, 0 }, // Head
{ a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0 }, // Torso
{ a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0 } // Legs
})
)
{
return false;
}
@ -143,13 +136,16 @@ public:
}
// Try to place air blocks where the original recipe blocks were:
sSetBlockVector AirBlocks;
AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
AirBlocks.emplace_back(BodyPos, E_BLOCK_AIR, 0); // Torso
AirBlocks.emplace_back(BodyPos.addedY(-1), E_BLOCK_AIR, 0); // Legs
AirBlocks.emplace_back(BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
AirBlocks.emplace_back(BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
if (!a_Player.PlaceBlocks(AirBlocks))
if (
!a_Player.PlaceBlocks(
{
{ a_PumpkinPos, E_BLOCK_AIR, 0 }, // Head
{ BodyPos, E_BLOCK_AIR, 0 }, // Torso
{ BodyPos.addedY(-1), E_BLOCK_AIR, 0 }, // Legs
{ BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0 }, // Arm
{ BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0 } // Arm
})
)
{
return false;
}

60
src/Items/ItemQuartz.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include "ItemHandler.h"
class cItemQuartzHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
/** Converts the block face of the pillar block's "base" to the block's metadata. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_YM:
case BLOCK_FACE_YP:
{
return E_META_QUARTZ_PILLAR; // Top or bottom.
}
case BLOCK_FACE_ZP:
case BLOCK_FACE_ZM:
{
return 0x4; // North or south.
}
case BLOCK_FACE_XP:
case BLOCK_FACE_XM:
{
return 0x3; // East or west.
}
default: UNREACHABLE("Unsupported block face");
}
}
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
const auto Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
// Pillar block needs additional direction in the metadata:
if (Meta == E_META_QUARTZ_PILLAR)
{
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_QUARTZ_BLOCK, BlockFaceToMetaData(a_ClickedBlockFace));
}
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_QUARTZ_BLOCK, Meta);
}
};

28
src/Items/ItemRail.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockRail.h"
class cItemRailHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
cChunkInterface ChunkInterface(a_Player.GetWorld()->GetChunkMap());
const auto RailType = static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType);
return a_Player.PlaceBlock(a_PlacePosition, RailType, cBlockRailHandler::FindMeta(ChunkInterface, a_PlacePosition, RailType));
}
};

View File

@ -23,69 +23,19 @@ public:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_REDSTONE_WIRE, 0);
}
virtual bool IsPlaceable(void) override
{
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
{
// Check the block below, if it supports dust on top of it:
auto UnderPos = a_PlacedBlockPos.addedY(-1);
if (UnderPos.y < 0)
{
return false;
}
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
if (!a_World->GetBlockTypeMeta(UnderPos, BlockType, BlockMeta))
{
return false;
}
if (!IsBlockTypeUnderSuitable(BlockType, BlockMeta))
{
return false;
}
a_BlockType = E_BLOCK_REDSTONE_WIRE;
a_BlockMeta = 0;
return true;
}
/** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */
static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
if (cBlockInfo::FullyOccupiesVoxel(a_BlockType))
{
return true;
}
switch (a_BlockType)
{
case E_BLOCK_RED_SANDSTONE_SLAB:
case E_BLOCK_WOODEN_SLAB:
case E_BLOCK_STONE_SLAB:
{
// Slabs can support redstone if they're upside down:
return ((a_BlockMeta & 0x08) != 0);
}
}
return false;
}
} ;

View File

@ -24,25 +24,17 @@ public:
virtual bool IsPlaceable() override
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return true;
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_REDSTONE_REPEATER_OFF, cBlockRedstoneRepeaterHandler::YawToMetaData(a_Player.GetYaw()));
}
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
virtual bool IsPlaceable() override
{
a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF;
a_BlockMeta = cBlockRedstoneRepeaterHandler::YawToMetaData(a_Player->GetYaw());
return true;
}
} ;

View File

@ -20,30 +20,12 @@ public:
}
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
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
bool res = Super::GetPlacementBlockTypeMeta(
a_World, a_Player,
a_PlacedBlockPos, a_ClickedBlockFace,
a_CursorPos,
a_BlockType, a_BlockMeta
return a_Player.PlaceBlock(
a_PlacePosition,
static_cast<BLOCKTYPE>(m_ItemType),
static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x07) // Allow only the lowest 3 bits (top bit is for growth).
);
// Allow only the lowest 3 bits (top bit is for growth):
a_BlockMeta = a_BlockMeta & 0x07;
return res;
}
} ;

View File

@ -25,47 +25,38 @@ public:
virtual bool IsPlaceable(void) override
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return true;
// Only allow planting seeds from the top side of the block:
if (a_ClickedBlockFace != BLOCK_FACE_TOP)
{
return false;
}
BLOCKTYPE BlockType;
// Get the produce block based on the seed item:
switch (m_ItemType)
{
case E_ITEM_BEETROOT_SEEDS: BlockType = E_BLOCK_BEETROOTS; break;
case E_ITEM_CARROT: BlockType = E_BLOCK_CARROTS; break;
case E_ITEM_MELON_SEEDS: BlockType = E_BLOCK_MELON_STEM; break;
case E_ITEM_POTATO: BlockType = E_BLOCK_POTATOES; break;
case E_ITEM_PUMPKIN_SEEDS: BlockType = E_BLOCK_PUMPKIN_STEM; break;
case E_ITEM_SEEDS: BlockType = E_BLOCK_CROPS; break;
default: UNREACHABLE("Unsupported seed type");
}
return a_Player.PlaceBlock(a_PlacePosition, BlockType, 0);
}
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
virtual bool IsPlaceable(void) override
{
// Only allow planting seeds from the top side of the block:
if ((a_ClickedBlockFace != BLOCK_FACE_TOP) || (a_PlacedBlockPos.y <= 0))
{
return false;
}
// Only allow placement on farmland
if (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_FARMLAND)
{
return false;
}
// Get the produce block based on the seed item:
a_BlockMeta = 0;
switch (m_ItemType)
{
case E_ITEM_BEETROOT_SEEDS: a_BlockType = E_BLOCK_BEETROOTS; return true;
case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true;
case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true;
case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true;
case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true;
case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true;
default: a_BlockType = E_BLOCK_AIR; return true;
}
return true;
}
} ;

53
src/Items/ItemSideways.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockSideways.h"
/** Handler for blocks that have 3 orientations (hay bale, log), specified by the upper 2 bits in meta.
Handles setting the correct orientation on placement.
Additionally supports the metadata specifying block sub-type in its lower 2 bits. */
class cItemSidewaysHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace, NIBBLETYPE a_Meta)
{
switch (a_BlockFace)
{
case BLOCK_FACE_YM:
case BLOCK_FACE_YP:
{
return a_Meta; // Top or bottom, just return original.
}
case BLOCK_FACE_ZP:
case BLOCK_FACE_ZM:
{
return a_Meta | 0x8; // North or south.
}
case BLOCK_FACE_XP:
case BLOCK_FACE_XM:
{
return a_Meta | 0x4; // East or west.
}
default: UNREACHABLE("Unsupported block face");
}
}
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace, static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage)));
}
};

View File

@ -3,8 +3,6 @@
#include "ItemHandler.h"
#include "../World.h"
#include "../Blocks/BlockSignPost.h"
#include "../Blocks/BlockWallSign.h"
#include "../ClientHandle.h"
@ -18,77 +16,68 @@ class cItemSignHandler:
public:
cItemSignHandler(int a_ItemType):
Super(a_ItemType)
using Super::Super;
private:
/** Converts the block face of the neighbor to which the wallsign is attached to the wallsign block's meta. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
{
switch (a_NeighborBlockFace)
{
case BLOCK_FACE_ZM: return 0x02;
case BLOCK_FACE_ZP: return 0x03;
case BLOCK_FACE_XM: return 0x04;
case BLOCK_FACE_XP: return 0x05;
case BLOCK_FACE_NONE:
case BLOCK_FACE_YP:
case BLOCK_FACE_YM:
{
break;
}
}
return 0x02;
}
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
{
// Check if placing on something ignoring build collision to edit the correct sign later on:
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap());
bool IsReplacingClickedBlock = cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta);
// If the regular placement doesn't work, do no further processing:
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
if (a_ClickedBlockFace == BLOCK_FACE_TOP)
{
if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_SIGN_POST, RotationToMetaData(a_Player.GetYaw())))
{
return false;
}
}
else if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_WALLSIGN, BlockFaceToMetaData(a_ClickedBlockFace)))
{
return false;
}
// Use IsReplacingClickedBlock to make sure we will edit the right sign:
auto SignPos = IsReplacingClickedBlock ? a_ClickedBlockPos : AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
// After successfully placing the sign, open the sign editor for the player:
a_Player.GetClientHandle()->SendEditSign(SignPos.x, SignPos.y, SignPos.z);
a_Player.GetClientHandle()->SendEditSign(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z);
return true;
}
virtual bool IsPlaceable(void) override
{
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
/** Converts the (player) rotation to placed-signpost block meta. */
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
if (a_ClickedBlockFace == BLOCK_FACE_TOP)
a_Rotation += 180 + (180.f / 16); // So it's not aligned with axis.
if (a_Rotation > 360)
{
a_BlockMeta = cBlockSignPostHandler::RotationToMetaData(a_Player->GetYaw());
a_BlockType = E_BLOCK_SIGN_POST;
a_Rotation -= 360;
}
else
{
a_BlockMeta = cBlockWallSignHandler::BlockFaceToMetaData(a_ClickedBlockFace);
a_BlockType = E_BLOCK_WALLSIGN;
}
return true;
a_Rotation = (a_Rotation / 360) * 16;
return static_cast<char>(a_Rotation) % 16;
}
} ;

View File

@ -14,95 +14,87 @@ class cItemSlabHandler:
public:
/** Creates a new handler for the specified slab item type.
Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */
cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType):
Super(a_ItemType),
m_DoubleSlabBlockType(a_DoubleSlabBlockType)
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
}
// Confer BlockSlab.h, which we're in cahoots with to make the below logic work.
// cItemHandler overrides:
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
{
// If clicking a slab, try combining it into a double-slab:
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
if (
(ClickedBlockType == m_ItemType) && // Placing the same slab material
((ClickedBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
)
// If clicking a slab, combine it into a double-slab:
if (cBlockSlabHandler::IsAnySlabType(a_Player.GetWorld()->GetBlock(a_PlacePosition)))
{
if (
((a_ClickedBlockFace == BLOCK_FACE_TOP) && ((ClickedBlockMeta & 0x08) == 0)) || // Top side of a bottom-half-slab
((a_ClickedBlockFace == BLOCK_FACE_BOTTOM) && ((ClickedBlockMeta & 0x08) != 0)) // Bottom side of a top-half-slab
)
{
if (!a_Player.PlaceBlock(a_ClickedBlockPos.x, a_ClickedBlockPos.y, a_ClickedBlockPos.z, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07))
{
return false;
}
if (a_Player.IsGameModeSurvival())
{
a_Player.GetInventory().RemoveOneEquippedItem();
}
return true;
}
}
// If there's already a slab in the destination, combine it into a double-slab:
auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
BLOCKTYPE PlaceBlockType;
NIBBLETYPE PlaceBlockMeta;
a_World.GetBlockTypeMeta(PlacePos, PlaceBlockType, PlaceBlockMeta);
if (
(PlaceBlockType == m_ItemType) && // Placing the same slab material
((PlaceBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
)
{
if (!a_Player.PlaceBlock(PlacePos.x, PlacePos.y, PlacePos.z, m_DoubleSlabBlockType, PlaceBlockMeta & 0x07))
if (!a_Player.PlaceBlock(a_PlacePosition, GetDoubleSlabType(static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType)), static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage)))
{
return false;
}
if (a_Player.IsGameModeSurvival())
{
a_Player.GetInventory().RemoveOneEquippedItem();
}
a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z, 2); // (see below)
return true;
}
// The slabs didn't combine, use the default handler to place the slab:
bool res = Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos);
/*
The client has a bug when a slab replaces snow and there's a slab above it.
The client then combines the slab above, rather than replacing the snow.
We send the block above the currently placed block back to the client to fix the bug.
Ref.: https://forum.cuberite.org/thread-434-post-17388.html#pid17388
*/
if ((a_ClickedBlockFace == BLOCK_FACE_TOP) && (a_ClickedBlockPos.y < cChunkDef::Height - 1))
// Set the correct metadata based on player equipped item:
if (!a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), FaceToMetaData(static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage), a_ClickedBlockFace, a_CursorPosition)))
{
auto AbovePos = a_ClickedBlockPos.addedY(1);
a_Player.SendBlocksAround(AbovePos.x, AbovePos.y, AbovePos.z, 1);
return false;
}
return res;
/* This is a workaround for versions < 1.13, where the client combines a slab in the
direction of the clicked block face of a block ignoring build collision, rather than replacing said block.
Resend blocks to the client to fix the bug.
Ref.: https://forum.cuberite.org/thread-434-post-17388.html#pid17388 */
a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z, 2);
return true;
}
protected:
static NIBBLETYPE FaceToMetaData(const NIBBLETYPE a_BaseMeta, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
{
switch (a_ClickedBlockFace)
{
case BLOCK_FACE_TOP:
{
// Bottom half slab block:
return a_BaseMeta & 0x07;
}
case BLOCK_FACE_BOTTOM:
{
// Top half slab block:
return a_BaseMeta | 0x08;
}
case BLOCK_FACE_EAST:
case BLOCK_FACE_NORTH:
case BLOCK_FACE_SOUTH:
case BLOCK_FACE_WEST:
{
if (a_CursorPosition.y > 7)
{
// Cursor at top half of block, place top slab:
return a_BaseMeta | 0x08;
}
else
{
// Cursor at bottom half of block, place bottom slab:
return a_BaseMeta & 0x07;
}
}
default: UNREACHABLE("Unhandled block face");
}
}
/** The block type to use when the slab combines into a doubleslab block. */
BLOCKTYPE m_DoubleSlabBlockType;
/** Converts the single-slab blocktype to its equivalent double-slab blocktype. */
static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType)
{
switch (a_SingleSlabBlockType)
{
case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB;
case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB;
case E_BLOCK_RED_SANDSTONE_SLAB: return E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB;
case E_BLOCK_PURPUR_SLAB: return E_BLOCK_PURPUR_DOUBLE_SLAB;
}
UNREACHABLE("Unhandled slab type");
}
};

41
src/Items/ItemSnow.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include "ItemHandler.h"
class cItemSnowHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
BLOCKTYPE Block;
NIBBLETYPE Meta;
a_Player.GetWorld()->GetBlockTypeMeta(a_PlacePosition, Block, Meta);
// Check if incrementing existing snow height:
if (Block == E_BLOCK_SNOW)
{
ASSERT(Meta < 7); // BlockSnow.h ensures that if we replace a snow layer, it won't be at max height.
Meta++;
}
else
{
// First time placement:
Meta = 0;
}
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_SNOW, Meta);
}
};

47
src/Items/ItemStairs.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockStairs.h"
class cItemStairsHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
NIBBLETYPE Meta = cBlockStairsHandler::YawToMetaData(a_Player.GetYaw());
switch (a_ClickedBlockFace)
{
case BLOCK_FACE_TOP: break;
case BLOCK_FACE_BOTTOM: Meta |= 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block.
case BLOCK_FACE_EAST:
case BLOCK_FACE_NORTH:
case BLOCK_FACE_SOUTH:
case BLOCK_FACE_WEST:
{
// When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block:
if (a_CursorPosition.y > 8)
{
Meta |= 0x4;
}
break;
}
default: UNREACHABLE("Unhandled block face");
}
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), Meta);
}
};

82
src/Items/ItemTorch.h Normal file
View File

@ -0,0 +1,82 @@
#pragma once
#include "ItemHandler.h"
#include "Blocks/BlockTorch.h"
class cItemTorchHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
using Super::Super;
private:
/** Converts the block face of the neighbor to which the torch is attached, to the torch block's meta. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
{
case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR;
case BLOCK_FACE_EAST: return E_META_TORCH_EAST;
case BLOCK_FACE_WEST: return E_META_TORCH_WEST;
case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH;
case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH;
default: UNREACHABLE("Unsupported block face");
}
}
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
{
const auto & World = *a_Player.GetWorld();
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
World.GetBlockTypeMeta(AddFaceDirection(a_PlacePosition, a_ClickedBlockFace, true), ClickedBlockType, ClickedBlockMeta);
// Try finding a suitable neighbor block face for the torch; start with the given one:
if (!cBlockTorchHandler::CanBePlacedOn(ClickedBlockType, ClickedBlockMeta, a_ClickedBlockFace))
{
// Couldn't be placed on whatever face was clicked, last ditch resort - find another face:
a_ClickedBlockFace = FindSuitableFace(World, a_PlacePosition);
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{
// No attachable face found - don't place the torch:
return false;
}
}
return a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), BlockFaceToMetaData(a_ClickedBlockFace));
}
/** Returns a suitable neighbor's blockface to place the torch at the specified position.
Returns BLOCK_FACE_NONE on failure. */
static eBlockFace FindSuitableFace(const cWorld & a_World, const Vector3i a_Position)
{
for (const auto Face : { BLOCK_FACE_ZM, BLOCK_FACE_XP, BLOCK_FACE_ZP, BLOCK_FACE_XM, BLOCK_FACE_YP }) // Loop through all faces in specific order.
{
// The direction of Face is relative to the direction the torch faces.
// This is the position, computed inverted, that such a torch would attach to.
const auto NeighborPosition = AddFaceDirection(a_Position, Face, true);
BLOCKTYPE NeighborBlockType;
NIBBLETYPE NeighborBlockMeta;
a_World.GetBlockTypeMeta(NeighborPosition, NeighborBlockType, NeighborBlockMeta);
if (cBlockTorchHandler::CanBePlacedOn(NeighborBlockType, NeighborBlockMeta, Face))
{
return Face;
}
}
return BLOCK_FACE_NONE;
}
};

Some files were not shown because too many files have changed in this diff Show More