diff options
Diffstat (limited to 'src/Entities')
-rw-r--r-- | src/Entities/Entity.cpp | 194 | ||||
-rw-r--r-- | src/Entities/Entity.h | 9 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 68 | ||||
-rw-r--r-- | src/Entities/Player.h | 27 |
4 files changed, 269 insertions, 29 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 8f736a269..334cf5aa7 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,196 @@ void cEntity::DetectCacti(void) +void cEntity::DetectPortal() +{ + if (!GetWorld()->AreNetherPortalsEnabled() && !GetWorld()->AreEndPortalsEnabled()) + { + return; + } + + class cPortalChunkLoader : public cChunkStay + { + public: + cPortalChunkLoader(cEntity * a_Entity, Vector3i & a_PortalPos) : + m_Entity(a_Entity), + m_PortalPos(a_PortalPos) + {} + + private: + virtual bool OnAllChunksAvailable(void) override + { + m_Entity->CreateExitPortal(m_PortalPos.x, m_PortalPos.y, m_PortalPos.z); + return true; + } + + virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override {}; + virtual void OnDisabled(void) override {}; + + cEntity * m_Entity; + Vector3i m_PortalPos; + }; + + 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()) + { + return; + } + + switch (GetWorld()->GetDimension()) + { + case dimNether: MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName())); break; + case dimOverworld: + { + if (IsPlayer()) + { + ((cPlayer *)this)->AwardAchievement(achEnterPortal); + } + MoveToWorld(GetWorld()->GetNetherWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName())); + + cChunkStay * Stay = new cPortalChunkLoader(this, 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 OtherMinChunkX = MinChunkX; OtherMinChunkX <= MaxChunkX; ++OtherMinChunkX) + { + for (int OtherMinChunkZ = MinChunkZ; OtherMinChunkZ <= MaxChunkZ; ++OtherMinChunkZ) + { + Stay->Add(OtherMinChunkX, OtherMinChunkZ); + } + } + + Stay->Enable(*GetWorld()->GetChunkMap()); + break; + } + default: break; + } + break; + } + case E_BLOCK_END_PORTAL: + { + if (!GetWorld()->AreEndPortalsEnabled()) + { + return; + } + + switch (GetWorld()->GetDimension()) + { + case dimEnd: + { + MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName())); + + if (IsPlayer()) + { + cPlayer * Player = (cPlayer *)this; + Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z); + } + break; + } + case dimOverworld: + { + if (IsPlayer()) + { + ((cPlayer *)this)->AwardAchievement(achEnterTheEnd); + } + MoveToWorld(GetWorld()->GetEndWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName())); + break; + } + default: break; + } + } + default: break; + } + } +} + + + + + +void cEntity::CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cBlockArea Area; + Area.Read(GetWorld(), 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 + GetHeight()), z) == E_BLOCK_NETHER_PORTAL) || + (Area.GetBlockType(x, (int)floor(y - GetHeight()), z) == E_BLOCK_NETHER_PORTAL) + ) + ) + { + TeleportToCoords(x, y, z); + return; + } + } + + int MinX = std::max(a_BlockX - (int)ceil(GetWidth()), a_BlockX - 2), MaxX = std::max(a_BlockX + (int)ceil(GetWidth()), a_BlockX + 1); + int MinY = std::max(a_BlockY - (int)ceil(GetHeight()), a_BlockY - 2), MaxY = std::max(a_BlockY + (int)ceil(GetHeight()), 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(GetWorld(), MinX, MinY, a_BlockZ); +} + + + + + +bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World) +{ + 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) + { + return false; + } + + // Remove all links to the old world + GetWorld()->RemoveEntity(this); + GetWorld()->BroadcastDestroyEntity(*this); + + // Add to all the necessary parts of the new world + SetWorld(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..934e0302b 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 */ + void CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ); // 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); // tolua_end diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index feb09b5d2..4d6688694 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()); // Extinguish the fire: StopBurning(); - TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); + TeleportToCoords(GetLastBedPos().x, GetLastBedPos().y, GetLastBedPos().z); SetVisible(true); } @@ -1576,12 +1570,25 @@ void cPlayer::TossItems(const cItems & a_Items) -bool cPlayer::MoveToWorld(const char * a_WorldName) +bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * a_World) { - cWorld * World = cRoot::Get()->GetWorld(a_WorldName); - if (World == NULL) + 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) { - LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName); return false; } @@ -1600,6 +1607,13 @@ bool cPlayer::MoveToWorld(const char * a_WorldName) // Queue adding player to the new world, including all the necessary adjustments to the object World->AddPlayer(this); + if (GetWorld()->GetDimension() != World->GetDimension()) + { + GetClientHandle()->SendPlayerMoveLook(); + GetClientHandle()->SendHealth(); + GetClientHandle()->SendWholeInventory(*GetWindow()); + } + return true; } @@ -1646,8 +1660,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 +1721,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 +1742,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 +1784,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..99a0e601c 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; @@ -330,10 +330,15 @@ public: /** 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) override; // tolua_export + /** Saves all player data, such as inventory, to JSON */ bool SaveToDisk(void); - bool LoadFromDisk(void); + + /** 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); |