168 lines
4.6 KiB
C
168 lines
4.6 KiB
C
|
|
||
|
// ItemChest.h
|
||
|
|
||
|
// Declares the cItemChestHandler class representing the cItemHandler descendant responsible for chests
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "ItemHandler.h"
|
||
|
#include "../Blocks/BlockChest.h"
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
class cItemChestHandler:
|
||
|
public cItemHandler
|
||
|
{
|
||
|
typedef cItemHandler super;
|
||
|
public:
|
||
|
cItemChestHandler(int a_ItemType):
|
||
|
super(a_ItemType)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
virtual bool OnPlayerPlace(
|
||
|
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||
|
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
|
||
|
int a_CursorX, int a_CursorY, int a_CursorZ
|
||
|
) override
|
||
|
{
|
||
|
if (a_BlockFace < 0)
|
||
|
{
|
||
|
// Clicked in air
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||
|
{
|
||
|
// 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 ClickedBlock;
|
||
|
NIBBLETYPE ClickedBlockMeta;
|
||
|
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
|
||
|
if (
|
||
|
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
|
||
|
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta)
|
||
|
)
|
||
|
{
|
||
|
cChunkInterface ChunkInterface(a_World.GetChunkMap());
|
||
|
BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||
|
|
||
|
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||
|
{
|
||
|
// The block is being placed outside the world, ignore this packet altogether (#128)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
NIBBLETYPE PlaceMeta;
|
||
|
BLOCKTYPE PlaceBlock;
|
||
|
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, 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 (
|
||
|
!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() &&
|
||
|
!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta)
|
||
|
)
|
||
|
{
|
||
|
// 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check that there is at most one single neighbor of the same chest type:
|
||
|
static const Vector3i CrossCoords[] =
|
||
|
{
|
||
|
{-1, 0, 0},
|
||
|
{ 0, 0, -1},
|
||
|
{ 1, 0, 0},
|
||
|
{ 0, 0, 1},
|
||
|
};
|
||
|
int NeighborIdx = -1;
|
||
|
for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
|
||
|
{
|
||
|
if (a_World.GetBlock(a_BlockX + CrossCoords[i].x, a_BlockY, a_BlockZ + CrossCoords[i].z) != m_ItemType)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if (NeighborIdx >= 0)
|
||
|
{
|
||
|
// Can't place here, there are already two neighbors, this would form a 3-block chest
|
||
|
return false;
|
||
|
}
|
||
|
NeighborIdx = static_cast<int>(i);
|
||
|
|
||
|
// Check that this neighbor is a single chest:
|
||
|
int bx = a_BlockX + CrossCoords[i].x;
|
||
|
int bz = a_BlockZ + CrossCoords[i].z;
|
||
|
for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++)
|
||
|
{
|
||
|
if (a_World.GetBlock(bx + CrossCoords[j].x, a_BlockY, bz + CrossCoords[j].z) == m_ItemType)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
} // for j
|
||
|
} // for i
|
||
|
|
||
|
// If there's no chest neighbor, place the single block chest and bail out:
|
||
|
BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
|
||
|
if (NeighborIdx < 0)
|
||
|
{
|
||
|
NIBBLETYPE Meta = cBlockChestHandler::PlayerYawToMetaData(a_Player.GetYaw());
|
||
|
return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta);
|
||
|
}
|
||
|
|
||
|
// There is a neighbor to which we need to adjust
|
||
|
double yaw = a_Player.GetYaw();
|
||
|
if ((NeighborIdx == 0) || (NeighborIdx == 2))
|
||
|
{
|
||
|
// The neighbor is in the X axis, form a X-axis-aligned dblchest:
|
||
|
NIBBLETYPE Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP;
|
||
|
|
||
|
// Place the new chest:
|
||
|
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Adjust the existing chest:
|
||
|
a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// The neighbor is in the Z axis, form a Z-axis-aligned dblchest:
|
||
|
NIBBLETYPE Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP;
|
||
|
|
||
|
// Place the new chest:
|
||
|
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Adjust the existing chest:
|
||
|
a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
cItemChestHandler(const cItemChestHandler &) = delete;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|