#pragma once
#include "BlockHandler.h"
#include "MetaRotater.h"
class cBlockVineHandler :
public cMetaRotater<cBlockHandler,0xF,0x4,0x8,0x1,0x2,true>
{
public:
cBlockVineHandler(BLOCKTYPE a_BlockType)
: cMetaRotater<cBlockHandler,0xF,0x4,0x8,0x1,0x2,true>(a_BlockType)
{
}
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace 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_ChunkInterface.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 (size_t 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(cChunkInterface & a_ChunkInterface, cBlockPluginInterface & a_PluginInterface, 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_ChunkInterface, *a_Chunk.GetWorld(), a_PluginInterface, 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 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));
}
}
} ;