summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/Entity.cpp259
-rw-r--r--src/Entities/Entity.h15
-rw-r--r--src/Entities/Player.cpp71
-rw-r--r--src/Entities/Player.h35
4 files changed, 341 insertions, 39 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 8f736a269..1c8818fe5 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -12,6 +12,7 @@
#include "../Bindings/PluginManager.h"
#include "../Tracer.h"
#include "Player.h"
+#include "BlockArea.h"
@@ -629,6 +630,7 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
// Handle drowning
HandleAir();
}
+ DetectPortal();
// None of the above functions change position, we remain in the chunk of NextChunk
HandlePhysics(a_Dt, *NextChunk);
@@ -868,7 +870,7 @@ void cEntity::TickBurning(cChunk & a_Chunk)
// Remember the current burning state:
bool HasBeenBurning = (m_TicksLeftBurning > 0);
- if (m_World->IsWeatherWet())
+ if (IsBiomeNoDownfall(a_Chunk.GetBiomeAt(POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width, POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width)) || GetWorld()->IsWeatherWet())
{
if (POSY_TOINT > m_World->GetHeight(POSX_TOINT, POSZ_TOINT))
{
@@ -1039,6 +1041,261 @@ void cEntity::DetectCacti(void)
+void cEntity::DetectPortal()
+{
+ if (!GetWorld()->AreNetherPortalsEnabled() && !GetWorld()->AreEndPortalsEnabled())
+ {
+ return;
+ }
+
+ class cPortalChunkLoader : public cChunkStay
+ {
+ public:
+ cPortalChunkLoader(cEntity * a_Entity, cWorld & a_World, const Vector3i & a_PortalPos) :
+ m_Entity(a_Entity),
+ m_PortalPos(a_PortalPos),
+ m_World(a_World)
+ {}
+
+ private:
+ virtual bool OnAllChunksAvailable(void) override
+ {
+ cEntity::CreateExitPortal(m_PortalPos.x, m_PortalPos.y, m_PortalPos.z, m_Entity->GetWidth(), m_Entity->GetHeight(), m_World, m_Entity->GetUniqueID());
+ return true;
+ }
+
+ virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override {};
+ virtual void OnDisabled(void) override {};
+
+ cEntity * m_Entity;
+ Vector3i m_PortalPos;
+ cWorld & m_World;
+ };
+
+ int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT;
+ if ((Y > 0) && (Y < cChunkDef::Height))
+ {
+ switch (GetWorld()->GetBlock(X, Y, Z))
+ {
+ case E_BLOCK_NETHER_PORTAL:
+ {
+ if (!GetWorld()->AreNetherPortalsEnabled() || m_PortalCooldownData.second)
+ {
+ return;
+ }
+
+ if (m_PortalCooldownData.first != 80)
+ {
+ m_PortalCooldownData.first++;
+ return;
+ }
+ m_PortalCooldownData.first = 0;
+
+ switch (GetWorld()->GetDimension())
+ {
+ case dimNether:
+ {
+ m_PortalCooldownData.second = true; // Stop portals from working on respawn
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld);
+ }
+ MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+
+ return;
+ }
+ case dimOverworld:
+ {
+ m_PortalCooldownData.second = true; // Stop portals from working on respawn
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->AwardAchievement(achEnterPortal);
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether);
+ }
+ MoveToWorld(GetWorld()->GetNetherWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()), false);
+
+ cChunkStay * Stay = new cPortalChunkLoader(this, *cRoot::Get()->GetWorld(GetWorld()->GetNetherWorldName()), Vector3i(X, Y, Z));
+
+ int MinChunkX, MaxChunkX;
+ int MinChunkZ, MaxChunkZ;
+ cChunkDef::BlockToChunk(X - 128, Z - 128, MinChunkX, MinChunkZ);
+ cChunkDef::BlockToChunk(X + 128, Z + 128, MaxChunkX, MaxChunkZ);
+
+ for (int ChunkX = MinChunkX; ChunkX <= MaxChunkX; ++ChunkX)
+ {
+ for (int ChunkZ = MinChunkZ; ChunkZ <= MaxChunkZ; ++ChunkZ)
+ {
+ LOG("Queue %i %i", ChunkX, ChunkZ);
+ Stay->Add(ChunkX, ChunkZ);
+ }
+ }
+
+ Stay->Enable(*GetWorld()->GetChunkMap());
+ return;
+ }
+ default: break;
+ }
+ return;
+ }
+ case E_BLOCK_END_PORTAL:
+ {
+ if (!GetWorld()->AreEndPortalsEnabled() || m_PortalCooldownData.second)
+ {
+ return;
+ }
+
+ if (m_PortalCooldownData.first != 80)
+ {
+ m_PortalCooldownData.first++;
+ return;
+ }
+ m_PortalCooldownData.first = 0;
+
+ switch (GetWorld()->GetDimension())
+ {
+ case dimEnd:
+ {
+ m_PortalCooldownData.second = true; // Stop portals from working on respawn
+
+ if (IsPlayer())
+ {
+ cPlayer * Player = (cPlayer *)this;
+ Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z);
+ Player->GetClientHandle()->SendRespawn(dimOverworld);
+ }
+ MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+
+ return;
+ }
+ case dimOverworld:
+ {
+ m_PortalCooldownData.second = true; // Stop portals from working on respawn
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->AwardAchievement(achEnterTheEnd);
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimEnd);
+ }
+ MoveToWorld(GetWorld()->GetEndWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName()), false);
+
+ return;
+ }
+ default: break;
+ }
+ return;
+ }
+ default: break;
+ }
+ }
+
+ // Allow portals to work again
+ m_PortalCooldownData.second = false;
+ m_PortalCooldownData.first = 0;
+}
+
+
+
+
+
+void cEntity::CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ, double a_EntityWidth, double a_EntityHeight, cWorld & a_World, int a_UniqueIDToTeleport)
+{
+ cBlockArea Area;
+ Area.Read(&a_World, a_BlockX - 128, a_BlockX + 128, 0, 128, a_BlockZ - 128, a_BlockZ + 128);
+ for (int x = a_BlockX - 128; x <= a_BlockX + 128; ++x) for (int y = 0; y <= 128; ++y) for (int z = a_BlockZ - 128; z <= a_BlockZ + 128; ++z)
+ {
+ if (
+ (Area.GetBlockType(x, y, z) == E_BLOCK_NETHER_PORTAL) &&
+ (
+ (Area.GetBlockType(x, (int)floor(y + a_EntityHeight), z) == E_BLOCK_NETHER_PORTAL) ||
+ (Area.GetBlockType(x, (int)floor(y - a_EntityHeight), z) == E_BLOCK_NETHER_PORTAL)
+ )
+ )
+ {
+ class cTeleportEntityToPortalCallback : public cEntityCallback
+ {
+ public:
+ cTeleportEntityToPortalCallback(int a_X, int a_Y, int a_Z) :
+ m_X(a_X),
+ m_Y(a_Y),
+ m_Z(a_Z)
+ {}
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ a_Entity->TeleportToCoords(m_X, m_Y, m_Z);
+ return true;
+ }
+
+ private:
+ int m_X, m_Y, m_Z;
+ };
+
+ cTeleportEntityToPortalCallback TETPC(x, y, z);
+ a_World.DoWithEntityByID(a_UniqueIDToTeleport, TETPC);
+ return;
+ }
+ }
+
+ int MinX = std::max(a_BlockX - (int)ceil(a_EntityWidth), a_BlockX - 2), MaxX = std::max(a_BlockX + (int)ceil(a_EntityWidth), a_BlockX + 1);
+ int MinY = std::max(a_BlockY - (int)ceil(a_EntityHeight), a_BlockY - 2), MaxY = std::max(a_BlockY + (int)ceil(a_EntityHeight), a_BlockY + 1);
+
+ for (int y = MinY; y < MaxY + 1; y += MaxY - MinY) for (int x = MinX; x < MaxX + 1; ++x)
+ {
+ Area.SetBlockType(x, y, a_BlockZ, E_BLOCK_OBSIDIAN);
+ }
+ for (int y = MinY; y < MaxY + 1; ++y) for (int x = MinX; x < MaxX + 1; x += MaxX - MinX)
+ {
+ Area.SetBlockType(x, y, a_BlockZ, E_BLOCK_OBSIDIAN);
+ }
+
+ Area.Write(&a_World, MinX, MinY, a_BlockZ);
+}
+
+
+
+
+
+bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World, bool a_ShouldSendRespawn)
+{
+ UNUSED(a_ShouldSendRespawn);
+
+ cWorld * World;
+ if (a_World == NULL)
+ {
+ World = cRoot::Get()->GetWorld(a_WorldName);
+ if (World == NULL)
+ {
+ LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName.c_str());
+ return false;
+ }
+ }
+ else
+ {
+ World = a_World;
+ }
+
+ if (GetWorld() == World)
+ {
+ // Don't move to same world
+ return false;
+ }
+
+ // Remove all links to the old world
+ GetWorld()->RemoveEntity(this);
+ GetWorld()->BroadcastDestroyEntity(*this);
+
+ // Queue add to new world
+ World->AddEntity(this);
+
+ return true;
+}
+
+
+
+
+
void cEntity::SetSwimState(cChunk & a_Chunk)
{
int RelY = (int)floor(GetPosY() + 0.1);
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 85ad42d54..4d922aa11 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -324,6 +324,9 @@ public:
/** Detects the time for application of cacti damage */
virtual void DetectCacti(void);
+
+ /** Detects whether we are in a portal block and begins teleportation procedures if so */
+ virtual void DetectPortal(void);
/// Handles when the entity is in the void
virtual void TickInVoid(cChunk & a_Chunk);
@@ -333,6 +336,9 @@ public:
/// Called when the entity finishes burning
virtual void OnFinishedBurning(void);
+
+ /** Creates exit portal at given coordinates */
+ static void CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ, double a_EntityWidth, double a_EntityHeight, cWorld & a_World, int a_UniqueIDToTeleport);
// tolua_begin
@@ -366,6 +372,9 @@ public:
/// Teleports to the coordinates specified
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ);
+
+ /** Moves entity to specified world */
+ virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL, bool a_ShouldSendRespawn = true);
// tolua_end
@@ -501,6 +510,12 @@ protected:
/** Air level of a mobile */
int m_AirLevel;
int m_AirTickTimer;
+
+ /** Portal delay timer and cooldown boolean
+ First value is to delay sending the repsawn packet (which triggers the Entering the {Dimension} screen).
+ Second value is to prevent a teleportation loop by ensuring we do not reenter a portal that we came out of.
+ */
+ std::pair<unsigned short, bool> m_PortalCooldownData;
private:
/** Measured in degrees, [-180, +180) */
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index feb09b5d2..a346e68cf 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -82,13 +82,14 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
m_PlayerName = a_PlayerName;
- if (!LoadFromDisk())
+ cWorld * World;
+ if (!LoadFromDisk(World))
{
m_Inventory.Clear();
- cWorld * DefaultWorld = cRoot::Get()->GetDefaultWorld();
- SetPosX(DefaultWorld->GetSpawnX());
- SetPosY(DefaultWorld->GetSpawnY());
- SetPosZ(DefaultWorld->GetSpawnZ());
+ SetPosX(World->GetSpawnX());
+ SetPosY(World->GetSpawnY());
+ SetPosZ(World->GetSpawnZ());
+ SetBedPos(Vector3i(World->GetSpawnX(), World->GetSpawnY(), World->GetSpawnZ()));
LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
@@ -101,11 +102,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
if (m_GameMode == gmNotSet)
{
- cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
- if (World == NULL)
- {
- World = cRoot::Get()->GetDefaultWorld();
- }
if (World->IsGameModeCreative())
{
m_CanFly = true;
@@ -134,7 +130,7 @@ cPlayer::~cPlayer(void)
SaveToDisk();
- m_World->RemovePlayer( this );
+ m_World->RemovePlayer(this);
m_ClientHandle = NULL;
@@ -150,8 +146,6 @@ cPlayer::~cPlayer(void)
void cPlayer::Destroyed()
{
CloseWindow(false);
-
- m_ClientHandle = NULL;
}
@@ -954,12 +948,12 @@ void cPlayer::Respawn(void)
m_LifetimeTotalXp = 0;
// ToDo: send score to client? How?
- m_ClientHandle->SendRespawn(*m_World);
+ m_ClientHandle->SendRespawn(GetWorld()->GetDimension());
// Extinguish the fire:
StopBurning();
- TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ());
+ TeleportToCoords(GetLastBedPos().x, GetLastBedPos().y, GetLastBedPos().z);
SetVisible(true);
}
@@ -1576,27 +1570,38 @@ void cPlayer::TossItems(const cItems & a_Items)
-bool cPlayer::MoveToWorld(const char * a_WorldName)
+bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * a_World, bool a_ShouldSendRespawn)
{
- cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
- if (World == NULL)
+ cWorld * World;
+ if (a_World == NULL)
{
- LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName);
+ World = cRoot::Get()->GetWorld(a_WorldName);
+ if (World == NULL)
+ {
+ LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName.c_str());
+ return false;
+ }
+ }
+ else
+ {
+ World = a_World;
+ }
+
+ if (GetWorld() == World)
+ {
+ // Don't move to same world
return false;
}
// Send the respawn packet:
- if (m_ClientHandle != NULL)
+ if (a_ShouldSendRespawn && (m_ClientHandle != NULL))
{
- m_ClientHandle->SendRespawn(*World);
+ m_ClientHandle->SendRespawn(World->GetDimension());
}
- // Remove all links to the old world
+ // Remove player from old world
m_World->RemovePlayer(this);
- // If the dimension is different, we can send the respawn packet
- // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
-
// Queue adding player to the new world, including all the necessary adjustments to the object
World->AddPlayer(this);
@@ -1646,8 +1651,14 @@ void cPlayer::LoadPermissionsFromDisk()
-bool cPlayer::LoadFromDisk()
+bool cPlayer::LoadFromDisk(cWorld *& a_World)
{
+ a_World = cRoot::Get()->GetWorld(GetLoadedWorldName());
+ if (a_World == NULL)
+ {
+ a_World = cRoot::Get()->GetDefaultWorld();
+ }
+
LoadPermissionsFromDisk();
AString SourceFile;
@@ -1701,6 +1712,9 @@ bool cPlayer::LoadFromDisk()
m_LifetimeTotalXp = (short) root.get("xpTotal", 0).asInt();
m_CurrentXp = (short) root.get("xpCurrent", 0).asInt();
m_IsFlying = root.get("isflying", 0).asBool();
+ m_LastBedPos.x = root.get("SpawnX", a_World->GetSpawnX()).asInt();
+ m_LastBedPos.y = root.get("SpawnY", a_World->GetSpawnY()).asInt();
+ m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt();
m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
@@ -1719,7 +1733,7 @@ bool cPlayer::LoadFromDisk()
StatSerializer.Load();
LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
- GetName().c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str()
+ GetName().c_str(), GetPosX(), GetPosY(), GetPosZ(), GetLoadedWorldName().c_str()
);
return true;
@@ -1761,6 +1775,9 @@ bool cPlayer::SaveToDisk()
root["foodExhaustion"] = m_FoodExhaustionLevel;
root["world"] = GetWorld()->GetName();
root["isflying"] = IsFlying();
+ root["SpawnX"] = GetLastBedPos().x;
+ root["SpawnY"] = GetLastBedPos().y;
+ root["SpawnZ"] = GetLastBedPos().z;
if (m_GameMode == GetWorld()->GetGameMode())
{
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 83b9ad593..8f319f1ae 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -127,7 +127,7 @@ public:
inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export
- /** Returns whether the player is climbing (ladders, vines e.t.c). */
+ /** Returns whether the player is climbing (ladders, vines etc.) */
bool IsClimbing(void) const;
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override;
@@ -323,18 +323,23 @@ public:
virtual void Killed(cEntity * a_Victim) override;
- void Respawn(void); // tolua_export
+ void Respawn(void); // tolua_export
- void SetVisible( bool a_bVisible ); // tolua_export
- bool IsVisible(void) const { return m_bVisible; } // tolua_export
+ void SetVisible(bool a_bVisible); // tolua_export
+ bool IsVisible(void) const { return m_bVisible; } // tolua_export
/** Moves the player to the specified world.
Returns true if successful, false on failure (world not found). */
- bool MoveToWorld(const char * a_WorldName); // tolua_export
+ virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL, bool a_ShouldSendRespawn = true) override; // tolua_export
+ /** Saves all player data, such as inventory, to JSON */
bool SaveToDisk(void);
- bool LoadFromDisk(void);
- void LoadPermissionsFromDisk(void); // tolua_export
+
+ /** Loads player data from JSON to the object
+ Takes a (NULL) cWorld pointer which it will assign a value to based on either the loaded world or default world
+ */
+ bool LoadFromDisk(cWorld *& a_World);
+ void LoadPermissionsFromDisk(void); // tolua_export
const AString & GetLoadedWorldName() { return m_LoadedWorldName; }
@@ -344,8 +349,7 @@ public:
void SendExperience(void);
- // In UI windows, the item that the player is dragging:
- bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); }
+ /** In UI windows, the item that the player is dragging */
cItem & GetDraggingItem(void) {return m_DraggingItem; }
// In UI windows, when inventory-painting:
@@ -393,11 +397,19 @@ public:
/** If true the player can fly even when he's not in creative. */
void SetCanFly(bool a_CanFly);
+ /** Gets the last position that the player slept in */
+ Vector3i GetLastBedPos(void) const { return m_LastBedPos; }
+
+ /** Sets the player's bed (home) position */
+ void SetBedPos(const Vector3i & a_Pos) { m_LastBedPos = a_Pos; }
+
/** Update movement-related statistics. */
void UpdateMovementStats(const Vector3d & a_DeltaPos);
/** Returns wheter the player can fly or not. */
virtual bool CanFly(void) const { return m_CanFly; }
+
+
// tolua_end
// cEntity overrides:
@@ -452,6 +464,9 @@ protected:
cWindow * m_CurrentWindow;
cWindow * m_InventoryWindow;
+ /** The player's last saved bed position */
+ Vector3i m_LastBedPos;
+
char m_Color;
eGameMode m_GameMode;
@@ -510,8 +525,6 @@ protected:
cStatManager m_Stats;
-
-
void ResolvePermissions(void);
void ResolveGroups(void);