224 lines
7.6 KiB
C++
224 lines
7.6 KiB
C++
|
|
#pragma once
|
|
|
|
#include "BlockHandler.h"
|
|
|
|
|
|
|
|
|
|
|
|
class cBlockFireHandler :
|
|
public cBlockHandler
|
|
{
|
|
public:
|
|
cBlockFireHandler(BLOCKTYPE a_BlockType)
|
|
: cBlockHandler(a_BlockType)
|
|
{
|
|
}
|
|
|
|
/// Portal boundary and direction variables
|
|
// 2014_03_30 _X: What are these used for? Why do we need extra variables?
|
|
int XZP, XZM;
|
|
NIBBLETYPE Dir;
|
|
|
|
virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, 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 - 1: Because we want the block below the fire
|
|
FindAndSetPortalFrame(a_BlockX, a_BlockY - 1, a_BlockZ, a_ChunkInterface, a_WorldInterface);
|
|
}
|
|
|
|
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
|
|
{
|
|
// No pickups from this block
|
|
}
|
|
|
|
virtual bool IsClickedThrough(void) override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/** 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, cChunkInterface & a_ChunkInterface, int MaxY = 0)
|
|
{
|
|
if (a_ChunkInterface.GetBlock(X, Y, Z) != E_BLOCK_OBSIDIAN)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (int newY = Y + 1; newY < cChunkDef::Height; newY++)
|
|
{
|
|
BLOCKTYPE Block = a_ChunkInterface.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_ChunkInterface) ? -1 /* -1 = found a frame */ : 0;
|
|
}
|
|
else
|
|
{
|
|
// Return ceiling Y, whoever called this function will decide if it's part of a portal or not
|
|
return newY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Evaluates if coords have a valid border on top, based on MaxY */
|
|
bool EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cChunkInterface & a_ChunkInterface)
|
|
{
|
|
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_ChunkInterface.GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN)
|
|
{
|
|
// Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal
|
|
return false;
|
|
}
|
|
}
|
|
// Everything was obsidian, found a border!
|
|
return true;
|
|
}
|
|
|
|
/// 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, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface)
|
|
{
|
|
int MaxY = FindObsidianCeiling(X, Y, Z, a_ChunkInterface); // 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_ChunkInterface))
|
|
{
|
|
if (!FindPortalSliceZ(X, Y, Z1, Z2, MaxY, a_ChunkInterface))
|
|
{
|
|
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_ChunkInterface.SetBlock(Width, Height, Z, E_BLOCK_NETHER_PORTAL, Dir);
|
|
}
|
|
else
|
|
{
|
|
a_ChunkInterface.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, cChunkInterface & a_ChunkInterface)
|
|
{
|
|
Dir = 1; // Set assumed direction (will change if portal turns out to be facing the other direction)
|
|
bool FoundFrameXP = false, FoundFrameXM = false;
|
|
for (; ((a_ChunkInterface.GetBlock(X1, Y, Z) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock(X1, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X1++) // Check XP for obsidian blocks, exempting corners
|
|
{
|
|
int Value = FindObsidianCeiling(X1, Y, Z, a_ChunkInterface, MaxY);
|
|
int ValueTwo = FindObsidianCeiling(X1, Y + 1, Z, a_ChunkInterface, 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
|
|
}
|
|
}
|
|
XZP = X1 - 1; // Set boundary of frame interior
|
|
for (; ((a_ChunkInterface.GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM)
|
|
{
|
|
int Value = FindObsidianCeiling(X2, Y, Z, a_ChunkInterface, MaxY);
|
|
int ValueTwo = FindObsidianCeiling(X2, Y + 1, Z, a_ChunkInterface, MaxY);
|
|
if ((Value == -1) || (ValueTwo == -1))
|
|
{
|
|
FoundFrameXM = true;
|
|
break;
|
|
}
|
|
else if ((Value != MaxY) && (ValueTwo != MaxY))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
XZM = X2 + 1; // 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, cChunkInterface & a_ChunkInterface)
|
|
{
|
|
Dir = 2;
|
|
bool FoundFrameZP = false, FoundFrameZM = false;
|
|
for (; ((a_ChunkInterface.GetBlock(X, Y, Z1) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock(X, Y + 1, Z1) == E_BLOCK_OBSIDIAN)); Z1++)
|
|
{
|
|
int Value = FindObsidianCeiling(X, Y, Z1, a_ChunkInterface, MaxY);
|
|
int ValueTwo = FindObsidianCeiling(X, Y + 1, Z1, a_ChunkInterface, MaxY);
|
|
if ((Value == -1) || (ValueTwo == -1))
|
|
{
|
|
FoundFrameZP = true;
|
|
break;
|
|
}
|
|
else if ((Value != MaxY) && (ValueTwo != MaxY))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
XZP = Z1 - 1;
|
|
for (; ((a_ChunkInterface.GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--)
|
|
{
|
|
int Value = FindObsidianCeiling(X, Y, Z2, a_ChunkInterface, MaxY);
|
|
int ValueTwo = FindObsidianCeiling(X, Y + 1, Z2, a_ChunkInterface, MaxY);
|
|
if ((Value == -1) || (ValueTwo == -1))
|
|
{
|
|
FoundFrameZM = true;
|
|
break;
|
|
}
|
|
else if ((Value != MaxY) && (ValueTwo != MaxY))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
XZM = Z2 + 1;
|
|
|
|
return (FoundFrameZP && FoundFrameZM);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|