summaryrefslogtreecommitdiffstats
path: root/src/Entities/Entity.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities/Entity.cpp')
-rw-r--r--src/Entities/Entity.cpp203
1 files changed, 194 insertions, 9 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index db0fd0fd6..da578013d 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 "Items/ItemHandler.h"
@@ -37,6 +38,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_Gravity(-9.81f)
, m_LastPos(a_X, a_Y, a_Z)
, m_IsInitialized(false)
+ , m_WorldTravellingFrom(NULL)
, m_EntityType(a_EntityType)
, m_World(NULL)
, m_IsFireproof(false)
@@ -289,11 +291,6 @@ void cEntity::SetPitchFromSpeed(void)
bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
{
- if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))
- {
- return false;
- }
-
if (m_Health <= 0)
{
// Can't take damage if already dead
@@ -306,10 +303,17 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
return false;
}
+ if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))
+ {
+ return false;
+ }
+
if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer()))
{
cPlayer * Player = (cPlayer *)a_TDI.Attacker;
+ Player->GetEquippedItem().GetHandler()->OnEntityAttack(Player, this);
+
// IsOnGround() only is false if the player is moving downwards
// TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain)
if (!Player->IsOnGround())
@@ -614,9 +618,12 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
// Handle drowning
HandleAir();
}
-
- // None of the above functions change position, we remain in the chunk of NextChunk
- HandlePhysics(a_Dt, *NextChunk);
+
+ if (!DetectPortal()) // Our chunk is invalid if we have moved to another world
+ {
+ // None of the above functions changed position, we remain in the chunk of NextChunk
+ HandlePhysics(a_Dt, *NextChunk);
+ }
}
}
@@ -853,7 +860,7 @@ void cEntity::TickBurning(cChunk & a_Chunk)
// Remember the current burning state:
bool HasBeenBurning = (m_TicksLeftBurning > 0);
- if (m_World->IsWeatherWet())
+ if (GetWorld()->IsWeatherWetAt(POSX_TOINT, POSZ_TOINT))
{
if (POSY_TOINT > m_World->GetHeight(POSX_TOINT, POSZ_TOINT))
{
@@ -1024,6 +1031,184 @@ void cEntity::DetectCacti(void)
+bool cEntity::DetectPortal()
+{
+ if (GetWorld()->GetDimension() == dimOverworld)
+ {
+ if (GetWorld()->GetNetherWorldName().empty() && GetWorld()->GetEndWorldName().empty())
+ {
+ // Teleportation to either dimension not enabled, don't bother proceeding
+ return false;
+ }
+ }
+ else if (GetWorld()->GetLinkedOverworldName().empty())
+ {
+ // Overworld teleportation disabled, abort
+ return false;
+ }
+
+ 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 (m_PortalCooldownData.m_ShouldPreventTeleportation)
+ {
+ // Just exited a portal, don't teleport again
+ return false;
+ }
+
+ if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && m_PortalCooldownData.m_TicksDelayed != 80)
+ {
+ // Delay teleportation for four seconds if the entity is a non-creative player
+ m_PortalCooldownData.m_TicksDelayed++;
+ return false;
+ }
+ m_PortalCooldownData.m_TicksDelayed = 0;
+
+ switch (GetWorld()->GetDimension())
+ {
+ case dimNether:
+ {
+ if (GetWorld()->GetLinkedOverworldName().empty())
+ {
+ return false;
+ }
+
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); // Send a respawn packet before world is loaded/generated so the client isn't left in limbo
+ }
+
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+ }
+ case dimOverworld:
+ {
+ if (GetWorld()->GetNetherWorldName().empty())
+ {
+ return false;
+ }
+
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true;
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->AwardAchievement(achEnterPortal);
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether);
+ }
+
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()), false);
+ }
+ default: return false;
+ }
+ }
+ case E_BLOCK_END_PORTAL:
+ {
+ if (m_PortalCooldownData.m_ShouldPreventTeleportation)
+ {
+ return false;
+ }
+
+ switch (GetWorld()->GetDimension())
+ {
+ case dimEnd:
+ {
+ if (GetWorld()->GetLinkedOverworldName().empty())
+ {
+ return false;
+ }
+
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true;
+
+ if (IsPlayer())
+ {
+ cPlayer * Player = (cPlayer *)this;
+ Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z);
+ Player->GetClientHandle()->SendRespawn(dimOverworld);
+ }
+
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+ }
+ case dimOverworld:
+ {
+ if (GetWorld()->GetEndWorldName().empty())
+ {
+ return false;
+ }
+
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true;
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->AwardAchievement(achEnterTheEnd);
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimEnd);
+ }
+
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName()), false);
+ }
+ default: return false;
+ }
+ }
+ default: break;
+ }
+ }
+
+ // Allow portals to work again
+ m_PortalCooldownData.m_ShouldPreventTeleportation = false;
+ m_PortalCooldownData.m_TicksDelayed = 0;
+ return false;
+}
+
+
+
+
+
+bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
+{
+ UNUSED(a_ShouldSendRespawn);
+ ASSERT(a_World != NULL);
+
+ if (GetWorld() == a_World)
+ {
+ // Don't move to same world
+ return false;
+ }
+
+ // Remove all links to the old world
+ SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
+ GetWorld()->BroadcastDestroyEntity(*this);
+
+ // Queue add to new world
+ a_World->AddEntity(this);
+ SetWorld(a_World);
+
+ return true;
+}
+
+
+
+
+
+bool cEntity::MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn)
+{
+ cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
+ if (World == NULL)
+ {
+ LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName.c_str());
+ return false;
+ }
+
+ return DoMoveToWorld(World, a_ShouldSendRespawn);
+}
+
+
+
+
+
void cEntity::SetSwimState(cChunk & a_Chunk)
{
int RelY = (int)floor(GetPosY() + 0.1);