Merge pull request #2162 from lkolbly/master
Nether Portal Jumping & Creation Code
This commit is contained in:
commit
d436a71cbc
@ -14,6 +14,7 @@ jan64
|
|||||||
jasperarmstrong
|
jasperarmstrong
|
||||||
keyboard
|
keyboard
|
||||||
Lapayo
|
Lapayo
|
||||||
|
lkolbly
|
||||||
Luksor
|
Luksor
|
||||||
linnemannr (Reid Linnemann)
|
linnemannr (Reid Linnemann)
|
||||||
M10360
|
M10360
|
||||||
|
@ -54,6 +54,7 @@ SET (SRCS
|
|||||||
MobProximityCounter.cpp
|
MobProximityCounter.cpp
|
||||||
MobSpawner.cpp
|
MobSpawner.cpp
|
||||||
MonsterConfig.cpp
|
MonsterConfig.cpp
|
||||||
|
NetherPortalScanner.cpp
|
||||||
OverridesSettingsRepository.cpp
|
OverridesSettingsRepository.cpp
|
||||||
ProbabDistrib.cpp
|
ProbabDistrib.cpp
|
||||||
RankManager.cpp
|
RankManager.cpp
|
||||||
@ -126,6 +127,7 @@ SET (HDRS
|
|||||||
MobProximityCounter.h
|
MobProximityCounter.h
|
||||||
MobSpawner.h
|
MobSpawner.h
|
||||||
MonsterConfig.h
|
MonsterConfig.h
|
||||||
|
NetherPortalScanner.h
|
||||||
OverridesSettingsRepository.h
|
OverridesSettingsRepository.h
|
||||||
ProbabDistrib.h
|
ProbabDistrib.h
|
||||||
RankManager.h
|
RankManager.h
|
||||||
|
@ -640,6 +640,7 @@ void cChunk::Tick(std::chrono::milliseconds a_Dt)
|
|||||||
else if ((*itr)->IsWorldTravellingFrom(m_World))
|
else if ((*itr)->IsWorldTravellingFrom(m_World))
|
||||||
{
|
{
|
||||||
// Remove all entities that are travelling to another world
|
// Remove all entities that are travelling to another world
|
||||||
|
LOGD("Removing entity from [%d, %d] that's travelling between worlds.", m_PosX, m_PosZ);
|
||||||
MarkDirty();
|
MarkDirty();
|
||||||
(*itr)->SetWorldTravellingFrom(nullptr);
|
(*itr)->SetWorldTravellingFrom(nullptr);
|
||||||
itr = m_Entities.erase(itr);
|
itr = m_Entities.erase(itr);
|
||||||
@ -695,7 +696,6 @@ void cChunk::MoveEntityToNewChunk(cEntity * a_Entity)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(Neighbor != this); // Moving into the same chunk? wtf?
|
ASSERT(Neighbor != this); // Moving into the same chunk? wtf?
|
||||||
|
|
||||||
Neighbor->AddEntity(a_Entity);
|
Neighbor->AddEntity(a_Entity);
|
||||||
|
|
||||||
class cMover :
|
class cMover :
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "Items/ItemHandler.h"
|
#include "Items/ItemHandler.h"
|
||||||
#include "../FastRandom.h"
|
#include "../FastRandom.h"
|
||||||
|
#include "../NetherPortalScanner.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
|
|||||||
m_WorldTravellingFrom(nullptr),
|
m_WorldTravellingFrom(nullptr),
|
||||||
m_EntityType(a_EntityType),
|
m_EntityType(a_EntityType),
|
||||||
m_World(nullptr),
|
m_World(nullptr),
|
||||||
|
m_IsWorldChangeScheduled(false),
|
||||||
m_IsFireproof(false),
|
m_IsFireproof(false),
|
||||||
m_TicksSinceLastBurnDamage(0),
|
m_TicksSinceLastBurnDamage(0),
|
||||||
m_TicksSinceLastLavaDamage(0),
|
m_TicksSinceLastLavaDamage(0),
|
||||||
@ -1260,9 +1262,26 @@ void cEntity::DetectCacti(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cEntity::ScheduleMoveToWorld(cWorld * a_World, Vector3d a_NewPosition)
|
||||||
|
{
|
||||||
|
m_NewWorld = a_World;
|
||||||
|
m_NewWorldPosition = a_NewPosition;
|
||||||
|
m_IsWorldChangeScheduled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cEntity::DetectPortal()
|
bool cEntity::DetectPortal()
|
||||||
{
|
{
|
||||||
|
// If somebody scheduled a world change with ScheduleMoveToWorld, change worlds now.
|
||||||
|
if (m_IsWorldChangeScheduled)
|
||||||
|
{
|
||||||
|
m_IsWorldChangeScheduled = false;
|
||||||
|
MoveToWorld(m_NewWorld, false, m_NewWorldPosition);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (GetWorld()->GetDimension() == dimOverworld)
|
if (GetWorld()->GetDimension() == dimOverworld)
|
||||||
{
|
{
|
||||||
if (GetWorld()->GetLinkedNetherWorldName().empty() && GetWorld()->GetLinkedEndWorldName().empty())
|
if (GetWorld()->GetLinkedNetherWorldName().empty() && GetWorld()->GetLinkedEndWorldName().empty())
|
||||||
@ -1312,8 +1331,15 @@ bool cEntity::DetectPortal()
|
|||||||
// Send a respawn packet before world is loaded / generated so the client isn't left in limbo
|
// Send a respawn packet before world is loaded / generated so the client isn't left in limbo
|
||||||
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld);
|
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
|
Vector3d TargetPos = GetPosition();
|
||||||
|
TargetPos.x *= 8.0;
|
||||||
|
TargetPos.z *= 8.0;
|
||||||
|
|
||||||
|
cWorld * TargetWorld = cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName(), dimNether, GetWorld()->GetName());
|
||||||
|
LOGD("Jumping nether -> overworld");
|
||||||
|
new cNetherPortalScanner(this, TargetWorld, TargetPos, 256);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1329,8 +1355,15 @@ bool cEntity::DetectPortal()
|
|||||||
((cPlayer *)this)->AwardAchievement(achEnterPortal);
|
((cPlayer *)this)->AwardAchievement(achEnterPortal);
|
||||||
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether);
|
((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedNetherWorldName(), dimNether, GetWorld()->GetName()), false);
|
Vector3d TargetPos = GetPosition();
|
||||||
|
TargetPos.x /= 8.0;
|
||||||
|
TargetPos.z /= 8.0;
|
||||||
|
|
||||||
|
cWorld * TargetWorld = cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedNetherWorldName(), dimNether, GetWorld()->GetName());
|
||||||
|
LOGD("Jumping overworld -> nether");
|
||||||
|
new cNetherPortalScanner(this, TargetWorld, TargetPos, 128);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case E_BLOCK_END_PORTAL:
|
case E_BLOCK_END_PORTAL:
|
||||||
@ -1392,7 +1425,7 @@ bool cEntity::DetectPortal()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition)
|
||||||
{
|
{
|
||||||
UNUSED(a_ShouldSendRespawn);
|
UNUSED(a_ShouldSendRespawn);
|
||||||
ASSERT(a_World != nullptr);
|
ASSERT(a_World != nullptr);
|
||||||
@ -1414,6 +1447,8 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
|||||||
SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
|
SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
|
||||||
GetWorld()->BroadcastDestroyEntity(*this);
|
GetWorld()->BroadcastDestroyEntity(*this);
|
||||||
|
|
||||||
|
SetPosition(a_NewPosition);
|
||||||
|
|
||||||
// Queue add to new world
|
// Queue add to new world
|
||||||
a_World->AddEntity(this);
|
a_World->AddEntity(this);
|
||||||
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
|
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
|
||||||
@ -1438,7 +1473,7 @@ bool cEntity::MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DoMoveToWorld(World, a_ShouldSendRespawn);
|
return DoMoveToWorld(World, a_ShouldSendRespawn, GetPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -392,15 +392,20 @@ public:
|
|||||||
/// Teleports to the coordinates specified
|
/// Teleports to the coordinates specified
|
||||||
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ);
|
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ);
|
||||||
|
|
||||||
|
/// Schedules a MoveToWorld call to occur on the next Tick of the entity
|
||||||
|
void ScheduleMoveToWorld(cWorld * a_World, Vector3d a_NewPosition);
|
||||||
|
|
||||||
|
bool MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition) { return DoMoveToWorld(a_World, a_ShouldSendRespawn, a_NewPosition); }
|
||||||
|
|
||||||
/** Moves entity to specified world, taking a world pointer */
|
/** Moves entity to specified world, taking a world pointer */
|
||||||
bool MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn = true) { return DoMoveToWorld(a_World, a_ShouldSendRespawn); }
|
bool MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn = true) { return MoveToWorld(a_World, a_ShouldSendRespawn, GetPosition()); }
|
||||||
|
|
||||||
/** Moves entity to specified world, taking a world name */
|
/** Moves entity to specified world, taking a world name */
|
||||||
bool MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn = true);
|
bool MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn = true);
|
||||||
|
|
||||||
// tolua_end
|
// tolua_end
|
||||||
|
|
||||||
virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn);
|
virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition);
|
||||||
|
|
||||||
/** Returns if the entity is travelling away from a specified world */
|
/** Returns if the entity is travelling away from a specified world */
|
||||||
bool IsWorldTravellingFrom(cWorld * a_World) const { return (m_WorldTravellingFrom == a_World); }
|
bool IsWorldTravellingFrom(cWorld * a_World) const { return (m_WorldTravellingFrom == a_World); }
|
||||||
@ -530,23 +535,28 @@ protected:
|
|||||||
eEntityType m_EntityType;
|
eEntityType m_EntityType;
|
||||||
|
|
||||||
cWorld * m_World;
|
cWorld * m_World;
|
||||||
|
|
||||||
|
/** State variables for ScheduleMoveToWorld. */
|
||||||
|
bool m_IsWorldChangeScheduled;
|
||||||
|
cWorld * m_NewWorld;
|
||||||
|
Vector3d m_NewWorldPosition;
|
||||||
|
|
||||||
/// Whether the entity is capable of taking fire or lava damage.
|
/** Whether the entity is capable of taking fire or lava damage. */
|
||||||
bool m_IsFireproof;
|
bool m_IsFireproof;
|
||||||
|
|
||||||
/// Time, in ticks, since the last damage dealt by being on fire. Valid only if on fire (IsOnFire())
|
/** Time, in ticks, since the last damage dealt by being on fire. Valid only if on fire (IsOnFire()) */
|
||||||
int m_TicksSinceLastBurnDamage;
|
int m_TicksSinceLastBurnDamage;
|
||||||
|
|
||||||
/// Time, in ticks, since the last damage dealt by standing in lava. Reset to zero when moving out of lava.
|
/** Time, in ticks, since the last damage dealt by standing in lava. Reset to zero when moving out of lava. */
|
||||||
int m_TicksSinceLastLavaDamage;
|
int m_TicksSinceLastLavaDamage;
|
||||||
|
|
||||||
/// Time, in ticks, since the last damage dealt by standing in fire. Reset to zero when moving out of fire.
|
/** Time, in ticks, since the last damage dealt by standing in fire. Reset to zero when moving out of fire. */
|
||||||
int m_TicksSinceLastFireDamage;
|
int m_TicksSinceLastFireDamage;
|
||||||
|
|
||||||
/// Time, in ticks, until the entity extinguishes its fire
|
/** Time, in ticks, until the entity extinguishes its fire */
|
||||||
int m_TicksLeftBurning;
|
int m_TicksLeftBurning;
|
||||||
|
|
||||||
/// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void.
|
/** Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void. */
|
||||||
int m_TicksSinceLastVoidDamage;
|
int m_TicksSinceLastVoidDamage;
|
||||||
|
|
||||||
/** Does the actual speed-setting. The default implementation just sets the member variable value;
|
/** Does the actual speed-setting. The default implementation just sets the member variable value;
|
||||||
|
@ -1620,10 +1620,7 @@ void cPlayer::TossItems(const cItems & a_Items)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition)
|
||||||
|
|
||||||
|
|
||||||
bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
|
||||||
{
|
{
|
||||||
ASSERT(a_World != nullptr);
|
ASSERT(a_World != nullptr);
|
||||||
|
|
||||||
@ -1653,6 +1650,8 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
|
|||||||
SetWorldTravellingFrom(GetWorld()); // cChunk handles entity removal
|
SetWorldTravellingFrom(GetWorld()); // cChunk handles entity removal
|
||||||
GetWorld()->RemovePlayer(this, false);
|
GetWorld()->RemovePlayer(this, false);
|
||||||
|
|
||||||
|
SetPosition(a_NewPosition);
|
||||||
|
|
||||||
// Queue adding player to the new world, including all the necessary adjustments to the object
|
// Queue adding player to the new world, including all the necessary adjustments to the object
|
||||||
a_World->AddPlayer(this);
|
a_World->AddPlayer(this);
|
||||||
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
|
cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
|
||||||
|
@ -358,7 +358,7 @@ public:
|
|||||||
|
|
||||||
/** Moves the player to the specified world.
|
/** Moves the player to the specified world.
|
||||||
Returns true if successful, false on failure (world not found). */
|
Returns true if successful, false on failure (world not found). */
|
||||||
virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) override;
|
virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d a_NewPosition) override;
|
||||||
|
|
||||||
/** Saves all player data, such as inventory, to JSON */
|
/** Saves all player data, such as inventory, to JSON */
|
||||||
bool SaveToDisk(void);
|
bool SaveToDisk(void);
|
||||||
|
290
src/NetherPortalScanner.cpp
Normal file
290
src/NetherPortalScanner.cpp
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
|
||||||
|
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||||
|
|
||||||
|
#include "NetherPortalScanner.h"
|
||||||
|
#include "Vector3.h"
|
||||||
|
#include "Entities/Entity.h"
|
||||||
|
#include "World.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
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.0;
|
||||||
|
}
|
||||||
|
m_PortalLoc.y += 1.0;
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
LOGD("Building nether portal at {%d, %d, %d}", m_PortalLoc.x, m_PortalLoc.y, m_PortalLoc.z);
|
||||||
|
BuildNetherPortal(m_PortalLoc, m_Dir, m_BuildPlatform);
|
||||||
|
m_PortalLoc.x += 1;
|
||||||
|
m_PortalLoc.y += 2;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("Placing player at {%f, %f, %f}", Position.x, Position.y, Position.z);
|
||||||
|
m_Entity->ScheduleMoveToWorld(m_World, Position);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
75
src/NetherPortalScanner.h
Normal file
75
src/NetherPortalScanner.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Vector3.h"
|
||||||
|
#include "ChunkStay.h"
|
||||||
|
|
||||||
|
|
||||||
|
class cEntity;
|
||||||
|
class cWorld;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This is the chunk stay which finds nearby nether portals
|
||||||
|
class cNetherPortalScanner : public cChunkStay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cNetherPortalScanner(cEntity * a_MovingEntity, cWorld * a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY);
|
||||||
|
virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkY) override;
|
||||||
|
virtual bool OnAllChunksAvailable(void) override;
|
||||||
|
virtual void OnDisabled(void) override;
|
||||||
|
|
||||||
|
enum class Direction
|
||||||
|
{
|
||||||
|
X,
|
||||||
|
Y
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** Length and height, including the obsidian. */
|
||||||
|
static const int PortalLength = 4;
|
||||||
|
static const int PortalHeight = 5;
|
||||||
|
|
||||||
|
static const int SearchRadius = 128;
|
||||||
|
static const int BuildSearchRadius = 16;
|
||||||
|
|
||||||
|
/** The width of a solid base to search for when building. */
|
||||||
|
static const int SearchSolidBaseWidth = 3;
|
||||||
|
|
||||||
|
/** Where to place the player out from the face and across the face */
|
||||||
|
const double OutOffset = 1.5;
|
||||||
|
const double AcrossOffset = 0.5;
|
||||||
|
|
||||||
|
/** Builds a portal. */
|
||||||
|
void BuildNetherPortal(Vector3i a_Location, Direction a_Direction, bool a_IncludePlatform);
|
||||||
|
|
||||||
|
/** Whether the given location is a valid location to build a portal. */
|
||||||
|
bool IsValidBuildLocation(Vector3i a_BlockPosition);
|
||||||
|
|
||||||
|
/** The entity that's being moved. */
|
||||||
|
cEntity * m_Entity;
|
||||||
|
|
||||||
|
/** The world we're moving the entity to. */
|
||||||
|
cWorld * m_World;
|
||||||
|
|
||||||
|
/** Whether we found a portal during the loading of the chunks. */
|
||||||
|
bool m_FoundPortal;
|
||||||
|
|
||||||
|
/** Whether to build a platform. True if we couldn't build the portal on solid ground */
|
||||||
|
bool m_BuildPlatform;
|
||||||
|
|
||||||
|
/** The direction of the portal. */
|
||||||
|
Direction m_Dir;
|
||||||
|
|
||||||
|
/** The position of the pre-existing portal. */
|
||||||
|
Vector3i m_PortalLoc;
|
||||||
|
|
||||||
|
/** The center of the search area */
|
||||||
|
Vector3d m_Position;
|
||||||
|
|
||||||
|
/** The maximum Y to scan to */
|
||||||
|
int m_MaxY;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user