1
0
cuberite-2a/src/NetherPortalScanner.cpp
Mattes D 01b8ed5295
Pulled the BlockID and BlockInfo headers from Globals.h. (#4591)
The BlockID.h file was removed from Globals.h and renamed to BlockType.h (main change)
The BlockInfo.h file was removed from Globals.h (main change)
The ENUM_BLOCK_ID and ENUM_ITEM_ID enum names were replaced with ENUM_BLOCK_TYPE and ENUM_ITEM_TYPE (cosmetics)
The various enums, such as eDimension, eDamageType and eExplosionSource were moved from BlockType.h to Defines.h, together with the helper functions for converting between them and strings (StringToDimension et al.) (minor)
Many inline functions were moved from headers to their respective cpp files, so that BlockType.h could be included only into the cpp file, rather than the header.
That broke our tests a bit, since they pick bits and pieces out of the main code and provide stubs for the rest; they had to be re-stubbed and re-verified.
eMonsterType values are no longer tied to E_ITEM_SPAWN_EGG_META_* values
2020-04-03 08:57:01 +02:00

304 lines
6.9 KiB
C++

#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "NetherPortalScanner.h"
#include "BlockInfo.h"
#include "Entities/Entity.h"
#include "World.h"
const double cNetherPortalScanner::OutOffset = 0.5;
const double cNetherPortalScanner::AcrossOffset = 0.5;
cNetherPortalScanner::cNetherPortalScanner(cEntity * a_MovingEntity, cWorld * a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY) :
m_Entity(a_MovingEntity),
m_World(a_DestinationWorld),
m_FoundPortal(false),
m_BuildPlatform(true),
m_Dir(Direction::X),
m_PortalLoc(a_DestPosition.Floor()),
m_Position(a_DestPosition),
m_MaxY(a_MaxY)
{
int MinX = FloorC((m_Position.x - SearchRadius) / cChunkDef::Width);
int MinZ = FloorC((m_Position.z - SearchRadius) / cChunkDef::Width);
int MaxX = CeilC((m_Position.x + SearchRadius) / cChunkDef::Width);
int MaxZ = CeilC((m_Position.z + SearchRadius) / cChunkDef::Width);
for (int x = MinX; x < MaxX; x++)
{
for (int z = MinZ; z < MaxZ; z++)
{
Add(x, z);
}
}
Enable(*a_DestinationWorld->GetChunkMap());
}
void cNetherPortalScanner::OnChunkAvailable(int a_ChunkX, int a_ChunkZ)
{
cChunkDef::BlockTypes blocks;
m_World->GetChunkBlockTypes(a_ChunkX, a_ChunkZ, blocks);
// Iterate through all of the blocks in the chunk
for (unsigned int i = 0; i < cChunkDef::NumBlocks; i++)
{
if (blocks[i] == E_BLOCK_NETHER_PORTAL)
{
Vector3i Coordinate = cChunkDef::IndexToCoordinate(i);
if (Coordinate.y >= m_MaxY)
{
// This is above the map, don't consider it.
continue;
}
Vector3d PortalLoc = Vector3d(Coordinate.x + a_ChunkX * cChunkDef::Width, Coordinate.y, Coordinate.z + a_ChunkZ * cChunkDef::Width);
if (!m_FoundPortal)
{
m_FoundPortal = true;
m_PortalLoc = PortalLoc;
}
else
{
if ((PortalLoc - m_Position).SqrLength() < (m_PortalLoc - m_Position).SqrLength())
{
m_FoundPortal = true;
m_PortalLoc = PortalLoc;
}
}
}
}
}
bool cNetherPortalScanner::IsValidBuildLocation(Vector3i a_BlockPos)
{
// Check the base
for (int i = 0; i < SearchSolidBaseWidth; i++)
{
for (int j = 0; j < PortalLength; j++)
{
BLOCKTYPE blocktype = m_World->GetBlock(a_BlockPos.x + i, a_BlockPos.y, a_BlockPos.z + j);
if (!cBlockInfo::IsSolid(blocktype))
{
return false;
}
// Check the airspace
for (int k = 1; k < PortalHeight; k++)
{
blocktype = m_World->GetBlock(a_BlockPos.x + i, a_BlockPos.y + k, a_BlockPos.z + j);
if (blocktype != E_BLOCK_AIR)
{
return false;
}
}
}
}
return true;
}
bool cNetherPortalScanner::OnAllChunksAvailable(void)
{
if (m_FoundPortal)
{
// Find the bottom of this portal
while (m_World->GetBlock(m_PortalLoc.x, m_PortalLoc.y, m_PortalLoc.z) == E_BLOCK_NETHER_PORTAL)
{
m_PortalLoc.y -= 1;
}
m_PortalLoc.y += 1;
// Figure out which way the portal is facing
int BXP = m_World->GetBlock(m_PortalLoc.x + 1, m_PortalLoc.y, m_PortalLoc.z);
int BXM = m_World->GetBlock(m_PortalLoc.x - 1, m_PortalLoc.y, m_PortalLoc.z);
if ((BXP == E_BLOCK_NETHER_PORTAL) || (BXM == E_BLOCK_NETHER_PORTAL))
{
// The long axis is along X
m_Dir = Direction::X;
}
else
{
// The long axis is along Z
m_Dir = Direction::Y;
}
}
else
{
// Scan the area for a suitable location
int minx = FloorC(m_Position.x) - BuildSearchRadius;
int minz = FloorC(m_Position.z) - BuildSearchRadius;
int maxx = FloorC(m_Position.x) + BuildSearchRadius;
int maxz = FloorC(m_Position.z) + BuildSearchRadius;
int maxy = m_MaxY;
std::vector<Vector3i> Possibilities;
int x, y, z;
for (y = 0; y < maxy - PortalHeight; y++)
{
for (x = minx; x < maxx - PortalLength; x++)
{
for (z = minz; z < maxz - SearchSolidBaseWidth; z++)
{
Vector3i Location = Vector3i(x, y, z);
if (IsValidBuildLocation(Location))
{
Possibilities.push_back(Vector3i(x, y, z));
}
}
}
}
if (Possibilities.size() > 0)
{
m_BuildPlatform = false;
// Find the nearest
double DistanceToClosest = (Possibilities[0] - m_Position).SqrLength();
Vector3i Closest = Possibilities[0];
for (const auto & itr : Possibilities)
{
double Distance = (itr - m_Position).SqrLength();
if (Distance < DistanceToClosest)
{
DistanceToClosest = Distance;
Closest = itr;
}
}
m_PortalLoc = Closest;
}
}
return true;
}
void cNetherPortalScanner::BuildNetherPortal(Vector3i a_Location, Direction a_Direction, bool a_IncludePlatform)
{
int x = a_Location.x;
int y = a_Location.y;
int z = a_Location.z;
// Clear a 3x4x4 area starting right above the base
for (int i = 0; i < SearchSolidBaseWidth; i++)
{
for (int j = 0; j < PortalLength; j++)
{
for (int k = 1; k < PortalHeight; k++)
{
if (a_Direction == Direction::Y)
{
m_World->SetBlock(x + i, y + k, z + j, E_BLOCK_AIR, 0);
}
else if (a_Direction == Direction::X)
{
m_World->SetBlock(x + j, y + k, z + i, E_BLOCK_AIR, 0);
}
}
}
}
// Put in an obsidian base
if (a_IncludePlatform)
{
for (int j = 0; j < PortalLength; j++)
{
// +2 on the short axis because that's where we deposit the entity
if (a_Direction == Direction::Y)
{
m_World->SetBlock(x + 2, y, z + j, E_BLOCK_OBSIDIAN, 0);
}
else if (a_Direction == Direction::X)
{
m_World->SetBlock(x + j, y, z + 2, E_BLOCK_OBSIDIAN, 0);
}
}
}
// Build an obsidian frame
for (int i = 0; i < PortalHeight; i++)
{
if (a_Direction == Direction::Y)
{
m_World->SetBlock(x + 1, y + i, z, E_BLOCK_OBSIDIAN, 0);
m_World->SetBlock(x + 1, y + i, z + 3, E_BLOCK_OBSIDIAN, 0);
}
else if (a_Direction == Direction::X)
{
m_World->SetBlock(x, y + i, z + 1, E_BLOCK_OBSIDIAN, 0);
m_World->SetBlock(x + 3, y + i, z + 1, E_BLOCK_OBSIDIAN, 0);
}
}
for (int i = 0; i < PortalLength; i++)
{
if (a_Direction == Direction::Y)
{
m_World->SetBlock(x + 1, y + 4, z + i, E_BLOCK_OBSIDIAN, 0);
m_World->SetBlock(x + 1, y, z + i, E_BLOCK_OBSIDIAN, 0);
}
else if (a_Direction == Direction::X)
{
m_World->SetBlock(x + i, y + 4, z + 1, E_BLOCK_OBSIDIAN, 0);
m_World->SetBlock(x + i, y, z + 1, E_BLOCK_OBSIDIAN, 0);
}
}
// Fill the frame (place a fire in the bottom)
m_World->SetBlock(x + 1, y + 1, z + 1, E_BLOCK_FIRE, 0);
}
void cNetherPortalScanner::OnDisabled(void)
{
// Now we actually move the player
if (!m_FoundPortal)
{
// Build a new nether portal.
FLOGD("Building nether portal at {0}", m_PortalLoc);
BuildNetherPortal(m_PortalLoc, m_Dir, m_BuildPlatform);
m_PortalLoc.x += 1;
m_PortalLoc.y += 1;
m_PortalLoc.z += 1;
}
// Put the entity near the opening
Vector3d Position = m_PortalLoc;
if (m_Dir == Direction::Y)
{
Position.x += OutOffset;
Position.z += AcrossOffset;
}
else if (m_Dir == Direction::X)
{
Position.x += AcrossOffset;
Position.z += OutOffset;
}
FLOGD("Placing player at {0}", Position);
m_Entity->MoveToWorld(m_World, Position, true, false);
delete this;
}