summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/ArrowEntity.cpp73
-rw-r--r--src/Entities/ArrowEntity.h6
-rw-r--r--src/Entities/Entity.cpp17
-rw-r--r--src/Entities/Entity.h4
-rw-r--r--src/Entities/Pickup.cpp35
-rw-r--r--src/Entities/Player.cpp102
-rw-r--r--src/Entities/Player.h18
-rw-r--r--src/Entities/ProjectileEntity.cpp71
-rw-r--r--src/Entities/ProjectileEntity.h34
-rw-r--r--src/Entities/ThrownEggEntity.cpp7
-rw-r--r--src/Entities/ThrownEggEntity.h21
-rw-r--r--src/Entities/ThrownEnderPearlEntity.cpp40
-rw-r--r--src/Entities/ThrownEnderPearlEntity.h25
-rw-r--r--src/Entities/ThrownSnowballEntity.cpp11
-rw-r--r--src/Entities/ThrownSnowballEntity.h21
15 files changed, 334 insertions, 151 deletions
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp
index 8d2569125..d59088e72 100644
--- a/src/Entities/ArrowEntity.cpp
+++ b/src/Entities/ArrowEntity.cpp
@@ -3,6 +3,7 @@
#include "Player.h"
#include "ArrowEntity.h"
#include "../Chunk.h"
+#include "FastRandom.h"
@@ -24,9 +25,9 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a
SetYawFromSpeed();
SetPitchFromSpeed();
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
- m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
- GetYaw(), GetPitch()
- );
+ m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
+ GetYaw(), GetPitch()
+ );
}
@@ -44,6 +45,10 @@ cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
m_bIsCollected(false),
m_HitBlockPos(0, 0, 0)
{
+ if (a_Player.IsGameModeCreative())
+ {
+ m_PickupState = psInCreative;
+ }
}
@@ -67,26 +72,24 @@ bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
-{
- if (a_HitFace == BLOCK_FACE_NONE) { return; }
-
- super::OnHitSolidBlock(a_HitPos, a_HitFace);
- int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z;
-
- switch (a_HitFace)
+{
+ if (GetSpeed().EqualsEps(Vector3d(0, 0, 0), 0.0000001))
{
- case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed
- case BLOCK_FACE_YM:
- {
- break;
- }
- default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true);
+ SetSpeed(GetLookVector().NormalizeCopy() * 0.1); // Ensure that no division by zero happens later
}
-
- m_HitBlockPos = Vector3i(a_X, a_Y, a_Z);
+
+ Vector3d Hit = a_HitPos;
+ Vector3d SinkMovement = (GetSpeed() / 800);
+ Hit += (SinkMovement * 0.01) / SinkMovement.Length(); // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside)
+
+ super::OnHitSolidBlock(Hit, a_HitFace);
+ Vector3i BlockHit = Hit.Floor();
+
+ int X = BlockHit.x, Y = BlockHit.y, Z = BlockHit.z;
+ m_HitBlockPos = Vector3i(X, Y, Z);
// Broadcast arrow hit sound
- m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+ m_World->BroadcastSoundEffect("random.bowhit", X * 8, Y * 8, Z * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
}
@@ -94,13 +97,7 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa
void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
-{
- if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat())
- {
- // Not an entity that interacts with an arrow
- return;
- }
-
+{
int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5);
if (m_IsCritical)
{
@@ -109,7 +106,7 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
// Broadcast successful hit sound
- m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+ GetWorld()->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
Destroy();
}
@@ -120,16 +117,22 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
void cArrowEntity::CollectedBy(cPlayer * a_Dest)
{
- if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest)))
+ if (m_IsInGround && !m_bIsCollected && CanPickup(*a_Dest))
{
- int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW);
- if (NumAdded > 0) // Only play effects if there was space in inventory
+ // Do not add the arrow to the inventory when the player is in creative:
+ if (!a_Dest->IsGameModeCreative())
{
- m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest);
- // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
- m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
- m_bIsCollected = true;
+ int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW);
+ if (NumAdded == 0)
+ {
+ // No space in the inventory
+ return;
+ }
}
+
+ GetWorld()->BroadcastCollectEntity(*this, *a_Dest);
+ GetWorld()->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+ m_bIsCollected = true;
}
}
@@ -165,7 +168,7 @@ void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk)
if (!m_HasTeleported) // Sent a teleport already, don't do again
{
- if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case
+ if (m_HitGroundTimer > 500.f) // Send after half a second, could be less, but just in case
{
m_World->BroadcastTeleportEntity(*this);
m_HasTeleported = true;
diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h
index 1fe3032ee..76cb24449 100644
--- a/src/Entities/ArrowEntity.h
+++ b/src/Entities/ArrowEntity.h
@@ -58,8 +58,14 @@ public:
/// Sets the IsCritical flag
void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; }
+
+ /** Gets the block arrow is in */
+ Vector3i GetBlockHit(void) const { return m_HitBlockPos; }
// tolua_end
+
+ /** Sets the block arrow is in. To be used by the MCA loader only! */
+ void SetBlockHit(const Vector3i & a_BlockHit) { m_HitBlockPos = a_BlockHit; }
protected:
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 06833e1ba..042c4b4c3 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -1092,7 +1092,10 @@ void cEntity::HandleAir(void)
if (IsSubmerged())
{
- SetSpeedY(1); // Float in the water
+ if (!IsPlayer()) // Players control themselves
+ {
+ SetSpeedY(1); // Float in the water
+ }
// Either reduce air level or damage player
if (m_AirLevel < 1)
@@ -1478,8 +1481,7 @@ void cEntity::SetWidth(double a_Width)
void cEntity::AddPosX(double a_AddPosX)
{
- m_Pos.x += a_AddPosX;
-
+ m_Pos.x += a_AddPosX;
}
@@ -1487,8 +1489,7 @@ void cEntity::AddPosX(double a_AddPosX)
void cEntity::AddPosY(double a_AddPosY)
{
- m_Pos.y += a_AddPosY;
-
+ m_Pos.y += a_AddPosY;
}
@@ -1496,8 +1497,7 @@ void cEntity::AddPosY(double a_AddPosY)
void cEntity::AddPosZ(double a_AddPosZ)
{
- m_Pos.z += a_AddPosZ;
-
+ m_Pos.z += a_AddPosZ;
}
@@ -1507,8 +1507,7 @@ void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ)
{
m_Pos.x += a_AddPosX;
m_Pos.y += a_AddPosY;
- m_Pos.z += a_AddPosZ;
-
+ m_Pos.z += a_AddPosZ;
}
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index b5d5cc34c..867d87bb7 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -238,9 +238,9 @@ public:
void AddPosY (double a_AddPosY);
void AddPosZ (double a_AddPosZ);
void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ);
- void AddPosition(const Vector3d & a_AddPos) { AddPosition(a_AddPos.x,a_AddPos.y,a_AddPos.z);}
+ void AddPosition(const Vector3d & a_AddPos) { AddPosition(a_AddPos.x, a_AddPos.y, a_AddPos.z); }
void AddSpeed (double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ);
- void AddSpeed (const Vector3d & a_AddSpeed) { AddSpeed(a_AddSpeed.x,a_AddSpeed.y,a_AddSpeed.z);}
+ void AddSpeed (const Vector3d & a_AddSpeed) { AddSpeed(a_AddSpeed.x, a_AddSpeed.y, a_AddSpeed.z); }
void AddSpeedX (double a_AddSpeedX);
void AddSpeedY (double a_AddSpeedY);
void AddSpeedZ (double a_AddSpeedZ);
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index 0fd006485..24fa591da 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -30,7 +30,7 @@ public:
virtual bool Item(cEntity * a_Entity) override
{
- if (!a_Entity->IsPickup() || (a_Entity->GetUniqueID() == m_Pickup->GetUniqueID()) || a_Entity->IsDestroyed())
+ if (!a_Entity->IsPickup() || (a_Entity->GetUniqueID() <= m_Pickup->GetUniqueID()) || a_Entity->IsDestroyed())
{
return false;
}
@@ -38,10 +38,31 @@ public:
Vector3d EntityPos = a_Entity->GetPosition();
double Distance = (EntityPos - m_Position).Length();
- if ((Distance < 1.2) && ((cPickup *)a_Entity)->GetItem().IsEqual(m_Pickup->GetItem()))
+ cItem & Item = ((cPickup *)a_Entity)->GetItem();
+ if ((Distance < 1.2) && Item.IsEqual(m_Pickup->GetItem()))
{
- m_Pickup->GetItem().AddCount(((cPickup *)a_Entity)->GetItem().m_ItemCount);
- a_Entity->Destroy();
+ short CombineCount = Item.m_ItemCount;
+ if ((CombineCount + m_Pickup->GetItem().m_ItemCount) > Item.GetMaxStackSize())
+ {
+ CombineCount = Item.GetMaxStackSize() - m_Pickup->GetItem().m_ItemCount;
+ }
+
+ if (CombineCount <= 0)
+ {
+ return false;
+ }
+
+ m_Pickup->GetItem().AddCount((char)CombineCount);
+ Item.m_ItemCount -= CombineCount;
+
+ if (Item.m_ItemCount <= 0)
+ {
+ a_Entity->Destroy();
+ }
+ else
+ {
+ a_Entity->GetWorld()->BroadcastEntityMetadata(*a_Entity);
+ }
m_FoundMatchingPickup = true;
}
return false;
@@ -129,7 +150,7 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
}
}
- if (!IsDestroyed()) // Don't try to combine if someone has tried to combine me
+ if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine into an already full pickup
{
cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
@@ -203,10 +224,10 @@ bool cPickup::CollectedBy(cPlayer * a_Dest)
}
m_Item.m_ItemCount -= NumAdded;
- m_World->BroadcastCollectPickup(*this, *a_Dest);
+ m_World->BroadcastCollectEntity(*this, *a_Dest);
// Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
m_World->BroadcastSoundEffect("random.pop",(int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
- if (m_Item.m_ItemCount == 0)
+ if (m_Item.m_ItemCount <= 0)
{
// All of the pickup has been collected, schedule the pickup for destroying
m_bCollected = true;
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 77ab6d309..cc07c25a8 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -1,4 +1,4 @@
-
+
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Player.h"
@@ -8,6 +8,7 @@
#include "../World.h"
#include "../Bindings/PluginManager.h"
#include "../BlockEntities/BlockEntity.h"
+#include "../BlockEntities/EnderChestEntity.h"
#include "../GroupManager.h"
#include "../Group.h"
#include "../Root.h"
@@ -37,14 +38,15 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
: super(etPlayer, 0.6, 1.8)
, m_bVisible(true)
, m_FoodLevel(MAX_FOOD_LEVEL)
- , m_FoodSaturationLevel(5)
+ , m_FoodSaturationLevel(5.0)
, m_FoodTickTimer(0)
- , m_FoodExhaustionLevel(0)
+ , m_FoodExhaustionLevel(0.0)
, m_LastJumpHeight(0)
, m_LastGroundHeight(0)
, m_bTouchGround(false)
, m_Stance(0.0)
, m_Inventory(*this)
+ , m_EnderChestContents(9, 3)
, m_CurrentWindow(NULL)
, m_InventoryWindow(NULL)
, m_Color('-')
@@ -70,6 +72,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_FloaterID(-1)
, m_Team(NULL)
, m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL)
+ , m_bIsTeleporting(false)
{
LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
@@ -145,6 +148,7 @@ cPlayer::~cPlayer(void)
m_ClientHandle = NULL;
delete m_InventoryWindow;
+ m_InventoryWindow = NULL;
LOGD("Player %p deleted", this);
}
@@ -223,7 +227,7 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
SendExperience();
}
- if (GetPosition() != m_LastPos) // Change in position from last tick?
+ if (!GetPosition().EqualsEps(m_LastPos, 0.01)) // Non negligible change in position from last tick?
{
// Apply food exhaustion from movement:
ApplyFoodExhaustionFromMovement();
@@ -410,6 +414,7 @@ void cPlayer::StartChargingBow(void)
LOGD("Player \"%s\" started charging their bow", GetName().c_str());
m_IsChargingBow = true;
m_BowCharge = 0;
+ m_World->BroadcastEntityMetadata(*this, m_ClientHandle);
}
@@ -422,6 +427,8 @@ int cPlayer::FinishChargingBow(void)
int res = m_BowCharge;
m_IsChargingBow = false;
m_BowCharge = 0;
+ m_World->BroadcastEntityMetadata(*this, m_ClientHandle);
+
return res;
}
@@ -434,6 +441,7 @@ void cPlayer::CancelChargingBow(void)
LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", GetName().c_str(), m_BowCharge);
m_IsChargingBow = false;
m_BowCharge = 0;
+ m_World->BroadcastEntityMetadata(*this, m_ClientHandle);
}
@@ -515,7 +523,15 @@ void cPlayer::Heal(int a_Health)
void cPlayer::SetFoodLevel(int a_FoodLevel)
{
- m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL));
+ int FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL));
+
+ if (cRoot::Get()->GetPluginManager()->CallHookPlayerFoodLevelChange(*this, FoodLevel))
+ {
+ m_FoodSaturationLevel = 5.0;
+ return;
+ }
+
+ m_FoodLevel = FoodLevel;
SendHealth();
}
@@ -556,11 +572,9 @@ bool cPlayer::Feed(int a_Food, double a_Saturation)
{
return false;
}
-
- m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL);
- m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel);
-
- SendHealth();
+
+ SetFoodSaturationLevel(m_FoodSaturationLevel + a_Saturation);
+ SetFoodLevel(m_FoodLevel + a_Food);
return true;
}
@@ -944,14 +958,15 @@ void cPlayer::Respawn(void)
// Reset food level:
m_FoodLevel = MAX_FOOD_LEVEL;
- m_FoodSaturationLevel = 5;
+ m_FoodSaturationLevel = 5.0;
+ m_FoodExhaustionLevel = 0.0;
// Reset Experience
m_CurrentXp = 0;
m_LifetimeTotalXp = 0;
// ToDo: send score to client? How?
- m_ClientHandle->SendRespawn(*m_World);
+ m_ClientHandle->SendRespawn(*m_World, true);
// Extinguish the fire:
StopBurning();
@@ -1201,6 +1216,7 @@ void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
SetPosition(a_PosX, a_PosY, a_PosZ);
m_LastGroundHeight = (float)a_PosY;
m_LastJumpHeight = (float)a_PosY;
+ m_bIsTeleporting = true;
m_World->BroadcastTeleportEntity(*this, GetClientHandle());
m_ClientHandle->SendPlayerMoveLook();
@@ -1718,6 +1734,7 @@ bool cPlayer::LoadFromDisk()
}
m_Inventory.LoadFromJson(root["inventory"]);
+ cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents);
m_LoadedWorldName = root.get("world", "world").asString();
@@ -1755,20 +1772,24 @@ bool cPlayer::SaveToDisk()
Json::Value JSON_Inventory;
m_Inventory.SaveToJson(JSON_Inventory);
+ Json::Value JSON_EnderChestInventory;
+ cEnderChestEntity::SaveToJson(JSON_EnderChestInventory, m_EnderChestContents);
+
Json::Value root;
- root["position"] = JSON_PlayerPosition;
- root["rotation"] = JSON_PlayerRotation;
- root["inventory"] = JSON_Inventory;
- root["health"] = m_Health;
- root["xpTotal"] = m_LifetimeTotalXp;
- root["xpCurrent"] = m_CurrentXp;
- root["air"] = m_AirLevel;
- root["food"] = m_FoodLevel;
- root["foodSaturation"] = m_FoodSaturationLevel;
- root["foodTickTimer"] = m_FoodTickTimer;
- root["foodExhaustion"] = m_FoodExhaustionLevel;
- root["world"] = GetWorld()->GetName();
- root["isflying"] = IsFlying();
+ root["position"] = JSON_PlayerPosition;
+ root["rotation"] = JSON_PlayerRotation;
+ root["inventory"] = JSON_Inventory;
+ root["enderchestinventory"] = JSON_EnderChestInventory;
+ root["health"] = m_Health;
+ root["xpTotal"] = m_LifetimeTotalXp;
+ root["xpCurrent"] = m_CurrentXp;
+ root["air"] = m_AirLevel;
+ root["food"] = m_FoodLevel;
+ root["foodSaturation"] = m_FoodSaturationLevel;
+ root["foodTickTimer"] = m_FoodTickTimer;
+ root["foodExhaustion"] = m_FoodExhaustionLevel;
+ root["world"] = GetWorld()->GetName();
+ root["isflying"] = IsFlying();
if (m_GameMode == GetWorld()->GetGameMode())
{
@@ -1870,16 +1891,13 @@ void cPlayer::TickBurning(cChunk & a_Chunk)
void cPlayer::HandleFood(void)
{
// Ref.: http://www.minecraftwiki.net/wiki/Hunger
-
+
if (IsGameModeCreative())
{
// Hunger is disabled for Creative
return;
}
-
- // Remember the food level before processing, for later comparison
- int LastFoodLevel = m_FoodLevel;
-
+
// Heal or damage, based on the food level, using the m_FoodTickTimer:
if ((m_FoodLevel > 17) || (m_FoodLevel <= 0))
{
@@ -1888,11 +1906,11 @@ void cPlayer::HandleFood(void)
{
m_FoodTickTimer = 0;
- if (m_FoodLevel >= 17)
+ if ((m_FoodLevel > 17) && (GetHealth() < GetMaxHealth()))
{
// Regenerate health from food, incur 3 pts of food exhaustion:
Heal(1);
- m_FoodExhaustionLevel += 3;
+ m_FoodExhaustionLevel += 3.0;
}
else if ((m_FoodLevel <= 0) && (m_Health > 1))
{
@@ -1903,24 +1921,19 @@ void cPlayer::HandleFood(void)
}
// Apply food exhaustion that has accumulated:
- if (m_FoodExhaustionLevel >= 4)
+ if (m_FoodExhaustionLevel >= 4.0)
{
- m_FoodExhaustionLevel -= 4;
+ m_FoodExhaustionLevel -= 4.0;
- if (m_FoodSaturationLevel >= 1)
+ if (m_FoodSaturationLevel >= 1.0)
{
- m_FoodSaturationLevel -= 1;
+ m_FoodSaturationLevel -= 1.0;
}
else
{
- m_FoodLevel = std::max(m_FoodLevel - 1, 0);
+ SetFoodLevel(m_FoodLevel - 1);
}
}
-
- if (m_FoodLevel != LastFoodLevel)
- {
- SendHealth();
- }
}
@@ -2043,6 +2056,11 @@ void cPlayer::ApplyFoodExhaustionFromMovement()
{
return;
}
+ if (m_bIsTeleporting)
+ {
+ m_bIsTeleporting = false;
+ return;
+ }
// If riding anything, apply no food exhaustion
if (m_AttachedTo != NULL)
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index e80b82901..2053305ea 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -124,6 +124,9 @@ public:
inline double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc.
inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export
inline const cInventory & GetInventory(void) const { return m_Inventory; }
+
+ /** Gets the contents of the player's associated enderchest */
+ cItemGrid & GetEnderChestContents(void) { return m_EnderChestContents; }
inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export
@@ -402,7 +405,7 @@ public:
// cEntity overrides:
virtual bool IsCrouched (void) const { return m_IsCrouched; }
virtual bool IsSprinting(void) const { return m_IsSprinting; }
- virtual bool IsRclking (void) const { return IsEating(); }
+ virtual bool IsRclking (void) const { return IsEating() || IsChargingBow(); }
virtual void Detach(void);
@@ -444,7 +447,13 @@ protected:
float m_LastGroundHeight;
bool m_bTouchGround;
double m_Stance;
+
+ /** Stores the player's inventory, consisting of crafting grid, hotbar, and main slots */
cInventory m_Inventory;
+
+ /** An item grid that stores the player specific enderchest contents */
+ cItemGrid m_EnderChestContents;
+
cWindow * m_CurrentWindow;
cWindow * m_InventoryWindow;
@@ -505,8 +514,6 @@ protected:
cStatManager m_Stats;
-
-
/** Sets the speed and sends it to the client, so that they are forced to move so. */
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override;
@@ -541,6 +548,11 @@ protected:
Default save interval is #defined in PLAYER_INVENTORY_SAVE_INTERVAL */
unsigned int m_TicksUntilNextSave;
+ /** Flag used by food handling system to determine whether a teleport has just happened
+ Will not apply food penalties if found to be true; will set to false after processing
+ */
+ bool m_bIsTeleporting;
+
} ; // tolua_export
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 664f929f6..c2d97589f 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -22,6 +22,7 @@
#include "FireworkEntity.h"
#include "GhastFireballEntity.h"
#include "WitherSkullEntity.h"
+#include "Player.h"
@@ -68,16 +69,17 @@ protected:
if (cBlockInfo::IsSolid(a_BlockType))
{
- // The projectile hit a solid block
- // Calculate the exact hit coords:
- cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1);
- Vector3d Line1 = m_Projectile->GetPosition();
- Vector3d Line2 = Line1 + m_Projectile->GetSpeed();
- double LineCoeff = 0;
- eBlockFace Face;
- if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face))
+ // The projectile hit a solid block, calculate the exact hit coords:
+ cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1); // Bounding box of the block hit
+ const Vector3d LineStart = m_Projectile->GetPosition(); // Start point for the imaginary line that goes through the block hit
+ const Vector3d LineEnd = LineStart + m_Projectile->GetSpeed(); // End point for the imaginary line that goes through the block hit
+ double LineCoeff = 0; // Used to calculate where along the line an intersection with the bounding box occurs
+ eBlockFace Face; // Face hit
+
+ if (bb.CalcLineIntersection(LineStart, LineEnd, LineCoeff, Face))
{
- Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff;
+ Vector3d Intersection = LineStart + m_Projectile->GetSpeed() * LineCoeff; // Point where projectile goes into the hit block
+
if (cPluginManager::Get()->CallHookProjectileHitBlock(*m_Projectile, a_BlockX, a_BlockY, a_BlockZ, Face, &Intersection))
{
return false;
@@ -141,7 +143,7 @@ public:
{
if (
(a_Entity == m_Projectile) || // Do not check collisions with self
- (a_Entity == m_Projectile->GetCreator()) // Do not check whoever shot the projectile
+ (a_Entity->GetUniqueID() == m_Projectile->GetCreatorUniqueID()) // Do not check whoever shot the projectile
)
{
// TODO: Don't check creator only for the first 5 ticks
@@ -162,7 +164,12 @@ public:
return false;
}
- // TODO: Some entities don't interact with the projectiles (pickups, falling blocks)
+ if (!a_Entity->IsMob() && !a_Entity->IsMinecart() && !a_Entity->IsPlayer() && !a_Entity->IsBoat())
+ {
+ // Not an entity that interacts with a projectile
+ return false;
+ }
+
if (cPluginManager::Get()->CallHookProjectileHitEntity(*m_Projectile, *a_Entity))
{
// A plugin disagreed.
@@ -210,7 +217,10 @@ protected:
cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) :
super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height),
m_ProjectileKind(a_Kind),
- m_Creator(a_Creator),
+ m_CreatorData(
+ ((a_Creator != NULL) ? a_Creator->GetUniqueID() : -1),
+ ((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : "")
+ ),
m_IsInGround(false)
{
}
@@ -222,7 +232,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) :
super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height),
m_ProjectileKind(a_Kind),
- m_Creator(a_Creator),
+ m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : ""),
m_IsInGround(false)
{
SetSpeed(a_Speed);
@@ -300,7 +310,7 @@ AString cProjectileEntity::GetMCAClassName(void) const
case pkEgg: return "Egg";
case pkGhastFireball: return "Fireball";
case pkFireCharge: return "SmallFireball";
- case pkEnderPearl: return "ThrownEnderPearl";
+ case pkEnderPearl: return "ThrownEnderpearl";
case pkExpBottle: return "ThrownExpBottle";
case pkSplashPotion: return "ThrownPotion";
case pkWitherSkull: return "WitherSkull";
@@ -318,8 +328,9 @@ AString cProjectileEntity::GetMCAClassName(void) const
void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
-
- if (GetProjectileKind() != pkArrow) // See cArrow::Tick
+
+ // TODO: see BroadcastMovementUpdate; RelativeMove packet jerkiness affects projectiles too (cause of sympton described in cArrowEntity::Tick())
+ if (GetProjectileKind() != pkArrow)
{
BroadcastMovementUpdate();
}
@@ -337,19 +348,10 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
return;
}
- Vector3d PerTickSpeed = GetSpeed() / 20;
- Vector3d Pos = GetPosition();
-
- // Trace the tick's worth of movement as a line:
- Vector3d NextPos = Pos + PerTickSpeed;
- cProjectileTracerCallback TracerCallback(this);
- if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
- {
- // Something has been hit, abort all other processing
- return;
- }
- // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff
-
+ const Vector3d PerTickSpeed = GetSpeed() / 20;
+ const Vector3d Pos = GetPosition();
+ const Vector3d NextPos = Pos + PerTickSpeed;
+
// Test for entity collisions:
cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos);
a_Chunk.ForEachEntity(EntityCollisionCallback);
@@ -366,10 +368,19 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
HitPos.x, HitPos.y, HitPos.z,
EntityCollisionCallback.GetMinCoeff()
);
-
+
OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos);
}
// TODO: Test the entities in the neighboring chunks, too
+
+ // Trace the tick's worth of movement as a line:
+ cProjectileTracerCallback TracerCallback(this);
+ if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
+ {
+ // Something has been hit, abort all other processing
+ return;
+ }
+ // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff
// Update the position:
SetPosition(NextPos);
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
index ae06b072f..7b38169e2 100644
--- a/src/Entities/ProjectileEntity.h
+++ b/src/Entities/ProjectileEntity.h
@@ -66,8 +66,15 @@ public:
/// Returns the kind of the projectile (fast class identification)
eKind GetProjectileKind(void) const { return m_ProjectileKind; }
- /// Returns the entity who created this projectile; may be NULL
- cEntity * GetCreator(void) { return m_Creator; }
+ /** Returns the unique ID of the entity who created this projectile
+ May return an ID <0
+ */
+ int GetCreatorUniqueID(void) { return m_CreatorData.m_UniqueID; }
+
+ /** Returns the name of the player that created the projectile
+ Will be empty for non-player creators
+ */
+ AString GetCreatorName(void) const { return m_CreatorData.m_Name; }
/// Returns the string that is used as the entity type (class name) in MCA files
AString GetMCAClassName(void) const;
@@ -81,10 +88,29 @@ public:
void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; }
protected:
+
+ /** A structure that stores the Entity ID and Playername of the projectile's creator
+ Used to migitate invalid pointers caused by the creator being destroyed
+ */
+ struct CreatorData
+ {
+ CreatorData(int a_UniqueID, const AString & a_Name) :
+ m_UniqueID(a_UniqueID),
+ m_Name(a_Name)
+ {
+ }
+
+ const int m_UniqueID;
+ AString m_Name;
+ };
+
+ /** The type of projectile I am */
eKind m_ProjectileKind;
- /// The entity who has created this projectile; may be NULL (e. g. for dispensers)
- cEntity * m_Creator;
+ /** The structure for containing the entity ID and name who has created this projectile
+ The ID and/or name may be NULL (e.g. for dispensers/mobs)
+ */
+ CreatorData m_CreatorData;
/// True if the projectile has hit the ground and is stuck there
bool m_IsInGround;
diff --git a/src/Entities/ThrownEggEntity.cpp b/src/Entities/ThrownEggEntity.cpp
index 224019091..456083108 100644
--- a/src/Entities/ThrownEggEntity.cpp
+++ b/src/Entities/ThrownEggEntity.cpp
@@ -8,7 +8,8 @@
cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
- super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+ super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
+ m_DestroyTimer(-1)
{
SetSpeed(a_Speed);
}
@@ -21,7 +22,7 @@ void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_H
{
TrySpawnChicken(a_HitPos);
- Destroy();
+ m_DestroyTimer = 2;
}
@@ -36,7 +37,7 @@ void cThrownEggEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_Hit
TrySpawnChicken(a_HitPos);
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
- Destroy(true);
+ m_DestroyTimer = 5;
}
diff --git a/src/Entities/ThrownEggEntity.h b/src/Entities/ThrownEggEntity.h
index 5ba8f051b..dc72c279f 100644
--- a/src/Entities/ThrownEggEntity.h
+++ b/src/Entities/ThrownEggEntity.h
@@ -30,8 +30,29 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
+ virtual void Tick (float a_Dt, cChunk & a_Chunk) override
+ {
+ if (m_DestroyTimer > 0)
+ {
+ m_DestroyTimer--;
+ if (m_DestroyTimer == 0)
+ {
+ Destroy();
+ return;
+ }
+ }
+ else
+ {
+ super::Tick(a_Dt, a_Chunk);
+ }
+ }
// Randomly decides whether to spawn a chicken where the egg lands.
void TrySpawnChicken(const Vector3d & a_HitPos);
+
+private:
+
+ /** Time in ticks to wait for the hit animation to begin before destroying */
+ int m_DestroyTimer;
} ; // tolua_export
diff --git a/src/Entities/ThrownEnderPearlEntity.cpp b/src/Entities/ThrownEnderPearlEntity.cpp
index c37161145..c7407e6ae 100644
--- a/src/Entities/ThrownEnderPearlEntity.cpp
+++ b/src/Entities/ThrownEnderPearlEntity.cpp
@@ -1,13 +1,15 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "ThrownEnderPearlEntity.h"
+#include "Player.h"
cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
- super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+ super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
+ m_DestroyTimer(-1)
{
SetSpeed(a_Speed);
}
@@ -21,7 +23,7 @@ void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockF
// TODO: Tweak a_HitPos based on block face.
TeleportCreator(a_HitPos);
- Destroy();
+ m_DestroyTimer = 2;
}
@@ -36,7 +38,7 @@ void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d
TeleportCreator(a_HitPos);
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
- Destroy(true);
+ m_DestroyTimer = 5;
}
@@ -45,10 +47,34 @@ void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d
void cThrownEnderPearlEntity::TeleportCreator(const Vector3d & a_HitPos)
{
- // Teleport the creator here, make them take 5 damage:
- if (m_Creator != NULL)
+ if (m_CreatorData.m_Name.empty())
{
- m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5);
- m_Creator->TakeDamage(dtEnderPearl, this, 5, 0);
+ return;
}
+
+ class cProjectileCreatorCallbackForPlayers : public cPlayerListCallback
+ {
+ public:
+ cProjectileCreatorCallbackForPlayers(cEntity * a_Attacker, Vector3i a_HitPos) :
+ m_Attacker(a_Attacker),
+ m_HitPos(a_HitPos)
+ {
+ }
+
+ virtual bool Item(cPlayer * a_Entity) override
+ {
+ // Teleport the creator here, make them take 5 damage:
+ a_Entity->TeleportToCoords(m_HitPos.x, m_HitPos.y + 0.2, m_HitPos.z);
+ a_Entity->TakeDamage(dtEnderPearl, m_Attacker, 5, 0);
+ return true;
+ }
+
+ private:
+
+ cEntity * m_Attacker;
+ Vector3i m_HitPos;
+ };
+
+ cProjectileCreatorCallbackForPlayers PCCFP(this, a_HitPos);
+ GetWorld()->FindAndDoWithPlayer(m_CreatorData.m_Name, PCCFP);
}
diff --git a/src/Entities/ThrownEnderPearlEntity.h b/src/Entities/ThrownEnderPearlEntity.h
index ddee5babe..1cea5f7d9 100644
--- a/src/Entities/ThrownEnderPearlEntity.h
+++ b/src/Entities/ThrownEnderPearlEntity.h
@@ -30,8 +30,29 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
-
- // Teleports the creator where the ender pearl lands.
+ virtual void Tick (float a_Dt, cChunk & a_Chunk) override
+ {
+ if (m_DestroyTimer > 0)
+ {
+ m_DestroyTimer--;
+ if (m_DestroyTimer == 0)
+ {
+ Destroy();
+ return;
+ }
+ }
+ else
+ {
+ super::Tick(a_Dt, a_Chunk);
+ }
+ }
+
+ /** Teleports the creator where the ender pearl lands */
void TeleportCreator(const Vector3d & a_HitPos);
+
+private:
+
+ /** Time in ticks to wait for the hit animation to begin before destroying */
+ int m_DestroyTimer;
} ; // tolua_export
diff --git a/src/Entities/ThrownSnowballEntity.cpp b/src/Entities/ThrownSnowballEntity.cpp
index 427f630f7..d94e75898 100644
--- a/src/Entities/ThrownSnowballEntity.cpp
+++ b/src/Entities/ThrownSnowballEntity.cpp
@@ -8,7 +8,8 @@
cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
- super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+ super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
+ m_DestroyTimer(-1)
{
SetSpeed(a_Speed);
}
@@ -19,7 +20,7 @@ cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, do
void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
- Destroy();
+ m_DestroyTimer = 2;
}
@@ -36,13 +37,9 @@ void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d &
{
TotalDamage = 3;
}
- else if (MobType == cMonster::mtEnderDragon)
- {
- TotalDamage = 1;
- }
}
// TODO: If entity is Ender Crystal, destroy it
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
- Destroy(true);
+ m_DestroyTimer = 5;
}
diff --git a/src/Entities/ThrownSnowballEntity.h b/src/Entities/ThrownSnowballEntity.h
index a09512e37..9a8770379 100644
--- a/src/Entities/ThrownSnowballEntity.h
+++ b/src/Entities/ThrownSnowballEntity.h
@@ -30,5 +30,26 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
+ virtual void Tick (float a_Dt, cChunk & a_Chunk) override
+ {
+ if (m_DestroyTimer > 0)
+ {
+ m_DestroyTimer--;
+ if (m_DestroyTimer == 0)
+ {
+ Destroy();
+ return;
+ }
+ }
+ else
+ {
+ super::Tick(a_Dt, a_Chunk);
+ }
+ }
+
+private:
+
+ /** Time in ticks to wait for the hit animation to begin before destroying */
+ int m_DestroyTimer;
} ; // tolua_export