commit
56c97d6c30
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9,00"
|
||||
@ -2259,6 +2259,10 @@
|
||||
RelativePath="..\source\blocks\BlockPlanks.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\blocks\BlockPortal.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\Blocks\BlockPumpkin.h"
|
||||
>
|
||||
|
@ -16,6 +16,28 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/// Portal boundary and direction variables
|
||||
int XZP, XZM, Dir; // For wont of a better name...
|
||||
|
||||
virtual void OnPlaced(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
|
||||
{
|
||||
/*
|
||||
PORTAL FINDING ALGORITH
|
||||
=======================
|
||||
-Get clicked base block
|
||||
-Trace upwards to find first obsidian block; aborts if anything other than obsidian or air is encountered.
|
||||
Uses this value as a reference (the 'ceiling')
|
||||
-For both directions (if one fails, try the other), BASE (clicked) block:
|
||||
-Go in one direction, only stop if a non obsidian block is encountered (abort) OR a portal border is encountered (FindObsidianCeiling returns -1)
|
||||
-If a border was encountered, go the other direction and repeat above
|
||||
-Write borders to XZP and XZM, write direction portal faces to Dir
|
||||
-Loop through boundary variables, and fill with portal blocks based on Dir with meta from Dir
|
||||
*/
|
||||
|
||||
a_BlockY--; // Because we want the block below the fire
|
||||
FindAndSetPortalFrame(a_BlockX, a_BlockY, a_BlockZ, a_World); // Brought to you by Aperture Science
|
||||
}
|
||||
|
||||
virtual void OnDigging(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
|
||||
{
|
||||
a_World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
@ -30,12 +52,176 @@ public:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual const char * GetStepSound(void) override
|
||||
{
|
||||
return "step.wood";
|
||||
}
|
||||
} ;
|
||||
|
||||
/// 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
|
||||
int FindObsidianCeiling(int X, int Y, int Z, cWorld * a_World, int MaxY = 0)
|
||||
{
|
||||
if (a_World->GetBlock(X, Y, Z) != E_BLOCK_OBSIDIAN)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int newY = Y + 1;
|
||||
|
||||
for (newY; newY < cChunkDef::Height; newY++)
|
||||
{
|
||||
BLOCKTYPE Block = a_World->GetBlock(X, newY, Z);
|
||||
if ((Block == E_BLOCK_AIR) || (Block == E_BLOCK_FIRE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (Block == E_BLOCK_OBSIDIAN)
|
||||
{
|
||||
// We found an obsidian ceiling
|
||||
// Make sure MaxY has a value and newY ('ceiling' location) is at one above the base block
|
||||
// This is because the frame is a solid obsidian pillar
|
||||
if ((MaxY != 0) && (newY == Y + 1))
|
||||
{
|
||||
return EvaluatePortalBorder(X, newY, Z, MaxY, a_World);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return ceiling Y, whoever called this function will decide if it's part of a portal or not
|
||||
return newY;
|
||||
}
|
||||
}
|
||||
else { return 0; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Evaluates if coords have a valid border on top, based on MaxY
|
||||
int EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cWorld * a_World)
|
||||
{
|
||||
for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners
|
||||
{
|
||||
if (a_World->GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN)
|
||||
{
|
||||
// Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Everything was obsidian, found a border!
|
||||
return -1; // Return -1 for a frame border
|
||||
}
|
||||
|
||||
/// Finds entire frame in any direction with the coordinates of a base block and fills hole with nether portal (START HERE)
|
||||
void FindAndSetPortalFrame(int X, int Y, int Z, cWorld * a_World)
|
||||
{
|
||||
int MaxY = FindObsidianCeiling(X, Y, Z, a_World); // Get topmost obsidian block as reference for all other checks
|
||||
int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add/subtract one as we've checked the original already the line above
|
||||
|
||||
if (MaxY == 0) // Oh noes! Not a portal coordinate :(
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FindPortalSliceX(X1, X2, Y, Z, MaxY, a_World))
|
||||
{
|
||||
if (!FindPortalSliceZ(X, Y, Z1, Z2, MaxY, a_World))
|
||||
{
|
||||
return; // No eligible portal construct, abort abort abort!!
|
||||
}
|
||||
}
|
||||
|
||||
for (int Height = Y + 1; Height <= MaxY - 1; Height++) // Loop through boundary to set portal blocks
|
||||
{
|
||||
for (int Width = XZM; Width <= XZP; Width++)
|
||||
{
|
||||
if (Dir == 1)
|
||||
{
|
||||
a_World->SetBlock(Width, Height, Z, E_BLOCK_NETHER_PORTAL, Dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_World->SetBlock(X, Height, Width, E_BLOCK_NETHER_PORTAL, Dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// Evaluates if coordinates are a portal going XP/XM; returns true if so, and writes boundaries to variable
|
||||
/// Takes coordinates of base block and Y coord of target obsidian ceiling
|
||||
bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cWorld * a_World)
|
||||
{
|
||||
Dir = 1; // Set assumed direction (will change if portal turns out to be facing the other direction)
|
||||
bool FoundFrameXP = false, FoundFrameXM = false;
|
||||
for (X1; ((a_World->GetBlock(X1, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X1, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X1++) // Check XP for obsidian blocks, exempting corners
|
||||
{
|
||||
int Value = FindObsidianCeiling(X1, Y, Z, a_World, MaxY);
|
||||
int ValueTwo = FindObsidianCeiling(X1, Y + 1, Z, a_World, MaxY); // For corners without obsidian
|
||||
if ((Value == -1) || (ValueTwo == -1)) // FindObsidianCeiling returns -1 upon frame-find
|
||||
{
|
||||
FoundFrameXP = true; // Found a frame border in this direction, proceed in other direction (don't go further)
|
||||
break;
|
||||
}
|
||||
else if ((Value != MaxY) && (ValueTwo != MaxY)) // Make sure that there is a valid portal 'slice'
|
||||
{
|
||||
return false; // Not valid slice, no portal can be formed
|
||||
}
|
||||
} XZM = X1 - 2; // Set boundary of frame interior (hence the -2)
|
||||
for (X2; ((a_World->GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM)
|
||||
{
|
||||
int Value = FindObsidianCeiling(X2, Y, Z, a_World, MaxY);
|
||||
int ValueTwo = FindObsidianCeiling(X2, Y + 1, Z, a_World, MaxY);
|
||||
if ((Value == -1) || (ValueTwo == -1))
|
||||
{
|
||||
FoundFrameXM = true;
|
||||
break;
|
||||
}
|
||||
else if ((Value != MaxY) && (ValueTwo != MaxY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} XZP = X2 + 2; // Set boundary, see previous
|
||||
return (FoundFrameXP && FoundFrameXM);
|
||||
}
|
||||
|
||||
/// Evaluates if coords are a portal going ZP/ZM; returns true if so, and writes boundaries to variable
|
||||
bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cWorld * a_World)
|
||||
{
|
||||
Dir = 2;
|
||||
bool FoundFrameZP = false, FoundFrameZM = false;
|
||||
for (Z1; ((a_World->GetBlock(X, Y, Z1) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z1) == E_BLOCK_OBSIDIAN)); Z1++)
|
||||
{
|
||||
int Value = FindObsidianCeiling(X, Y, Z1, a_World, MaxY);
|
||||
int ValueTwo = FindObsidianCeiling(X, Y + 1, Z1, a_World, MaxY);
|
||||
if ((Value == -1) || (ValueTwo == -1))
|
||||
{
|
||||
FoundFrameZP = true;
|
||||
continue;
|
||||
}
|
||||
else if ((Value != MaxY) && (ValueTwo != MaxY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} XZP = Z1 - 2;
|
||||
for (Z2; ((a_World->GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_World->GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--)
|
||||
{
|
||||
int Value = FindObsidianCeiling(X, Y, Z2, a_World, MaxY);
|
||||
int ValueTwo = FindObsidianCeiling(X, Y + 1, Z2, a_World, MaxY);
|
||||
if ((Value == -1) || (ValueTwo == -1))
|
||||
{
|
||||
FoundFrameZM = true;
|
||||
continue;
|
||||
}
|
||||
else if ((Value != MaxY) && (ValueTwo != MaxY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} XZM = Z2 + 2;
|
||||
return (FoundFrameZP && FoundFrameZM);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "BlockOre.h"
|
||||
#include "BlockPiston.h"
|
||||
#include "BlockPlanks.h"
|
||||
#include "BlockPortal.h"
|
||||
#include "BlockPumpkin.h"
|
||||
#include "BlockRail.h"
|
||||
#include "BlockRedstone.h"
|
||||
@ -159,6 +160,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
|
||||
case E_BLOCK_PISTON: return new cBlockPistonHandler (a_BlockType);
|
||||
case E_BLOCK_PISTON_EXTENSION: return new cBlockPistonHeadHandler ( );
|
||||
case E_BLOCK_PLANKS: return new cBlockPlanksHandler (a_BlockType);
|
||||
case E_BLOCK_NETHER_PORTAL: return new cBlockPortalHandler (a_BlockType);
|
||||
case E_BLOCK_PUMPKIN: return new cBlockPumpkinHandler (a_BlockType);
|
||||
case E_BLOCK_JACK_O_LANTERN: return new cBlockPumpkinHandler (a_BlockType);
|
||||
case E_BLOCK_PUMPKIN_STEM: return new cBlockStemsHandler (a_BlockType);
|
||||
|
108
source/Blocks/BlockPortal.h
Normal file
108
source/Blocks/BlockPortal.h
Normal file
@ -0,0 +1,108 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BlockHandler.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBlockPortalHandler :
|
||||
public cBlockHandler
|
||||
{
|
||||
public:
|
||||
cBlockPortalHandler(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
|
||||
{
|
||||
// We set to zero so MCS doesn't stop you from building weird portals like vanilla does
|
||||
// CanBeAt doesn't do anything if meta is zero
|
||||
// We set to zero because the client sends meta = 2 to the server (it calculates rotation itself)
|
||||
|
||||
a_BlockType = m_BlockType;
|
||||
a_BlockMeta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
|
||||
{
|
||||
return; // No pickups
|
||||
}
|
||||
|
||||
|
||||
virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
|
||||
{
|
||||
if ((a_RelY - 1 < 0) || (a_RelY + 1 > cChunkDef::Height))
|
||||
{
|
||||
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_RelX, a_RelY, a_RelZ))
|
||||
{
|
||||
case 0x1:
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
int x, y, z;
|
||||
} PortalCheck[] =
|
||||
{
|
||||
{ 0, 1, 0},
|
||||
{ 0,-1, 0},
|
||||
{ 1, 0, 0},
|
||||
{-1, 0, 0},
|
||||
} ;
|
||||
|
||||
for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++)
|
||||
{
|
||||
BLOCKTYPE Block;
|
||||
a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block);
|
||||
|
||||
if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x2:
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
int x, y, z;
|
||||
} PortalCheck[] =
|
||||
{
|
||||
{ 0, 1, 0},
|
||||
{ 0,-1, 0},
|
||||
{ 0, 0, -1},
|
||||
{ 0, 0, 1},
|
||||
} ;
|
||||
|
||||
for (int i = 0; i < ARRAYCOUNT(PortalCheck); i++)
|
||||
{
|
||||
BLOCKTYPE Block;
|
||||
a_Chunk.UnboundedRelGetBlockType(a_RelX + PortalCheck[i].x, a_RelY + PortalCheck[i].y, a_RelZ + PortalCheck[i].z, Block);
|
||||
|
||||
if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user