1
0
Fork 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.", 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 = IsOneHitDig =
{ {
IsStatic = true, 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) bool cBlockInfo::IsOneHitDig(const BLOCKTYPE Block)
{ {
#ifdef __clang__ #ifdef __clang__

View File

@ -24,6 +24,10 @@ public:
/** Does this block fully occupy its voxel - is it a 'full' block? */ /** Does this block fully occupy its voxel - is it a 'full' block? */
static bool FullyOccupiesVoxel(BLOCKTYPE 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? /** Is a block destroyed after a single hit?
Warning: IsOneHitDig does not take into account enchantments / status effects / swim state / floating state 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. 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 virtual bool IsUseable() const override
{ {
return true; 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 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 "../World.h"
#include "../BoundingBox.h" #include "../BoundingBox.h"
#include "../Mobs/Monster.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 cItems cBlockBedHandler::ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const
{ {
// Drops handled by the block entity: // 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 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: 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)) if (IsMetaTopPart(a_Meta))
{ {
BLOCKTYPE BottomType; BLOCKTYPE BottomType;
if ( if (
(a_Pos.y < 1) || (a_Position.y < 1) ||
!a_ChunkInterface.GetBlockTypeMeta(a_Pos - Vector3i(0, 1, 0), BottomType, a_Meta) || !a_World.GetBlockTypeMeta(a_Position - Vector3i(0, 1, 0), BottomType, a_Meta) ||
(BottomType != E_BLOCK_BIG_FLOWER) (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) // 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 false;
}
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BlockType, BlockMeta);
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. */ /** 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) 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_Position, BlockMetaDataToBlockFace(a_Meta), true);
auto SupportRelPos = AddFaceDirection(a_RelPos, BlockMetaDataToBlockFace(Meta), true);
if (!cChunkDef::IsValidHeight(SupportRelPos.y)) if (!cChunkDef::IsValidHeight(SupportRelPos.y))
{ {
return false; return false;

View File

@ -18,47 +18,13 @@ public:
private: private:
/** Called before a cactus block is placed by a player, overrides cItemHandler::GetPlacementBlockTypeMeta(). virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
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
{ {
if ( if (a_Position.y <= 0)
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)
{ {
return false; 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)) if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS))
{ {
// Cactus can only be placed on sand and itself // Cactus can only be placed on sand and itself
@ -78,7 +44,7 @@ private:
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
if ( if (
a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[i], BlockType, BlockMeta) && a_Chunk.UnboundedRelGetBlock(a_Position + Coords[i], BlockType, BlockMeta) &&
( (
cBlockInfo::IsSolid(BlockType) || cBlockInfo::IsSolid(BlockType) ||
(BlockType == E_BLOCK_LAVA) || (BlockType == E_BLOCK_LAVA) ||

View File

@ -25,27 +25,9 @@ public:
private: private:
virtual bool GetPlacementBlockTypeMeta( virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
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; return (a_Position.y > 0) && (a_Chunk.GetBlock(a_Position.addedY(-1)) != E_BLOCK_AIR);
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);
} }

View File

@ -18,184 +18,6 @@ public:
using Super::Super; 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 virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{ {
@ -203,7 +25,3 @@ private:
return 13; return 13;
} }
} ; } ;

View File

@ -36,11 +36,11 @@ public:
private: 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: // Check that we're attached to a jungle log block:
eBlockFace BlockFace = MetaToBlockFace(a_Chunk.GetMeta(a_RelPos)); eBlockFace BlockFace = MetaToBlockFace(a_Meta);
auto LogPos = AddFaceDirection(a_RelPos, BlockFace, true); auto LogPos = AddFaceDirection(a_Position, BlockFace, true);
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
a_Chunk.UnboundedRelGetBlock(LogPos, BlockType, 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; return false;
} }
BLOCKTYPE BelowBlock; BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta; NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta); a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock)) 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: 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 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; return false;
} }
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1)); BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_Position.addedY(-1));
switch (BelowBlock) switch (BelowBlock)
{ {
case E_BLOCK_CLAY: case E_BLOCK_CLAY:

View File

@ -11,6 +11,8 @@
class cBlockDoorHandler final : class cBlockDoorHandler final :
public cYawRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x01, 0x02> 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). */ /** Returns a vector pointing one block in the direction the door is facing (where the outside is). */
inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta) inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta)
{ {
@ -167,38 +150,6 @@ private:
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) const override; virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) const override;
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) const override; virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) const override;
virtual NIBBLETYPE MetaMirrorYZ(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; 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 : class cBlockEndPortalFrameHandler final :
public cMetaRotator<cBlockHandler, 0x03, public 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_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, 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_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: public:
@ -29,54 +29,6 @@ public:
private: 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 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. // 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 virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{ {
UNUSED(a_Meta); 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 /** 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 */ 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) static int FindObsidianCeiling(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, int MaxY = 0)
@ -256,7 +248,7 @@ private:
return (FoundFrameZP && FoundFrameZM); 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; 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; return true;
} }

View File

@ -456,24 +456,6 @@ namespace
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cBlockHandler: // 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( void cBlockHandler::OnUpdate(
cChunkInterface & a_ChunkInterface, cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface, cWorldInterface & a_WorldInterface,
@ -490,7 +472,7 @@ void cBlockHandler::OnUpdate(
void cBlockHandler::OnNeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const 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; 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; 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; return m_BlockType == E_BLOCK_AIR;
}
bool cBlockHandler::DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const
{
return (m_BlockType == E_BLOCK_AIR);
} }

View File

@ -14,6 +14,7 @@ class cPlayer;
class cChunk; class cChunk;
class cBlockPluginInterface; class cBlockPluginInterface;
class cChunkInterface; class cChunkInterface;
class cWorld;
class cWorldInterface; class cWorldInterface;
class cItems; class cItems;
@ -45,22 +46,6 @@ public:
blocktype of the minus-X neighbor, the positive-X neighbor, etc. */ 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; 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 */ /** Called by cWorld::SetBlock() after the block has been set */
virtual void OnPlaced( virtual void OnPlaced(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
@ -68,11 +53,6 @@ public:
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
) const {} ) 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. /** 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. 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. 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; 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 */ /** Checks if the block can stay at the specified relative coords in the chunk */
virtual bool CanBeAt( virtual bool CanBeAt(const cChunk & a_Chunk, Vector3i a_Position, NIBBLETYPE a_Meta) const;
cChunkInterface & a_ChunkInterface,
const Vector3i a_RelPos,
const cChunk & a_Chunk
) const;
/** Checks whether the block has an effect on growing the plant */ /** Checks whether the block has an effect on growing the plant */
virtual bool CanSustainPlant(BLOCKTYPE a_Plant) const { return false; } virtual bool CanSustainPlant(BLOCKTYPE a_Plant) const { return false; }
@ -155,16 +131,12 @@ public:
If it returns true, OnUse() is called */ If it returns true, OnUse() is called */
virtual bool IsUseable(void) const; 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. /** 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 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_Pos Position of the block
@param a_Player Player trying to build on the block @param a_Player Player trying to build on the block
@param a_Meta Meta value of the block currently at a_Pos */ @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. /** 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. */ Coords in a_RelPosition are guaranteed to be in the [0..1] range. */

View File

@ -8,9 +8,9 @@
class cBlockHopperHandler final : 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: public:
@ -18,42 +18,9 @@ public:
private: 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 virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);
return 11; return 11;
} }
} ; } ;

View File

@ -2,6 +2,7 @@
#pragma once #pragma once
#include "BlockHandler.h" #include "BlockHandler.h"
#include "BlockInfo.h"
#include "Mixins.h" #include "Mixins.h"
@ -17,59 +18,24 @@ public:
using Super::Super; 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: 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. */ /** 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) static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
{ {
@ -87,51 +53,13 @@ private:
/** Finds a suitable block face value for the Ladder. virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
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)
{ {
for (int FaceInt = BLOCK_FACE_ZM; FaceInt <= BLOCK_FACE_XP; FaceInt++) auto Face = MetaDataToBlockFace(a_Meta);
{ auto NeighborRelPos = AddFaceDirection(a_Position, Face, true);
eBlockFace Face = static_cast<eBlockFace>(FaceInt); BLOCKTYPE NeighborBlockType;
if (LadderCanBePlacedAt(a_ChunkInterface, a_LadderPos, Face)) a_Chunk.UnboundedRelGetBlockType(NeighborRelPos, NeighborBlockType);
{ return CanBePlacedOn(NeighborBlockType, 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);
} }

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. */ /** 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) 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: // Find the type of block the lever is attached to:
auto Meta = a_Chunk.GetMeta(a_RelPos); auto NeighborFace = BlockMetaDataToBlockFace(a_Meta);
auto NeighborFace = BlockMetaDataToBlockFace(Meta); auto NeighborPos = AddFaceDirection(a_Position, NeighborFace, true);
auto NeighborPos = AddFaceDirection(a_RelPos, NeighborFace, true);
if (!cChunkDef::IsValidHeight(NeighborPos.y)) if (!cChunkDef::IsValidHeight(NeighborPos.y))
{ {
return false; return false;
} }
BLOCKTYPE NeighborBlockType; BLOCKTYPE NeighborBlockType;
if (!a_Chunk.UnboundedRelGetBlock(NeighborPos, NeighborBlockType, Meta)) NIBBLETYPE NeighborMeta;
if (!a_Chunk.UnboundedRelGetBlock(NeighborPos, NeighborBlockType, NeighborMeta))
{ {
return false; return false;
} }
@ -152,8 +113,8 @@ private:
else if (cBlockSlabHandler::IsAnySlabType(NeighborBlockType)) else if (cBlockSlabHandler::IsAnySlabType(NeighborBlockType))
{ {
return ( return (
(((Meta & 0x08) == 0x08) && (NeighborFace == BLOCK_FACE_TOP)) || (((NeighborMeta & 0x08) == 0x08) && (NeighborFace == BLOCK_FACE_TOP)) ||
(((Meta & 0x08) == 0) && (NeighborFace == BLOCK_FACE_BOTTOM)) (((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)) if (!cChunkDef::IsValidHeight(UnderPos.y))
{ {
return false; return false;

View File

@ -21,16 +21,16 @@ private:
// TODO: Add Mushroom Spread // 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; return false;
} }
// TODO: Cannot be at too much daylight // 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_GLASS:
case E_BLOCK_CACTUS: 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: // 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: 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 virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{ {
switch (a_Meta) switch (a_Meta)

View File

@ -18,28 +18,6 @@ public:
private: 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 virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
{ {
// No pickups // 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: case 0x1:
{ {
@ -95,7 +73,7 @@ private:
for (const auto & Direction : PortalCheck) for (const auto & Direction : PortalCheck)
{ {
BLOCKTYPE Block; 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)) if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
{ {
return false; return false;
@ -118,7 +96,7 @@ private:
for (const auto & Direction : PortalCheck) for (const auto & Direction : PortalCheck)
{ {
BLOCKTYPE Block; 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)) if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
{ {
return false; return false;

View File

@ -17,15 +17,16 @@ public:
private: 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; return false;
} }
// TODO: check if the block is upside-down slab or upside-down stairs // 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) switch (Block)
{ {
case E_BLOCK_ACACIA_FENCE: case E_BLOCK_ACACIA_FENCE:
@ -41,7 +42,7 @@ private:
} }
default: default:
{ {
return (!cBlockInfo::IsTransparent(Block)); return !cBlockInfo::IsTransparent(Block);
} }
} }
} }

View File

@ -18,66 +18,6 @@ public:
private: 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 virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -25,141 +25,8 @@ public:
using Super::Super; using Super::Super;
private:
virtual bool GetPlacementBlockTypeMeta( static NIBBLETYPE FindMeta(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, BLOCKTYPE a_RailType)
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
{ {
NIBBLETYPE Meta = 0; NIBBLETYPE Meta = 0;
char RailsCnt = 0; char RailsCnt = 0;
@ -234,19 +101,21 @@ private:
} }
if (RailsCnt > 1) 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; 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; 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; 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; return E_META_RAIL_CURVED_ZM_XM;
} }
@ -275,7 +144,7 @@ private:
return E_META_RAIL_ZM_ZP; return E_META_RAIL_ZM_ZP;
} }
if (CanThisRailCurve()) if (CanCurve)
{ {
ASSERT(!"Weird neighbor count"); ASSERT(!"Weird neighbor count");
} }
@ -283,10 +152,42 @@ private:
return Meta; 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 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. // 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: 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; return false;
} }
BLOCKTYPE BelowBlock; BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta; NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta); a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock)) 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; return false;
} }
BLOCKTYPE BelowBlock; BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta; NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta); a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock)) 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. /** 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. */ Additionally supports the metadata specifying block sub-type in its lower 2 bits. */
class cBlockSidewaysHandler final : class cBlockSidewaysHandler final :
public cBlockHandler public cBlockHandler
@ -21,66 +20,9 @@ public:
private: 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 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); 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; 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: private:
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override 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; 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 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
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; /* Double slab combining uses build collision checks to replace single slabs with double slabs in the right conditions.
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage); For us to be replaced, the player must be:
1. Placing the same slab material.
// Set the correct metadata based on player equipped item (i.e. a_BlockMeta not initialised yet) 2. Placing the same slab sub-kind (and existing slab is single). */
switch (a_ClickedBlockFace) if ((m_BlockType != a_HeldItem.m_ItemType) || ((a_Meta & 0x07) != a_HeldItem.m_ItemDamage))
{ {
case BLOCK_FACE_TOP: return false;
{
// 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 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 virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
{ {
// Toggle the 4th bit - up / down: // Toggle the 4th bit - up / down:

View File

@ -20,58 +20,21 @@ private:
enum 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, 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
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;
// 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) 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; return false;
@ -83,7 +46,7 @@ private:
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override 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)) if ((a_Tool == nullptr) || !ItemCategory::IsShovel(a_Tool->m_ItemType))
{ {
return {}; 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; return false;
} }
auto BelowPos = a_RelPos.addedY(-1); auto BelowPos = a_Position.addedY(-1);
auto BlockBelow = a_Chunk.GetBlock(BelowPos); auto BlockBelow = a_Chunk.GetBlock(BelowPos);
auto MetaBelow = a_Chunk.GetMeta(BelowPos); auto MetaBelow = a_Chunk.GetMeta(BelowPos);
return CanBeOn(BlockBelow, MetaBelow); 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. */ /** 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) 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 ( return (
cBlockInfo::IsSnowable(a_BlockType) || cBlockInfo::IsSnowable(a_BlockType) ||
( (

View File

@ -8,9 +8,9 @@
class cBlockStairsHandler final : 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: public:
@ -18,74 +18,6 @@ public:
private: 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 virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
{ {
// Toggle bit 3: // 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; 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_DIRT:
case E_BLOCK_GRASS: case E_BLOCK_GRASS:
@ -52,7 +52,7 @@ private:
{ {
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; 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 // Too close to the edge, cannot simulate
return true; return true;

View File

@ -20,7 +20,7 @@ public:
private: 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 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; return false;
} }
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1)); BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_Position.addedY(-1));
return IsBlockTypeOfDirt(BelowBlock); return IsBlockTypeOfDirt(BelowBlock);
} }

View File

@ -18,91 +18,6 @@ public:
using Super::Super; 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. */ /** 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) static bool CanBePlacedOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_BlockFace)
@ -163,37 +78,40 @@ private:
} }
} }
protected:
~cBlockTorchBaseHandler() = default;
private:
/** Converts the torch block's meta to the block face of the neighbor to which the torch is attached. */
/** Returns a suitable neighbor's blockface to place the torch at the specified pos inline static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
Returns BLOCK_FACE_NONE on failure */
static eBlockFace FindSuitableFace(cChunkInterface & a_ChunkInterface, const Vector3i a_TorchPos)
{ {
for (int i = BLOCK_FACE_YM; i <= BLOCK_FACE_XP; i++) // Loop through all faces switch (a_MetaData)
{ {
auto Face = static_cast<eBlockFace>(i); case 0: return BLOCK_FACE_TOP; // By default, the torches stand on the ground
auto NeighborPos = AddFaceDirection(a_TorchPos, Face, true); case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP;
BLOCKTYPE NeighborBlockType; case E_META_TORCH_EAST: return BLOCK_FACE_EAST;
NIBBLETYPE NeighborBlockMeta; case E_META_TORCH_WEST: return BLOCK_FACE_WEST;
a_ChunkInterface.GetBlockTypeMeta(NeighborPos, NeighborBlockType, NeighborBlockMeta); case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH;
if (CanBePlacedOn(NeighborBlockType, NeighborBlockMeta, Face)) 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 Face = MetaDataToBlockFace(a_Meta);
auto NeighborRelPos = AddFaceDirection(a_RelPos, Face, true); auto NeighborRelPos = AddFaceDirection(a_Position, Face, true);
BLOCKTYPE NeighborBlockType; BLOCKTYPE NeighborBlockType;
NIBBLETYPE NeighborBlockMeta; NIBBLETYPE NeighborBlockMeta;
if (!a_Chunk.UnboundedRelGetBlock(NeighborRelPos, NeighborBlockType, 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) inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
{ {
switch (a_Meta & 0x3) switch (a_Meta & 0x3)

View File

@ -30,57 +30,9 @@ public:
private: private:
virtual bool GetPlacementBlockTypeMeta( virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
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; const auto RearPosition = AddFaceDirection(a_Position, MetadataToDirection(a_Meta), true);
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);
BLOCKTYPE NeighborBlockType; BLOCKTYPE NeighborBlockType;
if (!a_Chunk.UnboundedRelGetBlockType(RearPosition, NeighborBlockType)) if (!a_Chunk.UnboundedRelGetBlockType(RearPosition, NeighborBlockType))

View File

@ -17,35 +17,16 @@ public:
private: private:
virtual bool GetPlacementBlockTypeMeta( static const NIBBLETYPE VINE_LOST_SUPPORT = 16;
cChunkInterface & a_ChunkInterface, static const NIBBLETYPE VINE_UNCHANGED = 17;
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace, virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) const override
{ {
// TODO: Disallow placement where the vine doesn't attach to something properly return GetMaxMeta(a_Chunk, a_Position, a_Meta) != VINE_LOST_SUPPORT;
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;
} }
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
{ {
// Only drops self when using shears, otherwise drops nothing: // 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) static char MetaDataToDirection(NIBBLETYPE a_MetaData)
{ {
switch (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 */ /** Returns the meta that has the maximum allowable sides of the vine, given the surroundings and current vine meta.
static NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, Vector3i a_RelPos) 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 static const struct
{ {
@ -136,21 +102,41 @@ private:
{ 0, -1, 4}, // north, ZM { 0, -1, 4}, // north, ZM
{ 1, 0, 8}, // east, XP { 1, 0, 8}, // east, XP
} ; } ;
NIBBLETYPE res = 0;
NIBBLETYPE MaxMeta = 0;
for (auto & Coord : Coords) for (auto & Coord : Coords)
{ {
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
auto checkPos = a_RelPos.addedXZ(Coord.x, Coord.z); auto checkPos = a_Position.addedXZ(Coord.x, Coord.z);
if ( if (
a_Chunk.UnboundedRelGetBlock(checkPos.x, checkPos.y, checkPos.z, BlockType, BlockMeta) && a_Chunk.UnboundedRelGetBlock(checkPos.x, checkPos.y, checkPos.z, BlockType, BlockMeta) &&
IsBlockAttachable(BlockType) 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) a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & a_Chunk)
{ {
const auto a_RelPos = cChunkDef::AbsoluteToRelative(a_BlockPos); const auto Position = cChunkDef::AbsoluteToRelative(a_BlockPos);
NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelPos); const auto MaxMeta = GetMaxMeta(a_Chunk, Position, a_Chunk.GetMeta(Position));
NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelPos);
// Check if vine above us, add its meta to MaxMeta if (MaxMeta == VINE_UNCHANGED)
if ((a_RelPos.y < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelPos.addedY(1)) == m_BlockType))
{ {
MaxMeta |= a_Chunk.GetMeta(a_RelPos.addedY(1)); return false;
} }
NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal // There is a neighbor missing, need to update the meta or even destroy the block.
if (Common != CurMeta)
if (MaxMeta == VINE_LOST_SUPPORT)
{ {
// There is a neighbor missing, need to update the meta or even destroy the block // The vine just lost all its support, destroy the block:
bool HasTop = (a_RelPos.y < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelPos.addedY(1))); a_Chunk.SetBlock(Position, E_BLOCK_AIR, 0);
if ((Common == 0) && !HasTop) }
{ else
// The vine just lost all its support, destroy the block: {
a_Chunk.SetBlock(a_RelPos, E_BLOCK_AIR, 0); // It lost some of its support, set it to what remains (SetBlock to notify neighbors):
return false; a_Chunk.SetBlock(Position, E_BLOCK_VINES, MaxMeta);
}
a_Chunk.SetBlock(a_RelPos, m_BlockType, Common);
} }
return false; 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; 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: private:
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override 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; BLOCKTYPE NeighborType;
if (!a_Chunk.UnboundedRelGetBlockType(NeighborPos, NeighborType)) if (!a_Chunk.UnboundedRelGetBlockType(NeighborPos, NeighborType))
{ {
// The neighbor is not accessible (unloaded chunk), bail out without changing this // The neighbor is not accessible (unloaded chunk), bail out without changing this
return true; 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; 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 /** Converts the rotation value as returned by cPlayer::GetYaw() to the appropriate metadata
value for a block placed by a player facing that way */ value for a block placed by a player facing that way */
@ -241,48 +220,6 @@ public:
using Super::Super; 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() /** 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 */ 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); 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; virtual std::vector<UInt32> SpawnSplitExperienceOrbs(Vector3d a_Pos, int a_Reward) = 0;
/** Sends the block on those coords to the player */ /** 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 */ /** 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); 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; res = false;
continue; continue;
} }
if (!cChunkDef::IsValidHeight(itr->m_RelY))
{
continue;
}
itr->m_BlockType = Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ); 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); 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; int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, 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. /** Sends the block at the specified coords to the specified player.
Uses a blockchange packet to send the block. Uses a blockchange packet to send the block.
If the relevant chunk isn't loaded, doesn't do anything. */ 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 */ /** 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); 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: /* Check for clickthrough-blocks:
When the user breaks a fire block, the client send the wrong block location. When the user breaks a fire block, the client send the wrong block location.
We must find the right block with the face direction. */ We must find the right block with the face direction. */
int BlockX = a_BlockX; int BlockX = a_BlockX;
int BlockY = a_BlockY; int BlockY = a_BlockY;
int BlockZ = a_BlockZ; int BlockZ = a_BlockZ;
AddFaceDirection(BlockX, BlockY, BlockZ, a_BlockFace); AddFaceDirection(BlockX, BlockY, BlockZ, a_BlockFace);
if ( if (cChunkDef::IsValidHeight(BlockY) && cBlockInfo::IsClickedThrough(m_Player->GetWorld()->GetBlock({ BlockX, BlockY, BlockZ })))
cChunkDef::IsValidHeight(BlockY) &&
cBlockHandler::For(m_Player->GetWorld()->GetBlock({ BlockX, BlockY, BlockZ })).IsClickedThrough()
)
{ {
a_BlockX = BlockX; a_BlockX = BlockX;
a_BlockY = BlockY; 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(); cWorld * World = m_Player->GetWorld();
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
bool Success = false;
if ( if (
!PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ) && !PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ) &&
IsWithinReach && !m_Player->IsFrozen() 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())) if (BlockUsable && !(m_Player->IsCrouched() && !HeldItem.IsEmpty()))
{ {
// use a block
cChunkInterface ChunkInterface(World->GetChunkMap()); 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)) 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)) 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); 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 // Check if the item is place able, for example a torch on a fence:
if (!Success && Placeable) if (Placeable)
{ {
// place a block // Place a block:
Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ}); ItemHandler->OnPlayerPlace(*m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
} }
}
else return;
{
// 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);
} }
} }
else if (Placeable) 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?"); Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
return; 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 // All plugins agree with using the item.
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.
{
// All plugins agree with using the item cBlockInServerPluginInterface PluginInterface(*World);
cBlockInServerPluginInterface PluginInterface(*World); ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
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);
PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); return;
Success = true;
}
} }
} }
if (!Success)
{ // TODO: delete OnItemUse bool return, delete onCancelRightClick
// Update the target block including the block above and below for 2 block high things
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Update the target block including the block above and below for 2 block high things:
for (int y = a_BlockY - 1; y <= a_BlockY + 1; y++) m_Player->SendBlocksAround(a_BlockX, a_BlockY, a_BlockZ, 2);
{
if (cChunkDef::IsValidHeight(y)) // TODO: Send corresponding slot based on hand
{ m_Player->GetInventory().SendEquippedSlot();
World->SendBlockTo(a_BlockX, y, a_BlockZ, *m_Player);
}
}
// 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({ { a_Position, a_BlockType, a_BlockMeta } });
return PlaceBlocks(blk);
} }
@ -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 // Compute the bounding box for each block to be placed
std::vector<cBoundingBox> PlacementBoxes; 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)) if (DoesPlacingBlocksIntersectEntity(a_Blocks))
{ {
// Abort - re-send all the current blocks in the a_Blocks' coords to the client: // 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; return false;
} }
// Call the "placing" hooks; if any fail, abort:
cPluginManager * pm = cPluginManager::Get(); 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: // 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; return false;
} }
} // for blk - a_Blocks[] }
cChunkInterface ChunkInterface(m_World->GetChunkMap()); cChunkInterface ChunkInterface(m_World->GetChunkMap());
for (auto blk: a_Blocks) for (const auto & Block : a_Blocks)
{ {
// Set the blocks: // Set the blocks:
m_World->PlaceBlock(blk.GetAbsolutePos(), blk.m_BlockType, blk.m_BlockMeta); m_World->PlaceBlock(Block.GetAbsolutePos(), Block.m_BlockType, Block.m_BlockMeta);
// Notify the blockhandlers:
cBlockHandler::For(blk.m_BlockType).OnPlacedByPlayer(ChunkInterface, *m_World, *this, blk);
// Call the "placed" hooks: // Call the "placed" hooks:
pm->CallHookPlayerPlacedBlock(*this, blk); pm->CallHookPlayerPlacedBlock(*this, Block);
} }
return true; return true;

View File

@ -534,7 +534,7 @@ public:
void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround); void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround);
/** Whether placing the given blocks would intersect any entitiy */ /** 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. */ /** Returns the UUID that has been read from the client, or nil if not available. */
const cUUID & GetUUID(void) const; // Exported in ManualBindings.cpp 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. 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. Assumes that the block is in a currently loaded chunk.
Returns true if the block is successfully placed. */ 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 /** Sends the block in the specified range around the specified coord to the client
as a block change packet. 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. 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. Returns true if all the blocks are placed.
Assumes that all the blocks are in currently loaded chunks. */ 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 /** 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. @param a_Opponent the opponent we're fighting.

View File

@ -3,6 +3,7 @@ target_sources(
ItemHandler.cpp ItemHandler.cpp
ItemAnvil.h
ItemArmor.h ItemArmor.h
ItemAxe.h ItemAxe.h
ItemBanner.h ItemBanner.h
@ -12,24 +13,35 @@ target_sources(
ItemBottle.h ItemBottle.h
ItemBow.h ItemBow.h
ItemBucket.h ItemBucket.h
ItemButton.h
ItemChest.h ItemChest.h
ItemCloth.h ItemCloth.h
ItemComparator.h ItemComparator.h
ItemCookedFish.h ItemCookedFish.h
ItemDoor.h ItemDoor.h
ItemDropSpenser.h
ItemDye.h ItemDye.h
ItemEmptyMap.h ItemEmptyMap.h
ItemEnchantingTable.h ItemEnchantingTable.h
ItemEndCrystal.h ItemEndCrystal.h
ItemEndPortalFrame.h
ItemEnderchest.h
ItemEyeOfEnder.h ItemEyeOfEnder.h
ItemFenceGate.h
ItemFishingRod.h ItemFishingRod.h
ItemFood.h ItemFood.h
ItemFoodSeeds.h ItemFoodSeeds.h
ItemFurnace.h
ItemGlazedTerracotta.h
ItemGoldenApple.h ItemGoldenApple.h
ItemHandler.h ItemHandler.h
ItemHoe.h ItemHoe.h
ItemHopper.h
ItemItemFrame.h ItemItemFrame.h
ItemJackOLantern.h
ItemLadder.h
ItemLeaves.h ItemLeaves.h
ItemLever.h
ItemLighter.h ItemLighter.h
ItemLilypad.h ItemLilypad.h
ItemMap.h ItemMap.h
@ -37,11 +49,16 @@ target_sources(
ItemMinecart.h ItemMinecart.h
ItemMobHead.h ItemMobHead.h
ItemNetherWart.h ItemNetherWart.h
ItemObserver.h
ItemPainting.h ItemPainting.h
ItemPickaxe.h ItemPickaxe.h
ItemPiston.h
ItemPlanks.h
ItemPoisonousPotato.h ItemPoisonousPotato.h
ItemPotion.h ItemPotion.h
ItemPumpkin.h ItemPumpkin.h
ItemQuartz.h
ItemRail.h
ItemRawChicken.h ItemRawChicken.h
ItemRawFish.h ItemRawFish.h
ItemRedstoneDust.h ItemRedstoneDust.h
@ -51,12 +68,19 @@ target_sources(
ItemSeeds.h ItemSeeds.h
ItemShears.h ItemShears.h
ItemShovel.h ItemShovel.h
ItemSideways.h
ItemSign.h ItemSign.h
ItemSlab.h ItemSlab.h
ItemSnow.h
ItemSoup.h ItemSoup.h
ItemSpawnEgg.h ItemSpawnEgg.h
ItemSpiderEye.h ItemSpiderEye.h
ItemStairs.h
ItemSword.h ItemSword.h
ItemThrowable.h ItemThrowable.h
ItemTorch.h
ItemTrapdoor.h
ItemTripwireHook.h
ItemVine.h
SimplePlaceableItemHandler.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:
static bool TryPlaceBanner(cPlayer & a_Player, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace)
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_BlockMeta = 0x00; const auto Rotation = a_Player.GetYaw();
const double Rotation = a_Player->GetYaw();
// Placing on the floor // Placing on the floor:
if (a_ClickedBlockFace == BLOCK_FACE_TOP) if (a_ClickedBlockFace == BLOCK_FACE_TOP)
{ {
if ((Rotation >= - 11.25f) && (Rotation < 11.25f)) NIBBLETYPE Meta;
if ((Rotation >= -11.25f) && (Rotation < 11.25f))
{ {
// South // South
a_BlockMeta |= 0x08; Meta = 0x08;
} }
else if ((Rotation >= 11.25f) && (Rotation < 33.75f)) else if ((Rotation >= 11.25f) && (Rotation < 33.75f))
{ {
// SouthSouthWest // SouthSouthWest
a_BlockMeta |= 0x09; Meta = 0x09;
} }
else if ((Rotation >= 23.75f) && (Rotation < 56.25f)) else if ((Rotation >= 23.75f) && (Rotation < 56.25f))
{ {
// SouthWest // SouthWest
a_BlockMeta |= 0x0a; Meta = 0x0a;
} }
else if ((Rotation >= 56.25f) && (Rotation < 78.75f)) else if ((Rotation >= 56.25f) && (Rotation < 78.75f))
{ {
// WestSouthWest // WestSouthWest
a_BlockMeta |= 0x0b; Meta = 0x0b;
} }
else if ((Rotation >= 78.75f) && (Rotation < 101.25f)) else if ((Rotation >= 78.75f) && (Rotation < 101.25f))
{ {
// West // West
a_BlockMeta |= 0x0c; Meta = 0x0c;
} }
else if ((Rotation >= 101.25f) && (Rotation < 123.75f)) else if ((Rotation >= 101.25f) && (Rotation < 123.75f))
{ {
// WestNorthWest // WestNorthWest
a_BlockMeta |= 0x0d; Meta = 0x0d;
} }
else if ((Rotation >= 123.75f) && (Rotation < 146.25f)) else if ((Rotation >= 123.75f) && (Rotation < 146.25f))
{ {
// NorthWest // NorthWest
a_BlockMeta |= 0x0e; Meta = 0x0e;
} }
else if ((Rotation >= 146.25f) && (Rotation < 168.75f)) else if ((Rotation >= 146.25f) && (Rotation < 168.75f))
{ {
// NorthNorthWest // NorthNorthWest
a_BlockMeta |= 0x0f; Meta = 0x0f;
} }
else if ((Rotation >= -168.75f) && (Rotation < -146.25f)) else if ((Rotation >= -168.75f) && (Rotation < -146.25f))
{ {
// NorthNorthEast // NorthNorthEast
a_BlockMeta |= 0x01; Meta = 0x01;
} }
else if ((Rotation >= -146.25f) && (Rotation < -123.75f)) else if ((Rotation >= -146.25f) && (Rotation < -123.75f))
{ {
// NorthEast // NorthEast
a_BlockMeta |= 0x02; Meta = 0x02;
} }
else if ((Rotation >= -123.75f) && (Rotation < -101.25f)) else if ((Rotation >= -123.75f) && (Rotation < -101.25f))
{ {
// EastNorthEast // EastNorthEast
a_BlockMeta |= 0x03; Meta = 0x03;
} }
else if ((Rotation >= -101.25) && (Rotation < -78.75f)) else if ((Rotation >= -101.25) && (Rotation < -78.75f))
{ {
// East // East
a_BlockMeta |= 0x04; Meta = 0x04;
} }
else if ((Rotation >= -78.75) && (Rotation < -56.25f)) else if ((Rotation >= -78.75) && (Rotation < -56.25f))
{ {
// EastSouthEast // EastSouthEast
a_BlockMeta |= 0x05; Meta = 0x05;
} }
else if ((Rotation >= -56.25f) && (Rotation < -33.75f)) else if ((Rotation >= -56.25f) && (Rotation < -33.75f))
{ {
// SouthEast // SouthEast
a_BlockMeta |= 0x06; Meta = 0x06;
} }
else if ((Rotation >= -33.75f) && (Rotation < -11.25f)) else if ((Rotation >= -33.75f) && (Rotation < -11.25f))
{ {
// SouthSouthEast // SouthSouthEast
a_BlockMeta |= 0x07; Meta = 0x07;
} }
else // degrees jumping from 180 to -180 else // degrees jumping from 180 to -180
{ {
// North // North
a_BlockMeta |= 0x00; Meta = 0x00;
} }
a_BlockType = E_BLOCK_STANDING_BANNER;
} return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_STANDING_BANNER, Meta);
// 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 true; // We must be placing on the side of a block.
}
NIBBLETYPE Meta;
if (a_ClickedBlockFace == BLOCK_FACE_EAST)
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))
{ {
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 return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_WALL_BANNER, Meta);
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;
} }
}; };

View File

@ -2,8 +2,8 @@
#pragma once #pragma once
#include "ItemHandler.h" #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 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 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
{ {
const auto BlockMeta = cBlockBedHandler::YawToMetaData(a_Player.GetYaw()); 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: // 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; return false;
} }
// The "foot", and the "head" block: // The "foot", and the "head" block:
a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BED, BlockMeta); if (
a_BlocksToPlace.emplace_back(HeadPosition, E_BLOCK_BED, BlockMeta | 0x08); !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; return true;
} }
}; };

View File

@ -24,41 +24,29 @@ public:
virtual bool GetBlocksToPlace( virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
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
{ {
// Can only be placed on dirt: // Needs at least two free blocks to build in:
if ((a_PlacedBlockPos.y <= 0) || !IsBlockTypeOfDirt(a_World.GetBlock(a_PlacedBlockPos.addedY(-1)))) if (a_PlacePosition.y >= (cChunkDef::Height - 1))
{ {
return false; return false;
} }
// Needs at least two free blocks to build in const auto & World = *a_Player.GetWorld();
if (a_PlacedBlockPos.y >= cChunkDef::Height - 1) const auto TopPos = a_PlacePosition.addedY(1);
{
return false;
}
auto TopPos = a_PlacedBlockPos.addedY(1);
BLOCKTYPE TopType; BLOCKTYPE TopType;
NIBBLETYPE TopMeta; NIBBLETYPE TopMeta;
a_World.GetBlockTypeMeta(TopPos, TopType, TopMeta); World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap());
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; return false;
} }
a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07); return a_Player.PlaceBlocks(
a_BlocksToPlace.emplace_back(TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP); {
return true; { 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; return false;
} }
// Remove water / lava block (unless plugins disagree) // Remove water / lava block (unless plugins disagree):
if (!a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0)) if (!a_Player->PlaceBlock(BlockPos, E_BLOCK_AIR, 0))
{ {
return false; return false;
} }
@ -175,7 +175,7 @@ public:
} }
// Place the actual fluid block: // 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 #pragma once
#include "ItemHandler.h" #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, private:
the parent class cannot do that. */
virtual bool OnPlayerPlace( virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) 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: // Check that there is at most one single neighbor of the same chest type:
static const Vector3i CrossCoords[] = static const Vector3i CrossCoords[] =
{ {
@ -82,11 +34,14 @@ public:
{ 1, 0, 0}, { 1, 0, 0},
{ 0, 0, 1}, { 0, 0, 1},
}; };
auto & World = *a_Player.GetWorld();
int NeighborIdx = -1; int NeighborIdx = -1;
for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++) for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
{ {
auto NeighborPos = PlacePos + CrossCoords[i]; const auto NeighborPos = a_PlacePosition + CrossCoords[i];
if (a_World.GetBlock(NeighborPos) != m_ItemType) if (World.GetBlock(NeighborPos) != m_ItemType)
{ {
continue; continue;
} }
@ -100,7 +55,7 @@ public:
// Check that this neighbor is a single chest: // Check that this neighbor is a single chest:
for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++) 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 // Trying to place next to a dblchest
return false; return false;
@ -111,7 +66,7 @@ public:
// Get the meta of the placed chest; take existing neighbors into account: // Get the meta of the placed chest; take existing neighbors into account:
BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType); BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
NIBBLETYPE Meta; NIBBLETYPE Meta;
auto yaw = a_Player.GetYaw(); const auto yaw = a_Player.GetYaw();
switch (NeighborIdx) switch (NeighborIdx)
{ {
case 0: case 0:
@ -131,13 +86,13 @@ public:
default: default:
{ {
// No neighbor, place based on yaw: // No neighbor, place based on yaw:
Meta = cBlockChestHandler::PlayerYawToMetaData(yaw); Meta = cBlockChestHandler::YawToMetaData(yaw);
break; break;
} }
} // switch (NeighborIdx) } // switch (NeighborIdx)
// Place the new chest: // 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; return false;
} }
@ -145,17 +100,9 @@ public:
// Adjust the existing chest, if any: // Adjust the existing chest, if any:
if (NeighborIdx != -1) 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; 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( virtual bool IsPlaceable(void) override
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_INACTIVE_COMPARATOR;
a_BlockMeta = cBlockComparatorHandler::YawToMetaData(a_Player->GetYaw());
return true; return true;
} }
} ; } ;

View File

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

View File

@ -20,40 +20,32 @@ public:
private: 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; if (!Super::CommitPlacement(a_Player, a_HeldItem, a_PlacePosition, a_ClickedBlockFace, a_CursorPosition))
}
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))
{ {
return false; return false;
} }
if (a_EquippedItem.IsCustomNameEmpty()) if (a_HeldItem.IsCustomNameEmpty())
{ {
return true; return true;
} }
const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace); a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_HeldItem](cBlockEntity & a_BlockEntity)
a_World.DoWithBlockEntityAt(PlacePos, [&a_EquippedItem](cBlockEntity & a_Entity)
{ {
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 false;
}); });
return true; 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" #include "../Chunk.h"
// Handlers: // Handlers:
#include "ItemAnvil.h"
#include "ItemArmor.h" #include "ItemArmor.h"
#include "ItemAxe.h" #include "ItemAxe.h"
#include "ItemBanner.h" #include "ItemBanner.h"
@ -17,23 +18,34 @@
#include "ItemBottle.h" #include "ItemBottle.h"
#include "ItemBow.h" #include "ItemBow.h"
#include "ItemBucket.h" #include "ItemBucket.h"
#include "ItemButton.h"
#include "ItemChest.h" #include "ItemChest.h"
#include "ItemCloth.h" #include "ItemCloth.h"
#include "ItemComparator.h" #include "ItemComparator.h"
#include "ItemCookedFish.h" #include "ItemCookedFish.h"
#include "ItemDoor.h" #include "ItemDoor.h"
#include "ItemDropSpenser.h"
#include "ItemDye.h" #include "ItemDye.h"
#include "ItemEmptyMap.h" #include "ItemEmptyMap.h"
#include "ItemEnchantingTable.h" #include "ItemEnchantingTable.h"
#include "ItemEndCrystal.h" #include "ItemEndCrystal.h"
#include "ItemEnderchest.h"
#include "ItemEndPortalFrame.h"
#include "ItemEyeOfEnder.h" #include "ItemEyeOfEnder.h"
#include "ItemFenceGate.h"
#include "ItemFishingRod.h" #include "ItemFishingRod.h"
#include "ItemFood.h" #include "ItemFood.h"
#include "ItemFoodSeeds.h" #include "ItemFoodSeeds.h"
#include "ItemFurnace.h"
#include "ItemGlazedTerracotta.h"
#include "ItemGoldenApple.h" #include "ItemGoldenApple.h"
#include "ItemHoe.h" #include "ItemHoe.h"
#include "ItemHopper.h"
#include "ItemItemFrame.h" #include "ItemItemFrame.h"
#include "ItemJackOLantern.h"
#include "ItemLadder.h"
#include "ItemLeaves.h" #include "ItemLeaves.h"
#include "ItemLever.h"
#include "ItemLighter.h" #include "ItemLighter.h"
#include "ItemLilypad.h" #include "ItemLilypad.h"
#include "ItemMap.h" #include "ItemMap.h"
@ -41,11 +53,16 @@
#include "ItemMinecart.h" #include "ItemMinecart.h"
#include "ItemMobHead.h" #include "ItemMobHead.h"
#include "ItemNetherWart.h" #include "ItemNetherWart.h"
#include "ItemObserver.h"
#include "ItemPainting.h" #include "ItemPainting.h"
#include "ItemPickaxe.h" #include "ItemPickaxe.h"
#include "ItemPiston.h"
#include "ItemPlanks.h"
#include "ItemPoisonousPotato.h" #include "ItemPoisonousPotato.h"
#include "ItemPotion.h" #include "ItemPotion.h"
#include "ItemPumpkin.h" #include "ItemPumpkin.h"
#include "ItemQuartz.h"
#include "ItemRail.h"
#include "ItemRawChicken.h" #include "ItemRawChicken.h"
#include "ItemRawFish.h" #include "ItemRawFish.h"
#include "ItemRedstoneDust.h" #include "ItemRedstoneDust.h"
@ -55,13 +72,20 @@
#include "ItemSeeds.h" #include "ItemSeeds.h"
#include "ItemShears.h" #include "ItemShears.h"
#include "ItemShovel.h" #include "ItemShovel.h"
#include "ItemSideways.h"
#include "ItemSign.h" #include "ItemSign.h"
#include "ItemSlab.h" #include "ItemSlab.h"
#include "ItemSnow.h"
#include "ItemSoup.h" #include "ItemSoup.h"
#include "ItemSpawnEgg.h" #include "ItemSpawnEgg.h"
#include "ItemSpiderEye.h" #include "ItemSpiderEye.h"
#include "ItemStairs.h"
#include "ItemSword.h" #include "ItemSword.h"
#include "ItemThrowable.h" #include "ItemThrowable.h"
#include "ItemTorch.h"
#include "ItemTrapdoor.h"
#include "ItemTripwireHook.h"
#include "ItemVine.h"
#include "../Blocks/BlockHandler.h" #include "../Blocks/BlockHandler.h"
#include "SimplePlaceableItemHandler.h" #include "SimplePlaceableItemHandler.h"
@ -113,56 +137,87 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
default: return new cItemHandler(a_ItemType); default: return new cItemHandler(a_ItemType);
// Single item per handler, alphabetically sorted: // Single item per handler, alphabetically sorted:
case E_ITEM_BANNER: return new cItemBannerHandler(a_ItemType); case E_BLOCK_ACTIVATOR_RAIL: return new cItemRailHandler(a_ItemType);
case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler; case E_BLOCK_ANVIL: return new cItemAnvilHandler(a_ItemType);
case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType); case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler;
case E_BLOCK_ENCHANTMENT_TABLE: return new cItemEnchantingTableHandler(a_ItemType); case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); case E_BLOCK_DETECTOR_RAIL: return new cItemRailHandler(a_ItemType);
case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType); case E_BLOCK_DISPENSER: return new cItemDropSpenserHandler(a_ItemType);
case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType); case E_BLOCK_DROPPER: return new cItemDropSpenserHandler(a_ItemType);
case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType); case E_BLOCK_ENCHANTMENT_TABLE: return new cItemEnchantingTableHandler(a_ItemType);
case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler; case E_BLOCK_ENDER_CHEST: return new cItemEnderchestHandler(a_ItemType);
case E_BLOCK_PURPUR_SLAB: return new cItemSlabHandler(E_BLOCK_PURPUR_SLAB, E_BLOCK_PURPUR_DOUBLE_SLAB); case E_BLOCK_END_PORTAL_FRAME: return new cItemEndPortalFrameHandler(a_ItemType);
case E_BLOCK_RED_SANDSTONE_SLAB: return new cItemSlabHandler(E_BLOCK_RED_SANDSTONE_SLAB, E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB); case E_BLOCK_FURNACE: return new cItemFurnaceHandler(a_ItemType);
case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); case E_BLOCK_HAY_BALE: return new cItemSidewaysHandler(a_ItemType);
case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB); case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType); case E_BLOCK_HOPPER: return new cItemHopperHandler(a_ItemType);
case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB); case E_BLOCK_IRON_TRAPDOOR: return new cItemTrapdoorHandler(a_ItemType);
case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); case E_BLOCK_JACK_O_LANTERN: return new cItemJackOLanternHandler(a_ItemType);
case E_ITEM_BED: return new cItemBedHandler(a_ItemType); case E_BLOCK_LADDER: return new cItemLadderHandler(a_ItemType);
case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler(); case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
case E_ITEM_BOW: return new cItemBowHandler(); case E_BLOCK_LEVER: return new cItemLeverHandler(a_ItemType);
case E_ITEM_BREWING_STAND: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_BREWING_STAND); case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
case E_ITEM_CAKE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAKE); case E_BLOCK_LOG: return new cItemSidewaysHandler(a_ItemType);
case E_ITEM_CAULDRON: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAULDRON); case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); case E_BLOCK_NEW_LOG: return new cItemSidewaysHandler(a_ItemType);
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); case E_BLOCK_OBSERVER: return new cItemObserverHandler(a_ItemType);
case E_ITEM_EGG: return new cItemEggHandler(); case E_BLOCK_PISTON: return new cItemPistonHandler(a_ItemType);
case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler(); case E_BLOCK_PLANKS: return new cItemPlanksHandler(a_ItemType);
case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); case E_BLOCK_POWERED_RAIL: return new cItemRailHandler(a_ItemType);
case E_ITEM_END_CRYSTAL: return new cItemEndCrystalHandler(a_ItemType); case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler(a_ItemType);
case E_ITEM_EYE_OF_ENDER: return new cItemEyeOfEnderHandler(); case E_BLOCK_PURPUR_SLAB: return new cItemSlabHandler(a_ItemType);
case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType); case E_BLOCK_QUARTZ_BLOCK: return new cItemQuartzHandler(a_ItemType);
case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler(); case E_BLOCK_RAIL: return new cItemRailHandler(a_ItemType);
case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType); case E_BLOCK_REDSTONE_TORCH_ON: return new cItemTorchHandler(a_ItemType);
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); case E_BLOCK_RED_SANDSTONE_SLAB: return new cItemSlabHandler(a_ItemType);
case E_ITEM_FLOWER_POT: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_FLOWER_POT); case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
case E_ITEM_GLASS_BOTTLE: return new cItemBottleHandler(); case E_BLOCK_SNOW: return new cItemSnowHandler(a_ItemType);
case E_ITEM_MAP: return new cItemMapHandler(); case E_BLOCK_STICKY_PISTON: return new cItemPistonHandler(a_ItemType);
case E_ITEM_MILK: return new cItemMilkHandler(); case E_BLOCK_STONE_BUTTON: return new cItemButtonHandler(a_ItemType);
case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType); case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(a_ItemType);
case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); case E_BLOCK_TORCH: return new cItemTorchHandler(a_ItemType);
case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType); case E_BLOCK_TRAPDOOR: return new cItemTrapdoorHandler(a_ItemType);
case E_ITEM_POTIONS: return new cItemPotionHandler(); case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); case E_BLOCK_TRIPWIRE_HOOK: return new cItemTripwireHookHandler(a_ItemType);
case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); case E_BLOCK_VINES: return new cItemVineHandler(a_ItemType);
case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); case E_BLOCK_WOODEN_BUTTON: return new cItemButtonHandler(a_ItemType);
case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(a_ItemType);
case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType); case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); case E_ITEM_BANNER: return new cItemBannerHandler(a_ItemType);
case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
case E_ITEM_STRING: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_TRIPWIRE); case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
case E_ITEM_SUGARCANE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_SUGARCANE); 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_WOODEN_HOE:
case E_ITEM_STONE_HOE: case E_ITEM_STONE_HOE:
@ -247,6 +302,54 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
return new cItemMinecartHandler(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): // Food (please keep alpha-sorted):
case E_ITEM_BAKED_POTATO: return new cItemFoodHandler(a_ItemType, FoodInfo(5, 6)); 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)); 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( void cItemHandler::OnPlayerPlace(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_ClickedBlockPosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
)
{ {
if (a_ClickedBlockFace == BLOCK_FACE_NONE) if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{ {
// Clicked in the air, no placement possible // 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) // The clicked block is outside the world, ignore this call altogether (GH #128):
return false; return;
} }
const auto & World = *a_Player.GetWorld();
BLOCKTYPE ClickedBlockType; BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta; NIBBLETYPE ClickedBlockMeta;
World.GetBlockTypeMeta(a_ClickedBlockPosition, ClickedBlockType, ClickedBlockMeta);
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap());
// Check if the block ignores build collision (water, grass etc.): // Check if the block ignores build collision (water, grass etc.):
auto PlacedBlockPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace); if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(World, a_HeldItem, a_ClickedBlockPosition, ClickedBlockMeta, a_ClickedBlockFace, true))
if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
{ {
// Replace the clicked block: // Try to place the block at the clicked position:
a_World.DropBlockAsPickups(a_ClickedBlockPos, &a_Player, nullptr); if (!CommitPlacement(a_Player, a_HeldItem, a_ClickedBlockPosition, a_ClickedBlockFace, a_CursorPosition))
PlacedBlockPos = a_ClickedBlockPos; {
// The placement failed, the blocks have already been re-sent, re-send inventory:
a_Player.GetInventory().SendEquippedSlot();
return;
}
} }
else 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) // The block is being placed outside the world, ignore this packet altogether (GH #128):
return false; return;
} }
NIBBLETYPE PlaceMeta; NIBBLETYPE PlaceMeta;
BLOCKTYPE PlaceBlock; 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. // 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. // 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? // 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 // 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: // Try to place the block:
sSetBlockVector blocks; if (!CommitPlacement(a_Player, a_HeldItem, PlacedPosition, a_ClickedBlockFace, a_CursorPosition))
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)
{ {
const auto & AbsPos = blk.GetAbsolutePos(); // The placement failed, the blocks have already been re-sent, re-send inventory:
a_World.SendBlockTo(AbsPos, a_Player); 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: // Remove the "placed" item:
@ -432,29 +517,6 @@ bool cItemHandler::OnPlayerPlace(
{ {
a_Player.GetInventory().RemoveOneEquippedItem(); 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) bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
{ {
auto FoodInfo = GetFoodInfo(a_Item); auto FoodInfo = GetFoodInfo(a_Item);
@ -873,3 +907,19 @@ float cItemHandler::GetBlockBreakingStrength(BLOCKTYPE a_Block)
{ {
return 1.0f; 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: // fwd:
class cChunk;
class cWorld; class cWorld;
class cPlayer; class cPlayer;
class cBlockPluginInterface; class cBlockPluginInterface;
@ -36,55 +37,14 @@ public:
/** Force virtual destructor */ /** Force virtual destructor */
virtual ~cItemHandler() {} virtual ~cItemHandler() {}
/** Called when the player tries to place the item (right mouse button, IsPlaceable() == true). /** 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_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_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. a_CursorPos is the position of the player's cursor within a_ClickedBlockFace.
The default handler uses GetBlocksToPlace() and places the returned blocks. 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. 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. 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. */ void OnPlayerPlace(cPlayer & a_Player, const cItem & a_HeldItem, Vector3i a_ClickedBlockPosition, eBlockFace a_ClickedBlockFace, Vector3i a_CursorPosition);
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
);
/** Called when the player tries to use the item (right mouse button). /** Called when the player tries to use the item (right mouse button).
Descendants can return false to abort the usage (default behavior). */ Descendants can return false to abort the usage (default behavior). */
@ -97,7 +57,6 @@ public:
eBlockFace a_ClickedBlockFace eBlockFace a_ClickedBlockFace
); );
/** Called when the client sends the SHOOT status in the lclk packet (releasing the bow). */ /** 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) virtual void OnItemShoot(cPlayer *, const Vector3i a_BlockPos, eBlockFace a_BlockFace)
{ {
@ -181,9 +140,16 @@ public:
static void Deinit(); static void Deinit();
protected: protected:
int m_ItemType; int m_ItemType;
static cItemHandler * CreateItemHandler(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 cItemHandler * m_ItemHandler[E_ITEM_LAST + 1];
static bool m_HandlerInitialized; // used to detect if the itemhandlers are initialized 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( virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
cWorld * a_World, cPlayer * a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{ {
bool res = Super::GetPlacementBlockTypeMeta( return a_Player.PlaceBlock(
a_World, a_Player, a_PlacePosition,
a_PlacedBlockPos, static_cast<BLOCKTYPE>(m_ItemType),
a_ClickedBlockFace, static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage | 0x4) // 0x4 bit set means this is a player-placed leaves block, not to be decayed.
a_CursorPos,
a_BlockType, a_BlockMeta
); );
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( virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
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 head at "no face" and from the bottom: // Cannot place a head at "no face" and from the bottom:
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_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 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; return true;
} }
// Wither not created, proceed with regular head placement // 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 (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_ClickedBlockFace)))
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
{ {
return false; return false;
} }
RegularHeadPlaced(a_World, a_Player, ItemCopy, PlacePos, a_ClickedBlockFace);
RegularHeadPlaced(a_Player, a_HeldItem, a_PlacePosition, a_ClickedBlockFace);
return true; return true;
} }
@ -66,16 +58,13 @@ public:
/** Called after placing a regular head block with no mob spawning. /** Called after placing a regular head block with no mob spawning.
Adjusts the mob head entity based on the equipped item's data. */ Adjusts the mob head entity based on the equipped item's data. */
void RegularHeadPlaced( void RegularHeadPlaced(const cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace)
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
const Vector3i a_PlacePos, eBlockFace a_ClickedBlockFace
)
{ {
auto HeadType = static_cast<eMobHeadType>(a_EquippedItem.m_ItemDamage); const auto HeadType = static_cast<eMobHeadType>(a_HeldItem.m_ItemDamage);
auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace); const auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace);
// Use a callback to set the properties of the mob head block entity: // 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); 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. /** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula.
Returns true if the wither was created. */ Returns true if the wither was created. */
bool TrySpawnWitherAround( bool TrySpawnWitherAround(cPlayer & a_Player, const Vector3i a_BlockPos)
cWorld & a_World, cPlayer & a_Player,
const Vector3i a_BlockPos
)
{ {
// No wither can be created at Y < 2 - not enough space for the formula: // No wither can be created at Y < 2 - not enough space for the formula:
if (a_BlockPos.y < 2) if (a_BlockPos.y < 2)
@ -119,10 +105,11 @@ public:
{ 0, 0, 1}, { 0, 0, 1},
{ 0, 0, -1}, { 0, 0, -1},
}; };
for (auto & RelCoord : RelCoords) for (auto & RelCoord : RelCoords)
{ {
if (TrySpawnWitherAt( if (TrySpawnWitherAt(
a_World, a_Player, *a_Player.GetWorld(), a_Player,
a_BlockPos, a_BlockPos,
RelCoord.x, RelCoord.z RelCoord.x, RelCoord.z
)) ))
@ -177,12 +164,12 @@ public:
// Try to spawn the wither from each image: // Try to spawn the wither from each image:
return ( return (
TrySpawnWitherFromImage( TrySpawnWitherFromImage(
a_World, a_Player, ImageWitherX, ARRAYCOUNT(ImageWitherX), a_World, a_Player, ImageWitherX,
a_PlacedHeadPos, a_PlacedHeadPos,
a_OffsetX, a_OffsetZ a_OffsetX, a_OffsetZ
) || ) ||
TrySpawnWitherFromImage( TrySpawnWitherFromImage(
a_World, a_Player, ImageWitherZ, ARRAYCOUNT(ImageWitherZ), a_World, a_Player, ImageWitherZ,
a_PlacedHeadPos, a_PlacedHeadPos,
a_OffsetX, a_OffsetZ a_OffsetX, a_OffsetZ
) )
@ -199,35 +186,39 @@ public:
Offset is used to shift the image around the X and Z axis. Offset is used to shift the image around the X and Z axis.
Returns true iff the wither was created successfully. */ Returns true iff the wither was created successfully. */
bool TrySpawnWitherFromImage( 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, Vector3i a_PlacedHeadPos,
int a_OffsetX, int a_OffsetZ int a_OffsetX, int a_OffsetZ
) )
{ {
// Check each block individually; simultaneously build the SetBlockVector for clearing the blocks: std::array<Vector3i, 9> PositionsToClear;
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();
// If the query is for the placed head, short-circuit-evaluate it: // Check each block individually:
if ((BlockX == a_PlacedHeadPos.x) && (BlockY == a_PlacedHeadPos.y) && (BlockZ == a_PlacedHeadPos.z)) 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) 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: // Query the world block:
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; 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 // Cannot query block, assume unloaded chunk, fail to spawn the wither
return false; return false;
@ -242,7 +233,7 @@ public:
// If it is a mob head, check it's a wither skull using the block entity: // If it is a mob head, check it's a wither skull using the block entity:
if ( if (
(BlockType == E_BLOCK_HEAD) && (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); ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_HEAD);
@ -253,12 +244,25 @@ public:
return false; return false;
} }
// Matched, continue checking // Matched, continue checking:
AirBlocks.emplace_back(BlockX, BlockY, BlockZ, E_BLOCK_AIR, 0); PositionsToClear[i] = Block;
} // for i - a_Image } // for i - a_Image
// All image blocks matched, try replace the image with air blocks: // 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; return false;
} }
@ -323,23 +327,6 @@ public:
{ {
return true; 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 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 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
{ {
// Only allow planting nether wart onto the top side of the block: // Only allow planting nether wart onto the top side of the block:
if (a_ClickedBlockFace != BLOCK_FACE_TOP) if (a_ClickedBlockFace != BLOCK_FACE_TOP)
{ {
return false; return true;
} }
// Only allow placement on soulsand return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_NETHER_WART, 0);
if ((a_PlacedBlockPos.y < 1) || (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_SOULSAND)) }
{
return false;
}
a_BlockMeta = 0;
a_BlockType = E_BLOCK_NETHER_WART;
virtual bool IsPlaceable(void) override
{
return true; 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 #pragma once
#include "ItemHandler.h" #include "ItemHandler.h"
#include "Blocks/BlockPumpkin.h"
@ -14,35 +15,22 @@ class cItemPumpkinHandler:
public: public:
cItemPumpkinHandler(): using Super::Super;
Super(E_BLOCK_PUMPKIN)
{
}
private:
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
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
{ {
// First try spawning a snow golem or an iron golem: // First try spawning a snow golem or an iron golem:
auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace); if (TrySpawnGolem(a_Player, a_PlacePosition))
if (TrySpawnGolem(a_World, a_Player, PlacePos))
{ {
// The client thinks that they placed the pumpkin, let them know it's been replaced: // 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; return true;
} }
// No golem at these coords, place the block normally: // 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. /** 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. */ 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)) if ((a_PumpkinPos.y < 2) || (a_PumpkinPos.y >= cChunkDef::Height))
{ {
return false; return false;
} }
auto & World = *a_Player.GetWorld();
// Decide which golem to try spawning based on the block below the placed pumpkin: // 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_SNOW_BLOCK: return TrySpawnSnowGolem(World, a_Player, a_PumpkinPos);
case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_PumpkinPos); case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(World, a_Player, a_PumpkinPos);
default: default:
{ {
// No golem here // No golem here:
return false; return false;
} }
} }
@ -91,11 +81,14 @@ public:
} }
// Try to place air blocks where the original recipe blocks were: // Try to place air blocks where the original recipe blocks were:
sSetBlockVector AirBlocks; if (
AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head !a_Player.PlaceBlocks(
AirBlocks.emplace_back(a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0); // Torso {
AirBlocks.emplace_back(a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0); // Legs { a_PumpkinPos, E_BLOCK_AIR, 0 }, // Head
if (!a_Player.PlaceBlocks(AirBlocks)) { a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0 }, // Torso
{ a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0 } // Legs
})
)
{ {
return false; return false;
} }
@ -143,13 +136,16 @@ public:
} }
// Try to place air blocks where the original recipe blocks were: // Try to place air blocks where the original recipe blocks were:
sSetBlockVector AirBlocks; if (
AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head !a_Player.PlaceBlocks(
AirBlocks.emplace_back(BodyPos, E_BLOCK_AIR, 0); // Torso {
AirBlocks.emplace_back(BodyPos.addedY(-1), E_BLOCK_AIR, 0); // Legs { a_PumpkinPos, E_BLOCK_AIR, 0 }, // Head
AirBlocks.emplace_back(BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0); // Arm { BodyPos, E_BLOCK_AIR, 0 }, // Torso
AirBlocks.emplace_back(BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0); // Arm { BodyPos.addedY(-1), E_BLOCK_AIR, 0 }, // Legs
if (!a_Player.PlaceBlocks(AirBlocks)) { BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0 }, // Arm
{ BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0 } // Arm
})
)
{ {
return false; 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 virtual bool IsPlaceable(void) override
{ {
return true; 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( virtual bool IsPlaceable() override
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_REDSTONE_REPEATER_OFF;
a_BlockMeta = cBlockRedstoneRepeaterHandler::YawToMetaData(a_Player->GetYaw());
return true; return true;
} }
} ; } ;

View File

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

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 "ItemHandler.h"
#include "../World.h" #include "../World.h"
#include "../Blocks/BlockSignPost.h"
#include "../Blocks/BlockWallSign.h"
#include "../ClientHandle.h" #include "../ClientHandle.h"
@ -18,77 +16,68 @@ class cItemSignHandler:
public: public:
cItemSignHandler(int a_ItemType): using Super::Super;
Super(a_ItemType)
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 CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
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
{ {
// Check if placing on something ignoring build collision to edit the correct sign later on: if (a_ClickedBlockFace == BLOCK_FACE_TOP)
BLOCKTYPE ClickedBlockType; {
NIBBLETYPE ClickedBlockMeta; if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_SIGN_POST, RotationToMetaData(a_Player.GetYaw())))
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta); {
cChunkInterface ChunkInterface(a_World.GetChunkMap()); return false;
bool IsReplacingClickedBlock = cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta); }
}
// If the regular placement doesn't work, do no further processing: else if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_WALLSIGN, BlockFaceToMetaData(a_ClickedBlockFace)))
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
{ {
return false; 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: // 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; return true;
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
/** Converts the (player) rotation to placed-signpost block meta. */
static NIBBLETYPE RotationToMetaData(double a_Rotation)
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
{ {
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_Rotation -= 360;
a_BlockType = E_BLOCK_SIGN_POST;
} }
else
{ a_Rotation = (a_Rotation / 360) * 16;
a_BlockMeta = cBlockWallSignHandler::BlockFaceToMetaData(a_ClickedBlockFace);
a_BlockType = E_BLOCK_WALLSIGN; return static_cast<char>(a_Rotation) % 16;
}
return true;
} }
} ; } ;

View File

@ -14,95 +14,87 @@ class cItemSlabHandler:
public: public:
/** Creates a new handler for the specified slab item type. using Super::Super;
Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */
cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType): private:
Super(a_ItemType),
m_DoubleSlabBlockType(a_DoubleSlabBlockType) 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.
// If clicking a slab, combine it into a double-slab:
if (cBlockSlabHandler::IsAnySlabType(a_Player.GetWorld()->GetBlock(a_PlacePosition)))
// 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 ( if (!a_Player.PlaceBlock(a_PlacePosition, GetDoubleSlabType(static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType)), static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage)))
((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))
{ {
return false; return false;
} }
if (a_Player.IsGameModeSurvival())
{ a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z, 2); // (see below)
a_Player.GetInventory().RemoveOneEquippedItem();
}
return true; return true;
} }
// The slabs didn't combine, use the default handler to place the slab: // Set the correct metadata based on player equipped item:
bool res = Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos); 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)))
/*
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))
{ {
auto AbovePos = a_ClickedBlockPos.addedY(1); return false;
a_Player.SendBlocksAround(AbovePos.x, AbovePos.y, AbovePos.z, 1);
} }
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