208 lines
4.9 KiB
C++
208 lines
4.9 KiB
C++
|
|
#pragma once
|
|
|
|
#include "BlockHandler.h"
|
|
|
|
|
|
|
|
|
|
|
|
class cBlockVineHandler :
|
|
public cBlockHandler
|
|
{
|
|
public:
|
|
cBlockVineHandler(BLOCKTYPE a_BlockType)
|
|
: cBlockHandler(a_BlockType)
|
|
{
|
|
}
|
|
|
|
|
|
virtual bool GetPlacementBlockTypeMeta(
|
|
cWorld * a_World, cPlayer * a_Player,
|
|
int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace,
|
|
int a_CursorX, int a_CursorY, int a_CursorZ,
|
|
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
|
) override
|
|
{
|
|
// TODO: Disallow placement where the vine doesn't attach to something properly
|
|
BLOCKTYPE BlockType = 0;
|
|
NIBBLETYPE BlockMeta;
|
|
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
|
|
if (BlockType == m_BlockType)
|
|
{
|
|
a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace);
|
|
}
|
|
else
|
|
{
|
|
a_BlockMeta = DirectionToMetaData(a_BlockFace);
|
|
}
|
|
a_BlockType = m_BlockType;
|
|
return true;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
case 0x1: return BLOCK_FACE_NORTH;
|
|
case 0x4: return BLOCK_FACE_SOUTH;
|
|
case 0x8: return BLOCK_FACE_WEST;
|
|
case 0x2: return BLOCK_FACE_EAST;
|
|
default: return BLOCK_FACE_TOP;
|
|
}
|
|
}
|
|
|
|
|
|
/// Returns true if the specified block type is good for vines to attach to
|
|
static bool IsBlockAttachable(BLOCKTYPE a_BlockType)
|
|
{
|
|
return (a_BlockType == E_BLOCK_LEAVES) || g_BlockIsSolid[a_BlockType];
|
|
}
|
|
|
|
|
|
/// Returns the meta that has the maximum allowable sides of the vine, given the surroundings
|
|
NIBBLETYPE GetMaxMeta(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
|
|
{
|
|
static const struct
|
|
{
|
|
int x, z;
|
|
int Bit;
|
|
} Coords[] =
|
|
{
|
|
{ 0, 1, 1}, // south, ZP
|
|
{-1, 0, 2}, // west, XM
|
|
{ 0, -1, 4}, // north, ZM
|
|
{ 1, 0, 8}, // east, XP
|
|
} ;
|
|
int res = 0;
|
|
for (int i = 0; i < ARRAYCOUNT(Coords); i++)
|
|
{
|
|
BLOCKTYPE BlockType;
|
|
NIBBLETYPE BlockMeta;
|
|
if (
|
|
a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) &&
|
|
IsBlockAttachable(BlockType)
|
|
)
|
|
{
|
|
res |= Coords[i].Bit;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
void Check(int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override
|
|
{
|
|
NIBBLETYPE CurMeta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
|
|
NIBBLETYPE MaxMeta = GetMaxMeta(a_Chunk, a_RelX, a_RelY, a_RelZ);
|
|
|
|
// Check if vine above us, add its meta to MaxMeta
|
|
if ((a_RelY < cChunkDef::Height - 1) && (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == m_BlockType))
|
|
{
|
|
MaxMeta |= a_Chunk.GetMeta(a_RelX, a_RelY + 1, a_RelZ);
|
|
}
|
|
|
|
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
|
|
bool HasTop = (a_RelY < cChunkDef::Height - 1) && IsBlockAttachable(a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ));
|
|
if ((Common == 0) && !HasTop)
|
|
{
|
|
// The vine just lost all its support, destroy the block:
|
|
if (DoesDropOnUnsuitable())
|
|
{
|
|
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
|
|
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
|
|
DropBlock(a_Chunk.GetWorld(), NULL, BlockX, a_RelY, BlockZ);
|
|
}
|
|
a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
|
|
return;
|
|
}
|
|
a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, m_BlockType, Common);
|
|
}
|
|
else
|
|
{
|
|
// Wake up the simulators for this block:
|
|
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
|
|
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
|
|
a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk);
|
|
}
|
|
}
|
|
|
|
|
|
virtual bool DoesIgnoreBuildCollision(void) override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
virtual bool DoesAllowBlockOnTop(void) override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
virtual const char * GetStepSound(void) override
|
|
{
|
|
return "step.grass";
|
|
}
|
|
|
|
|
|
virtual bool DoesDropOnUnsuitable(void) override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual void OnUpdate(cWorld * a_World, int X, int Y, int Z)
|
|
{
|
|
if (a_World->GetBlock(X, Y - 1, Z) == E_BLOCK_AIR)
|
|
{
|
|
a_World->SetBlock(X, Y - 1, Z, E_BLOCK_VINES, a_World->GetBlockMeta(X, Y, Z));
|
|
}
|
|
}
|
|
|
|
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
|
|
{
|
|
return ((a_Meta >> 1) | (a_Meta << 3)) & 0x0f; // Rotate bits to the right
|
|
}
|
|
|
|
|
|
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
|
|
{
|
|
return ((a_Meta << 1) | (a_Meta >> 3)) & 0x0f; // Rotate bits to the left
|
|
}
|
|
|
|
|
|
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
|
|
{
|
|
// Bits 2 and 4 stay, bits 1 and 3 swap
|
|
return ((a_Meta & 0x0a) | ((a_Meta & 0x01) << 2) | ((a_Meta & 0x04) >> 2));
|
|
}
|
|
|
|
|
|
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
|
|
{
|
|
// Bits 1 and 3 stay, bits 2 and 4 swap
|
|
return ((a_Meta & 0x05) | ((a_Meta & 0x02) << 2) | ((a_Meta & 0x08) >> 2));
|
|
}
|
|
} ;
|
|
|
|
|
|
|
|
|