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.cpp215
1 files changed, 138 insertions, 77 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 8ef45f1a5..7c8e18b51 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -45,6 +45,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_IsInitialized(false)
, m_EntityType(a_EntityType)
, m_World(NULL)
+ , m_IsFireproof(false)
, m_TicksSinceLastBurnDamage(0)
, m_TicksSinceLastLavaDamage(0)
, m_TicksSinceLastFireDamage(0)
@@ -325,12 +326,41 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
m_Health = 0;
}
- if (IsMob() || IsPlayer()) // Knockback for only players and mobs
+ if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL)) // Knockback for only players and mobs
{
- AddSpeed(a_TDI.Knockback * 2);
+ int KnockbackLevel = 0;
+ if (a_TDI.Attacker->GetEquippedWeapon().m_ItemType == E_ITEM_BOW)
+ {
+ KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchPunch);
+ }
+ else
+ {
+ KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback);
+ }
+
+ Vector3d additionalSpeed(0, 0, 0);
+ switch (KnockbackLevel)
+ {
+ case 1:
+ {
+ additionalSpeed.Set(5, .3, 5);
+ break;
+ }
+ case 2:
+ {
+ additionalSpeed.Set(8, .3, 8);
+ break;
+ }
+ default:
+ {
+ additionalSpeed.Set(2, .3, 2);
+ break;
+ }
+ }
+ AddSpeed(a_TDI.Knockback * additionalSpeed);
}
- m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT);
+ m_World->BroadcastEntityStatus(*this, esGenericHurt);
if (m_Health <= 0)
{
@@ -380,11 +410,8 @@ int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver)
-int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
+bool cEntity::ArmorCoversAgainst(eDamageType a_DamageType)
{
- // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
-
- // Filter out damage types that are not protected by armor:
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20
switch (a_DamageType)
{
@@ -399,9 +426,33 @@ int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_Dama
case dtLightning:
case dtPlugin:
{
- return 0;
+ return false;
+ }
+
+ case dtAttack:
+ case dtArrowAttack:
+ case dtCactusContact:
+ case dtLavaContact:
+ case dtFireContact:
+ case dtEnderPearl:
+ case dtExplosion:
+ {
+ return true;
}
}
+ ASSERT(!"Invalid damage type!");
+}
+
+
+
+
+
+int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
+{
+ // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
+
+ // Filter out damage types that are not protected by armor:
+ if (!ArmorCoversAgainst(a_DamageType)) return 0;
// Add up all armor points:
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20
@@ -479,7 +530,7 @@ void cEntity::KilledBy(cEntity * a_Killer)
GetDrops(Drops, a_Killer);
m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ());
- m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD);
+ m_World->BroadcastEntityStatus(*this, esGenericDead);
}
@@ -519,37 +570,36 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
}
else
{
- if (a_Chunk.IsValid())
+ if (!a_Chunk.IsValid())
{
- cChunk * NextChunk = a_Chunk.GetNeighborChunk(POSX_TOINT, POSZ_TOINT);
+ return;
+ }
- if ((NextChunk == NULL) || !NextChunk->IsValid())
- {
- return;
- }
+ // Position changed -> super::Tick() called
+ GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, POSX_TOINT, POSZ_TOINT)
- TickBurning(*NextChunk);
+ TickBurning(*NextChunk);
- if (GetPosY() < VOID_BOUNDARY)
- {
- TickInVoid(*NextChunk);
- }
- else
- {
- m_TicksSinceLastVoidDamage = 0;
- }
-
- if (IsMob() || IsPlayer())
- {
- // Set swimming state
- SetSwimState(*NextChunk);
+ if (GetPosY() < VOID_BOUNDARY)
+ {
+ TickInVoid(*NextChunk);
+ }
+ else
+ {
+ m_TicksSinceLastVoidDamage = 0;
+ }
- // Handle drowning
- HandleAir();
- }
+ if (IsMob() || IsPlayer())
+ {
+ // Set swimming state
+ SetSwimState(*NextChunk);
- HandlePhysics(a_Dt, *NextChunk);
+ // Handle drowning
+ HandleAir();
}
+
+ // None of the above functions change position, we remain in the chunk of NextChunk
+ HandlePhysics(a_Dt, *NextChunk);
}
}
@@ -559,34 +609,30 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
+ int BlockX = POSX_TOINT;
+ int BlockY = POSY_TOINT;
+ int BlockZ = POSZ_TOINT;
+
+ // Position changed -> super::HandlePhysics() called
+ GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, BlockX, BlockZ)
+
// TODO Add collision detection with entities.
a_Dt /= 1000; // Convert from msec to sec
- Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ());
- Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ());
- int BlockX = (int) floor(NextPos.x);
- int BlockY = (int) floor(NextPos.y);
- int BlockZ = (int) floor(NextPos.z);
+ Vector3d NextPos = Vector3d(GetPosX(), GetPosY(), GetPosZ());
+ Vector3d NextSpeed = Vector3d(GetSpeedX(), GetSpeedY(), GetSpeedZ());
if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
{
- // Outside of the world
-
- cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
- // See if we can commit our changes. If not, we will discard them.
- if (NextChunk != NULL)
- {
- SetSpeed(NextSpeed);
- NextPos += (NextSpeed * a_Dt);
- SetPosition(NextPos);
- }
-
+ // Outside of the world
+ AddSpeedY(m_Gravity * a_Dt);
+ AddPosition(GetSpeed() * a_Dt);
return;
}
- int RelBlockX = BlockX - (a_Chunk.GetPosX() * cChunkDef::Width);
- int RelBlockZ = BlockZ - (a_Chunk.GetPosZ() * cChunkDef::Width);
- BLOCKTYPE BlockIn = a_Chunk.GetBlock( RelBlockX, BlockY, RelBlockZ );
- BLOCKTYPE BlockBelow = (BlockY > 0) ? a_Chunk.GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
+ int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
+ int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
+ BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
+ BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
if (!cBlockInfo::IsSolid(BlockIn)) // Making sure we are not inside a solid block
{
if (m_bOnGround) // check if it's still on the ground
@@ -616,7 +662,7 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
bool IsNoAirSurrounding = true;
for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
{
- if (!a_Chunk.UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
+ if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
{
// The pickup is too close to an unloaded chunk, bail out of any physics handling
return;
@@ -764,20 +810,8 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
}
- BlockX = (int) floor(NextPos.x);
- BlockZ = (int) floor(NextPos.z);
-
- cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
- // See if we can commit our changes. If not, we will discard them.
- if (NextChunk != NULL)
- {
- if (NextPos.x != GetPosX()) SetPosX(NextPos.x);
- if (NextPos.y != GetPosY()) SetPosY(NextPos.y);
- if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z);
- if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x);
- if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y);
- if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z);
- }
+ SetPosition(NextPos);
+ SetSpeed(NextSpeed);
}
@@ -788,6 +822,14 @@ void cEntity::TickBurning(cChunk & a_Chunk)
{
// Remember the current burning state:
bool HasBeenBurning = (m_TicksLeftBurning > 0);
+
+ if (m_World->IsWeatherWet())
+ {
+ if (POSY_TOINT > m_World->GetHeight(POSX_TOINT, POSZ_TOINT))
+ {
+ m_TicksLeftBurning = 0;
+ }
+ }
// Do the burning damage:
if (m_TicksLeftBurning > 0)
@@ -795,7 +837,10 @@ void cEntity::TickBurning(cChunk & a_Chunk)
m_TicksSinceLastBurnDamage++;
if (m_TicksSinceLastBurnDamage >= BURN_TICKS_PER_DAMAGE)
{
- TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0);
+ if (!m_IsFireproof)
+ {
+ TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0);
+ }
m_TicksSinceLastBurnDamage = 0;
}
m_TicksLeftBurning--;
@@ -806,7 +851,7 @@ void cEntity::TickBurning(cChunk & a_Chunk)
int MaxRelX = (int)floor(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width;
int MinRelZ = (int)floor(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width;
int MaxRelZ = (int)floor(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width;
- int MinY = std::max(0, std::min(cChunkDef::Height - 1, (int)floor(GetPosY())));
+ int MinY = std::max(0, std::min(cChunkDef::Height - 1, POSY_TOINT));
int MaxY = std::max(0, std::min(cChunkDef::Height - 1, (int)ceil (GetPosY() + m_Height)));
bool HasWater = false;
bool HasLava = false;
@@ -863,7 +908,10 @@ void cEntity::TickBurning(cChunk & a_Chunk)
m_TicksSinceLastLavaDamage++;
if (m_TicksSinceLastLavaDamage >= LAVA_TICKS_PER_DAMAGE)
{
- TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0);
+ if (!m_IsFireproof)
+ {
+ TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0);
+ }
m_TicksSinceLastLavaDamage = 0;
}
}
@@ -881,7 +929,10 @@ void cEntity::TickBurning(cChunk & a_Chunk)
m_TicksSinceLastFireDamage++;
if (m_TicksSinceLastFireDamage >= FIRE_TICKS_PER_DAMAGE)
{
- TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0);
+ if (!m_IsFireproof)
+ {
+ TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0);
+ }
m_TicksSinceLastFireDamage = 0;
}
}
@@ -981,13 +1032,13 @@ void cEntity::HandleAir(void)
}
else
{
- m_AirTickTimer -= 1;
+ m_AirTickTimer--;
}
}
else
{
// Reduce air supply
- m_AirLevel -= 1;
+ m_AirLevel--;
}
}
else
@@ -1040,6 +1091,16 @@ void cEntity::SetMaxHealth(int a_MaxHealth)
+/// Sets whether the entity is fireproof
+void cEntity::SetIsFireproof(bool a_IsFireproof)
+{
+ m_IsFireproof = a_IsFireproof;
+}
+
+
+
+
+
/// Puts the entity on fire for the specified amount of ticks
void cEntity::StartBurning(int a_TicksLeftBurning)
{
@@ -1099,15 +1160,15 @@ void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude)
{
- //We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks
- if( (m_Speed.SqrLength() > 0.0004f || m_bDirtySpeed) && (m_World->GetWorldAge() - m_TimeLastSpeedPacket >= 2))
+ // Send velocity packet every two ticks if: speed is not negligible or speed was set (as indicated by the DirtySpeed flag)
+ if (((m_Speed.SqrLength() > 0.0004f) || m_bDirtySpeed) && ((m_World->GetWorldAge() - m_TimeLastSpeedPacket) >= 2))
{
m_World->BroadcastEntityVelocity(*this,a_Exclude);
m_bDirtySpeed = false;
m_TimeLastSpeedPacket = m_World->GetWorldAge();
}
- //Have to process position related packets this every two ticks
+ // Have to process position related packets this every two ticks
if (m_World->GetWorldAge() % 2 == 0)
{
int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0));