diff options
Diffstat (limited to 'src/Entities/Entity.cpp')
-rw-r--r-- | src/Entities/Entity.cpp | 259 |
1 files changed, 258 insertions, 1 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); |