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:
parent
34bf5c0d9d
commit
a62b2b1be2
@ -222,6 +222,24 @@ return
|
||||
},
|
||||
Notes = "Returns how much light the specified block type consumes.",
|
||||
},
|
||||
IsClickedThrough =
|
||||
{
|
||||
IsStatic = true,
|
||||
Params =
|
||||
{
|
||||
{
|
||||
Name = "BlockType",
|
||||
Type = "number",
|
||||
},
|
||||
},
|
||||
Returns =
|
||||
{
|
||||
{
|
||||
Type = "boolean",
|
||||
},
|
||||
},
|
||||
Notes = "Returns true if the specified block type is ignored by the client on left and right clicks, that is, treated as if it were air.",
|
||||
},
|
||||
IsOneHitDig =
|
||||
{
|
||||
IsStatic = true,
|
||||
|
@ -715,6 +715,17 @@ bool cBlockInfo::FullyOccupiesVoxel(const BLOCKTYPE Block)
|
||||
|
||||
|
||||
|
||||
bool cBlockInfo::IsClickedThrough(const BLOCKTYPE a_Block)
|
||||
{
|
||||
// TODO: Nether Fire too.
|
||||
|
||||
return a_Block == E_BLOCK_FIRE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cBlockInfo::IsOneHitDig(const BLOCKTYPE Block)
|
||||
{
|
||||
#ifdef __clang__
|
||||
|
@ -24,6 +24,10 @@ public:
|
||||
/** Does this block fully occupy its voxel - is it a 'full' block? */
|
||||
static bool FullyOccupiesVoxel(BLOCKTYPE Block);
|
||||
|
||||
/** Does the client pretend the block doesn't exist when clicking?
|
||||
For example, digging a fire will hit the block below the fire, so fire is "clicked through". */
|
||||
static bool IsClickedThrough(BLOCKTYPE a_Block);
|
||||
|
||||
/** Is a block destroyed after a single hit?
|
||||
Warning: IsOneHitDig does not take into account enchantments / status effects / swim state / floating state
|
||||
and therefore may be incorrect. Only use to check if hardness is 0.
|
||||
|
@ -48,28 +48,6 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
a_BlockMeta = a_BlockMeta | static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage << 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool IsUseable() const override
|
||||
{
|
||||
return true;
|
||||
|
@ -29,14 +29,14 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y < 1)
|
||||
if (a_Position.y < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return cBlockInfo::IsSolid(a_Chunk.GetBlock(a_RelPos.addedY(-1)));
|
||||
return cBlockInfo::IsSolid(a_Chunk.GetBlock(a_Position.addedY(-1)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "../World.h"
|
||||
#include "../BoundingBox.h"
|
||||
#include "../Mobs/Monster.h"
|
||||
#include "../BlockEntities/BedEntity.h"
|
||||
|
||||
|
||||
|
||||
@ -158,21 +157,6 @@ bool cBlockBedHandler::OnUse(
|
||||
|
||||
|
||||
|
||||
void cBlockBedHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange) const
|
||||
{
|
||||
a_Player.GetWorld()->DoWithBlockEntityAt(a_BlockChange.GetAbsolutePos(), [&a_Player](cBlockEntity & a_BlockEntity)
|
||||
{
|
||||
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_BED);
|
||||
|
||||
static_cast<cBedEntity &>(a_BlockEntity).SetColor(a_Player.GetEquippedItem().m_ItemDamage);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cItems cBlockBedHandler::ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const
|
||||
{
|
||||
// Drops handled by the block entity:
|
||||
|
@ -91,11 +91,6 @@ private:
|
||||
|
||||
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, const cItem * a_Tool) const override;
|
||||
|
||||
virtual void OnPlacedByPlayer(
|
||||
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player,
|
||||
const sSetBlock & a_BlockChange
|
||||
) const override;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -20,14 +20,14 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
|
||||
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
|
||||
{
|
||||
if (IsMetaTopPart(a_Meta))
|
||||
{
|
||||
BLOCKTYPE BottomType;
|
||||
if (
|
||||
(a_Pos.y < 1) ||
|
||||
!a_ChunkInterface.GetBlockTypeMeta(a_Pos - Vector3i(0, 1, 0), BottomType, a_Meta) ||
|
||||
(a_Position.y < 1) ||
|
||||
!a_World.GetBlockTypeMeta(a_Position - Vector3i(0, 1, 0), BottomType, a_Meta) ||
|
||||
(BottomType != E_BLOCK_BIG_FLOWER)
|
||||
)
|
||||
{
|
||||
@ -98,17 +98,13 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BlockType, BlockMeta);
|
||||
// CanBeAt is also called on placement, so the top part can't check for the bottom part.
|
||||
// Both parts can only that they're rooted in grass.
|
||||
|
||||
return IsBlockTypeOfDirt(BlockType) || ((BlockType == E_BLOCK_BIG_FLOWER) && !IsMetaTopPart(BlockMeta));
|
||||
const auto RootPosition = a_Position.addedY(IsMetaTopPart(a_Meta) ? -2 : -1);
|
||||
return (RootPosition.y >= 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(RootPosition));
|
||||
}
|
||||
|
||||
|
||||
|
@ -101,47 +101,6 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the block face of the neighbor to which the button is attached, to the block meta for this button. */
|
||||
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
|
||||
{
|
||||
switch (a_BlockFace)
|
||||
{
|
||||
case BLOCK_FACE_YP: return 0x5;
|
||||
case BLOCK_FACE_ZM: return 0x4;
|
||||
case BLOCK_FACE_ZP: return 0x3;
|
||||
case BLOCK_FACE_XM: return 0x2;
|
||||
case BLOCK_FACE_XP: return 0x1;
|
||||
case BLOCK_FACE_YM: return 0x0;
|
||||
case BLOCK_FACE_NONE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
UNREACHABLE("Unsupported block face");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the block meta of this button into a block face of the neighbor to which the button is attached. */
|
||||
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
|
||||
{
|
||||
@ -165,10 +124,9 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
auto Meta = a_Chunk.GetMeta(a_RelPos);
|
||||
auto SupportRelPos = AddFaceDirection(a_RelPos, BlockMetaDataToBlockFace(Meta), true);
|
||||
auto SupportRelPos = AddFaceDirection(a_Position, BlockMetaDataToBlockFace(a_Meta), true);
|
||||
if (!cChunkDef::IsValidHeight(SupportRelPos.y))
|
||||
{
|
||||
return false;
|
||||
|
@ -18,47 +18,13 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
/** Called before a cactus block is placed by a player, overrides cItemHandler::GetPlacementBlockTypeMeta().
|
||||
Calls CanBeAt function to determine if a cactus block can be placed on a given block. */
|
||||
bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (
|
||||
a_Player.GetWorld()->DoWithChunkAt(a_PlacedBlockPos,
|
||||
[this, a_PlacedBlockPos, &a_ChunkInterface](cChunk & a_Chunk)
|
||||
{
|
||||
auto RelPos = cChunkDef::AbsoluteToRelative(a_PlacedBlockPos);
|
||||
return CanBeAt(a_ChunkInterface, RelPos, a_Chunk);
|
||||
}
|
||||
))
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
// Setting a_BlockMeta to meta copied from the lowest 4 bits of the player's equipped item's damage value.
|
||||
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
|
||||
a_BlockMeta = Meta & 0x0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelPos.addedY(-1));
|
||||
BLOCKTYPE Surface = a_Chunk.GetBlock(a_Position.addedY(-1));
|
||||
if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS))
|
||||
{
|
||||
// Cactus can only be placed on sand and itself
|
||||
@ -78,7 +44,7 @@ private:
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (
|
||||
a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[i], BlockType, BlockMeta) &&
|
||||
a_Chunk.UnboundedRelGetBlock(a_Position + Coords[i], BlockType, BlockMeta) &&
|
||||
(
|
||||
cBlockInfo::IsSolid(BlockType) ||
|
||||
(BlockType == E_BLOCK_LAVA) ||
|
||||
|
@ -25,27 +25,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = a_Player.GetEquippedItem().m_ItemDamage & 0x0f;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
{
|
||||
return (a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) != E_BLOCK_AIR);
|
||||
return (a_Position.y > 0) && (a_Chunk.GetBlock(a_Position.addedY(-1)) != E_BLOCK_AIR);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,184 +18,6 @@ public:
|
||||
|
||||
using Super::Super;
|
||||
|
||||
/** Translates player yaw when placing a chest into the chest block metadata. Valid for single chests only */
|
||||
static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
|
||||
{
|
||||
a_Yaw += 90 + 45; // So its not aligned with axis
|
||||
|
||||
if (a_Yaw > 360.f)
|
||||
{
|
||||
a_Yaw -= 360.f;
|
||||
}
|
||||
if ((a_Yaw >= 0.f) && (a_Yaw < 90.f))
|
||||
{
|
||||
return 0x04;
|
||||
}
|
||||
else if ((a_Yaw >= 180) && (a_Yaw < 270))
|
||||
{
|
||||
return 0x05;
|
||||
}
|
||||
else if ((a_Yaw >= 90) && (a_Yaw < 180))
|
||||
{
|
||||
return 0x02;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0x03;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
// Cannot place right next to double-chest:
|
||||
if (!CanBeAt(a_ChunkInterface, a_PlacedBlockPos))
|
||||
{
|
||||
// Yup, cannot form a triple-chest, refuse:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to read double-chest information:
|
||||
cBlockArea Area;
|
||||
if (!Area.Read(a_ChunkInterface, a_PlacedBlockPos - Vector3i(1, 0, 1), a_PlacedBlockPos + Vector3i(1, 0, 1)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get meta as if this was a single-chest:
|
||||
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if this forms a doublechest, if so, need to adjust the meta:
|
||||
double yaw = a_Player.GetYaw();
|
||||
if (
|
||||
(Area.GetRelBlockType(0, 0, 1) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(2, 0, 1) == m_BlockType)
|
||||
)
|
||||
{
|
||||
a_BlockMeta = ((yaw >= -90) && (yaw < 90)) ? 2 : 3;
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
(Area.GetRelBlockType(1, 0, 0) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(1, 0, 2) == m_BlockType)
|
||||
)
|
||||
{
|
||||
a_BlockMeta = (yaw < 0) ? 4 : 5;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
{
|
||||
auto BlockPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||
return CanBeAt(a_ChunkInterface, BlockPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos) const
|
||||
{
|
||||
cBlockArea Area;
|
||||
if (!Area.Read(a_ChunkInterface, a_BlockPos - Vector3i(2, 0, 2), a_BlockPos + Vector3i(2, 0, 2)))
|
||||
{
|
||||
// Cannot read the surroundings, probably at the edge of loaded chunks. Disallow.
|
||||
return false;
|
||||
}
|
||||
|
||||
int NumChestNeighbors = 0;
|
||||
if (Area.GetRelBlockType(1, 0, 2) == m_BlockType)
|
||||
{
|
||||
if (
|
||||
(Area.GetRelBlockType(0, 0, 2) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(1, 0, 1) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(1, 0, 3) == m_BlockType)
|
||||
)
|
||||
{
|
||||
// Already a doublechest neighbor, disallow:
|
||||
return false;
|
||||
}
|
||||
NumChestNeighbors += 1;
|
||||
}
|
||||
if (Area.GetRelBlockType(3, 0, 2) == m_BlockType)
|
||||
{
|
||||
if (
|
||||
(Area.GetRelBlockType(4, 0, 2) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(3, 0, 1) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(3, 0, 3) == m_BlockType)
|
||||
)
|
||||
{
|
||||
// Already a doublechest neighbor, disallow:
|
||||
return false;
|
||||
}
|
||||
NumChestNeighbors += 1;
|
||||
}
|
||||
if (Area.GetRelBlockType(2, 0, 1) == m_BlockType)
|
||||
{
|
||||
if (
|
||||
(Area.GetRelBlockType(2, 0, 0) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(1, 0, 1) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(3, 0, 1) == m_BlockType)
|
||||
)
|
||||
{
|
||||
// Already a doublechest neighbor, disallow:
|
||||
return false;
|
||||
}
|
||||
NumChestNeighbors += 1;
|
||||
}
|
||||
if (Area.GetRelBlockType(2, 0, 3) == m_BlockType)
|
||||
{
|
||||
if (
|
||||
(Area.GetRelBlockType(2, 0, 4) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(1, 0, 3) == m_BlockType) ||
|
||||
(Area.GetRelBlockType(3, 0, 3) == m_BlockType)
|
||||
)
|
||||
{
|
||||
// Already a doublechest neighbor, disallow:
|
||||
return false;
|
||||
}
|
||||
NumChestNeighbors += 1;
|
||||
}
|
||||
return (NumChestNeighbors < 2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true. */
|
||||
bool CheckAndAdjustNeighbor(cChunkInterface & a_ChunkInterface, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta) const
|
||||
{
|
||||
if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != m_BlockType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
a_ChunkInterface.SetBlockMeta(a_Area.GetOriginX() + a_RelX, a_Area.GetOriginY(), a_Area.GetOriginZ() + a_RelZ, a_NewMeta);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
@ -203,7 +25,3 @@ private:
|
||||
return 13;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -36,11 +36,11 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
// Check that we're attached to a jungle log block:
|
||||
eBlockFace BlockFace = MetaToBlockFace(a_Chunk.GetMeta(a_RelPos));
|
||||
auto LogPos = AddFaceDirection(a_RelPos, BlockFace, true);
|
||||
eBlockFace BlockFace = MetaToBlockFace(a_Meta);
|
||||
auto LogPos = AddFaceDirection(a_Position, BlockFace, true);
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
a_Chunk.UnboundedRelGetBlock(LogPos, BlockType, BlockMeta);
|
||||
|
@ -152,16 +152,16 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BLOCKTYPE BelowBlock;
|
||||
NIBBLETYPE BelowBlockMeta;
|
||||
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
|
||||
a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
|
||||
|
||||
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
|
||||
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -27,14 +27,14 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1));
|
||||
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_Position.addedY(-1));
|
||||
switch (BelowBlock)
|
||||
{
|
||||
case E_BLOCK_CLAY:
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBlockDoorHandler final :
|
||||
public cYawRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x01, 0x02>
|
||||
{
|
||||
@ -43,25 +45,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static bool CanReplaceBlock(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
switch (a_BlockType)
|
||||
{
|
||||
case E_BLOCK_AIR:
|
||||
case E_BLOCK_TALL_GRASS:
|
||||
case E_BLOCK_WATER:
|
||||
case E_BLOCK_STATIONARY_WATER:
|
||||
case E_BLOCK_LAVA:
|
||||
case E_BLOCK_STATIONARY_LAVA:
|
||||
case E_BLOCK_SNOW:
|
||||
case E_BLOCK_FIRE:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns a vector pointing one block in the direction the door is facing (where the outside is). */
|
||||
inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
@ -167,38 +150,6 @@ private:
|
||||
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) const override;
|
||||
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) const override;
|
||||
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) const override;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
// If clicking a bottom face, place the door one block lower:
|
||||
auto PlacedPos = a_PlacedBlockPos;
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_BOTTOM)
|
||||
{
|
||||
PlacedPos.y--;
|
||||
}
|
||||
|
||||
if (
|
||||
!CanReplaceBlock(a_ChunkInterface.GetBlock(PlacedPos)) ||
|
||||
!CanReplaceBlock(a_ChunkInterface.GetBlock(PlacedPos.addedY(1)))
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, PlacedPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
|
||||
virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP) const override;
|
||||
|
||||
|
||||
@ -237,9 +188,17 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
return ((a_RelPos.y > 0) && CanBeOn(a_Chunk.GetBlock(a_RelPos.addedY(-1)), a_Chunk.GetMeta(a_RelPos.addedY(-1))));
|
||||
// CanBeAt is also called on placement, so the top part can't check for the bottom part.
|
||||
// Both parts can only that their base is a valid block.
|
||||
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
const auto BasePosition = a_Position.addedY(((a_Meta & 0x8) == 0x8) ? -2 : -1);
|
||||
a_Chunk.GetBlockTypeMeta(BasePosition, BlockType, BlockMeta);
|
||||
|
||||
return (BasePosition.y >= 0) && CanBeOn(BlockType, BlockMeta);
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,19 +8,19 @@
|
||||
|
||||
|
||||
class cBlockEndPortalFrameHandler final :
|
||||
public cMetaRotator<cBlockHandler, 0x03,
|
||||
E_META_END_PORTAL_FRAME_ZM,
|
||||
E_META_END_PORTAL_FRAME_XP,
|
||||
public cYawRotator<cBlockHandler, 0x03,
|
||||
E_META_END_PORTAL_FRAME_ZP,
|
||||
E_META_END_PORTAL_FRAME_XM
|
||||
E_META_END_PORTAL_FRAME_XM,
|
||||
E_META_END_PORTAL_FRAME_ZM,
|
||||
E_META_END_PORTAL_FRAME_XP
|
||||
>
|
||||
{
|
||||
using Super = cMetaRotator<
|
||||
using Super = cYawRotator<
|
||||
cBlockHandler, 0x03,
|
||||
E_META_END_PORTAL_FRAME_ZM,
|
||||
E_META_END_PORTAL_FRAME_XP,
|
||||
E_META_END_PORTAL_FRAME_ZP,
|
||||
E_META_END_PORTAL_FRAME_XM
|
||||
E_META_END_PORTAL_FRAME_XM,
|
||||
E_META_END_PORTAL_FRAME_ZM,
|
||||
E_META_END_PORTAL_FRAME_XP
|
||||
>;
|
||||
|
||||
public:
|
||||
@ -29,54 +29,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = YawToMetaData(a_Player.GetYaw());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline static NIBBLETYPE YawToMetaData(double a_Rotation)
|
||||
{
|
||||
a_Rotation += 90 + 45; // So its not aligned with axis
|
||||
if (a_Rotation > 360)
|
||||
{
|
||||
a_Rotation -= 360;
|
||||
}
|
||||
|
||||
if ((a_Rotation >= 0) && (a_Rotation < 90))
|
||||
{
|
||||
return E_META_END_PORTAL_FRAME_XM;
|
||||
}
|
||||
else if ((a_Rotation >= 180) && (a_Rotation < 270))
|
||||
{
|
||||
return E_META_END_PORTAL_FRAME_XP;
|
||||
}
|
||||
else if ((a_Rotation >= 90) && (a_Rotation < 180))
|
||||
{
|
||||
return E_META_END_PORTAL_FRAME_ZM;
|
||||
}
|
||||
else
|
||||
{
|
||||
return E_META_END_PORTAL_FRAME_ZP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) const override
|
||||
{
|
||||
// E_META_END_PORTAL_FRAME_EYE is the bit which signifies the eye of ender is in it.
|
||||
@ -237,16 +189,6 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool IsClickedThrough(void) const override
|
||||
{
|
||||
// TODO: Colision
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
UNUSED(a_Meta);
|
||||
|
@ -65,14 +65,6 @@ private:
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool IsClickedThrough(void) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Traces along YP until it finds an obsidian block, returns Y difference or 0 if no portal, and -1 for border
|
||||
Takes the X, Y, and Z of the base block; with an optional MaxY for portal border finding */
|
||||
static int FindObsidianCeiling(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, int MaxY = 0)
|
||||
@ -256,7 +248,7 @@ private:
|
||||
return (FoundFrameZP && FoundFrameZM);
|
||||
}
|
||||
|
||||
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
|
||||
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -456,24 +456,6 @@ namespace
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cBlockHandler:
|
||||
|
||||
bool cBlockHandler::GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const
|
||||
{
|
||||
// By default, all blocks can be placed and the meta is copied over from the item's damage value:
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage & 0x0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockHandler::OnUpdate(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cWorldInterface & a_WorldInterface,
|
||||
@ -490,7 +472,7 @@ void cBlockHandler::OnUpdate(
|
||||
|
||||
void cBlockHandler::OnNeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const
|
||||
{
|
||||
if (a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & a_Chunk) { return CanBeAt(a_ChunkInterface, cChunkDef::AbsoluteToRelative(a_BlockPos), a_Chunk); }))
|
||||
if (a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & a_Chunk) { return CanBeAt(a_Chunk, cChunkDef::AbsoluteToRelative(a_BlockPos), a_Chunk.GetMeta(cChunkDef::AbsoluteToRelative(a_BlockPos))); }))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -528,7 +510,7 @@ cItems cBlockHandler::ConvertToPickups(NIBBLETYPE a_BlockMeta, const cItem * con
|
||||
|
||||
|
||||
|
||||
bool cBlockHandler::CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const
|
||||
bool cBlockHandler::CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -546,18 +528,9 @@ bool cBlockHandler::IsUseable() const
|
||||
|
||||
|
||||
|
||||
bool cBlockHandler::IsClickedThrough(void) const
|
||||
bool cBlockHandler::DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cBlockHandler::DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const
|
||||
{
|
||||
return (m_BlockType == E_BLOCK_AIR);
|
||||
return m_BlockType == E_BLOCK_AIR;
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@ class cPlayer;
|
||||
class cChunk;
|
||||
class cBlockPluginInterface;
|
||||
class cChunkInterface;
|
||||
class cWorld;
|
||||
class cWorldInterface;
|
||||
class cItems;
|
||||
|
||||
@ -45,22 +46,6 @@ public:
|
||||
blocktype of the minus-X neighbor, the positive-X neighbor, etc. */
|
||||
virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP) const;
|
||||
|
||||
/** Called before a block is placed into a world by player, by cItemHandler::GetPlacementBlockTypeMeta().
|
||||
The handler should return true to allow placement, false to refuse.
|
||||
a_PlacedBlockPos is the coords of the block being placed
|
||||
a_ClickedBlockFace is the face of the neighbor block clicked by the client to place this block.
|
||||
a_CursorPos is the position of the cursor within the neighbor's face
|
||||
The descendant handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
|
||||
The default handler uses the stored block type and meta copied from the lowest 4 bits of the player's equipped item's damage value. */
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const;
|
||||
|
||||
/** Called by cWorld::SetBlock() after the block has been set */
|
||||
virtual void OnPlaced(
|
||||
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
|
||||
@ -68,11 +53,6 @@ public:
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) const {}
|
||||
|
||||
/** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */
|
||||
virtual void OnPlacedByPlayer(
|
||||
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange
|
||||
) const {}
|
||||
|
||||
/** Called after a block gets broken (replaced with air), by natural means.
|
||||
The block is already dug up in the world, the original block type and meta is passed in a_OldBlockType and a_OldBlockMeta.
|
||||
By default notifies all direct neighbors via their OnNeighborChanged() callbacks.
|
||||
@ -142,11 +122,7 @@ public:
|
||||
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, const cItem * a_Tool = nullptr) const;
|
||||
|
||||
/** Checks if the block can stay at the specified relative coords in the chunk */
|
||||
virtual bool CanBeAt(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
const Vector3i a_RelPos,
|
||||
const cChunk & a_Chunk
|
||||
) const;
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, Vector3i a_Position, NIBBLETYPE a_Meta) const;
|
||||
|
||||
/** Checks whether the block has an effect on growing the plant */
|
||||
virtual bool CanSustainPlant(BLOCKTYPE a_Plant) const { return false; }
|
||||
@ -155,16 +131,12 @@ public:
|
||||
If it returns true, OnUse() is called */
|
||||
virtual bool IsUseable(void) const;
|
||||
|
||||
/** Indicates whether the client will click through this block.
|
||||
For example digging a fire will hit the block below the fire so fire is clicked through. */
|
||||
virtual bool IsClickedThrough(void) const;
|
||||
|
||||
/** Checks if the player can build "inside" this block.
|
||||
For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision
|
||||
@param a_Pos Position of the block
|
||||
@param a_Player Player trying to build on the block
|
||||
@param a_Meta Meta value of the block currently at a_Pos */
|
||||
virtual bool DoesIgnoreBuildCollision(cChunkInterface & ChunkInterface, const Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const;
|
||||
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, Vector3i a_Position, NIBBLETYPE a_Meta, eBlockFace a_ClickedBlockFace, bool a_ClickedDirectly) const;
|
||||
|
||||
/** Tests if a_RelPosition is inside the block, where a_RelPosition is relative to the origin of the block.
|
||||
Coords in a_RelPosition are guaranteed to be in the [0..1] range. */
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
|
||||
class cBlockHopperHandler final :
|
||||
public cPitchYawRotator<cClearMetaOnDrop<cBlockEntityHandler>>
|
||||
public cClearMetaOnDrop<cMetaRotator<cBlockEntityHandler, 0x7, E_META_HOPPER_FACING_ZP, E_META_HOPPER_FACING_XM, E_META_HOPPER_FACING_ZM, E_META_HOPPER_FACING_XP>>
|
||||
{
|
||||
using Super = cPitchYawRotator<cClearMetaOnDrop<cBlockEntityHandler>>;
|
||||
using Super = cClearMetaOnDrop<cMetaRotator<cBlockEntityHandler, 0x7, E_META_HOPPER_FACING_ZP, E_META_HOPPER_FACING_XM, E_META_HOPPER_FACING_ZM, E_META_HOPPER_FACING_XP>>;
|
||||
|
||||
public:
|
||||
|
||||
@ -18,42 +18,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
|
||||
// Convert the blockface into meta:
|
||||
switch (a_ClickedBlockFace)
|
||||
{
|
||||
case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
|
||||
case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
|
||||
case BLOCK_FACE_EAST: a_BlockMeta = E_META_HOPPER_FACING_XM; break;
|
||||
case BLOCK_FACE_NORTH: a_BlockMeta = E_META_HOPPER_FACING_ZP; break;
|
||||
case BLOCK_FACE_SOUTH: a_BlockMeta = E_META_HOPPER_FACING_ZM; break;
|
||||
case BLOCK_FACE_WEST: a_BlockMeta = E_META_HOPPER_FACING_XP; break;
|
||||
case BLOCK_FACE_NONE: a_BlockMeta = E_META_HOPPER_UNATTACHED; break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
UNUSED(a_Meta);
|
||||
return 11;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "BlockHandler.h"
|
||||
#include "BlockInfo.h"
|
||||
#include "Mixins.h"
|
||||
|
||||
|
||||
@ -17,59 +18,24 @@ public:
|
||||
|
||||
using Super::Super;
|
||||
|
||||
|
||||
/** Returns true if the ladder will be supported by the block through the given blockface. */
|
||||
static bool CanBePlacedOn(const BLOCKTYPE a_BlockType, const eBlockFace a_BlockFace)
|
||||
{
|
||||
if (
|
||||
(a_BlockFace == BLOCK_FACE_NONE) ||
|
||||
(a_BlockFace == BLOCK_FACE_BOTTOM) ||
|
||||
(a_BlockFace == BLOCK_FACE_TOP)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return cBlockInfo::IsSolid(a_BlockType);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
// Try finding a suitable neighbor block face for the ladder; start with the given one.
|
||||
if (!LadderCanBePlacedAt(a_ChunkInterface, a_PlacedBlockPos, a_ClickedBlockFace))
|
||||
{
|
||||
a_ClickedBlockFace = FindSuitableBlockFace(a_ChunkInterface, a_PlacedBlockPos);
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the block face of the neighbor to which the ladder is attached to the ladder block's meta. */
|
||||
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
|
||||
{
|
||||
switch (a_NeighborBlockFace)
|
||||
{
|
||||
case BLOCK_FACE_ZM: return 0x2;
|
||||
case BLOCK_FACE_ZP: return 0x3;
|
||||
case BLOCK_FACE_XM: return 0x4;
|
||||
case BLOCK_FACE_XP: return 0x5;
|
||||
case BLOCK_FACE_NONE:
|
||||
case BLOCK_FACE_YM:
|
||||
case BLOCK_FACE_YP:
|
||||
{
|
||||
return 0x2;
|
||||
}
|
||||
}
|
||||
UNREACHABLE("Unsupported neighbor block face");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the ladder block's meta to the block face of the neighbor to which the ladder is attached. */
|
||||
static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
|
||||
{
|
||||
@ -87,51 +53,13 @@ private:
|
||||
|
||||
|
||||
|
||||
/** Finds a suitable block face value for the Ladder.
|
||||
The returned value is the block face of the neighbor of the specified block to which a ladder at a_LadderPos can be attached.
|
||||
Returns BLOCK_FACE_NONE if no valid location is found */
|
||||
static eBlockFace FindSuitableBlockFace(cChunkInterface & a_ChunkInterface, const Vector3i a_LadderPos)
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
for (int FaceInt = BLOCK_FACE_ZM; FaceInt <= BLOCK_FACE_XP; FaceInt++)
|
||||
{
|
||||
eBlockFace Face = static_cast<eBlockFace>(FaceInt);
|
||||
if (LadderCanBePlacedAt(a_ChunkInterface, a_LadderPos, Face))
|
||||
{
|
||||
return Face;
|
||||
}
|
||||
}
|
||||
return BLOCK_FACE_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns true if the ladder in the specified position will be supported by its neghbor through the specified neighbor's blockface. */
|
||||
static bool LadderCanBePlacedAt(cChunkInterface & a_ChunkInterface, const Vector3i a_LadderPos, eBlockFace a_NeighborBlockFace)
|
||||
{
|
||||
if (
|
||||
(a_NeighborBlockFace == BLOCK_FACE_NONE) ||
|
||||
(a_NeighborBlockFace == BLOCK_FACE_BOTTOM) ||
|
||||
(a_NeighborBlockFace == BLOCK_FACE_TOP)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto NeighborPos = AddFaceDirection(a_LadderPos, a_NeighborBlockFace, true);
|
||||
return cBlockInfo::IsSolid(a_ChunkInterface.GetBlock(NeighborPos));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
{
|
||||
auto NeighborBlockFace = MetaDataToBlockFace(a_Chunk.GetMeta(a_RelPos));
|
||||
auto LadderAbsPos = a_Chunk.RelativeToAbsolute(a_RelPos);
|
||||
return LadderCanBePlacedAt(a_ChunkInterface, LadderAbsPos, NeighborBlockFace);
|
||||
auto Face = MetaDataToBlockFace(a_Meta);
|
||||
auto NeighborRelPos = AddFaceDirection(a_Position, Face, true);
|
||||
BLOCKTYPE NeighborBlockType;
|
||||
a_Chunk.UnboundedRelGetBlockType(NeighborRelPos, NeighborBlockType);
|
||||
return CanBePlacedOn(NeighborBlockType, Face);
|
||||
}
|
||||
|
||||
|
||||
|
@ -64,45 +64,6 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = LeverDirectionToMetaData(a_ClickedBlockFace);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the block face of the neighbor to which the lever is attached to the lever block's meta. */
|
||||
inline static NIBBLETYPE LeverDirectionToMetaData(eBlockFace a_Dir)
|
||||
{
|
||||
// Determine lever direction:
|
||||
switch (a_Dir)
|
||||
{
|
||||
case BLOCK_FACE_YP: return 0x6;
|
||||
case BLOCK_FACE_XP: return 0x1;
|
||||
case BLOCK_FACE_XM: return 0x2;
|
||||
case BLOCK_FACE_ZP: return 0x3;
|
||||
case BLOCK_FACE_ZM: return 0x4;
|
||||
case BLOCK_FACE_YM: return 0x0;
|
||||
case BLOCK_FACE_NONE: return 0x6;
|
||||
}
|
||||
UNREACHABLE("Unsupported block face");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the leve block's meta to the block face of the neighbor to which the lever is attached. */
|
||||
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
|
||||
{
|
||||
@ -128,18 +89,18 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
// Find the type of block the lever is attached to:
|
||||
auto Meta = a_Chunk.GetMeta(a_RelPos);
|
||||
auto NeighborFace = BlockMetaDataToBlockFace(Meta);
|
||||
auto NeighborPos = AddFaceDirection(a_RelPos, NeighborFace, true);
|
||||
auto NeighborFace = BlockMetaDataToBlockFace(a_Meta);
|
||||
auto NeighborPos = AddFaceDirection(a_Position, NeighborFace, true);
|
||||
if (!cChunkDef::IsValidHeight(NeighborPos.y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BLOCKTYPE NeighborBlockType;
|
||||
if (!a_Chunk.UnboundedRelGetBlock(NeighborPos, NeighborBlockType, Meta))
|
||||
NIBBLETYPE NeighborMeta;
|
||||
if (!a_Chunk.UnboundedRelGetBlock(NeighborPos, NeighborBlockType, NeighborMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -152,8 +113,8 @@ private:
|
||||
else if (cBlockSlabHandler::IsAnySlabType(NeighborBlockType))
|
||||
{
|
||||
return (
|
||||
(((Meta & 0x08) == 0x08) && (NeighborFace == BLOCK_FACE_TOP)) ||
|
||||
(((Meta & 0x08) == 0) && (NeighborFace == BLOCK_FACE_BOTTOM))
|
||||
(((NeighborMeta & 0x08) == 0x08) && (NeighborFace == BLOCK_FACE_TOP)) ||
|
||||
(((NeighborMeta & 0x08) == 0) && (NeighborFace == BLOCK_FACE_BOTTOM))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -28,9 +28,9 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
auto UnderPos = a_RelPos.addedY(-1);
|
||||
auto UnderPos = a_Position.addedY(-1);
|
||||
if (!cChunkDef::IsValidHeight(UnderPos.y))
|
||||
{
|
||||
return false;
|
||||
|
@ -21,16 +21,16 @@ private:
|
||||
|
||||
// TODO: Add Mushroom Spread
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Cannot be at too much daylight
|
||||
|
||||
switch (a_Chunk.GetBlock(a_RelPos.addedY(-1)))
|
||||
switch (a_Chunk.GetBlock(a_Position.addedY(-1)))
|
||||
{
|
||||
case E_BLOCK_GLASS:
|
||||
case E_BLOCK_CACTUS:
|
||||
|
@ -57,10 +57,10 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
// Needs to be placed on top of a Soulsand block:
|
||||
return ((a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) == E_BLOCK_SOULSAND));
|
||||
return (a_Position.y > 0) && (a_Chunk.GetBlock(a_Position.addedY(-1)) == E_BLOCK_SOULSAND);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,24 +18,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
switch (a_Meta)
|
||||
|
@ -18,28 +18,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
// We set meta to zero so Cuberite doesn't stop a Creative-mode player from building custom portal shapes
|
||||
// CanBeAt doesn't do anything if meta is zero
|
||||
// We set to zero because the client sends meta = 1 or 2 to the server (it calculates rotation itself)
|
||||
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
|
||||
{
|
||||
// No pickups
|
||||
@ -71,14 +49,14 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if ((a_RelPos.y <= 0) || (a_RelPos.y >= cChunkDef::Height - 1))
|
||||
if ((a_Position.y <= 0) || (a_Position.y >= cChunkDef::Height - 1))
|
||||
{
|
||||
return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1
|
||||
return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1.
|
||||
}
|
||||
|
||||
switch (a_Chunk.GetMeta(a_RelPos))
|
||||
switch (a_Meta)
|
||||
{
|
||||
case 0x1:
|
||||
{
|
||||
@ -95,7 +73,7 @@ private:
|
||||
for (const auto & Direction : PortalCheck)
|
||||
{
|
||||
BLOCKTYPE Block;
|
||||
a_Chunk.UnboundedRelGetBlockType(a_RelPos + Direction, Block);
|
||||
a_Chunk.UnboundedRelGetBlockType(a_Position + Direction, Block);
|
||||
if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
|
||||
{
|
||||
return false;
|
||||
@ -118,7 +96,7 @@ private:
|
||||
for (const auto & Direction : PortalCheck)
|
||||
{
|
||||
BLOCKTYPE Block;
|
||||
a_Chunk.UnboundedRelGetBlockType(a_RelPos + Direction, Block);
|
||||
a_Chunk.UnboundedRelGetBlockType(a_Position + Direction, Block);
|
||||
if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
|
||||
{
|
||||
return false;
|
||||
|
@ -17,15 +17,16 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 1)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: check if the block is upside-down slab or upside-down stairs
|
||||
auto Block = a_Chunk.GetBlock(a_RelPos.addedY(-1));
|
||||
|
||||
const auto Block = a_Chunk.GetBlock(a_Position.addedY(-1));
|
||||
switch (Block)
|
||||
{
|
||||
case E_BLOCK_ACACIA_FENCE:
|
||||
@ -41,7 +42,7 @@ private:
|
||||
}
|
||||
default:
|
||||
{
|
||||
return (!cBlockInfo::IsTransparent(Block));
|
||||
return !cBlockInfo::IsTransparent(Block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,66 +18,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
auto Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
|
||||
|
||||
// Pillar block needs additional direction in the metadata:
|
||||
if (Meta != E_META_QUARTZ_PILLAR)
|
||||
{
|
||||
a_BlockMeta = Meta;
|
||||
return true;
|
||||
}
|
||||
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the block face of the pillar block's "base" to the block's metadata. */
|
||||
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
|
||||
{
|
||||
switch (a_BlockFace)
|
||||
{
|
||||
case BLOCK_FACE_YM:
|
||||
case BLOCK_FACE_YP:
|
||||
{
|
||||
return E_META_QUARTZ_PILLAR; // Top or bottom
|
||||
}
|
||||
|
||||
case BLOCK_FACE_ZP:
|
||||
case BLOCK_FACE_ZM:
|
||||
{
|
||||
return 0x4; // North or south
|
||||
}
|
||||
|
||||
case BLOCK_FACE_XP:
|
||||
case BLOCK_FACE_XM:
|
||||
{
|
||||
return 0x3; // East or west
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return E_META_QUARTZ_PILLAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
UNUSED(a_Meta);
|
||||
|
@ -25,141 +25,8 @@ public:
|
||||
|
||||
using Super::Super;
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = FindMeta(a_ChunkInterface, a_PlacedBlockPos);
|
||||
return a_Player.GetWorld()->DoWithChunkAt(a_PlacedBlockPos,
|
||||
[this, a_PlacedBlockPos, &a_ChunkInterface](cChunk & a_Chunk)
|
||||
{
|
||||
auto RelPos = cChunkDef::AbsoluteToRelative(a_PlacedBlockPos);
|
||||
return CanBeAt(a_ChunkInterface, RelPos, a_Chunk);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual void OnPlaced(
|
||||
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
|
||||
Vector3i a_BlockPos,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
Super::OnPlaced(a_ChunkInterface, a_WorldInterface, a_BlockPos, a_BlockType, a_BlockMeta);
|
||||
|
||||
// Alert diagonal rails:
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, 1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, 1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, 1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, -1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, -1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, -1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, 1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, -1), BLOCK_FACE_NONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual void OnBroken(
|
||||
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
|
||||
Vector3i a_BlockPos,
|
||||
BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta,
|
||||
const cEntity * a_Digger
|
||||
) const override
|
||||
{
|
||||
Super::OnBroken(a_ChunkInterface, a_WorldInterface, a_BlockPos, a_OldBlockType, a_OldBlockMeta, a_Digger);
|
||||
|
||||
// Alert diagonal rails:
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, 1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, 1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, 1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, -1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, -1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, -1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, 1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, -1), BLOCK_FACE_NONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const override
|
||||
{
|
||||
const auto Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
|
||||
const auto NewMeta = FindMeta(a_ChunkInterface, a_BlockPos);
|
||||
if ((Meta != NewMeta) && IsUnstable(a_ChunkInterface, a_BlockPos))
|
||||
{
|
||||
a_ChunkInterface.FastSetBlock(a_BlockPos, m_BlockType, (m_BlockType == E_BLOCK_RAIL) ? NewMeta : NewMeta | (Meta & 0x08));
|
||||
}
|
||||
|
||||
Super::OnNeighborChanged(a_ChunkInterface, a_BlockPos, a_WhichNeighbor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(a_RelPos.addedY(-1))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelPos);
|
||||
switch (Meta)
|
||||
{
|
||||
case E_META_RAIL_ASCEND_XP:
|
||||
case E_META_RAIL_ASCEND_XM:
|
||||
case E_META_RAIL_ASCEND_ZM:
|
||||
case E_META_RAIL_ASCEND_ZP:
|
||||
{
|
||||
// Mapping between the meta and the neighbors that need checking
|
||||
Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero
|
||||
static const Vector3i Coords[] =
|
||||
{
|
||||
{ 1, 0, 0}, // east, XP
|
||||
{-1, 0, 0}, // west, XM
|
||||
{ 0, 0, -1}, // north, ZM
|
||||
{ 0, 0, 1}, // south, ZP
|
||||
} ;
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[Meta], BlockType, BlockMeta))
|
||||
{
|
||||
// Too close to the edge, cannot simulate
|
||||
return true;
|
||||
}
|
||||
return cBlockInfo::FullyOccupiesVoxel(BlockType);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE FindMeta(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos) const
|
||||
static NIBBLETYPE FindMeta(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, BLOCKTYPE a_RailType)
|
||||
{
|
||||
NIBBLETYPE Meta = 0;
|
||||
char RailsCnt = 0;
|
||||
@ -234,19 +101,21 @@ private:
|
||||
}
|
||||
if (RailsCnt > 1)
|
||||
{
|
||||
if (Neighbors[3] && Neighbors[0] && CanThisRailCurve())
|
||||
const bool CanCurve = a_RailType == E_BLOCK_RAIL;
|
||||
|
||||
if (Neighbors[3] && Neighbors[0] && CanCurve)
|
||||
{
|
||||
return E_META_RAIL_CURVED_ZP_XP;
|
||||
}
|
||||
else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve())
|
||||
else if (Neighbors[3] && Neighbors[1] && CanCurve)
|
||||
{
|
||||
return E_META_RAIL_CURVED_ZP_XM;
|
||||
}
|
||||
else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve())
|
||||
else if (Neighbors[2] && Neighbors[0] && CanCurve)
|
||||
{
|
||||
return E_META_RAIL_CURVED_ZM_XP;
|
||||
}
|
||||
else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve())
|
||||
else if (Neighbors[2] && Neighbors[1] && CanCurve)
|
||||
{
|
||||
return E_META_RAIL_CURVED_ZM_XM;
|
||||
}
|
||||
@ -275,7 +144,7 @@ private:
|
||||
return E_META_RAIL_ZM_ZP;
|
||||
}
|
||||
|
||||
if (CanThisRailCurve())
|
||||
if (CanCurve)
|
||||
{
|
||||
ASSERT(!"Weird neighbor count");
|
||||
}
|
||||
@ -283,10 +152,42 @@ private:
|
||||
return Meta;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool CanThisRailCurve(void) const
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
return m_BlockType == E_BLOCK_RAIL;
|
||||
if ((a_Position.y <= 0) || !cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(a_Position.addedY(-1))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (a_Meta)
|
||||
{
|
||||
case E_META_RAIL_ASCEND_XP:
|
||||
case E_META_RAIL_ASCEND_XM:
|
||||
case E_META_RAIL_ASCEND_ZM:
|
||||
case E_META_RAIL_ASCEND_ZP:
|
||||
{
|
||||
// Mapping between the meta and the neighbors that need checking
|
||||
a_Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero
|
||||
static const Vector3i Coords[] =
|
||||
{
|
||||
{ 1, 0, 0}, // east, XP
|
||||
{-1, 0, 0}, // west, XM
|
||||
{ 0, 0, -1}, // north, ZM
|
||||
{ 0, 0, 1}, // south, ZP
|
||||
} ;
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_Chunk.UnboundedRelGetBlock(a_Position + Coords[a_Meta], BlockType, BlockMeta))
|
||||
{
|
||||
// Too close to the edge, cannot simulate
|
||||
return true;
|
||||
}
|
||||
return cBlockInfo::FullyOccupiesVoxel(BlockType);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -521,6 +422,60 @@ private:
|
||||
}
|
||||
|
||||
|
||||
virtual void OnBroken(
|
||||
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
|
||||
Vector3i a_BlockPos,
|
||||
BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta,
|
||||
const cEntity * a_Digger
|
||||
) const override
|
||||
{
|
||||
Super::OnBroken(a_ChunkInterface, a_WorldInterface, a_BlockPos, a_OldBlockType, a_OldBlockMeta, a_Digger);
|
||||
|
||||
// Alert diagonal rails:
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, 1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, 1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, 1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, -1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, -1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, -1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, 1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, -1), BLOCK_FACE_NONE);
|
||||
}
|
||||
|
||||
|
||||
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const override
|
||||
{
|
||||
const auto Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
|
||||
const auto NewMeta = FindMeta(a_ChunkInterface, a_BlockPos, m_BlockType);
|
||||
if ((Meta != NewMeta) && IsUnstable(a_ChunkInterface, a_BlockPos))
|
||||
{
|
||||
a_ChunkInterface.FastSetBlock(a_BlockPos, m_BlockType, (m_BlockType == E_BLOCK_RAIL) ? NewMeta : NewMeta | (Meta & 0x08));
|
||||
}
|
||||
|
||||
Super::OnNeighborChanged(a_ChunkInterface, a_BlockPos, a_WhichNeighbor);
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlaced(
|
||||
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
|
||||
Vector3i a_BlockPos,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
Super::OnPlaced(a_ChunkInterface, a_WorldInterface, a_BlockPos, a_BlockType, a_BlockMeta);
|
||||
|
||||
// Alert diagonal rails:
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, 1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, 1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, 1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, +1, -1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 1, -1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i(-1, -1, 0), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, 1), BLOCK_FACE_NONE);
|
||||
NeighborChanged(a_ChunkInterface, a_BlockPos + Vector3i( 0, -1, -1), BLOCK_FACE_NONE);
|
||||
}
|
||||
|
||||
|
||||
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
// Bit 0x08 is a flag when a_Meta is in the range 0x00--0x05 and 0x0A--0x0F.
|
||||
|
@ -19,16 +19,16 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BLOCKTYPE BelowBlock;
|
||||
NIBBLETYPE BelowBlockMeta;
|
||||
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
|
||||
a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
|
||||
|
||||
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
|
||||
{
|
||||
|
@ -105,16 +105,16 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BLOCKTYPE BelowBlock;
|
||||
NIBBLETYPE BelowBlockMeta;
|
||||
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
|
||||
a_Chunk.GetBlockTypeMeta(a_Position.addedY(-1), BelowBlock, BelowBlockMeta);
|
||||
|
||||
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
|
||||
{
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
|
||||
/** Handler for blocks that have 3 orientations (hay bale, log), specified by the upper 2 bits in meta.
|
||||
Handles setting the correct orientation on placement.
|
||||
Additionally supports the metadata specifying block sub-type in its lower 2 bits. */
|
||||
class cBlockSidewaysHandler final :
|
||||
public cBlockHandler
|
||||
@ -21,66 +20,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
|
||||
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace, Meta);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
|
||||
{
|
||||
// Reset the orientation part of meta, keep the sub-type part of meta
|
||||
// Reset the orientation part of meta, keep the sub-type part of meta:
|
||||
return cItem(m_BlockType, 1, a_BlockMeta & 0x03);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace, NIBBLETYPE a_Meta)
|
||||
{
|
||||
switch (a_BlockFace)
|
||||
{
|
||||
case BLOCK_FACE_YM:
|
||||
case BLOCK_FACE_YP:
|
||||
{
|
||||
return a_Meta; // Top or bottom, just return original
|
||||
}
|
||||
|
||||
case BLOCK_FACE_ZP:
|
||||
case BLOCK_FACE_ZM:
|
||||
{
|
||||
return a_Meta | 0x8; // North or south
|
||||
}
|
||||
|
||||
case BLOCK_FACE_XP:
|
||||
case BLOCK_FACE_XM:
|
||||
{
|
||||
return a_Meta | 0x4; // East or west
|
||||
}
|
||||
|
||||
case BLOCK_FACE_NONE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
UNREACHABLE("Unsupported block face");
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -17,20 +17,6 @@ public:
|
||||
|
||||
using Super::Super;
|
||||
|
||||
/** Converts the (player) rotation to placed-signpost block meta. */
|
||||
static NIBBLETYPE RotationToMetaData(double a_Rotation)
|
||||
{
|
||||
a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
|
||||
if (a_Rotation > 360)
|
||||
{
|
||||
a_Rotation -= 360;
|
||||
}
|
||||
|
||||
a_Rotation = (a_Rotation / 360) * 16;
|
||||
|
||||
return (static_cast<char>(a_Rotation)) % 16;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
|
||||
@ -42,14 +28,15 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BLOCKTYPE Type = a_Chunk.GetBlock(a_RelPos.addedY(-1));
|
||||
return ((Type == E_BLOCK_SIGN_POST) || (Type == E_BLOCK_WALLSIGN) || cBlockInfo::IsSolid(Type));
|
||||
|
||||
BLOCKTYPE Type = a_Chunk.GetBlock(a_Position.addedY(-1));
|
||||
return (Type == E_BLOCK_SIGN_POST) || (Type == E_BLOCK_WALLSIGN) || cBlockInfo::IsSolid(Type);
|
||||
}
|
||||
|
||||
|
||||
|
@ -46,64 +46,25 @@ private:
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
|
||||
|
||||
// Set the correct metadata based on player equipped item (i.e. a_BlockMeta not initialised yet)
|
||||
switch (a_ClickedBlockFace)
|
||||
/* Double slab combining uses build collision checks to replace single slabs with double slabs in the right conditions.
|
||||
For us to be replaced, the player must be:
|
||||
1. Placing the same slab material.
|
||||
2. Placing the same slab sub-kind (and existing slab is single). */
|
||||
if ((m_BlockType != a_HeldItem.m_ItemType) || ((a_Meta & 0x07) != a_HeldItem.m_ItemDamage))
|
||||
{
|
||||
case BLOCK_FACE_TOP:
|
||||
{
|
||||
// Bottom half slab block
|
||||
a_BlockMeta = Meta & 0x07;
|
||||
break;
|
||||
}
|
||||
case BLOCK_FACE_BOTTOM:
|
||||
{
|
||||
// Top half slab block
|
||||
a_BlockMeta = Meta | 0x08;
|
||||
break;
|
||||
}
|
||||
case BLOCK_FACE_EAST:
|
||||
case BLOCK_FACE_NORTH:
|
||||
case BLOCK_FACE_SOUTH:
|
||||
case BLOCK_FACE_WEST:
|
||||
{
|
||||
if (a_CursorPos.y > 7)
|
||||
{
|
||||
// Cursor at top half of block, place top slab
|
||||
a_BlockMeta = Meta | 0x08; break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cursor at bottom half of block, place bottom slab
|
||||
a_BlockMeta = Meta & 0x07; break;
|
||||
}
|
||||
}
|
||||
case BLOCK_FACE_NONE: return false;
|
||||
} // switch (a_BlockFace)
|
||||
|
||||
// Check if the block at the coordinates is a single slab. Eligibility for combining has already been processed in ClientHandle
|
||||
// Changed to-be-placed to a double slab if we are clicking on a single slab, as opposed to placing one for the first time
|
||||
if (IsAnySlabType(a_ChunkInterface.GetBlock(a_PlacedBlockPos)))
|
||||
{
|
||||
a_BlockType = GetDoubleSlabType(m_BlockType);
|
||||
a_BlockMeta = a_BlockMeta & 0x07;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
const bool IsTopSlab = (a_Meta & 0x08) == 0x08;
|
||||
const auto CanClickCombine = ((a_ClickedBlockFace == BLOCK_FACE_TOP) && !IsTopSlab) || ((a_ClickedBlockFace == BLOCK_FACE_BOTTOM) && IsTopSlab);
|
||||
|
||||
/* When the player clicks on us directly, we'll combine if we're
|
||||
a bottom slab and he clicked the top, or vice versa. Clicking on the sides will not combine.
|
||||
However, when indirectly clicked (on the side of another block, that caused placement to go to us)
|
||||
the conditions are exactly the opposite. */
|
||||
return a_ClickedDirectly ? CanClickCombine : !CanClickCombine;
|
||||
}
|
||||
|
||||
|
||||
@ -131,24 +92,6 @@ private:
|
||||
|
||||
|
||||
|
||||
/** Converts the single-slab blocktype to its equivalent double-slab blocktype */
|
||||
static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType)
|
||||
{
|
||||
switch (a_SingleSlabBlockType)
|
||||
{
|
||||
case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB;
|
||||
case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB;
|
||||
case E_BLOCK_RED_SANDSTONE_SLAB: return E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB;
|
||||
case E_BLOCK_PURPUR_SLAB: return E_BLOCK_PURPUR_DOUBLE_SLAB;
|
||||
}
|
||||
ASSERT(!"Unhandled slab type!");
|
||||
return E_BLOCK_AIR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
// Toggle the 4th bit - up / down:
|
||||
|
@ -20,58 +20,21 @@ private:
|
||||
|
||||
enum
|
||||
{
|
||||
FullBlockMeta = 7 // Meta value of a full-height snow block
|
||||
FullBlockMeta = 7 // Meta value of a full-height snow block.
|
||||
};
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
|
||||
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
|
||||
// Check if incrementing existing snow height:
|
||||
BLOCKTYPE BlockBeforePlacement;
|
||||
NIBBLETYPE MetaBeforePlacement;
|
||||
a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos, BlockBeforePlacement, MetaBeforePlacement);
|
||||
if ((BlockBeforePlacement == E_BLOCK_SNOW) && (MetaBeforePlacement < FullBlockMeta))
|
||||
{
|
||||
// Only increment if:
|
||||
// - A snow block was already there (not first time placement) AND
|
||||
// - Height is smaller than the maximum possible
|
||||
a_BlockMeta = MetaBeforePlacement + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// First time placement, check placement is valid
|
||||
a_BlockMeta = 0;
|
||||
BLOCKTYPE BlockBelow;
|
||||
NIBBLETYPE MetaBelow;
|
||||
return (
|
||||
(a_PlacedBlockPos.y > 0) &&
|
||||
a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos.addedY(-1), BlockBelow, MetaBelow) &&
|
||||
CanBeOn(BlockBelow, MetaBelow)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if ((a_Player.GetEquippedItem().m_ItemType == E_BLOCK_SNOW) && (a_Meta < FullBlockMeta))
|
||||
{
|
||||
return true; // If a player is holding a (thin) snow block and it's size can be increased, return collision ignored
|
||||
}
|
||||
|
||||
if (a_Meta == 0)
|
||||
{
|
||||
return true; // If at normal snowfall height (lowest), we ignore collision
|
||||
return true; // If at normal snowfall height (lowest), we ignore collision.
|
||||
}
|
||||
|
||||
// Special case if a player is holding a (thin) snow block and its size can be increased:
|
||||
if ((a_HeldItem.m_ItemType == E_BLOCK_SNOW) && (a_Meta < FullBlockMeta))
|
||||
{
|
||||
return !a_ClickedDirectly || (a_ClickedBlockFace == BLOCK_FACE_YP); // If clicked an adjacent block, or clicked YP directly, we ignore collision.
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -83,7 +46,7 @@ private:
|
||||
|
||||
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
|
||||
{
|
||||
// No drop unless dug up with a shovel
|
||||
// No drop unless dug up with a shovel:
|
||||
if ((a_Tool == nullptr) || !ItemCategory::IsShovel(a_Tool->m_ItemType))
|
||||
{
|
||||
return {};
|
||||
@ -104,13 +67,13 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto BelowPos = a_RelPos.addedY(-1);
|
||||
auto BelowPos = a_Position.addedY(-1);
|
||||
auto BlockBelow = a_Chunk.GetBlock(BelowPos);
|
||||
auto MetaBelow = a_Chunk.GetMeta(BelowPos);
|
||||
return CanBeOn(BlockBelow, MetaBelow);
|
||||
@ -141,7 +104,7 @@ private:
|
||||
/** Returns true if snow can be placed on top of a block with the given type and meta. */
|
||||
static bool CanBeOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
// If block below is snowable, or it is a thin slow block and is a full thin snow block, say yay
|
||||
// If block below is snowable, or it is a thin snow block and is a full thin snow block, say yay:
|
||||
return (
|
||||
cBlockInfo::IsSnowable(a_BlockType) ||
|
||||
(
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
|
||||
class cBlockStairsHandler final :
|
||||
public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>>
|
||||
public cClearMetaOnDrop<cYawRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>>
|
||||
{
|
||||
using Super = cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>>;
|
||||
using Super = cClearMetaOnDrop<cYawRotator<cBlockHandler, 0x03, 0x03, 0x00, 0x02, 0x01, true>>;
|
||||
|
||||
public:
|
||||
|
||||
@ -18,74 +18,6 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
UNUSED(a_ChunkInterface);
|
||||
UNUSED(a_PlacedBlockPos);
|
||||
UNUSED(a_CursorPos);
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = RotationToMetaData(a_Player.GetYaw());
|
||||
switch (a_ClickedBlockFace)
|
||||
{
|
||||
case BLOCK_FACE_TOP: break;
|
||||
case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block
|
||||
case BLOCK_FACE_EAST:
|
||||
case BLOCK_FACE_NORTH:
|
||||
case BLOCK_FACE_SOUTH:
|
||||
case BLOCK_FACE_WEST:
|
||||
{
|
||||
// When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block
|
||||
if (a_CursorPos.y > 8)
|
||||
{
|
||||
a_BlockMeta |= 0x4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BLOCK_FACE_NONE: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static NIBBLETYPE RotationToMetaData(double a_Rotation)
|
||||
{
|
||||
a_Rotation += 90 + 45; // So its not aligned with axis
|
||||
if (a_Rotation > 360)
|
||||
{
|
||||
a_Rotation -= 360;
|
||||
}
|
||||
if ((a_Rotation >= 0) && (a_Rotation < 90))
|
||||
{
|
||||
return 0x0;
|
||||
}
|
||||
else if ((a_Rotation >= 180) && (a_Rotation < 270))
|
||||
{
|
||||
return 0x1;
|
||||
}
|
||||
else if ((a_Rotation >= 90) && (a_Rotation < 180))
|
||||
{
|
||||
return 0x2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0x3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
// Toggle bit 3:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,14 +27,14 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (a_Chunk.GetBlock(a_RelPos.addedY(-1)))
|
||||
switch (a_Chunk.GetBlock(a_Position.addedY(-1)))
|
||||
{
|
||||
case E_BLOCK_DIRT:
|
||||
case E_BLOCK_GRASS:
|
||||
@ -52,7 +52,7 @@ private:
|
||||
{
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[i], BlockType, BlockMeta))
|
||||
if (!a_Chunk.UnboundedRelGetBlock(a_Position + Coords[i], BlockType, BlockMeta))
|
||||
{
|
||||
// Too close to the edge, cannot simulate
|
||||
return true;
|
||||
|
@ -20,7 +20,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
|
||||
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -52,14 +52,14 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
if (a_RelPos.y <= 0)
|
||||
if (a_Position.y <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1));
|
||||
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_Position.addedY(-1));
|
||||
return IsBlockTypeOfDirt(BelowBlock);
|
||||
}
|
||||
|
||||
|
@ -18,91 +18,6 @@ public:
|
||||
|
||||
using Super::Super;
|
||||
|
||||
protected:
|
||||
|
||||
~cBlockTorchBaseHandler() = default;
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
BLOCKTYPE ClickedBlockType;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
auto ClickedBlockPos = AddFaceDirection(a_PlacedBlockPos, a_ClickedBlockFace, true);
|
||||
a_ChunkInterface.GetBlockTypeMeta(ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
|
||||
if (!CanBePlacedOn(ClickedBlockType, ClickedBlockMeta, a_ClickedBlockFace))
|
||||
{
|
||||
// Couldn't be placed on whatever face was clicked, last ditch resort - find another face
|
||||
a_ClickedBlockFace = FindSuitableFace(a_ChunkInterface, a_PlacedBlockPos); // Set a_BlockFace to a valid direction which will be converted later to a metadata
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
|
||||
{
|
||||
// No attachable face found - don't place the torch
|
||||
return false;
|
||||
}
|
||||
}
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the block face of the neighbor to which the torch is attached, to the torch block's meta. */
|
||||
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
|
||||
{
|
||||
switch (a_NeighborBlockFace)
|
||||
{
|
||||
case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0;
|
||||
case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR;
|
||||
case BLOCK_FACE_EAST: return E_META_TORCH_EAST;
|
||||
case BLOCK_FACE_WEST: return E_META_TORCH_WEST;
|
||||
case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH;
|
||||
case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH;
|
||||
case BLOCK_FACE_NONE:
|
||||
{
|
||||
ASSERT(!"Unhandled torch direction!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the torch block's meta to the block face of the neighbor to which the torch is attached. */
|
||||
inline static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
|
||||
{
|
||||
switch (a_MetaData)
|
||||
{
|
||||
case 0: return BLOCK_FACE_TOP; // By default, the torches stand on the ground
|
||||
case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP;
|
||||
case E_META_TORCH_EAST: return BLOCK_FACE_EAST;
|
||||
case E_META_TORCH_WEST: return BLOCK_FACE_WEST;
|
||||
case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH;
|
||||
case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unhandled torch metadata");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return BLOCK_FACE_TOP;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns true if the torch can be placed on the specified block's face. */
|
||||
static bool CanBePlacedOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_BlockFace)
|
||||
@ -163,37 +78,40 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
~cBlockTorchBaseHandler() = default;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/** Returns a suitable neighbor's blockface to place the torch at the specified pos
|
||||
Returns BLOCK_FACE_NONE on failure */
|
||||
static eBlockFace FindSuitableFace(cChunkInterface & a_ChunkInterface, const Vector3i a_TorchPos)
|
||||
/** Converts the torch block's meta to the block face of the neighbor to which the torch is attached. */
|
||||
inline static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
|
||||
{
|
||||
for (int i = BLOCK_FACE_YM; i <= BLOCK_FACE_XP; i++) // Loop through all faces
|
||||
switch (a_MetaData)
|
||||
{
|
||||
auto Face = static_cast<eBlockFace>(i);
|
||||
auto NeighborPos = AddFaceDirection(a_TorchPos, Face, true);
|
||||
BLOCKTYPE NeighborBlockType;
|
||||
NIBBLETYPE NeighborBlockMeta;
|
||||
a_ChunkInterface.GetBlockTypeMeta(NeighborPos, NeighborBlockType, NeighborBlockMeta);
|
||||
if (CanBePlacedOn(NeighborBlockType, NeighborBlockMeta, Face))
|
||||
case 0: return BLOCK_FACE_TOP; // By default, the torches stand on the ground
|
||||
case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP;
|
||||
case E_META_TORCH_EAST: return BLOCK_FACE_EAST;
|
||||
case E_META_TORCH_WEST: return BLOCK_FACE_WEST;
|
||||
case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH;
|
||||
case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH;
|
||||
default:
|
||||
{
|
||||
return Face;
|
||||
ASSERT(!"Unhandled torch metadata");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return BLOCK_FACE_NONE;
|
||||
return BLOCK_FACE_TOP;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
auto Face = MetaDataToBlockFace(a_Chunk.GetMeta(a_RelPos));
|
||||
auto NeighborRelPos = AddFaceDirection(a_RelPos, Face, true);
|
||||
auto Face = MetaDataToBlockFace(a_Meta);
|
||||
auto NeighborRelPos = AddFaceDirection(a_Position, Face, true);
|
||||
BLOCKTYPE NeighborBlockType;
|
||||
NIBBLETYPE NeighborBlockMeta;
|
||||
if (!a_Chunk.UnboundedRelGetBlock(NeighborRelPos, NeighborBlockType, NeighborBlockMeta))
|
||||
|
@ -71,74 +71,6 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_YP)
|
||||
{
|
||||
// Trapdoor is placed on top of a block.
|
||||
// Engage yaw rotation to determine hinge direction:
|
||||
return Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta);
|
||||
}
|
||||
else if (a_ClickedBlockFace == BLOCK_FACE_YM)
|
||||
{
|
||||
// Trapdoor is placed on bottom of a block.
|
||||
// Engage yaw rotation to determine hinge direction:
|
||||
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Toggle 'Move up half-block' bit on:
|
||||
a_BlockMeta |= 0x8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Placement on block sides; hinge direction is determined by which side was clicked:
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
|
||||
|
||||
if (a_CursorPos.y > 7)
|
||||
{
|
||||
// Trapdoor is placed on a higher half of a vertical block.
|
||||
// Toggle 'Move up half-block' bit on:
|
||||
a_BlockMeta |= 0x8;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
|
||||
{
|
||||
switch (a_BlockFace)
|
||||
{
|
||||
case BLOCK_FACE_ZP: return 0x1;
|
||||
case BLOCK_FACE_ZM: return 0x0;
|
||||
case BLOCK_FACE_XP: return 0x3;
|
||||
case BLOCK_FACE_XM: return 0x2;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unhandled block face!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
|
||||
{
|
||||
switch (a_Meta & 0x3)
|
||||
|
@ -30,57 +30,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
a_BlockType = m_BlockType;
|
||||
|
||||
switch (a_ClickedBlockFace)
|
||||
{
|
||||
case BLOCK_FACE_XM:
|
||||
{
|
||||
a_BlockMeta = 0x1;
|
||||
return true;
|
||||
}
|
||||
case BLOCK_FACE_XP:
|
||||
{
|
||||
a_BlockMeta = 0x3;
|
||||
return true;
|
||||
}
|
||||
case BLOCK_FACE_ZM:
|
||||
{
|
||||
a_BlockMeta = 0x2;
|
||||
return true;
|
||||
}
|
||||
case BLOCK_FACE_ZP:
|
||||
{
|
||||
a_BlockMeta = 0x0;
|
||||
return true;
|
||||
}
|
||||
case BLOCK_FACE_NONE:
|
||||
case BLOCK_FACE_YM:
|
||||
case BLOCK_FACE_YP:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
UNREACHABLE("Unsupported block face");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
{
|
||||
const auto Meta = a_Chunk.GetMeta(a_RelPos);
|
||||
const auto RearPosition = AddFaceDirection(a_RelPos, MetadataToDirection(Meta), true);
|
||||
const auto RearPosition = AddFaceDirection(a_Position, MetadataToDirection(a_Meta), true);
|
||||
|
||||
BLOCKTYPE NeighborBlockType;
|
||||
if (!a_Chunk.UnboundedRelGetBlockType(RearPosition, NeighborBlockType))
|
||||
|
@ -17,35 +17,16 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
static const NIBBLETYPE VINE_LOST_SUPPORT = 16;
|
||||
static const NIBBLETYPE VINE_UNCHANGED = 17;
|
||||
|
||||
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
// TODO: Disallow placement where the vine doesn't attach to something properly
|
||||
BLOCKTYPE BlockType = 0;
|
||||
NIBBLETYPE BlockMeta;
|
||||
a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos, BlockType, BlockMeta);
|
||||
if (BlockType == m_BlockType)
|
||||
{
|
||||
a_BlockMeta = BlockMeta | DirectionToMetaData(a_ClickedBlockFace);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_BlockMeta = DirectionToMetaData(a_ClickedBlockFace);
|
||||
}
|
||||
a_BlockType = m_BlockType;
|
||||
return true;
|
||||
return GetMaxMeta(a_Chunk, a_Position, a_Meta) != VINE_LOST_SUPPORT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
|
||||
{
|
||||
// Only drops self when using shears, otherwise drops nothing:
|
||||
@ -60,22 +41,6 @@ private:
|
||||
|
||||
|
||||
|
||||
static NIBBLETYPE DirectionToMetaData(char a_BlockFace)
|
||||
{
|
||||
switch (a_BlockFace)
|
||||
{
|
||||
case BLOCK_FACE_NORTH: return 0x1;
|
||||
case BLOCK_FACE_SOUTH: return 0x4;
|
||||
case BLOCK_FACE_WEST: return 0x8;
|
||||
case BLOCK_FACE_EAST: return 0x2;
|
||||
default: return 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static char MetaDataToDirection(NIBBLETYPE a_MetaData)
|
||||
{
|
||||
switch (a_MetaData)
|
||||
@ -122,8 +87,9 @@ private:
|
||||
|
||||
|
||||
|
||||
/** Returns the meta that has the maximum allowable sides of the vine, given the surroundings */
|
||||
static NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, Vector3i a_RelPos)
|
||||
/** Returns the meta that has the maximum allowable sides of the vine, given the surroundings and current vine meta.
|
||||
Returns special values for a vine that can continue to exist unchanged, or must die completely. */
|
||||
static NIBBLETYPE GetMaxMeta(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_CurrentMeta)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
@ -136,21 +102,41 @@ private:
|
||||
{ 0, -1, 4}, // north, ZM
|
||||
{ 1, 0, 8}, // east, XP
|
||||
} ;
|
||||
NIBBLETYPE res = 0;
|
||||
|
||||
NIBBLETYPE MaxMeta = 0;
|
||||
for (auto & Coord : Coords)
|
||||
{
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
auto checkPos = a_RelPos.addedXZ(Coord.x, Coord.z);
|
||||
auto checkPos = a_Position.addedXZ(Coord.x, Coord.z);
|
||||
if (
|
||||
a_Chunk.UnboundedRelGetBlock(checkPos.x, checkPos.y, checkPos.z, BlockType, BlockMeta) &&
|
||||
IsBlockAttachable(BlockType)
|
||||
)
|
||||
{
|
||||
res |= Coord.Bit;
|
||||
MaxMeta |= Coord.Bit;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
||||
// Check if vine above us, add its meta to MaxMeta:
|
||||
if ((a_Position.y < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_Position.addedY(1)) == E_BLOCK_VINES))
|
||||
{
|
||||
MaxMeta |= a_Chunk.GetMeta(a_Position.addedY(1));
|
||||
}
|
||||
|
||||
NIBBLETYPE Common = a_CurrentMeta & MaxMeta; // Neighbors that we have and are legal.
|
||||
if (Common != a_CurrentMeta)
|
||||
{
|
||||
bool HasTop = (a_Position.y < (cChunkDef::Height - 1)) && IsBlockAttachable(a_Chunk.GetBlock(a_Position.addedY(1)));
|
||||
if ((Common == 0) && !HasTop) // Meta equals 0 also means top. Make a last-ditch attempt to save the vine.
|
||||
{
|
||||
return VINE_LOST_SUPPORT;
|
||||
}
|
||||
|
||||
return Common;
|
||||
}
|
||||
|
||||
return VINE_UNCHANGED;
|
||||
}
|
||||
|
||||
|
||||
@ -162,28 +148,25 @@ private:
|
||||
a_ChunkInterface.DoWithChunkAt(a_BlockPos, [&](cChunk & a_Chunk)
|
||||
{
|
||||
|
||||
const auto a_RelPos = cChunkDef::AbsoluteToRelative(a_BlockPos);
|
||||
NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelPos);
|
||||
NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelPos);
|
||||
const auto Position = cChunkDef::AbsoluteToRelative(a_BlockPos);
|
||||
const auto MaxMeta = GetMaxMeta(a_Chunk, Position, a_Chunk.GetMeta(Position));
|
||||
|
||||
// Check if vine above us, add its meta to MaxMeta
|
||||
if ((a_RelPos.y < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelPos.addedY(1)) == m_BlockType))
|
||||
if (MaxMeta == VINE_UNCHANGED)
|
||||
{
|
||||
MaxMeta |= a_Chunk.GetMeta(a_RelPos.addedY(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
NIBBLETYPE Common = CurMeta & MaxMeta; // Neighbors that we have and are legal
|
||||
if (Common != CurMeta)
|
||||
// There is a neighbor missing, need to update the meta or even destroy the block.
|
||||
|
||||
if (MaxMeta == VINE_LOST_SUPPORT)
|
||||
{
|
||||
// There is a neighbor missing, need to update the meta or even destroy the block
|
||||
bool HasTop = (a_RelPos.y < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelPos.addedY(1)));
|
||||
if ((Common == 0) && !HasTop)
|
||||
{
|
||||
// The vine just lost all its support, destroy the block:
|
||||
a_Chunk.SetBlock(a_RelPos, E_BLOCK_AIR, 0);
|
||||
return false;
|
||||
}
|
||||
a_Chunk.SetBlock(a_RelPos, m_BlockType, Common);
|
||||
// The vine just lost all its support, destroy the block:
|
||||
a_Chunk.SetBlock(Position, E_BLOCK_AIR, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It lost some of its support, set it to what remains (SetBlock to notify neighbors):
|
||||
a_Chunk.SetBlock(Position, E_BLOCK_VINES, MaxMeta);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -194,9 +177,9 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) const override
|
||||
virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
|
||||
{
|
||||
return true;
|
||||
return !a_ClickedDirectly || (a_HeldItem.m_ItemType != m_BlockType);
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,25 +17,6 @@ public:
|
||||
|
||||
using Super::Super;
|
||||
|
||||
/** Converts the block face of the neighbor to which the wallsign is attached to the wallsign block's meta. */
|
||||
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
|
||||
{
|
||||
switch (a_NeighborBlockFace)
|
||||
{
|
||||
case BLOCK_FACE_ZM: return 0x02;
|
||||
case BLOCK_FACE_ZP: return 0x03;
|
||||
case BLOCK_FACE_XM: return 0x04;
|
||||
case BLOCK_FACE_XP: return 0x05;
|
||||
case BLOCK_FACE_NONE:
|
||||
case BLOCK_FACE_YP:
|
||||
case BLOCK_FACE_YM:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0x02;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
|
||||
@ -47,16 +28,16 @@ private:
|
||||
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) const override
|
||||
virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
auto NeighborPos = a_RelPos + GetOffsetBehindTheSign(a_Chunk.GetMeta(a_RelPos));
|
||||
auto NeighborPos = a_Position + GetOffsetBehindTheSign(a_Meta);
|
||||
BLOCKTYPE NeighborType;
|
||||
if (!a_Chunk.UnboundedRelGetBlockType(NeighborPos, NeighborType))
|
||||
{
|
||||
// The neighbor is not accessible (unloaded chunk), bail out without changing this
|
||||
return true;
|
||||
}
|
||||
return ((NeighborType == E_BLOCK_WALLSIGN) || (NeighborType == E_BLOCK_SIGN_POST) || cBlockInfo::IsSolid(NeighborType));
|
||||
return (NeighborType == E_BLOCK_WALLSIGN) || (NeighborType == E_BLOCK_SIGN_POST) || cBlockInfo::IsSolid(NeighborType);
|
||||
}
|
||||
|
||||
|
||||
|
@ -167,27 +167,6 @@ public:
|
||||
|
||||
using Super::Super;
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
|
||||
const Vector3i a_BlockPos,
|
||||
eBlockFace a_BlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
NIBBLETYPE BaseMeta;
|
||||
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_BlockPos, a_BlockFace, a_CursorPos, a_BlockType, BaseMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
a_BlockMeta = (BaseMeta & ~BitMask) | YawToMetaData(a_Player.GetYaw());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the rotation value as returned by cPlayer::GetYaw() to the appropriate metadata
|
||||
value for a block placed by a player facing that way */
|
||||
@ -241,48 +220,6 @@ public:
|
||||
|
||||
using Super::Super;
|
||||
|
||||
protected:
|
||||
|
||||
~cPitchYawRotator() = default;
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface,
|
||||
cPlayer & a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) const override
|
||||
{
|
||||
NIBBLETYPE BaseMeta;
|
||||
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, BaseMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
a_BlockMeta = (BaseMeta & ~BitMask) | PitchYawToMetaData(a_Player.GetYaw(), a_Player.GetPitch());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
|
||||
switch (a_Meta & BitMask)
|
||||
{
|
||||
case Down: return Up | OtherMeta; // Down -> Up
|
||||
case Up: return Down | OtherMeta; // Up -> Down
|
||||
}
|
||||
// Not Facing Up or Down; No change.
|
||||
return a_Meta;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Converts the rotation and pitch values as returned by cPlayer::GetYaw() and cPlayer::GetPitch()
|
||||
respectively to the appropriate metadata value for a block placed by a player facing that way */
|
||||
@ -299,4 +236,21 @@ protected:
|
||||
|
||||
return Super::YawToMetaData(a_Rotation);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
~cPitchYawRotator() = default;
|
||||
|
||||
|
||||
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
|
||||
{
|
||||
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
|
||||
switch (a_Meta & BitMask)
|
||||
{
|
||||
case Down: return Up | OtherMeta; // Down -> Up
|
||||
case Up: return Down | OtherMeta; // Up -> Down
|
||||
}
|
||||
// Not Facing Up or Down; No change.
|
||||
return a_Meta;
|
||||
}
|
||||
};
|
||||
|
@ -54,10 +54,10 @@ public:
|
||||
virtual std::vector<UInt32> SpawnSplitExperienceOrbs(Vector3d a_Pos, int a_Reward) = 0;
|
||||
|
||||
/** Sends the block on those coords to the player */
|
||||
virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer & a_Player) = 0;
|
||||
virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, const cPlayer & a_Player) = 0;
|
||||
|
||||
/** Sends the block on those coords to the player */
|
||||
inline void SendBlockTo(const Vector3i a_BlockPos, cPlayer & a_Player)
|
||||
inline void SendBlockTo(const Vector3i a_BlockPos, const cPlayer & a_Player)
|
||||
{
|
||||
SendBlockTo(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Player);
|
||||
}
|
||||
|
@ -748,6 +748,10 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
|
||||
res = false;
|
||||
continue;
|
||||
}
|
||||
if (!cChunkDef::IsValidHeight(itr->m_RelY))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
itr->m_BlockType = Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
|
||||
itr->m_BlockMeta = Chunk->GetMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
|
||||
}
|
||||
@ -798,7 +802,7 @@ cItems cChunkMap::PickupsFromBlock(Vector3i a_BlockPos, const cEntity * a_Digger
|
||||
|
||||
|
||||
|
||||
void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer & a_Player)
|
||||
void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, const cPlayer & a_Player)
|
||||
{
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ);
|
||||
|
@ -153,7 +153,7 @@ public:
|
||||
/** Sends the block at the specified coords to the specified player.
|
||||
Uses a blockchange packet to send the block.
|
||||
If the relevant chunk isn't loaded, doesn't do anything. */
|
||||
void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer & a_Player);
|
||||
void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, const cPlayer & a_Player);
|
||||
|
||||
/** Compares clients of two chunks, calls the callback accordingly */
|
||||
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);
|
||||
|
@ -987,15 +987,13 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
|
||||
/* Check for clickthrough-blocks:
|
||||
When the user breaks a fire block, the client send the wrong block location.
|
||||
We must find the right block with the face direction. */
|
||||
|
||||
int BlockX = a_BlockX;
|
||||
int BlockY = a_BlockY;
|
||||
int BlockZ = a_BlockZ;
|
||||
AddFaceDirection(BlockX, BlockY, BlockZ, a_BlockFace);
|
||||
|
||||
if (
|
||||
cChunkDef::IsValidHeight(BlockY) &&
|
||||
cBlockHandler::For(m_Player->GetWorld()->GetBlock({ BlockX, BlockY, BlockZ })).IsClickedThrough()
|
||||
)
|
||||
if (cChunkDef::IsValidHeight(BlockY) && cBlockInfo::IsClickedThrough(m_Player->GetWorld()->GetBlock({ BlockX, BlockY, BlockZ })))
|
||||
{
|
||||
a_BlockX = BlockX;
|
||||
a_BlockY = BlockY;
|
||||
@ -1338,7 +1336,6 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
|
||||
cWorld * World = m_Player->GetWorld();
|
||||
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
|
||||
|
||||
bool Success = false;
|
||||
if (
|
||||
!PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ) &&
|
||||
IsWithinReach && !m_Player->IsFrozen()
|
||||
@ -1354,29 +1351,24 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
|
||||
|
||||
if (BlockUsable && !(m_Player->IsCrouched() && !HeldItem.IsEmpty()))
|
||||
{
|
||||
// use a block
|
||||
cChunkInterface ChunkInterface(World->GetChunkMap());
|
||||
if (!PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
|
||||
{
|
||||
// Use a block:
|
||||
if (BlockHandler.OnUse(ChunkInterface, *World, *m_Player, ClickedBlockPos, a_BlockFace, CursorPos))
|
||||
{
|
||||
// block use was successful, we're done
|
||||
PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
|
||||
Success = true;
|
||||
return; // Block use was successful, we're done.
|
||||
}
|
||||
|
||||
// Check if the item is place able, for example a torch on a fence
|
||||
if (!Success && Placeable)
|
||||
// Check if the item is place able, for example a torch on a fence:
|
||||
if (Placeable)
|
||||
{
|
||||
// place a block
|
||||
Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
|
||||
// Place a block:
|
||||
ItemHandler->OnPlayerPlace(*m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: OnCancelRightClick seems to do the same thing with updating blocks at the end of this function. Need to double check
|
||||
// A plugin doesn't agree with the action, replace the block on the client and quit:
|
||||
BlockHandler.OnCancelRightClick(ChunkInterface, *World, *m_Player, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Placeable)
|
||||
@ -1388,36 +1380,30 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
|
||||
Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
|
||||
return;
|
||||
}
|
||||
// place a block
|
||||
Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
|
||||
|
||||
// Place a block:
|
||||
ItemHandler->OnPlayerPlace(*m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
|
||||
return;
|
||||
}
|
||||
else
|
||||
else if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
|
||||
{
|
||||
// Use an item in hand with a target block
|
||||
if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
|
||||
{
|
||||
// All plugins agree with using the item
|
||||
cBlockInServerPluginInterface PluginInterface(*World);
|
||||
ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
|
||||
PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
|
||||
Success = true;
|
||||
}
|
||||
// All plugins agree with using the item.
|
||||
// Use an item in hand with a target block.
|
||||
|
||||
cBlockInServerPluginInterface PluginInterface(*World);
|
||||
ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
|
||||
PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!Success)
|
||||
{
|
||||
// Update the target block including the block above and below for 2 block high things
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
for (int y = a_BlockY - 1; y <= a_BlockY + 1; y++)
|
||||
{
|
||||
if (cChunkDef::IsValidHeight(y))
|
||||
{
|
||||
World->SendBlockTo(a_BlockX, y, a_BlockZ, *m_Player);
|
||||
}
|
||||
}
|
||||
// TODO: Send corresponding slot based on hand
|
||||
m_Player->GetInventory().SendEquippedSlot();
|
||||
}
|
||||
|
||||
// TODO: delete OnItemUse bool return, delete onCancelRightClick
|
||||
|
||||
// Update the target block including the block above and below for 2 block high things:
|
||||
m_Player->SendBlocksAround(a_BlockX, a_BlockY, a_BlockZ, 2);
|
||||
|
||||
// TODO: Send corresponding slot based on hand
|
||||
m_Player->GetInventory().SendEquippedSlot();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2391,10 +2391,9 @@ void cPlayer::LoadRank(void)
|
||||
|
||||
|
||||
|
||||
bool cPlayer::PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
bool cPlayer::PlaceBlock(const Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
sSetBlockVector blk{{a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta}};
|
||||
return PlaceBlocks(blk);
|
||||
return PlaceBlocks({ { a_Position, a_BlockType, a_BlockMeta } });
|
||||
}
|
||||
|
||||
|
||||
@ -2442,7 +2441,7 @@ void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_R
|
||||
|
||||
|
||||
|
||||
bool cPlayer::DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks)
|
||||
bool cPlayer::DoesPlacingBlocksIntersectEntity(const std::initializer_list<sSetBlock> a_Blocks) const
|
||||
{
|
||||
// Compute the bounding box for each block to be placed
|
||||
std::vector<cBoundingBox> PlacementBoxes;
|
||||
@ -2516,44 +2515,52 @@ const cUUID & cPlayer::GetUUID(void) const
|
||||
|
||||
|
||||
|
||||
bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks)
|
||||
bool cPlayer::PlaceBlocks(const std::initializer_list<sSetBlock> a_Blocks)
|
||||
{
|
||||
if (DoesPlacingBlocksIntersectEntity(a_Blocks))
|
||||
{
|
||||
// Abort - re-send all the current blocks in the a_Blocks' coords to the client:
|
||||
for (auto blk2: a_Blocks)
|
||||
for (const auto & ResendBlock : a_Blocks)
|
||||
{
|
||||
m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), *this);
|
||||
m_World->SendBlockTo(ResendBlock.GetX(), ResendBlock.GetY(), ResendBlock.GetZ(), *this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call the "placing" hooks; if any fail, abort:
|
||||
cPluginManager * pm = cPluginManager::Get();
|
||||
for (auto blk: a_Blocks)
|
||||
|
||||
// Check the blocks CanBeAt, and call the "placing" hooks; if any fail, abort:
|
||||
for (const auto & Block : a_Blocks)
|
||||
{
|
||||
if (pm->CallHookPlayerPlacingBlock(*this, blk))
|
||||
if (
|
||||
!m_World->DoWithChunkAt(Block.GetAbsolutePos(), [&Block](cChunk & a_Chunk)
|
||||
{
|
||||
return cBlockHandler::For(Block.m_BlockType).CanBeAt(a_Chunk, Block.GetRelativePos(), Block.m_BlockMeta);
|
||||
})
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pm->CallHookPlayerPlacingBlock(*this, Block))
|
||||
{
|
||||
// Abort - re-send all the current blocks in the a_Blocks' coords to the client:
|
||||
for (auto blk2: a_Blocks)
|
||||
for (const auto & ResendBlock : a_Blocks)
|
||||
{
|
||||
m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), *this);
|
||||
m_World->SendBlockTo(ResendBlock.GetX(), ResendBlock.GetY(), ResendBlock.GetZ(), *this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // for blk - a_Blocks[]
|
||||
}
|
||||
|
||||
cChunkInterface ChunkInterface(m_World->GetChunkMap());
|
||||
for (auto blk: a_Blocks)
|
||||
for (const auto & Block : a_Blocks)
|
||||
{
|
||||
// Set the blocks:
|
||||
m_World->PlaceBlock(blk.GetAbsolutePos(), blk.m_BlockType, blk.m_BlockMeta);
|
||||
|
||||
// Notify the blockhandlers:
|
||||
cBlockHandler::For(blk.m_BlockType).OnPlacedByPlayer(ChunkInterface, *m_World, *this, blk);
|
||||
m_World->PlaceBlock(Block.GetAbsolutePos(), Block.m_BlockType, Block.m_BlockMeta);
|
||||
|
||||
// Call the "placed" hooks:
|
||||
pm->CallHookPlayerPlacedBlock(*this, blk);
|
||||
pm->CallHookPlayerPlacedBlock(*this, Block);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -534,7 +534,7 @@ public:
|
||||
void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround);
|
||||
|
||||
/** Whether placing the given blocks would intersect any entitiy */
|
||||
bool DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks);
|
||||
bool DoesPlacingBlocksIntersectEntity(std::initializer_list<sSetBlock> a_Blocks) const;
|
||||
|
||||
/** Returns the UUID that has been read from the client, or nil if not available. */
|
||||
const cUUID & GetUUID(void) const; // Exported in ManualBindings.cpp
|
||||
@ -552,7 +552,7 @@ public:
|
||||
If the hook prevents the placement, sends the current block at the specified coords back to the client.
|
||||
Assumes that the block is in a currently loaded chunk.
|
||||
Returns true if the block is successfully placed. */
|
||||
bool PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
bool PlaceBlock(Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
|
||||
/** Sends the block in the specified range around the specified coord to the client
|
||||
as a block change packet.
|
||||
@ -571,7 +571,7 @@ public:
|
||||
If the any of the "placing" hooks aborts, none of the blocks are placed and the function returns false.
|
||||
Returns true if all the blocks are placed.
|
||||
Assumes that all the blocks are in currently loaded chunks. */
|
||||
bool PlaceBlocks(const sSetBlockVector & a_Blocks);
|
||||
bool PlaceBlocks(std::initializer_list<sSetBlock> a_Blocks);
|
||||
|
||||
/** Notify nearby wolves that the player or one of the player's wolves took damage or did damage to an entity
|
||||
@param a_Opponent the opponent we're fighting.
|
||||
|
@ -3,6 +3,7 @@ target_sources(
|
||||
|
||||
ItemHandler.cpp
|
||||
|
||||
ItemAnvil.h
|
||||
ItemArmor.h
|
||||
ItemAxe.h
|
||||
ItemBanner.h
|
||||
@ -12,24 +13,35 @@ target_sources(
|
||||
ItemBottle.h
|
||||
ItemBow.h
|
||||
ItemBucket.h
|
||||
ItemButton.h
|
||||
ItemChest.h
|
||||
ItemCloth.h
|
||||
ItemComparator.h
|
||||
ItemCookedFish.h
|
||||
ItemDoor.h
|
||||
ItemDropSpenser.h
|
||||
ItemDye.h
|
||||
ItemEmptyMap.h
|
||||
ItemEnchantingTable.h
|
||||
ItemEndCrystal.h
|
||||
ItemEndPortalFrame.h
|
||||
ItemEnderchest.h
|
||||
ItemEyeOfEnder.h
|
||||
ItemFenceGate.h
|
||||
ItemFishingRod.h
|
||||
ItemFood.h
|
||||
ItemFoodSeeds.h
|
||||
ItemFurnace.h
|
||||
ItemGlazedTerracotta.h
|
||||
ItemGoldenApple.h
|
||||
ItemHandler.h
|
||||
ItemHoe.h
|
||||
ItemHopper.h
|
||||
ItemItemFrame.h
|
||||
ItemJackOLantern.h
|
||||
ItemLadder.h
|
||||
ItemLeaves.h
|
||||
ItemLever.h
|
||||
ItemLighter.h
|
||||
ItemLilypad.h
|
||||
ItemMap.h
|
||||
@ -37,11 +49,16 @@ target_sources(
|
||||
ItemMinecart.h
|
||||
ItemMobHead.h
|
||||
ItemNetherWart.h
|
||||
ItemObserver.h
|
||||
ItemPainting.h
|
||||
ItemPickaxe.h
|
||||
ItemPiston.h
|
||||
ItemPlanks.h
|
||||
ItemPoisonousPotato.h
|
||||
ItemPotion.h
|
||||
ItemPumpkin.h
|
||||
ItemQuartz.h
|
||||
ItemRail.h
|
||||
ItemRawChicken.h
|
||||
ItemRawFish.h
|
||||
ItemRedstoneDust.h
|
||||
@ -51,12 +68,19 @@ target_sources(
|
||||
ItemSeeds.h
|
||||
ItemShears.h
|
||||
ItemShovel.h
|
||||
ItemSideways.h
|
||||
ItemSign.h
|
||||
ItemSlab.h
|
||||
ItemSnow.h
|
||||
ItemSoup.h
|
||||
ItemSpawnEgg.h
|
||||
ItemSpiderEye.h
|
||||
ItemStairs.h
|
||||
ItemSword.h
|
||||
ItemThrowable.h
|
||||
ItemTorch.h
|
||||
ItemTrapdoor.h
|
||||
ItemTripwireHook.h
|
||||
ItemVine.h
|
||||
SimplePlaceableItemHandler.h
|
||||
)
|
||||
|
30
src/Items/ItemAnvil.h
Normal file
30
src/Items/ItemAnvil.h
Normal 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)
|
||||
);
|
||||
}
|
||||
};
|
@ -24,6 +24,32 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
// Cannot place a banner at "no face" and from the bottom:
|
||||
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryPlaceBanner(a_Player, a_PlacePosition, a_ClickedBlockFace))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_HeldItem](cBlockEntity & a_BlockEntity)
|
||||
{
|
||||
ASSERT((a_BlockEntity.GetBlockType() == E_BLOCK_STANDING_BANNER) || (a_BlockEntity.GetBlockType() == E_BLOCK_WALL_BANNER));
|
||||
|
||||
static_cast<cBannerEntity &>(a_BlockEntity).SetBaseColor(static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage));
|
||||
return false;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -36,182 +62,120 @@ public:
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
static bool TryPlaceBanner(cPlayer & a_Player, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace)
|
||||
{
|
||||
a_BlockMeta = 0x00;
|
||||
const double Rotation = a_Player->GetYaw();
|
||||
const auto Rotation = a_Player.GetYaw();
|
||||
|
||||
// Placing on the floor
|
||||
// Placing on the floor:
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_TOP)
|
||||
{
|
||||
if ((Rotation >= - 11.25f) && (Rotation < 11.25f))
|
||||
NIBBLETYPE Meta;
|
||||
|
||||
if ((Rotation >= -11.25f) && (Rotation < 11.25f))
|
||||
{
|
||||
// South
|
||||
a_BlockMeta |= 0x08;
|
||||
Meta = 0x08;
|
||||
}
|
||||
else if ((Rotation >= 11.25f) && (Rotation < 33.75f))
|
||||
{
|
||||
// SouthSouthWest
|
||||
a_BlockMeta |= 0x09;
|
||||
Meta = 0x09;
|
||||
}
|
||||
else if ((Rotation >= 23.75f) && (Rotation < 56.25f))
|
||||
{
|
||||
// SouthWest
|
||||
a_BlockMeta |= 0x0a;
|
||||
Meta = 0x0a;
|
||||
}
|
||||
else if ((Rotation >= 56.25f) && (Rotation < 78.75f))
|
||||
{
|
||||
// WestSouthWest
|
||||
a_BlockMeta |= 0x0b;
|
||||
Meta = 0x0b;
|
||||
}
|
||||
else if ((Rotation >= 78.75f) && (Rotation < 101.25f))
|
||||
{
|
||||
// West
|
||||
a_BlockMeta |= 0x0c;
|
||||
Meta = 0x0c;
|
||||
}
|
||||
else if ((Rotation >= 101.25f) && (Rotation < 123.75f))
|
||||
{
|
||||
// WestNorthWest
|
||||
a_BlockMeta |= 0x0d;
|
||||
Meta = 0x0d;
|
||||
}
|
||||
else if ((Rotation >= 123.75f) && (Rotation < 146.25f))
|
||||
{
|
||||
// NorthWest
|
||||
a_BlockMeta |= 0x0e;
|
||||
Meta = 0x0e;
|
||||
}
|
||||
else if ((Rotation >= 146.25f) && (Rotation < 168.75f))
|
||||
{
|
||||
// NorthNorthWest
|
||||
a_BlockMeta |= 0x0f;
|
||||
Meta = 0x0f;
|
||||
}
|
||||
else if ((Rotation >= -168.75f) && (Rotation < -146.25f))
|
||||
{
|
||||
// NorthNorthEast
|
||||
a_BlockMeta |= 0x01;
|
||||
Meta = 0x01;
|
||||
}
|
||||
else if ((Rotation >= -146.25f) && (Rotation < -123.75f))
|
||||
{
|
||||
// NorthEast
|
||||
a_BlockMeta |= 0x02;
|
||||
Meta = 0x02;
|
||||
}
|
||||
else if ((Rotation >= -123.75f) && (Rotation < -101.25f))
|
||||
{
|
||||
// EastNorthEast
|
||||
a_BlockMeta |= 0x03;
|
||||
Meta = 0x03;
|
||||
}
|
||||
else if ((Rotation >= -101.25) && (Rotation < -78.75f))
|
||||
{
|
||||
// East
|
||||
a_BlockMeta |= 0x04;
|
||||
Meta = 0x04;
|
||||
}
|
||||
else if ((Rotation >= -78.75) && (Rotation < -56.25f))
|
||||
{
|
||||
// EastSouthEast
|
||||
a_BlockMeta |= 0x05;
|
||||
Meta = 0x05;
|
||||
}
|
||||
else if ((Rotation >= -56.25f) && (Rotation < -33.75f))
|
||||
{
|
||||
// SouthEast
|
||||
a_BlockMeta |= 0x06;
|
||||
Meta = 0x06;
|
||||
}
|
||||
else if ((Rotation >= -33.75f) && (Rotation < -11.25f))
|
||||
{
|
||||
// SouthSouthEast
|
||||
a_BlockMeta |= 0x07;
|
||||
Meta = 0x07;
|
||||
}
|
||||
else // degrees jumping from 180 to -180
|
||||
{
|
||||
// North
|
||||
a_BlockMeta |= 0x00;
|
||||
Meta = 0x00;
|
||||
}
|
||||
a_BlockType = E_BLOCK_STANDING_BANNER;
|
||||
}
|
||||
// placing on the sides
|
||||
else if (a_ClickedBlockFace != BLOCK_FACE_NONE)
|
||||
{
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_EAST)
|
||||
{
|
||||
a_BlockMeta |= 0x05;
|
||||
}
|
||||
else if (a_ClickedBlockFace == BLOCK_FACE_WEST)
|
||||
{
|
||||
a_BlockMeta |= 0x04;
|
||||
}
|
||||
else if (a_ClickedBlockFace == BLOCK_FACE_NORTH)
|
||||
{
|
||||
a_BlockMeta |= 0x02;
|
||||
}
|
||||
else // degrees jumping from 180 to -180
|
||||
{
|
||||
a_BlockMeta |= 0x03;
|
||||
}
|
||||
a_BlockType = E_BLOCK_WALL_BANNER;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
|
||||
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_STANDING_BANNER, Meta);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// We must be placing on the side of a block.
|
||||
|
||||
NIBBLETYPE Meta;
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
) override
|
||||
{
|
||||
// Cannot place a banner at "no face" and from the bottom:
|
||||
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_EAST)
|
||||
{
|
||||
return true;
|
||||
Meta = 0x05;
|
||||
}
|
||||
else if (a_ClickedBlockFace == BLOCK_FACE_WEST)
|
||||
{
|
||||
Meta = 0x04;
|
||||
}
|
||||
else if (a_ClickedBlockFace == BLOCK_FACE_NORTH)
|
||||
{
|
||||
Meta = 0x02;
|
||||
}
|
||||
else // degrees jumping from 180 to -180
|
||||
{
|
||||
Meta = 0x03;
|
||||
}
|
||||
|
||||
// Checks if the banner replaced the block
|
||||
BLOCKTYPE ClickedBlockType;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
|
||||
cChunkInterface ChunkInterface(a_World.GetChunkMap());
|
||||
bool IsReplacingClickedBlock = cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta);
|
||||
if (IsReplacingClickedBlock)
|
||||
{
|
||||
// TODO: There is a bug in the network which prevents the client from receiving the new block entity on placement
|
||||
// For now the replaced blocks are disabled
|
||||
return false;
|
||||
}
|
||||
|
||||
// saving the color of the banner in case it's the players last one
|
||||
NIBBLETYPE Color = static_cast<NIBBLETYPE>(a_EquippedItem.m_ItemDamage);
|
||||
|
||||
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_ClickedBlockPos))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto BannerPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
|
||||
a_World.DoWithBlockEntityAt(BannerPos, [Color](cBlockEntity & a_BlockEntity)
|
||||
{
|
||||
ASSERT((a_BlockEntity.GetBlockType() == E_BLOCK_STANDING_BANNER) || (a_BlockEntity.GetBlockType() == E_BLOCK_WALL_BANNER));
|
||||
|
||||
auto & Banner = static_cast<cBannerEntity &>(a_BlockEntity);
|
||||
Banner.SetBaseColor(Color);
|
||||
return false;
|
||||
});
|
||||
|
||||
return true;
|
||||
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_WALL_BANNER, Meta);
|
||||
}
|
||||
};
|
||||
|
@ -2,8 +2,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "ItemHandler.h"
|
||||
#include "../World.h"
|
||||
#include "../Blocks/BlockBed.h"
|
||||
#include "Blocks/BlockBed.h"
|
||||
#include "BlockEntities/BedEntity.h"
|
||||
|
||||
|
||||
|
||||
@ -22,35 +22,51 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual bool GetBlocksToPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
sSetBlockVector & a_BlocksToPlace
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
const auto BlockMeta = cBlockBedHandler::YawToMetaData(a_Player.GetYaw());
|
||||
const auto HeadPosition = a_PlacedBlockPos + cBlockBedHandler::MetaDataToDirection(BlockMeta);
|
||||
const auto HeadPosition = a_PlacePosition + cBlockBedHandler::MetaDataToDirection(BlockMeta);
|
||||
|
||||
// Vanilla only allows beds to be placed into air
|
||||
auto & World = *a_Player.GetWorld();
|
||||
BLOCKTYPE HeadType;
|
||||
NIBBLETYPE HeadMeta;
|
||||
World.GetBlockTypeMeta(HeadPosition, HeadType, HeadMeta);
|
||||
|
||||
// Vanilla only allows beds to be placed into air.
|
||||
// Check if there is empty space for the "head" block:
|
||||
if (a_World.GetBlock(HeadPosition) != E_BLOCK_AIR)
|
||||
if (!cBlockHandler::For(HeadType).DoesIgnoreBuildCollision(World, a_HeldItem, HeadPosition, HeadMeta, a_ClickedBlockFace, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The "foot", and the "head" block:
|
||||
a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BED, BlockMeta);
|
||||
a_BlocksToPlace.emplace_back(HeadPosition, E_BLOCK_BED, BlockMeta | 0x08);
|
||||
if (
|
||||
!a_Player.PlaceBlocks(
|
||||
{
|
||||
{ a_PlacePosition, E_BLOCK_BED, BlockMeta },
|
||||
{ HeadPosition, E_BLOCK_BED, static_cast<NIBBLETYPE>(BlockMeta | 0x08) }
|
||||
})
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto SetColor = [&a_HeldItem](cBlockEntity & a_BlockEntity)
|
||||
{
|
||||
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_BED);
|
||||
|
||||
static_cast<cBedEntity &>(a_BlockEntity).SetColor(a_HeldItem.m_ItemDamage);
|
||||
return false;
|
||||
};
|
||||
World.DoWithBlockEntityAt(a_PlacePosition, SetColor);
|
||||
World.DoWithBlockEntityAt(HeadPosition, SetColor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -24,41 +24,29 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool GetBlocksToPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
sSetBlockVector & a_BlocksToPlace
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
// Can only be placed on dirt:
|
||||
if ((a_PlacedBlockPos.y <= 0) || !IsBlockTypeOfDirt(a_World.GetBlock(a_PlacedBlockPos.addedY(-1))))
|
||||
// Needs at least two free blocks to build in:
|
||||
if (a_PlacePosition.y >= (cChunkDef::Height - 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Needs at least two free blocks to build in
|
||||
if (a_PlacedBlockPos.y >= cChunkDef::Height - 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto TopPos = a_PlacedBlockPos.addedY(1);
|
||||
const auto & World = *a_Player.GetWorld();
|
||||
const auto TopPos = a_PlacePosition.addedY(1);
|
||||
BLOCKTYPE TopType;
|
||||
NIBBLETYPE TopMeta;
|
||||
a_World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
|
||||
cChunkInterface ChunkInterface(a_World.GetChunkMap());
|
||||
World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
|
||||
|
||||
if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(ChunkInterface, TopPos, a_Player, TopMeta))
|
||||
if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(World, a_HeldItem, TopPos, TopMeta, a_ClickedBlockFace, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
|
||||
a_BlocksToPlace.emplace_back(TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP);
|
||||
return true;
|
||||
return a_Player.PlaceBlocks(
|
||||
{
|
||||
{ a_PlacePosition, E_BLOCK_BIG_FLOWER, static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x07) },
|
||||
{ TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -105,8 +105,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove water / lava block (unless plugins disagree)
|
||||
if (!a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, E_BLOCK_AIR, 0))
|
||||
// Remove water / lava block (unless plugins disagree):
|
||||
if (!a_Player->PlaceBlock(BlockPos, E_BLOCK_AIR, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -175,7 +175,7 @@ public:
|
||||
}
|
||||
|
||||
// Place the actual fluid block:
|
||||
return a_Player->PlaceBlock(BlockPos.x, BlockPos.y, BlockPos.z, a_FluidBlock, 0);
|
||||
return a_Player->PlaceBlock(BlockPos, a_FluidBlock, 0);
|
||||
}
|
||||
|
||||
|
||||
|
45
src/Items/ItemButton.h
Normal file
45
src/Items/ItemButton.h
Normal 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));
|
||||
}
|
||||
};
|
@ -2,8 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ItemHandler.h"
|
||||
#include "../BlockInfo.h"
|
||||
#include "../Blocks/BlockChest.h"
|
||||
#include "Blocks/BlockChest.h"
|
||||
|
||||
|
||||
|
||||
@ -21,59 +20,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
cItemChestHandler(const cItemChestHandler &) = delete;
|
||||
|
||||
/** We need an OnPlayerPlace override because we're processing neighbor chests and changing their metas,
|
||||
the parent class cannot do that. */
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
) override
|
||||
private:
|
||||
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
if (a_ClickedBlockFace < 0)
|
||||
{
|
||||
// Clicked in air
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cChunkDef::IsValidHeight(a_ClickedBlockPos.y))
|
||||
{
|
||||
// The clicked block is outside the world, ignore this call altogether (#128)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the block ignores build collision (water, grass etc.):
|
||||
BLOCKTYPE ClickedBlockType;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
|
||||
cChunkInterface ChunkInterface(a_World.GetChunkMap());
|
||||
Vector3i PlacePos;
|
||||
if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
|
||||
{
|
||||
PlacePos = a_ClickedBlockPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
|
||||
if (!cChunkDef::IsValidHeight(PlacePos.y))
|
||||
{
|
||||
// The block is being placed outside the world, ignore this packet altogether (#128)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the chest can overwrite the block at PlacePos:
|
||||
BLOCKTYPE PlaceBlock;
|
||||
NIBBLETYPE PlaceMeta;
|
||||
a_World.GetBlockTypeMeta(PlacePos, PlaceBlock, PlaceMeta);
|
||||
if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(ChunkInterface, PlacePos, a_Player, PlaceMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there is at most one single neighbor of the same chest type:
|
||||
static const Vector3i CrossCoords[] =
|
||||
{
|
||||
@ -82,11 +34,14 @@ public:
|
||||
{ 1, 0, 0},
|
||||
{ 0, 0, 1},
|
||||
};
|
||||
|
||||
auto & World = *a_Player.GetWorld();
|
||||
int NeighborIdx = -1;
|
||||
|
||||
for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
|
||||
{
|
||||
auto NeighborPos = PlacePos + CrossCoords[i];
|
||||
if (a_World.GetBlock(NeighborPos) != m_ItemType)
|
||||
const auto NeighborPos = a_PlacePosition + CrossCoords[i];
|
||||
if (World.GetBlock(NeighborPos) != m_ItemType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -100,7 +55,7 @@ public:
|
||||
// Check that this neighbor is a single chest:
|
||||
for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++)
|
||||
{
|
||||
if (a_World.GetBlock(NeighborPos + CrossCoords[j]) == m_ItemType)
|
||||
if (World.GetBlock(NeighborPos + CrossCoords[j]) == m_ItemType)
|
||||
{
|
||||
// Trying to place next to a dblchest
|
||||
return false;
|
||||
@ -111,7 +66,7 @@ public:
|
||||
// Get the meta of the placed chest; take existing neighbors into account:
|
||||
BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
|
||||
NIBBLETYPE Meta;
|
||||
auto yaw = a_Player.GetYaw();
|
||||
const auto yaw = a_Player.GetYaw();
|
||||
switch (NeighborIdx)
|
||||
{
|
||||
case 0:
|
||||
@ -131,13 +86,13 @@ public:
|
||||
default:
|
||||
{
|
||||
// No neighbor, place based on yaw:
|
||||
Meta = cBlockChestHandler::PlayerYawToMetaData(yaw);
|
||||
Meta = cBlockChestHandler::YawToMetaData(yaw);
|
||||
break;
|
||||
}
|
||||
} // switch (NeighborIdx)
|
||||
|
||||
// Place the new chest:
|
||||
if (!a_Player.PlaceBlock(PlacePos.x, PlacePos.y, PlacePos.z, ChestBlockType, Meta))
|
||||
if (!a_Player.PlaceBlock(a_PlacePosition, ChestBlockType, Meta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -145,17 +100,9 @@ public:
|
||||
// Adjust the existing chest, if any:
|
||||
if (NeighborIdx != -1)
|
||||
{
|
||||
a_World.FastSetBlock(PlacePos + CrossCoords[NeighborIdx], ChestBlockType, Meta);
|
||||
World.FastSetBlock(a_PlacePosition + CrossCoords[NeighborIdx], ChestBlockType, Meta);
|
||||
}
|
||||
|
||||
// Remove the "placed" item from inventory:
|
||||
if (a_Player.IsGameModeSurvival())
|
||||
{
|
||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
cItemChestHandler(const cItemChestHandler &) = delete;
|
||||
};
|
||||
|
@ -24,25 +24,17 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
return true;
|
||||
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_INACTIVE_COMPARATOR, cBlockComparatorHandler::YawToMetaData(a_Player.GetYaw()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
a_BlockType = E_BLOCK_INACTIVE_COMPARATOR;
|
||||
a_BlockMeta = cBlockComparatorHandler::YawToMetaData(a_Player->GetYaw());
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
@ -25,13 +25,7 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool GetBlocksToPlace(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
sSetBlockVector & a_BlocksToSet
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
// Vanilla only allows door placement while clicking on the top face of the block below the door:
|
||||
if (a_ClickedBlockFace != BLOCK_FACE_TOP)
|
||||
@ -39,19 +33,6 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Door (bottom block) can be placed in Y range of [1, 254]:
|
||||
if ((a_PlacedBlockPos.y < 1) || (a_PlacedBlockPos.y >= cChunkDef::Height - 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The door needs a compatible block below it:
|
||||
auto BelowPos = a_PlacedBlockPos.addedY(-1);
|
||||
if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(BelowPos), a_World.GetBlockMeta(BelowPos)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the block type of the door to place:
|
||||
BLOCKTYPE BlockType;
|
||||
switch (m_ItemType)
|
||||
@ -63,22 +44,22 @@ public:
|
||||
case E_ITEM_JUNGLE_DOOR: BlockType = E_BLOCK_JUNGLE_DOOR; break;
|
||||
case E_ITEM_DARK_OAK_DOOR: BlockType = E_BLOCK_DARK_OAK_DOOR; break;
|
||||
case E_ITEM_ACACIA_DOOR: BlockType = E_BLOCK_ACACIA_DOOR; break;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unhandled door type");
|
||||
return false;
|
||||
}
|
||||
default: UNREACHABLE("Unhandled door type");
|
||||
}
|
||||
|
||||
// Check the two blocks that will get replaced by the door:
|
||||
auto UpperBlockPos = a_PlacedBlockPos.addedY(1);
|
||||
BLOCKTYPE LowerBlockType = a_World.GetBlock(a_PlacedBlockPos);
|
||||
BLOCKTYPE UpperBlockType = a_World.GetBlock(UpperBlockPos);
|
||||
if (
|
||||
!cBlockDoorHandler::CanReplaceBlock(LowerBlockType) ||
|
||||
!cBlockDoorHandler::CanReplaceBlock(UpperBlockType))
|
||||
const auto & World = *a_Player.GetWorld();
|
||||
const auto UpperBlockPosition = a_PlacePosition.addedY(1);
|
||||
|
||||
// Check the block that will get replaced by the door:
|
||||
{
|
||||
return false;
|
||||
BLOCKTYPE TopType;
|
||||
NIBBLETYPE TopMeta;
|
||||
World.GetBlockTypeMeta(UpperBlockPosition, TopType, TopMeta);
|
||||
|
||||
if (!cBlockHandler::For(TopType).DoesIgnoreBuildCollision(World, a_HeldItem, UpperBlockPosition, TopMeta, a_ClickedBlockFace, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the coords of the neighboring blocks:
|
||||
@ -86,22 +67,24 @@ public:
|
||||
Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta);
|
||||
Vector3i LeftNeighborPos = RelDirToOutside;
|
||||
LeftNeighborPos.TurnCW();
|
||||
LeftNeighborPos.Move(a_PlacedBlockPos);
|
||||
LeftNeighborPos.Move(a_PlacePosition);
|
||||
Vector3i RightNeighborPos = RelDirToOutside;
|
||||
RightNeighborPos.TurnCCW();
|
||||
RightNeighborPos.Move(a_PlacedBlockPos);
|
||||
RightNeighborPos.Move(a_PlacePosition);
|
||||
|
||||
// Decide whether the hinge is on the left (default) or on the right:
|
||||
NIBBLETYPE UpperBlockMeta = 0x08;
|
||||
BLOCKTYPE LeftNeighborBlock = a_World.GetBlock(LeftNeighborPos);
|
||||
BLOCKTYPE RightNeighborBlock = a_World.GetBlock(RightNeighborPos);
|
||||
BLOCKTYPE LeftNeighborBlock = World.GetBlock(LeftNeighborPos);
|
||||
BLOCKTYPE RightNeighborBlock = World.GetBlock(RightNeighborPos);
|
||||
|
||||
/*
|
||||
// DEBUG:
|
||||
FLOGD("Door being placed at {0}", a_PlacedBlockPos);
|
||||
FLOGD("Door being placed at {0}", a_PlacePosition);
|
||||
FLOGD("RelDirToOutside: {0}", RelDirToOutside);
|
||||
FLOGD("Left neighbor at {0}: {1} ({2})", LeftNeighborPos, LeftNeighborBlock, ItemTypeToString(LeftNeighborBlock));
|
||||
FLOGD("Right neighbor at {0}: {1} ({2})", RightNeighborPos, RightNeighborBlock, ItemTypeToString(RightNeighborBlock));
|
||||
*/
|
||||
|
||||
if (
|
||||
cBlockDoorHandler::IsDoorBlockType(LeftNeighborBlock) || // The block to the left is a door block
|
||||
(
|
||||
@ -116,9 +99,11 @@ public:
|
||||
}
|
||||
|
||||
// Set the blocks:
|
||||
a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, LowerBlockMeta);
|
||||
a_BlocksToSet.emplace_back(UpperBlockPos, BlockType, UpperBlockMeta);
|
||||
return true;
|
||||
return a_Player.PlaceBlocks(
|
||||
{
|
||||
{ a_PlacePosition, BlockType, LowerBlockMeta },
|
||||
{ UpperBlockPosition, BlockType, UpperBlockMeta }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
26
src/Items/ItemDropSpenser.h
Normal file
26
src/Items/ItemDropSpenser.h
Normal 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()));
|
||||
}
|
||||
};
|
@ -74,7 +74,7 @@ public:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (a_Player->PlaceBlock(CocoaPos.x, CocoaPos.y, CocoaPos.z, E_BLOCK_COCOA_POD, BlockMeta))
|
||||
if (a_Player->PlaceBlock(CocoaPos, E_BLOCK_COCOA_POD, BlockMeta))
|
||||
{
|
||||
if (a_Player->IsGameModeSurvival())
|
||||
{
|
||||
|
@ -20,40 +20,32 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
) override
|
||||
{
|
||||
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
|
||||
if (!Super::CommitPlacement(a_Player, a_HeldItem, a_PlacePosition, a_ClickedBlockFace, a_CursorPosition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a_EquippedItem.IsCustomNameEmpty())
|
||||
if (a_HeldItem.IsCustomNameEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
|
||||
a_World.DoWithBlockEntityAt(PlacePos, [&a_EquippedItem](cBlockEntity & a_Entity)
|
||||
a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_HeldItem](cBlockEntity & a_BlockEntity)
|
||||
{
|
||||
ASSERT(a_Entity.GetBlockType() == E_BLOCK_ENCHANTMENT_TABLE);
|
||||
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_ENCHANTMENT_TABLE);
|
||||
|
||||
static_cast<cEnchantingTableEntity &>(a_Entity).SetCustomName(a_EquippedItem.m_CustomName);
|
||||
static_cast<cEnchantingTableEntity &>(a_BlockEntity).SetCustomName(a_HeldItem.m_CustomName);
|
||||
return false;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
26
src/Items/ItemEndPortalFrame.h
Normal file
26
src/Items/ItemEndPortalFrame.h
Normal 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()));
|
||||
}
|
||||
};
|
26
src/Items/ItemEnderchest.h
Normal file
26
src/Items/ItemEnderchest.h
Normal 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
26
src/Items/ItemFenceGate.h
Normal 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
26
src/Items/ItemFurnace.h
Normal 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()));
|
||||
}
|
||||
};
|
26
src/Items/ItemGlazedTerracotta.h
Normal file
26
src/Items/ItemGlazedTerracotta.h
Normal 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()));
|
||||
}
|
||||
};
|
@ -8,6 +8,7 @@
|
||||
#include "../Chunk.h"
|
||||
|
||||
// Handlers:
|
||||
#include "ItemAnvil.h"
|
||||
#include "ItemArmor.h"
|
||||
#include "ItemAxe.h"
|
||||
#include "ItemBanner.h"
|
||||
@ -17,23 +18,34 @@
|
||||
#include "ItemBottle.h"
|
||||
#include "ItemBow.h"
|
||||
#include "ItemBucket.h"
|
||||
#include "ItemButton.h"
|
||||
#include "ItemChest.h"
|
||||
#include "ItemCloth.h"
|
||||
#include "ItemComparator.h"
|
||||
#include "ItemCookedFish.h"
|
||||
#include "ItemDoor.h"
|
||||
#include "ItemDropSpenser.h"
|
||||
#include "ItemDye.h"
|
||||
#include "ItemEmptyMap.h"
|
||||
#include "ItemEnchantingTable.h"
|
||||
#include "ItemEndCrystal.h"
|
||||
#include "ItemEnderchest.h"
|
||||
#include "ItemEndPortalFrame.h"
|
||||
#include "ItemEyeOfEnder.h"
|
||||
#include "ItemFenceGate.h"
|
||||
#include "ItemFishingRod.h"
|
||||
#include "ItemFood.h"
|
||||
#include "ItemFoodSeeds.h"
|
||||
#include "ItemFurnace.h"
|
||||
#include "ItemGlazedTerracotta.h"
|
||||
#include "ItemGoldenApple.h"
|
||||
#include "ItemHoe.h"
|
||||
#include "ItemHopper.h"
|
||||
#include "ItemItemFrame.h"
|
||||
#include "ItemJackOLantern.h"
|
||||
#include "ItemLadder.h"
|
||||
#include "ItemLeaves.h"
|
||||
#include "ItemLever.h"
|
||||
#include "ItemLighter.h"
|
||||
#include "ItemLilypad.h"
|
||||
#include "ItemMap.h"
|
||||
@ -41,11 +53,16 @@
|
||||
#include "ItemMinecart.h"
|
||||
#include "ItemMobHead.h"
|
||||
#include "ItemNetherWart.h"
|
||||
#include "ItemObserver.h"
|
||||
#include "ItemPainting.h"
|
||||
#include "ItemPickaxe.h"
|
||||
#include "ItemPiston.h"
|
||||
#include "ItemPlanks.h"
|
||||
#include "ItemPoisonousPotato.h"
|
||||
#include "ItemPotion.h"
|
||||
#include "ItemPumpkin.h"
|
||||
#include "ItemQuartz.h"
|
||||
#include "ItemRail.h"
|
||||
#include "ItemRawChicken.h"
|
||||
#include "ItemRawFish.h"
|
||||
#include "ItemRedstoneDust.h"
|
||||
@ -55,13 +72,20 @@
|
||||
#include "ItemSeeds.h"
|
||||
#include "ItemShears.h"
|
||||
#include "ItemShovel.h"
|
||||
#include "ItemSideways.h"
|
||||
#include "ItemSign.h"
|
||||
#include "ItemSlab.h"
|
||||
#include "ItemSnow.h"
|
||||
#include "ItemSoup.h"
|
||||
#include "ItemSpawnEgg.h"
|
||||
#include "ItemSpiderEye.h"
|
||||
#include "ItemStairs.h"
|
||||
#include "ItemSword.h"
|
||||
#include "ItemThrowable.h"
|
||||
#include "ItemTorch.h"
|
||||
#include "ItemTrapdoor.h"
|
||||
#include "ItemTripwireHook.h"
|
||||
#include "ItemVine.h"
|
||||
|
||||
#include "../Blocks/BlockHandler.h"
|
||||
#include "SimplePlaceableItemHandler.h"
|
||||
@ -113,56 +137,87 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
|
||||
default: return new cItemHandler(a_ItemType);
|
||||
|
||||
// Single item per handler, alphabetically sorted:
|
||||
case E_ITEM_BANNER: return new cItemBannerHandler(a_ItemType);
|
||||
case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler;
|
||||
case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
|
||||
case E_BLOCK_ENCHANTMENT_TABLE: return new cItemEnchantingTableHandler(a_ItemType);
|
||||
case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
|
||||
case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
|
||||
case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
|
||||
case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
|
||||
case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler;
|
||||
case E_BLOCK_PURPUR_SLAB: return new cItemSlabHandler(E_BLOCK_PURPUR_SLAB, E_BLOCK_PURPUR_DOUBLE_SLAB);
|
||||
case E_BLOCK_RED_SANDSTONE_SLAB: return new cItemSlabHandler(E_BLOCK_RED_SANDSTONE_SLAB, E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB);
|
||||
case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
|
||||
case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB);
|
||||
case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
|
||||
case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB);
|
||||
case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
|
||||
case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
|
||||
case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
|
||||
case E_ITEM_BOW: return new cItemBowHandler();
|
||||
case E_ITEM_BREWING_STAND: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_BREWING_STAND);
|
||||
case E_ITEM_CAKE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAKE);
|
||||
case E_ITEM_CAULDRON: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAULDRON);
|
||||
case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
|
||||
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
|
||||
case E_ITEM_EGG: return new cItemEggHandler();
|
||||
case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
|
||||
case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
|
||||
case E_ITEM_END_CRYSTAL: return new cItemEndCrystalHandler(a_ItemType);
|
||||
case E_ITEM_EYE_OF_ENDER: return new cItemEyeOfEnderHandler();
|
||||
case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
|
||||
case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
|
||||
case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
|
||||
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
|
||||
case E_ITEM_FLOWER_POT: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_FLOWER_POT);
|
||||
case E_ITEM_GLASS_BOTTLE: return new cItemBottleHandler();
|
||||
case E_ITEM_MAP: return new cItemMapHandler();
|
||||
case E_ITEM_MILK: return new cItemMilkHandler();
|
||||
case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
|
||||
case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
|
||||
case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
|
||||
case E_ITEM_POTIONS: return new cItemPotionHandler();
|
||||
case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
|
||||
case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
|
||||
case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
|
||||
case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
|
||||
case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
|
||||
case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
|
||||
case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
|
||||
case E_ITEM_STRING: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_TRIPWIRE);
|
||||
case E_ITEM_SUGARCANE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_SUGARCANE);
|
||||
case E_BLOCK_ACTIVATOR_RAIL: return new cItemRailHandler(a_ItemType);
|
||||
case E_BLOCK_ANVIL: return new cItemAnvilHandler(a_ItemType);
|
||||
case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler;
|
||||
case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
|
||||
case E_BLOCK_DETECTOR_RAIL: return new cItemRailHandler(a_ItemType);
|
||||
case E_BLOCK_DISPENSER: return new cItemDropSpenserHandler(a_ItemType);
|
||||
case E_BLOCK_DROPPER: return new cItemDropSpenserHandler(a_ItemType);
|
||||
case E_BLOCK_ENCHANTMENT_TABLE: return new cItemEnchantingTableHandler(a_ItemType);
|
||||
case E_BLOCK_ENDER_CHEST: return new cItemEnderchestHandler(a_ItemType);
|
||||
case E_BLOCK_END_PORTAL_FRAME: return new cItemEndPortalFrameHandler(a_ItemType);
|
||||
case E_BLOCK_FURNACE: return new cItemFurnaceHandler(a_ItemType);
|
||||
case E_BLOCK_HAY_BALE: return new cItemSidewaysHandler(a_ItemType);
|
||||
case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
|
||||
case E_BLOCK_HOPPER: return new cItemHopperHandler(a_ItemType);
|
||||
case E_BLOCK_IRON_TRAPDOOR: return new cItemTrapdoorHandler(a_ItemType);
|
||||
case E_BLOCK_JACK_O_LANTERN: return new cItemJackOLanternHandler(a_ItemType);
|
||||
case E_BLOCK_LADDER: return new cItemLadderHandler(a_ItemType);
|
||||
case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
|
||||
case E_BLOCK_LEVER: return new cItemLeverHandler(a_ItemType);
|
||||
case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
|
||||
case E_BLOCK_LOG: return new cItemSidewaysHandler(a_ItemType);
|
||||
case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
|
||||
case E_BLOCK_NEW_LOG: return new cItemSidewaysHandler(a_ItemType);
|
||||
case E_BLOCK_OBSERVER: return new cItemObserverHandler(a_ItemType);
|
||||
case E_BLOCK_PISTON: return new cItemPistonHandler(a_ItemType);
|
||||
case E_BLOCK_PLANKS: return new cItemPlanksHandler(a_ItemType);
|
||||
case E_BLOCK_POWERED_RAIL: return new cItemRailHandler(a_ItemType);
|
||||
case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler(a_ItemType);
|
||||
case E_BLOCK_PURPUR_SLAB: return new cItemSlabHandler(a_ItemType);
|
||||
case E_BLOCK_QUARTZ_BLOCK: return new cItemQuartzHandler(a_ItemType);
|
||||
case E_BLOCK_RAIL: return new cItemRailHandler(a_ItemType);
|
||||
case E_BLOCK_REDSTONE_TORCH_ON: return new cItemTorchHandler(a_ItemType);
|
||||
case E_BLOCK_RED_SANDSTONE_SLAB: return new cItemSlabHandler(a_ItemType);
|
||||
case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
|
||||
case E_BLOCK_SNOW: return new cItemSnowHandler(a_ItemType);
|
||||
case E_BLOCK_STICKY_PISTON: return new cItemPistonHandler(a_ItemType);
|
||||
case E_BLOCK_STONE_BUTTON: return new cItemButtonHandler(a_ItemType);
|
||||
case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(a_ItemType);
|
||||
case E_BLOCK_TORCH: return new cItemTorchHandler(a_ItemType);
|
||||
case E_BLOCK_TRAPDOOR: return new cItemTrapdoorHandler(a_ItemType);
|
||||
case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
|
||||
case E_BLOCK_TRIPWIRE_HOOK: return new cItemTripwireHookHandler(a_ItemType);
|
||||
case E_BLOCK_VINES: return new cItemVineHandler(a_ItemType);
|
||||
case E_BLOCK_WOODEN_BUTTON: return new cItemButtonHandler(a_ItemType);
|
||||
case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(a_ItemType);
|
||||
case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
|
||||
case E_ITEM_BANNER: return new cItemBannerHandler(a_ItemType);
|
||||
case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
|
||||
case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler();
|
||||
case E_ITEM_BOW: return new cItemBowHandler();
|
||||
case E_ITEM_BREWING_STAND: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_BREWING_STAND);
|
||||
case E_ITEM_CAKE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAKE);
|
||||
case E_ITEM_CAULDRON: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_CAULDRON);
|
||||
case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
|
||||
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
|
||||
case E_ITEM_EGG: return new cItemEggHandler();
|
||||
case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
|
||||
case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
|
||||
case E_ITEM_END_CRYSTAL: return new cItemEndCrystalHandler(a_ItemType);
|
||||
case E_ITEM_EYE_OF_ENDER: return new cItemEyeOfEnderHandler();
|
||||
case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
|
||||
case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType);
|
||||
case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
|
||||
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
|
||||
case E_ITEM_FLOWER_POT: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_FLOWER_POT);
|
||||
case E_ITEM_GLASS_BOTTLE: return new cItemBottleHandler();
|
||||
case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType);
|
||||
case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
|
||||
case E_ITEM_MAP: return new cItemMapHandler();
|
||||
case E_ITEM_MILK: return new cItemMilkHandler();
|
||||
case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
|
||||
case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
|
||||
case E_ITEM_POTIONS: return new cItemPotionHandler();
|
||||
case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
|
||||
case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
|
||||
case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
|
||||
case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
|
||||
case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
|
||||
case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
|
||||
case E_ITEM_STRING: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_TRIPWIRE);
|
||||
case E_ITEM_SUGARCANE: return new cSimplePlaceableItemHandler(a_ItemType, E_BLOCK_SUGARCANE);
|
||||
|
||||
case E_ITEM_WOODEN_HOE:
|
||||
case E_ITEM_STONE_HOE:
|
||||
@ -247,6 +302,54 @@ cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType)
|
||||
return new cItemMinecartHandler(a_ItemType);
|
||||
}
|
||||
|
||||
case E_BLOCK_ACACIA_FENCE_GATE:
|
||||
case E_BLOCK_BIRCH_FENCE_GATE:
|
||||
case E_BLOCK_DARK_OAK_FENCE_GATE:
|
||||
case E_BLOCK_JUNGLE_FENCE_GATE:
|
||||
case E_BLOCK_OAK_FENCE_GATE:
|
||||
case E_BLOCK_SPRUCE_FENCE_GATE:
|
||||
{
|
||||
return new cItemFenceGateHandler(a_ItemType);
|
||||
}
|
||||
|
||||
case E_BLOCK_ACACIA_WOOD_STAIRS:
|
||||
case E_BLOCK_BIRCH_WOOD_STAIRS:
|
||||
case E_BLOCK_BRICK_STAIRS:
|
||||
case E_BLOCK_COBBLESTONE_STAIRS:
|
||||
case E_BLOCK_DARK_OAK_WOOD_STAIRS:
|
||||
case E_BLOCK_JUNGLE_WOOD_STAIRS:
|
||||
case E_BLOCK_NETHER_BRICK_STAIRS:
|
||||
case E_BLOCK_OAK_WOOD_STAIRS:
|
||||
case E_BLOCK_PURPUR_STAIRS:
|
||||
case E_BLOCK_QUARTZ_STAIRS:
|
||||
case E_BLOCK_RED_SANDSTONE_STAIRS:
|
||||
case E_BLOCK_SANDSTONE_STAIRS:
|
||||
case E_BLOCK_SPRUCE_WOOD_STAIRS:
|
||||
case E_BLOCK_STONE_BRICK_STAIRS:
|
||||
{
|
||||
return new cItemStairsHandler(a_ItemType);
|
||||
}
|
||||
|
||||
case E_BLOCK_WHITE_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_ORANGE_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_MAGENTA_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_LIGHT_BLUE_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_YELLOW_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_LIME_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_PINK_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_GRAY_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_LIGHT_GRAY_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_CYAN_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_PURPLE_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_BLUE_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_BROWN_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_GREEN_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_RED_GLAZED_TERRACOTTA:
|
||||
case E_BLOCK_BLACK_GLAZED_TERRACOTTA:
|
||||
{
|
||||
return new cItemGlazedTerracottaHandler(a_ItemType);
|
||||
}
|
||||
|
||||
// Food (please keep alpha-sorted):
|
||||
case E_ITEM_BAKED_POTATO: return new cItemFoodHandler(a_ItemType, FoodInfo(5, 6));
|
||||
case E_ITEM_BEETROOT: return new cItemFoodHandler(a_ItemType, FoodInfo(1, 1.2));
|
||||
@ -347,84 +450,66 @@ cItemHandler::cItemHandler(int a_ItemType)
|
||||
|
||||
|
||||
|
||||
bool cItemHandler::OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
)
|
||||
void cItemHandler::OnPlayerPlace(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_ClickedBlockPosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
|
||||
{
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
|
||||
{
|
||||
// Clicked in the air, no placement possible
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cChunkDef::IsValidHeight(a_ClickedBlockPos.y))
|
||||
if (!cChunkDef::IsValidHeight(a_ClickedBlockPosition.y))
|
||||
{
|
||||
// The clicked block is outside the world, ignore this call altogether (#128)
|
||||
return false;
|
||||
// The clicked block is outside the world, ignore this call altogether (GH #128):
|
||||
return;
|
||||
}
|
||||
|
||||
const auto & World = *a_Player.GetWorld();
|
||||
BLOCKTYPE ClickedBlockType;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
|
||||
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
|
||||
cChunkInterface ChunkInterface(a_World.GetChunkMap());
|
||||
World.GetBlockTypeMeta(a_ClickedBlockPosition, ClickedBlockType, ClickedBlockMeta);
|
||||
|
||||
// Check if the block ignores build collision (water, grass etc.):
|
||||
auto PlacedBlockPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
|
||||
if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
|
||||
if (cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(World, a_HeldItem, a_ClickedBlockPosition, ClickedBlockMeta, a_ClickedBlockFace, true))
|
||||
{
|
||||
// Replace the clicked block:
|
||||
a_World.DropBlockAsPickups(a_ClickedBlockPos, &a_Player, nullptr);
|
||||
PlacedBlockPos = a_ClickedBlockPos;
|
||||
// Try to place the block at the clicked position:
|
||||
if (!CommitPlacement(a_Player, a_HeldItem, a_ClickedBlockPosition, a_ClickedBlockFace, a_CursorPosition))
|
||||
{
|
||||
// The placement failed, the blocks have already been re-sent, re-send inventory:
|
||||
a_Player.GetInventory().SendEquippedSlot();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!cChunkDef::IsValidHeight(PlacedBlockPos.y))
|
||||
const auto PlacedPosition = AddFaceDirection(a_ClickedBlockPosition, a_ClickedBlockFace);
|
||||
|
||||
if (!cChunkDef::IsValidHeight(PlacedPosition.y))
|
||||
{
|
||||
// The block is being placed outside the world, ignore this packet altogether (#128)
|
||||
return false;
|
||||
// The block is being placed outside the world, ignore this packet altogether (GH #128):
|
||||
return;
|
||||
}
|
||||
|
||||
NIBBLETYPE PlaceMeta;
|
||||
BLOCKTYPE PlaceBlock;
|
||||
a_World.GetBlockTypeMeta(PlacedBlockPos, PlaceBlock, PlaceMeta);
|
||||
World.GetBlockTypeMeta(PlacedPosition, PlaceBlock, PlaceMeta);
|
||||
|
||||
// Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
|
||||
// No need to do combinability (dblslab) checks, client will do that here.
|
||||
if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(ChunkInterface, PlacedBlockPos, a_Player, PlaceMeta))
|
||||
if (!cBlockHandler::For(PlaceBlock).DoesIgnoreBuildCollision(World, a_HeldItem, PlacedPosition, PlaceMeta, a_ClickedBlockFace, false))
|
||||
{
|
||||
// Tried to place a block into another?
|
||||
// Happens when you place a block aiming at side of block with a torch on it or stem beside it
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get all the blocks to place:
|
||||
sSetBlockVector blocks;
|
||||
if (!GetBlocksToPlace(a_World, a_Player, a_EquippedItem, PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, blocks))
|
||||
{
|
||||
// Handler refused the placement, send that information back to the client:
|
||||
for (const auto & blk: blocks)
|
||||
// Try to place the block:
|
||||
if (!CommitPlacement(a_Player, a_HeldItem, PlacedPosition, a_ClickedBlockFace, a_CursorPosition))
|
||||
{
|
||||
const auto & AbsPos = blk.GetAbsolutePos();
|
||||
a_World.SendBlockTo(AbsPos, a_Player);
|
||||
// The placement failed, the blocks have already been re-sent, re-send inventory:
|
||||
a_Player.GetInventory().SendEquippedSlot();
|
||||
return;
|
||||
}
|
||||
a_World.SendBlockTo(PlacedBlockPos, a_Player);
|
||||
a_Player.GetInventory().SendEquippedSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to place the blocks:
|
||||
if (!a_Player.PlaceBlocks(blocks))
|
||||
{
|
||||
// The placement failed, the blocks have already been re-sent, re-send inventory:
|
||||
a_Player.GetInventory().SendEquippedSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the "placed" item:
|
||||
@ -432,29 +517,6 @@ bool cItemHandler::OnPlayerPlace(
|
||||
{
|
||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cItemHandler::GetBlocksToPlace(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
sSetBlockVector & a_BlocksToSet
|
||||
)
|
||||
{
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, BlockType, BlockMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, BlockMeta);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -821,34 +883,6 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
|
||||
|
||||
|
||||
|
||||
bool cItemHandler::GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos, eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
)
|
||||
{
|
||||
ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers
|
||||
|
||||
if (m_ItemType >= 256)
|
||||
{
|
||||
LOGERROR("%s: Item %d is not eligible for direct block placement!", __FUNCTION__, m_ItemType);
|
||||
return false;
|
||||
}
|
||||
|
||||
cChunkInterface ChunkInterface(a_World->GetChunkMap());
|
||||
return cBlockHandler::For(static_cast<BLOCKTYPE>(m_ItemType)).GetPlacementBlockTypeMeta(
|
||||
ChunkInterface, *a_Player,
|
||||
a_PlacedBlockPos, a_ClickedBlockFace,
|
||||
a_CursorPos,
|
||||
a_BlockType, a_BlockMeta
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
|
||||
{
|
||||
auto FoodInfo = GetFoodInfo(a_Item);
|
||||
@ -873,3 +907,19 @@ float cItemHandler::GetBlockBreakingStrength(BLOCKTYPE a_Block)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cItemHandler::CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
|
||||
{
|
||||
ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers.
|
||||
|
||||
// By default, all blocks can be placed and the meta is copied over from the item's damage value:
|
||||
return a_Player.PlaceBlock(
|
||||
a_PlacePosition,
|
||||
static_cast<BLOCKTYPE>(m_ItemType),
|
||||
static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x0f)
|
||||
);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
|
||||
// fwd:
|
||||
class cChunk;
|
||||
class cWorld;
|
||||
class cPlayer;
|
||||
class cBlockPluginInterface;
|
||||
@ -36,55 +37,14 @@ public:
|
||||
/** Force virtual destructor */
|
||||
virtual ~cItemHandler() {}
|
||||
|
||||
|
||||
/** Called when the player tries to place the item (right mouse button, IsPlaceable() == true).
|
||||
a_ClickedBlockPos is the (neighbor) block that has been clicked to place this item.
|
||||
a_ClickedBlockFace is the face of the neighbor that has been clicked to place this item.
|
||||
a_CursorPos is the position of the player's cursor within a_ClickedBlockFace.
|
||||
The default handler uses GetBlocksToPlace() and places the returned blocks.
|
||||
Override if the item needs advanced processing, such as spawning a mob based on the blocks being placed.
|
||||
If the block placement is refused inside this call, it will automatically revert the client-side changes.
|
||||
Returns true if the placement succeeded, false if the placement was aborted for any reason. */
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
);
|
||||
|
||||
|
||||
/** Called from OnPlayerPlace() to determine the blocks that the current placement operation should set.
|
||||
a_PlacedBlockPos points to the location where the new block should be set.
|
||||
a_ClickedBlockFace is the block face of the neighbor that was clicked to place this block.
|
||||
a_CursorPos is the position of the mouse cursor within the clicked (neighbor's) block face.
|
||||
The blocks in a_BlocksToPlace will be sent through cPlayer::PlaceBlocks() after returning from this function.
|
||||
The default handler uses GetPlacementBlockTypeMeta() and provides that as the single block at the specified coords.
|
||||
Returns true if the placement succeeded, false if the placement was aborted for any reason.
|
||||
If aborted, the server then sends all original blocks in the coords provided in a_BlocksToSet to the client. */
|
||||
virtual bool GetBlocksToPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
sSetBlockVector & a_BlocksToPlace
|
||||
);
|
||||
|
||||
|
||||
/** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlayerPlace() is not overridden.
|
||||
This function should provide the block type and meta for the placed block, or refuse the placement.
|
||||
Returns true to allow placement, false to refuse. */
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
);
|
||||
|
||||
If the block placement is refused inside this call, it will automatically revert the client-side changes. */
|
||||
void OnPlayerPlace(cPlayer & a_Player, const cItem & a_HeldItem, Vector3i a_ClickedBlockPosition, eBlockFace a_ClickedBlockFace, Vector3i a_CursorPosition);
|
||||
|
||||
/** Called when the player tries to use the item (right mouse button).
|
||||
Descendants can return false to abort the usage (default behavior). */
|
||||
@ -97,7 +57,6 @@ public:
|
||||
eBlockFace a_ClickedBlockFace
|
||||
);
|
||||
|
||||
|
||||
/** Called when the client sends the SHOOT status in the lclk packet (releasing the bow). */
|
||||
virtual void OnItemShoot(cPlayer *, const Vector3i a_BlockPos, eBlockFace a_BlockFace)
|
||||
{
|
||||
@ -181,9 +140,16 @@ public:
|
||||
static void Deinit();
|
||||
|
||||
protected:
|
||||
|
||||
int m_ItemType;
|
||||
static cItemHandler * CreateItemHandler(int m_ItemType);
|
||||
|
||||
/** Performs the actual placement of this placeable item.
|
||||
The descendant handler should call a_Player.PlaceBlock(s) supplying correct values for the newly placed block.
|
||||
The default handler uses the stored block type and meta copied from the lowest 4 bits of the player's equipped item's damage value.
|
||||
Handlers return what a_Player.PlaceBlock(s) returns, indicating whether the placement was successful. */
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, Vector3i a_PlacePosition, eBlockFace a_ClickedBlockFace, Vector3i a_CursorPosition);
|
||||
|
||||
static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1];
|
||||
static bool m_HandlerInitialized; // used to detect if the itemhandlers are initialized
|
||||
};
|
||||
|
40
src/Items/ItemHopper.h
Normal file
40
src/Items/ItemHopper.h
Normal 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));
|
||||
}
|
||||
};
|
27
src/Items/ItemJackOLantern.h
Normal file
27
src/Items/ItemJackOLantern.h
Normal 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
77
src/Items/ItemLadder.h
Normal 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;
|
||||
}
|
||||
};
|
@ -23,23 +23,13 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
bool res = Super::GetPlacementBlockTypeMeta(
|
||||
a_World, a_Player,
|
||||
a_PlacedBlockPos,
|
||||
a_ClickedBlockFace,
|
||||
a_CursorPos,
|
||||
a_BlockType, a_BlockMeta
|
||||
return a_Player.PlaceBlock(
|
||||
a_PlacePosition,
|
||||
static_cast<BLOCKTYPE>(m_ItemType),
|
||||
static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage | 0x4) // 0x4 bit set means this is a player-placed leaves block, not to be decayed.
|
||||
);
|
||||
a_BlockMeta = a_BlockMeta | 0x4; // 0x4 bit set means this is a player-placed leaves block, not to be decayed
|
||||
return res;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
43
src/Items/ItemLever.h
Normal file
43
src/Items/ItemLever.h
Normal 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));
|
||||
}
|
||||
};
|
@ -25,38 +25,30 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
// Cannot place a head at "no face" and from the bottom:
|
||||
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
|
||||
|
||||
// If the placed head is a wither, try to spawn the wither first:
|
||||
if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER)
|
||||
if (a_HeldItem.m_ItemDamage == E_META_HEAD_WITHER)
|
||||
{
|
||||
if (TrySpawnWitherAround(a_World, a_Player, PlacePos))
|
||||
if (TrySpawnWitherAround(a_Player, a_PlacePosition))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Wither not created, proceed with regular head placement
|
||||
}
|
||||
|
||||
cItem ItemCopy(a_EquippedItem); // Make a copy in case this is the player's last head item and OnPlayerPlace removes it
|
||||
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
|
||||
if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_ClickedBlockFace)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
RegularHeadPlaced(a_World, a_Player, ItemCopy, PlacePos, a_ClickedBlockFace);
|
||||
|
||||
RegularHeadPlaced(a_Player, a_HeldItem, a_PlacePosition, a_ClickedBlockFace);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -66,16 +58,13 @@ public:
|
||||
|
||||
/** Called after placing a regular head block with no mob spawning.
|
||||
Adjusts the mob head entity based on the equipped item's data. */
|
||||
void RegularHeadPlaced(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
const Vector3i a_PlacePos, eBlockFace a_ClickedBlockFace
|
||||
)
|
||||
void RegularHeadPlaced(const cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace)
|
||||
{
|
||||
auto HeadType = static_cast<eMobHeadType>(a_EquippedItem.m_ItemDamage);
|
||||
auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace);
|
||||
const auto HeadType = static_cast<eMobHeadType>(a_HeldItem.m_ItemDamage);
|
||||
const auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace);
|
||||
|
||||
// Use a callback to set the properties of the mob head block entity:
|
||||
a_World.DoWithBlockEntityAt(a_PlacePos, [&](cBlockEntity & a_BlockEntity)
|
||||
a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_Player, HeadType, BlockMeta](cBlockEntity & a_BlockEntity)
|
||||
{
|
||||
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_HEAD);
|
||||
|
||||
@ -99,10 +88,7 @@ public:
|
||||
|
||||
/** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula.
|
||||
Returns true if the wither was created. */
|
||||
bool TrySpawnWitherAround(
|
||||
cWorld & a_World, cPlayer & a_Player,
|
||||
const Vector3i a_BlockPos
|
||||
)
|
||||
bool TrySpawnWitherAround(cPlayer & a_Player, const Vector3i a_BlockPos)
|
||||
{
|
||||
// No wither can be created at Y < 2 - not enough space for the formula:
|
||||
if (a_BlockPos.y < 2)
|
||||
@ -119,10 +105,11 @@ public:
|
||||
{ 0, 0, 1},
|
||||
{ 0, 0, -1},
|
||||
};
|
||||
|
||||
for (auto & RelCoord : RelCoords)
|
||||
{
|
||||
if (TrySpawnWitherAt(
|
||||
a_World, a_Player,
|
||||
*a_Player.GetWorld(), a_Player,
|
||||
a_BlockPos,
|
||||
RelCoord.x, RelCoord.z
|
||||
))
|
||||
@ -177,12 +164,12 @@ public:
|
||||
// Try to spawn the wither from each image:
|
||||
return (
|
||||
TrySpawnWitherFromImage(
|
||||
a_World, a_Player, ImageWitherX, ARRAYCOUNT(ImageWitherX),
|
||||
a_World, a_Player, ImageWitherX,
|
||||
a_PlacedHeadPos,
|
||||
a_OffsetX, a_OffsetZ
|
||||
) ||
|
||||
TrySpawnWitherFromImage(
|
||||
a_World, a_Player, ImageWitherZ, ARRAYCOUNT(ImageWitherZ),
|
||||
a_World, a_Player, ImageWitherZ,
|
||||
a_PlacedHeadPos,
|
||||
a_OffsetX, a_OffsetZ
|
||||
)
|
||||
@ -199,35 +186,39 @@ public:
|
||||
Offset is used to shift the image around the X and Z axis.
|
||||
Returns true iff the wither was created successfully. */
|
||||
bool TrySpawnWitherFromImage(
|
||||
cWorld & a_World, cPlayer & a_Player, const sSetBlock * a_Image, size_t a_ImageCount,
|
||||
cWorld & a_World, cPlayer & a_Player, const sSetBlock (& a_Image)[9],
|
||||
Vector3i a_PlacedHeadPos,
|
||||
int a_OffsetX, int a_OffsetZ
|
||||
)
|
||||
{
|
||||
// Check each block individually; simultaneously build the SetBlockVector for clearing the blocks:
|
||||
sSetBlockVector AirBlocks;
|
||||
AirBlocks.reserve(a_ImageCount);
|
||||
for (size_t i = 0; i < a_ImageCount; i++)
|
||||
{
|
||||
// Get the absolute coords of the image:
|
||||
int BlockX = a_PlacedHeadPos.x + a_OffsetX + a_Image[i].GetX();
|
||||
int BlockY = a_PlacedHeadPos.y + a_Image[i].GetY();
|
||||
int BlockZ = a_PlacedHeadPos.z + a_OffsetZ + a_Image[i].GetZ();
|
||||
std::array<Vector3i, 9> PositionsToClear;
|
||||
|
||||
// If the query is for the placed head, short-circuit-evaluate it:
|
||||
if ((BlockX == a_PlacedHeadPos.x) && (BlockY == a_PlacedHeadPos.y) && (BlockZ == a_PlacedHeadPos.z))
|
||||
// Check each block individually:
|
||||
for (size_t i = 0; i != std::size(a_Image); i++)
|
||||
{
|
||||
// The absolute coords of the block in the image to check.
|
||||
const Vector3i Block(
|
||||
a_PlacedHeadPos.x + a_OffsetX + a_Image[i].GetX(),
|
||||
a_PlacedHeadPos.y + a_Image[i].GetY(),
|
||||
a_PlacedHeadPos.z + a_OffsetZ + a_Image[i].GetZ()
|
||||
);
|
||||
|
||||
// If the query is for the head the player is about to place (remember, it hasn't been set into the world yet), short-circuit-evaluate it:
|
||||
if (Block == a_PlacedHeadPos)
|
||||
{
|
||||
if (a_Image[i].m_BlockType != E_BLOCK_HEAD)
|
||||
{
|
||||
return false; // Didn't match
|
||||
return false; // Didn't match.
|
||||
}
|
||||
continue; // Matched, continue checking the rest of the image
|
||||
|
||||
PositionsToClear[i] = Block;
|
||||
continue; // Matched, continue checking the rest of the image.
|
||||
}
|
||||
|
||||
// Query the world block:
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_World.GetBlockTypeMeta({ BlockX, BlockY, BlockZ }, BlockType, BlockMeta))
|
||||
if (!a_World.GetBlockTypeMeta(Block, BlockType, BlockMeta))
|
||||
{
|
||||
// Cannot query block, assume unloaded chunk, fail to spawn the wither
|
||||
return false;
|
||||
@ -242,7 +233,7 @@ public:
|
||||
// If it is a mob head, check it's a wither skull using the block entity:
|
||||
if (
|
||||
(BlockType == E_BLOCK_HEAD) &&
|
||||
!a_World.DoWithBlockEntityAt({ BlockX, BlockY, BlockZ }, [&](cBlockEntity & a_BlockEntity)
|
||||
!a_World.DoWithBlockEntityAt(Block, [&](cBlockEntity & a_BlockEntity)
|
||||
{
|
||||
ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_HEAD);
|
||||
|
||||
@ -253,12 +244,25 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Matched, continue checking
|
||||
AirBlocks.emplace_back(BlockX, BlockY, BlockZ, E_BLOCK_AIR, 0);
|
||||
// Matched, continue checking:
|
||||
PositionsToClear[i] = Block;
|
||||
} // for i - a_Image
|
||||
|
||||
// All image blocks matched, try replace the image with air blocks:
|
||||
if (!a_Player.PlaceBlocks(AirBlocks))
|
||||
if (
|
||||
!a_Player.PlaceBlocks(
|
||||
{
|
||||
{ PositionsToClear[0], E_BLOCK_AIR, 0 },
|
||||
{ PositionsToClear[1], E_BLOCK_AIR, 0 },
|
||||
{ PositionsToClear[2], E_BLOCK_AIR, 0 },
|
||||
{ PositionsToClear[3], E_BLOCK_AIR, 0 },
|
||||
{ PositionsToClear[4], E_BLOCK_AIR, 0 },
|
||||
{ PositionsToClear[5], E_BLOCK_AIR, 0 },
|
||||
{ PositionsToClear[6], E_BLOCK_AIR, 0 },
|
||||
{ PositionsToClear[7], E_BLOCK_AIR, 0 },
|
||||
{ PositionsToClear[8], E_BLOCK_AIR, 0 },
|
||||
})
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -323,23 +327,6 @@ public:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
{
|
||||
a_BlockType = E_BLOCK_HEAD;
|
||||
a_BlockMeta = BlockFaceToBlockMeta(a_ClickedBlockFace);
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -24,37 +24,23 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
// Only allow planting nether wart onto the top side of the block:
|
||||
if (a_ClickedBlockFace != BLOCK_FACE_TOP)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only allow placement on soulsand
|
||||
if ((a_PlacedBlockPos.y < 1) || (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_SOULSAND))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_NETHER_WART, 0);
|
||||
}
|
||||
|
||||
a_BlockMeta = 0;
|
||||
a_BlockType = E_BLOCK_NETHER_WART;
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
26
src/Items/ItemObserver.h
Normal file
26
src/Items/ItemObserver.h
Normal 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
26
src/Items/ItemPiston.h
Normal 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
25
src/Items/ItemPlanks.h
Normal 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));
|
||||
}
|
||||
};
|
@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ItemHandler.h"
|
||||
#include "Blocks/BlockPumpkin.h"
|
||||
|
||||
|
||||
|
||||
@ -14,35 +15,22 @@ class cItemPumpkinHandler:
|
||||
|
||||
public:
|
||||
|
||||
cItemPumpkinHandler():
|
||||
Super(E_BLOCK_PUMPKIN)
|
||||
{
|
||||
}
|
||||
using Super::Super;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
// First try spawning a snow golem or an iron golem:
|
||||
auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
|
||||
if (TrySpawnGolem(a_World, a_Player, PlacePos))
|
||||
if (TrySpawnGolem(a_Player, a_PlacePosition))
|
||||
{
|
||||
// The client thinks that they placed the pumpkin, let them know it's been replaced:
|
||||
a_Player.SendBlocksAround(PlacePos.x, PlacePos.y, PlacePos.z);
|
||||
a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z);
|
||||
return true;
|
||||
}
|
||||
|
||||
// No golem at these coords, place the block normally:
|
||||
return Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos);
|
||||
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_PUMPKIN, cBlockPumpkinHandler::YawToMetaData(a_Player.GetYaw()));
|
||||
}
|
||||
|
||||
|
||||
@ -51,22 +39,24 @@ public:
|
||||
|
||||
/** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
|
||||
Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */
|
||||
bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, const Vector3i a_PumpkinPos)
|
||||
bool TrySpawnGolem(cPlayer & a_Player, const Vector3i a_PumpkinPos)
|
||||
{
|
||||
// A golem can't form with a pumpkin below level 2 or above level 255
|
||||
// A golem can't form with a pumpkin below level 2 or above level 255:
|
||||
if ((a_PumpkinPos.y < 2) || (a_PumpkinPos.y >= cChunkDef::Height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto & World = *a_Player.GetWorld();
|
||||
|
||||
// Decide which golem to try spawning based on the block below the placed pumpkin:
|
||||
switch (a_World.GetBlock(a_PumpkinPos.addedY(-1)))
|
||||
switch (World.GetBlock(a_PumpkinPos.addedY(-1)))
|
||||
{
|
||||
case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_PumpkinPos);
|
||||
case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_PumpkinPos);
|
||||
case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(World, a_Player, a_PumpkinPos);
|
||||
case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(World, a_Player, a_PumpkinPos);
|
||||
default:
|
||||
{
|
||||
// No golem here
|
||||
// No golem here:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -91,11 +81,14 @@ public:
|
||||
}
|
||||
|
||||
// Try to place air blocks where the original recipe blocks were:
|
||||
sSetBlockVector AirBlocks;
|
||||
AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
|
||||
AirBlocks.emplace_back(a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0); // Torso
|
||||
AirBlocks.emplace_back(a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0); // Legs
|
||||
if (!a_Player.PlaceBlocks(AirBlocks))
|
||||
if (
|
||||
!a_Player.PlaceBlocks(
|
||||
{
|
||||
{ a_PumpkinPos, E_BLOCK_AIR, 0 }, // Head
|
||||
{ a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0 }, // Torso
|
||||
{ a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0 } // Legs
|
||||
})
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -143,13 +136,16 @@ public:
|
||||
}
|
||||
|
||||
// Try to place air blocks where the original recipe blocks were:
|
||||
sSetBlockVector AirBlocks;
|
||||
AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
|
||||
AirBlocks.emplace_back(BodyPos, E_BLOCK_AIR, 0); // Torso
|
||||
AirBlocks.emplace_back(BodyPos.addedY(-1), E_BLOCK_AIR, 0); // Legs
|
||||
AirBlocks.emplace_back(BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
|
||||
AirBlocks.emplace_back(BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
|
||||
if (!a_Player.PlaceBlocks(AirBlocks))
|
||||
if (
|
||||
!a_Player.PlaceBlocks(
|
||||
{
|
||||
{ a_PumpkinPos, E_BLOCK_AIR, 0 }, // Head
|
||||
{ BodyPos, E_BLOCK_AIR, 0 }, // Torso
|
||||
{ BodyPos.addedY(-1), E_BLOCK_AIR, 0 }, // Legs
|
||||
{ BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0 }, // Arm
|
||||
{ BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0 } // Arm
|
||||
})
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
60
src/Items/ItemQuartz.h
Normal file
60
src/Items/ItemQuartz.h
Normal 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
28
src/Items/ItemRail.h
Normal 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));
|
||||
}
|
||||
};
|
@ -23,69 +23,19 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_REDSTONE_WIRE, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
{
|
||||
// Check the block below, if it supports dust on top of it:
|
||||
auto UnderPos = a_PlacedBlockPos.addedY(-1);
|
||||
if (UnderPos.y < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_World->GetBlockTypeMeta(UnderPos, BlockType, BlockMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!IsBlockTypeUnderSuitable(BlockType, BlockMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
a_BlockType = E_BLOCK_REDSTONE_WIRE;
|
||||
a_BlockMeta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */
|
||||
static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
if (cBlockInfo::FullyOccupiesVoxel(a_BlockType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (a_BlockType)
|
||||
{
|
||||
case E_BLOCK_RED_SANDSTONE_SLAB:
|
||||
case E_BLOCK_WOODEN_SLAB:
|
||||
case E_BLOCK_STONE_SLAB:
|
||||
{
|
||||
// Slabs can support redstone if they're upside down:
|
||||
return ((a_BlockMeta & 0x08) != 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -24,25 +24,17 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool IsPlaceable() override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
return true;
|
||||
return a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_REDSTONE_REPEATER_OFF, cBlockRedstoneRepeaterHandler::YawToMetaData(a_Player.GetYaw()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
virtual bool IsPlaceable() override
|
||||
{
|
||||
a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF;
|
||||
a_BlockMeta = cBlockRedstoneRepeaterHandler::YawToMetaData(a_Player->GetYaw());
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
@ -20,30 +20,12 @@ public:
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
bool res = Super::GetPlacementBlockTypeMeta(
|
||||
a_World, a_Player,
|
||||
a_PlacedBlockPos, a_ClickedBlockFace,
|
||||
a_CursorPos,
|
||||
a_BlockType, a_BlockMeta
|
||||
return a_Player.PlaceBlock(
|
||||
a_PlacePosition,
|
||||
static_cast<BLOCKTYPE>(m_ItemType),
|
||||
static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage & 0x07) // Allow only the lowest 3 bits (top bit is for growth).
|
||||
);
|
||||
|
||||
// Allow only the lowest 3 bits (top bit is for growth):
|
||||
a_BlockMeta = a_BlockMeta & 0x07;
|
||||
return res;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -25,47 +25,38 @@ public:
|
||||
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
return true;
|
||||
// Only allow planting seeds from the top side of the block:
|
||||
if (a_ClickedBlockFace != BLOCK_FACE_TOP)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockType;
|
||||
|
||||
// Get the produce block based on the seed item:
|
||||
switch (m_ItemType)
|
||||
{
|
||||
case E_ITEM_BEETROOT_SEEDS: BlockType = E_BLOCK_BEETROOTS; break;
|
||||
case E_ITEM_CARROT: BlockType = E_BLOCK_CARROTS; break;
|
||||
case E_ITEM_MELON_SEEDS: BlockType = E_BLOCK_MELON_STEM; break;
|
||||
case E_ITEM_POTATO: BlockType = E_BLOCK_POTATOES; break;
|
||||
case E_ITEM_PUMPKIN_SEEDS: BlockType = E_BLOCK_PUMPKIN_STEM; break;
|
||||
case E_ITEM_SEEDS: BlockType = E_BLOCK_CROPS; break;
|
||||
default: UNREACHABLE("Unsupported seed type");
|
||||
}
|
||||
|
||||
return a_Player.PlaceBlock(a_PlacePosition, BlockType, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
// Only allow planting seeds from the top side of the block:
|
||||
if ((a_ClickedBlockFace != BLOCK_FACE_TOP) || (a_PlacedBlockPos.y <= 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only allow placement on farmland
|
||||
if (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_FARMLAND)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the produce block based on the seed item:
|
||||
a_BlockMeta = 0;
|
||||
switch (m_ItemType)
|
||||
{
|
||||
case E_ITEM_BEETROOT_SEEDS: a_BlockType = E_BLOCK_BEETROOTS; return true;
|
||||
case E_ITEM_CARROT: a_BlockType = E_BLOCK_CARROTS; return true;
|
||||
case E_ITEM_MELON_SEEDS: a_BlockType = E_BLOCK_MELON_STEM; return true;
|
||||
case E_ITEM_POTATO: a_BlockType = E_BLOCK_POTATOES; return true;
|
||||
case E_ITEM_PUMPKIN_SEEDS: a_BlockType = E_BLOCK_PUMPKIN_STEM; return true;
|
||||
case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true;
|
||||
default: a_BlockType = E_BLOCK_AIR; return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
53
src/Items/ItemSideways.h
Normal file
53
src/Items/ItemSideways.h
Normal 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)));
|
||||
}
|
||||
};
|
@ -3,8 +3,6 @@
|
||||
|
||||
#include "ItemHandler.h"
|
||||
#include "../World.h"
|
||||
#include "../Blocks/BlockSignPost.h"
|
||||
#include "../Blocks/BlockWallSign.h"
|
||||
#include "../ClientHandle.h"
|
||||
|
||||
|
||||
@ -18,77 +16,68 @@ class cItemSignHandler:
|
||||
|
||||
public:
|
||||
|
||||
cItemSignHandler(int a_ItemType):
|
||||
Super(a_ItemType)
|
||||
using Super::Super;
|
||||
|
||||
private:
|
||||
|
||||
/** Converts the block face of the neighbor to which the wallsign is attached to the wallsign block's meta. */
|
||||
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
|
||||
{
|
||||
switch (a_NeighborBlockFace)
|
||||
{
|
||||
case BLOCK_FACE_ZM: return 0x02;
|
||||
case BLOCK_FACE_ZP: return 0x03;
|
||||
case BLOCK_FACE_XM: return 0x04;
|
||||
case BLOCK_FACE_XP: return 0x05;
|
||||
case BLOCK_FACE_NONE:
|
||||
case BLOCK_FACE_YP:
|
||||
case BLOCK_FACE_YM:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0x02;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
) override
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
// Check if placing on something ignoring build collision to edit the correct sign later on:
|
||||
BLOCKTYPE ClickedBlockType;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
|
||||
cChunkInterface ChunkInterface(a_World.GetChunkMap());
|
||||
bool IsReplacingClickedBlock = cBlockHandler::For(ClickedBlockType).DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta);
|
||||
|
||||
// If the regular placement doesn't work, do no further processing:
|
||||
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_TOP)
|
||||
{
|
||||
if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_SIGN_POST, RotationToMetaData(a_Player.GetYaw())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_WALLSIGN, BlockFaceToMetaData(a_ClickedBlockFace)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use IsReplacingClickedBlock to make sure we will edit the right sign:
|
||||
auto SignPos = IsReplacingClickedBlock ? a_ClickedBlockPos : AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
|
||||
|
||||
// After successfully placing the sign, open the sign editor for the player:
|
||||
a_Player.GetClientHandle()->SendEditSign(SignPos.x, SignPos.y, SignPos.z);
|
||||
a_Player.GetClientHandle()->SendEditSign(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
const Vector3i a_PlacedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
/** Converts the (player) rotation to placed-signpost block meta. */
|
||||
static NIBBLETYPE RotationToMetaData(double a_Rotation)
|
||||
{
|
||||
if (a_ClickedBlockFace == BLOCK_FACE_TOP)
|
||||
a_Rotation += 180 + (180.f / 16); // So it's not aligned with axis.
|
||||
if (a_Rotation > 360)
|
||||
{
|
||||
a_BlockMeta = cBlockSignPostHandler::RotationToMetaData(a_Player->GetYaw());
|
||||
a_BlockType = E_BLOCK_SIGN_POST;
|
||||
a_Rotation -= 360;
|
||||
}
|
||||
else
|
||||
{
|
||||
a_BlockMeta = cBlockWallSignHandler::BlockFaceToMetaData(a_ClickedBlockFace);
|
||||
a_BlockType = E_BLOCK_WALLSIGN;
|
||||
}
|
||||
return true;
|
||||
|
||||
a_Rotation = (a_Rotation / 360) * 16;
|
||||
|
||||
return static_cast<char>(a_Rotation) % 16;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -14,95 +14,87 @@ class cItemSlabHandler:
|
||||
|
||||
public:
|
||||
|
||||
/** Creates a new handler for the specified slab item type.
|
||||
Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */
|
||||
cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType):
|
||||
Super(a_ItemType),
|
||||
m_DoubleSlabBlockType(a_DoubleSlabBlockType)
|
||||
using Super::Super;
|
||||
|
||||
private:
|
||||
|
||||
virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) override
|
||||
{
|
||||
}
|
||||
// Confer BlockSlab.h, which we're in cahoots with to make the below logic work.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// cItemHandler overrides:
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World,
|
||||
cPlayer & a_Player,
|
||||
const cItem & a_EquippedItem,
|
||||
const Vector3i a_ClickedBlockPos,
|
||||
eBlockFace a_ClickedBlockFace,
|
||||
const Vector3i a_CursorPos
|
||||
) override
|
||||
{
|
||||
// If clicking a slab, try combining it into a double-slab:
|
||||
BLOCKTYPE ClickedBlockType;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
|
||||
if (
|
||||
(ClickedBlockType == m_ItemType) && // Placing the same slab material
|
||||
((ClickedBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
|
||||
)
|
||||
// If clicking a slab, combine it into a double-slab:
|
||||
if (cBlockSlabHandler::IsAnySlabType(a_Player.GetWorld()->GetBlock(a_PlacePosition)))
|
||||
{
|
||||
if (
|
||||
((a_ClickedBlockFace == BLOCK_FACE_TOP) && ((ClickedBlockMeta & 0x08) == 0)) || // Top side of a bottom-half-slab
|
||||
((a_ClickedBlockFace == BLOCK_FACE_BOTTOM) && ((ClickedBlockMeta & 0x08) != 0)) // Bottom side of a top-half-slab
|
||||
)
|
||||
{
|
||||
if (!a_Player.PlaceBlock(a_ClickedBlockPos.x, a_ClickedBlockPos.y, a_ClickedBlockPos.z, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (a_Player.IsGameModeSurvival())
|
||||
{
|
||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's already a slab in the destination, combine it into a double-slab:
|
||||
auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
|
||||
BLOCKTYPE PlaceBlockType;
|
||||
NIBBLETYPE PlaceBlockMeta;
|
||||
a_World.GetBlockTypeMeta(PlacePos, PlaceBlockType, PlaceBlockMeta);
|
||||
if (
|
||||
(PlaceBlockType == m_ItemType) && // Placing the same slab material
|
||||
((PlaceBlockMeta & 0x07) == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
|
||||
)
|
||||
{
|
||||
if (!a_Player.PlaceBlock(PlacePos.x, PlacePos.y, PlacePos.z, m_DoubleSlabBlockType, PlaceBlockMeta & 0x07))
|
||||
if (!a_Player.PlaceBlock(a_PlacePosition, GetDoubleSlabType(static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType)), static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (a_Player.IsGameModeSurvival())
|
||||
{
|
||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
|
||||
a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z, 2); // (see below)
|
||||
return true;
|
||||
}
|
||||
|
||||
// The slabs didn't combine, use the default handler to place the slab:
|
||||
bool res = Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos);
|
||||
|
||||
/*
|
||||
The client has a bug when a slab replaces snow and there's a slab above it.
|
||||
The client then combines the slab above, rather than replacing the snow.
|
||||
We send the block above the currently placed block back to the client to fix the bug.
|
||||
Ref.: https://forum.cuberite.org/thread-434-post-17388.html#pid17388
|
||||
*/
|
||||
if ((a_ClickedBlockFace == BLOCK_FACE_TOP) && (a_ClickedBlockPos.y < cChunkDef::Height - 1))
|
||||
// Set the correct metadata based on player equipped item:
|
||||
if (!a_Player.PlaceBlock(a_PlacePosition, static_cast<BLOCKTYPE>(a_HeldItem.m_ItemType), FaceToMetaData(static_cast<NIBBLETYPE>(a_HeldItem.m_ItemDamage), a_ClickedBlockFace, a_CursorPosition)))
|
||||
{
|
||||
auto AbovePos = a_ClickedBlockPos.addedY(1);
|
||||
a_Player.SendBlocksAround(AbovePos.x, AbovePos.y, AbovePos.z, 1);
|
||||
return false;
|
||||
}
|
||||
return res;
|
||||
|
||||
/* This is a workaround for versions < 1.13, where the client combines a slab in the
|
||||
direction of the clicked block face of a block ignoring build collision, rather than replacing said block.
|
||||
Resend blocks to the client to fix the bug.
|
||||
Ref.: https://forum.cuberite.org/thread-434-post-17388.html#pid17388 */
|
||||
a_Player.SendBlocksAround(a_PlacePosition.x, a_PlacePosition.y, a_PlacePosition.z, 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
static NIBBLETYPE FaceToMetaData(const NIBBLETYPE a_BaseMeta, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition)
|
||||
{
|
||||
switch (a_ClickedBlockFace)
|
||||
{
|
||||
case BLOCK_FACE_TOP:
|
||||
{
|
||||
// Bottom half slab block:
|
||||
return a_BaseMeta & 0x07;
|
||||
}
|
||||
case BLOCK_FACE_BOTTOM:
|
||||
{
|
||||
// Top half slab block:
|
||||
return a_BaseMeta | 0x08;
|
||||
}
|
||||
case BLOCK_FACE_EAST:
|
||||
case BLOCK_FACE_NORTH:
|
||||
case BLOCK_FACE_SOUTH:
|
||||
case BLOCK_FACE_WEST:
|
||||
{
|
||||
if (a_CursorPosition.y > 7)
|
||||
{
|
||||
// Cursor at top half of block, place top slab:
|
||||
return a_BaseMeta | 0x08;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cursor at bottom half of block, place bottom slab:
|
||||
return a_BaseMeta & 0x07;
|
||||
}
|
||||
}
|
||||
default: UNREACHABLE("Unhandled block face");
|
||||
}
|
||||
}
|
||||
|
||||
/** The block type to use when the slab combines into a doubleslab block. */
|
||||
BLOCKTYPE m_DoubleSlabBlockType;
|
||||
|
||||
/** Converts the single-slab blocktype to its equivalent double-slab blocktype. */
|
||||
static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType)
|
||||
{
|
||||
switch (a_SingleSlabBlockType)
|
||||
{
|
||||
case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB;
|
||||
case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB;
|
||||
case E_BLOCK_RED_SANDSTONE_SLAB: return E_BLOCK_DOUBLE_RED_SANDSTONE_SLAB;
|
||||
case E_BLOCK_PURPUR_SLAB: return E_BLOCK_PURPUR_DOUBLE_SLAB;
|
||||
}
|
||||
UNREACHABLE("Unhandled slab type");
|
||||
}
|
||||
};
|
||||
|
41
src/Items/ItemSnow.h
Normal file
41
src/Items/ItemSnow.h
Normal 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
47
src/Items/ItemStairs.h
Normal 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
82
src/Items/ItemTorch.h
Normal 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
Loading…
Reference in New Issue
Block a user