422 lines
11 KiB
C++
422 lines
11 KiB
C++
|
|
#pragma once
|
|
|
|
#include "BlockEntity.h"
|
|
#include "../World.h"
|
|
|
|
|
|
|
|
|
|
|
|
/// Meta values for the rail
|
|
enum ENUM_RAIL_DIRECTIONS
|
|
{
|
|
E_RAIL_NORTH_SOUTH = 0,
|
|
E_RAIL_EAST_WEST = 1,
|
|
E_RAIL_ASCEND_EAST = 2,
|
|
E_RAIL_ASCEND_WEST = 3,
|
|
E_RAIL_ASCEND_NORTH = 4,
|
|
E_RAIL_ASCEND_SOUTH = 5,
|
|
E_RAIL_CURVED_SOUTH_EAST = 6,
|
|
E_RAIL_CURVED_SOUTH_WEST = 7,
|
|
E_RAIL_CURVED_NORTH_WEST = 8,
|
|
E_RAIL_CURVED_NORTH_EAST = 9,
|
|
|
|
// Some useful synonyms:
|
|
E_RAIL_DIR_X = E_RAIL_EAST_WEST,
|
|
E_RAIL_DIR_Z = E_RAIL_NORTH_SOUTH,
|
|
E_RAIL_ASCEND_XP = E_RAIL_ASCEND_EAST,
|
|
E_RAIL_ASCEND_XM = E_RAIL_ASCEND_WEST,
|
|
E_RAIL_ASCEND_ZM = E_RAIL_ASCEND_NORTH,
|
|
E_RAIL_ASCEND_ZP = E_RAIL_ASCEND_SOUTH,
|
|
E_RAIL_CURVED_XPZP = E_RAIL_CURVED_SOUTH_EAST,
|
|
E_RAIL_CURVED_XMZP = E_RAIL_CURVED_SOUTH_WEST,
|
|
E_RAIL_CURVED_XMZM = E_RAIL_CURVED_NORTH_WEST,
|
|
E_RAIL_CURVED_XPZM = E_RAIL_CURVED_NORTH_EAST,
|
|
} ;
|
|
|
|
|
|
|
|
|
|
|
|
enum ENUM_PURE
|
|
{
|
|
E_PURE_UPDOWN = 0,
|
|
E_PURE_DOWN = 1,
|
|
E_PURE_NONE = 2
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class cBlockRailHandler :
|
|
public cBlockHandler
|
|
{
|
|
public:
|
|
cBlockRailHandler(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
|
|
{
|
|
a_BlockType = m_BlockType;
|
|
a_BlockMeta = FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ);
|
|
return true;
|
|
}
|
|
|
|
|
|
virtual void OnNeighborChanged(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ) override
|
|
{
|
|
NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
|
if (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ) && (Meta != FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ)))
|
|
{
|
|
a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, FindMeta(a_World, a_BlockX, a_BlockY, a_BlockZ));
|
|
}
|
|
}
|
|
|
|
|
|
virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
|
|
{
|
|
if (a_RelY <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
if (!g_BlockIsSolid[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)])
|
|
{
|
|
return false;
|
|
}
|
|
|
|
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
|
|
switch (Meta)
|
|
{
|
|
case E_RAIL_ASCEND_EAST:
|
|
case E_RAIL_ASCEND_WEST:
|
|
case E_RAIL_ASCEND_NORTH:
|
|
case E_RAIL_ASCEND_SOUTH:
|
|
{
|
|
// Mapping between the meta and the neighbors that need checking
|
|
Meta -= E_RAIL_ASCEND_EAST; // Base index at zero
|
|
static const struct
|
|
{
|
|
int x, z;
|
|
} Coords[] =
|
|
{
|
|
{ 1, 0}, // east, XP
|
|
{-1, 0}, // west, XM
|
|
{ 0, -1}, // north, ZM
|
|
{ 0, 1}, // south, ZP
|
|
} ;
|
|
BLOCKTYPE BlockType;
|
|
NIBBLETYPE BlockMeta;
|
|
if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta))
|
|
{
|
|
// Too close to the edge, cannot simulate
|
|
return true;
|
|
}
|
|
return g_BlockIsSolid[BlockType];
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
NIBBLETYPE FindMeta(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
|
|
{
|
|
NIBBLETYPE Meta = 0;
|
|
char RailsCnt = 0;
|
|
bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP
|
|
memset(Neighbors, false, sizeof(Neighbors));
|
|
Neighbors[0] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN));
|
|
Neighbors[1] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN));
|
|
Neighbors[2] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN));
|
|
Neighbors[3] = (IsUnstable(a_World, a_BlockX, a_BlockY, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN));
|
|
Neighbors[4] = (IsUnstable(a_World, a_BlockX + 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST, E_PURE_NONE));
|
|
Neighbors[5] = (IsUnstable(a_World, a_BlockX - 1, a_BlockY + 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST, E_PURE_NONE));
|
|
Neighbors[6] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE));
|
|
Neighbors[7] = (IsUnstable(a_World, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE));
|
|
if (IsUnstable(a_World, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST))
|
|
Neighbors[0] = true;
|
|
if (IsUnstable(a_World, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST))
|
|
Neighbors[1] = true;
|
|
if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH))
|
|
Neighbors[2] = true;
|
|
if (IsUnstable(a_World, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_World, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH))
|
|
Neighbors[3] = true;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (Neighbors[i])
|
|
{
|
|
RailsCnt++;
|
|
}
|
|
}
|
|
if (RailsCnt == 1)
|
|
{
|
|
if (Neighbors[7]) return E_RAIL_ASCEND_SOUTH;
|
|
else if (Neighbors[6]) return E_RAIL_ASCEND_NORTH;
|
|
else if (Neighbors[5]) return E_RAIL_ASCEND_WEST;
|
|
else if (Neighbors[4]) return E_RAIL_ASCEND_EAST;
|
|
else if (Neighbors[0] || Neighbors[1]) return E_RAIL_EAST_WEST;
|
|
else if (Neighbors[2] || Neighbors[3]) return E_RAIL_NORTH_SOUTH;
|
|
ASSERT(!"Weird neighbor count");
|
|
return Meta;
|
|
}
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (Neighbors[i + 4])
|
|
{
|
|
Neighbors[i] = true;
|
|
}
|
|
}
|
|
if (RailsCnt > 1)
|
|
{
|
|
if (Neighbors[3] && Neighbors[0]) return E_RAIL_CURVED_SOUTH_EAST;
|
|
else if (Neighbors[3] && Neighbors[1]) return E_RAIL_CURVED_SOUTH_WEST;
|
|
else if (Neighbors[2] && Neighbors[0]) return E_RAIL_CURVED_NORTH_EAST;
|
|
else if (Neighbors[2] && Neighbors[1]) return E_RAIL_CURVED_NORTH_WEST;
|
|
else if (Neighbors[7] && Neighbors[2]) return E_RAIL_ASCEND_SOUTH;
|
|
else if (Neighbors[3] && Neighbors[6]) return E_RAIL_ASCEND_NORTH;
|
|
else if (Neighbors[5] && Neighbors[0]) return E_RAIL_ASCEND_WEST;
|
|
else if (Neighbors[4] && Neighbors[1]) return E_RAIL_ASCEND_EAST;
|
|
else if (Neighbors[0] && Neighbors[1]) return E_RAIL_EAST_WEST;
|
|
else if (Neighbors[2] && Neighbors[3]) return E_RAIL_NORTH_SOUTH;
|
|
ASSERT(!"Weird neighbor count");
|
|
}
|
|
return Meta;
|
|
}
|
|
|
|
|
|
bool IsUnstable(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
|
|
{
|
|
if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
|
|
{
|
|
return false;
|
|
}
|
|
NIBBLETYPE Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
|
switch (Meta)
|
|
{
|
|
case E_RAIL_NORTH_SOUTH:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_DOWN)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_EAST_WEST:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_ASCEND_EAST:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_EAST) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_ASCEND_WEST:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_WEST)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_ASCEND_NORTH:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_ASCEND_SOUTH:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_CURVED_SOUTH_EAST:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_CURVED_SOUTH_WEST:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_SOUTH) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_CURVED_NORTH_WEST:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case E_RAIL_CURVED_NORTH_EAST:
|
|
{
|
|
if (
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH) ||
|
|
IsNotConnected(a_World, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST)
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool IsNotConnected(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Pure = 0)
|
|
{
|
|
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false);
|
|
NIBBLETYPE Meta;
|
|
if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
|
|
{
|
|
if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN))
|
|
{
|
|
if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY + 1, a_BlockZ);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Meta = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
|
}
|
|
|
|
switch (a_BlockFace)
|
|
{
|
|
case BLOCK_FACE_NORTH:
|
|
{
|
|
if (
|
|
(Meta == E_RAIL_NORTH_SOUTH) ||
|
|
(Meta == E_RAIL_ASCEND_NORTH) ||
|
|
(Meta == E_RAIL_ASCEND_SOUTH) ||
|
|
(Meta == E_RAIL_CURVED_SOUTH_EAST) ||
|
|
(Meta == E_RAIL_CURVED_SOUTH_WEST)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BLOCK_FACE_SOUTH:
|
|
{
|
|
if (
|
|
(Meta == E_RAIL_NORTH_SOUTH) ||
|
|
(Meta == E_RAIL_ASCEND_NORTH) ||
|
|
(Meta == E_RAIL_ASCEND_SOUTH) ||
|
|
(Meta == E_RAIL_CURVED_NORTH_EAST) ||
|
|
(Meta == E_RAIL_CURVED_NORTH_WEST)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BLOCK_FACE_EAST:
|
|
{
|
|
if (
|
|
(Meta == E_RAIL_EAST_WEST) ||
|
|
(Meta == E_RAIL_ASCEND_EAST) ||
|
|
(Meta == E_RAIL_ASCEND_WEST) ||
|
|
(Meta == E_RAIL_CURVED_SOUTH_WEST) ||
|
|
(Meta == E_RAIL_CURVED_NORTH_WEST)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case BLOCK_FACE_WEST:
|
|
{
|
|
if (
|
|
(Meta == E_RAIL_EAST_WEST) ||
|
|
(Meta == E_RAIL_ASCEND_EAST) ||
|
|
(Meta == E_RAIL_ASCEND_WEST) ||
|
|
(Meta == E_RAIL_CURVED_SOUTH_EAST) ||
|
|
(Meta == E_RAIL_CURVED_NORTH_EAST)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
} ;
|
|
|
|
|
|
|
|
|