From 29567c56107c86b70da130f995564beb2eaf424c Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Thu, 12 Jun 2014 15:21:07 +0100 Subject: Portals animate and delay correctly --- src/Entities/Entity.cpp | 121 +++++++++++++++++++++++++++++++++++++----------- src/Entities/Entity.h | 10 +++- src/Entities/Player.cpp | 21 +++------ src/Entities/Player.h | 10 ++-- 4 files changed, 112 insertions(+), 50 deletions(-) (limited to 'src/Entities') diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 334cf5aa7..a7b6cca27 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1051,15 +1051,16 @@ void cEntity::DetectPortal() class cPortalChunkLoader : public cChunkStay { public: - cPortalChunkLoader(cEntity * a_Entity, Vector3i & a_PortalPos) : + cPortalChunkLoader(cEntity * a_Entity, cWorld & a_World, Vector3i & a_PortalPos) : m_Entity(a_Entity), - m_PortalPos(a_PortalPos) + m_PortalPos(a_PortalPos), + m_World(a_World) {} private: virtual bool OnAllChunksAvailable(void) override { - m_Entity->CreateExitPortal(m_PortalPos.x, m_PortalPos.y, m_PortalPos.z); + cEntity::CreateExitPortal(m_PortalPos.x, m_PortalPos.y, m_PortalPos.z, m_Entity->GetWidth(), m_Entity->GetHeight(), m_World, m_Entity->GetUniqueID()); return true; } @@ -1068,6 +1069,7 @@ void cEntity::DetectPortal() cEntity * m_Entity; Vector3i m_PortalPos; + cWorld & m_World; }; int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT; @@ -1077,106 +1079,167 @@ void cEntity::DetectPortal() { case E_BLOCK_NETHER_PORTAL: { - if (!GetWorld()->AreNetherPortalsEnabled()) + 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: MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName())); break; + 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())); + MoveToWorld(GetWorld()->GetNetherWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()), false); - cChunkStay * Stay = new cPortalChunkLoader(this, Vector3i(X, Y, Z)); + 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 OtherMinChunkX = MinChunkX; OtherMinChunkX <= MaxChunkX; ++OtherMinChunkX) + for (int ChunkX = MinChunkX; ChunkX <= MaxChunkX; ++ChunkX) { - for (int OtherMinChunkZ = MinChunkZ; OtherMinChunkZ <= MaxChunkZ; ++OtherMinChunkZ) + for (int ChunkZ = MinChunkZ; ChunkZ <= MaxChunkZ; ++ChunkZ) { - Stay->Add(OtherMinChunkX, OtherMinChunkZ); + LOG("Queue %i %i", ChunkX, ChunkZ); + Stay->Add(ChunkX, ChunkZ); } } Stay->Enable(*GetWorld()->GetChunkMap()); - break; + return; } default: break; } - break; + return; } case E_BLOCK_END_PORTAL: { - if (!GetWorld()->AreEndPortalsEnabled()) + 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: { - MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName())); + 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); } - break; + 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())); - break; + 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) +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(GetWorld(), a_BlockX - 128, a_BlockX + 128, 0, 128, a_BlockZ - 128, a_BlockZ + 128); + 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 + GetHeight()), z) == E_BLOCK_NETHER_PORTAL) || - (Area.GetBlockType(x, (int)floor(y - GetHeight()), 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) ) ) { - TeleportToCoords(x, y, z); + 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(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); + 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) { @@ -1187,15 +1250,17 @@ void cEntity::CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ) Area.SetBlockType(x, y, a_BlockZ, E_BLOCK_OBSIDIAN); } - Area.Write(GetWorld(), MinX, MinY, a_BlockZ); + Area.Write(&a_World, MinX, MinY, a_BlockZ); } -bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World) +bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World, bool a_ShouldSendRespawn) { + UNUSED(a_ShouldSendRespawn); + cWorld * World; if (a_World == NULL) { @@ -1213,6 +1278,7 @@ bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World) if (GetWorld() == World) { + // Don't move to same world return false; } @@ -1220,8 +1286,7 @@ bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World) GetWorld()->RemoveEntity(this); GetWorld()->BroadcastDestroyEntity(*this); - // Add to all the necessary parts of the new world - SetWorld(World); + // Queue add to new world World->AddEntity(this); return true; diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 934e0302b..4d922aa11 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -338,7 +338,7 @@ public: virtual void OnFinishedBurning(void); /** Creates exit portal at given coordinates */ - void CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ); + 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 @@ -374,7 +374,7 @@ public: 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); + virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL, bool a_ShouldSendRespawn = true); // tolua_end @@ -510,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 m_PortalCooldownData; private: /** Measured in degrees, [-180, +180) */ diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 4d6688694..a346e68cf 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -948,7 +948,7 @@ void cPlayer::Respawn(void) m_LifetimeTotalXp = 0; // ToDo: send score to client? How? - m_ClientHandle->SendRespawn(*GetWorld()); + m_ClientHandle->SendRespawn(GetWorld()->GetDimension()); // Extinguish the fire: StopBurning(); @@ -1570,7 +1570,7 @@ void cPlayer::TossItems(const cItems & a_Items) -bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * a_World) +bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * a_World, bool a_ShouldSendRespawn) { cWorld * World; if (a_World == NULL) @@ -1589,31 +1589,22 @@ bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * 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); - if (GetWorld()->GetDimension() != World->GetDimension()) - { - GetClientHandle()->SendPlayerMoveLook(); - GetClientHandle()->SendHealth(); - GetClientHandle()->SendWholeInventory(*GetWindow()); - } - return true; } diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 99a0e601c..8f319f1ae 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -323,14 +323,14 @@ 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). */ - virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL) override; // 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); @@ -339,7 +339,7 @@ public: 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 + void LoadPermissionsFromDisk(void); // tolua_export const AString & GetLoadedWorldName() { return m_LoadedWorldName; } -- cgit v1.2.3