summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/Entity.cpp134
-rw-r--r--src/Entities/Entity.h38
-rw-r--r--src/Entities/Minecart.cpp474
-rw-r--r--src/Entities/Minecart.h24
-rw-r--r--src/Entities/Pickup.cpp67
-rw-r--r--src/Entities/Player.cpp302
-rw-r--r--src/Entities/Player.h47
-rw-r--r--src/Entities/ProjectileEntity.cpp5
8 files changed, 751 insertions, 340 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 565c78dfd..e22f689d9 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -4,9 +4,7 @@
#include "../World.h"
#include "../Server.h"
#include "../Root.h"
-#include "../Vector3d.h"
#include "../Matrix4f.h"
-#include "../ReferenceManager.h"
#include "../ClientHandle.h"
#include "../Chunk.h"
#include "../Simulator/FluidSimulator.h"
@@ -32,8 +30,6 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_MaxHealth(1)
, m_AttachedTo(NULL)
, m_Attachee(NULL)
- , m_Referencers(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCERS))
- , m_References(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCES))
, m_bDirtyHead(true)
, m_bDirtyOrientation(true)
, m_bDirtyPosition(true)
@@ -61,6 +57,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_Mass (0.001) // Default 1g
, m_Width(a_Width)
, m_Height(a_Height)
+ , m_IsSubmerged(false)
+ , m_IsSwimming(false)
{
cCSLock Lock(m_CSCount);
m_EntityCount++;
@@ -73,7 +71,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
cEntity::~cEntity()
{
- ASSERT(!m_World->HasEntity(m_UniqueID)); // Before deleting, the entity needs to have been removed from the world
+ // Before deleting, the entity needs to have been removed from the world, if ever added
+ ASSERT((m_World == NULL) || !m_World->HasEntity(m_UniqueID));
/*
// DEBUG:
@@ -99,8 +98,6 @@ cEntity::~cEntity()
LOGWARNING("ERROR: Entity deallocated without being destroyed");
ASSERT(!"Entity deallocated without being destroyed or unlinked");
}
- delete m_Referencers;
- delete m_References;
}
@@ -534,7 +531,17 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
TickInVoid(a_Chunk);
}
- else { m_TicksSinceLastVoidDamage = 0; }
+ else
+ m_TicksSinceLastVoidDamage = 0;
+
+ if (IsMob() || IsPlayer())
+ {
+ // Set swimming state
+ SetSwimState(a_Chunk);
+
+ // Handle drowning
+ HandleAir();
+ }
}
@@ -912,6 +919,87 @@ void cEntity::TickInVoid(cChunk & a_Chunk)
+void cEntity::SetSwimState(cChunk & a_Chunk)
+{
+ int RelY = (int)floor(m_LastPosY + 0.1);
+ if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
+ {
+ m_IsSwimming = false;
+ m_IsSubmerged = false;
+ return;
+ }
+
+ BLOCKTYPE BlockIn;
+ int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
+
+ // Check if the player is swimming:
+ // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
+ if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn))
+ {
+ // This sometimes happens on Linux machines
+ // Ref.: http://forum.mc-server.org/showthread.php?tid=1244
+ LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}",
+ RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ()
+ );
+ m_IsSwimming = false;
+ m_IsSubmerged = false;
+ return;
+ }
+ m_IsSwimming = IsBlockWater(BlockIn);
+
+ // Check if the player is submerged:
+ VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn));
+ m_IsSubmerged = IsBlockWater(BlockIn);
+}
+
+
+
+
+
+void cEntity::HandleAir(void)
+{
+ // Ref.: http://www.minecraftwiki.net/wiki/Chunk_format
+ // See if the entity is /submerged/ water (block above is water)
+ // Get the type of block the entity is standing in:
+
+ if (IsSubmerged())
+ {
+ SetSpeedY(1); // Float in the water
+
+ // Either reduce air level or damage player
+ if (m_AirLevel < 1)
+ {
+ if (m_AirTickTimer < 1)
+ {
+ // Damage player
+ TakeDamage(dtDrowning, NULL, 1, 1, 0);
+ // Reset timer
+ m_AirTickTimer = DROWNING_TICKS;
+ }
+ else
+ {
+ m_AirTickTimer -= 1;
+ }
+ }
+ else
+ {
+ // Reduce air supply
+ m_AirLevel -= 1;
+ }
+ }
+ else
+ {
+ // Set the air back to maximum
+ m_AirLevel = MAX_AIR_LEVEL;
+ m_AirTickTimer = DROWNING_TICKS;
+ }
+}
+
+
+
+
+
/// Called when the entity starts burning
void cEntity::OnStartedBurning(void)
{
@@ -1429,33 +1517,3 @@ void cEntity::SetPosZ(double a_PosZ)
-
-//////////////////////////////////////////////////////////////////////////
-// Reference stuffs
-void cEntity::AddReference(cEntity * & a_EntityPtr)
-{
- m_References->AddReference(a_EntityPtr);
- a_EntityPtr->ReferencedBy(a_EntityPtr);
-}
-
-
-
-
-
-void cEntity::ReferencedBy(cEntity * & a_EntityPtr)
-{
- m_Referencers->AddReference(a_EntityPtr);
-}
-
-
-
-
-
-void cEntity::Dereference(cEntity * & a_EntityPtr)
-{
- m_Referencers->Dereference(a_EntityPtr);
-}
-
-
-
-
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 91463bfd6..b2edfc2b4 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -4,6 +4,7 @@
#include "../Item.h"
#include "../Vector3d.h"
#include "../Vector3f.h"
+#include "../Vector3i.h"
@@ -28,12 +29,16 @@
return super::GetClass(); \
}
+#define POSX_TOINT (int)floor(GetPosX())
+#define POSY_TOINT (int)floor(GetPosY())
+#define POSZ_TOINT (int)floor(GetPosZ())
+#define POS_TOINT Vector3i(POSXTOINT, POSYTOINT, POSZTOINT)
+
class cWorld;
-class cReferenceManager;
class cClientHandle;
class cPlayer;
class cChunk;
@@ -110,6 +115,8 @@ public:
BURN_TICKS_PER_DAMAGE = 20, ///< How many ticks to wait between damaging an entity when it is burning
BURN_DAMAGE = 1, ///< How much damage to deal when the entity is burning
BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire
+ MAX_AIR_LEVEL = 300, ///< Maximum air an entity can have
+ DROWNING_TICKS = 20, ///< Number of ticks per heart of damage
} ;
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
@@ -344,7 +351,14 @@ public:
virtual bool IsRiding (void) const {return false; }
virtual bool IsSprinting(void) const {return false; }
virtual bool IsRclking (void) const {return false; }
- virtual bool IsInvisible(void) const {return false; }
+ virtual bool IsInvisible(void) const { return false; }
+
+ /** Returns whether the player is swimming or not */
+ virtual bool IsSwimming(void) const{ return m_IsSwimming; }
+ /** Return whether the player is under water or not */
+ virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
+ /** Gets remaining air of a monster */
+ int GetAirLevel(void) const { return m_AirLevel; }
// tolua_end
@@ -373,9 +387,6 @@ protected:
/// The entity which is attached to this entity (rider), NULL if none
cEntity * m_Attachee;
- cReferenceManager* m_Referencers;
- cReferenceManager* m_References;
-
// Flags that signal that we haven't updated the clients with the latest.
bool m_bDirtyHead;
bool m_bDirtyOrientation;
@@ -415,11 +426,18 @@ protected:
virtual void Destroyed(void) {} // Called after the entity has been destroyed
void SetWorld(cWorld * a_World) { m_World = a_World; }
-
- friend class cReferenceManager;
- void AddReference( cEntity*& a_EntityPtr );
- void ReferencedBy( cEntity*& a_EntityPtr );
- void Dereference( cEntity*& a_EntityPtr );
+
+ /** Called in each tick to handle air-related processing i.e. drowning */
+ virtual void HandleAir();
+ /** Called once per tick to set IsSwimming and IsSubmerged */
+ virtual void SetSwimState(cChunk & a_Chunk);
+
+ /** If an entity is currently swimming in or submerged under water */
+ bool m_IsSwimming, m_IsSubmerged;
+
+ /** Air level of a mobile */
+ int m_AirLevel;
+ int m_AirTickTimer;
private:
// Measured in degrees, [-180, +180)
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index d0d384481..a650927b1 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -19,6 +19,70 @@
+class cMinecartCollisionCallback :
+ public cEntityCallback
+{
+public:
+ cMinecartCollisionCallback(Vector3d a_Pos, double a_Height, double a_Width, int a_UniqueID, int a_AttacheeUniqueID) :
+ m_Pos(a_Pos),
+ m_Height(a_Height),
+ m_Width(a_Width),
+ m_DoesInteserct(false),
+ m_CollidedEntityPos(0, 0, 0),
+ m_UniqueID(a_UniqueID),
+ m_AttacheeUniqueID(a_AttacheeUniqueID)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ ASSERT(a_Entity != NULL);
+
+ if (!a_Entity->IsPlayer() && !a_Entity->IsMob() && !a_Entity->IsMinecart() && !a_Entity->IsBoat())
+ {
+ return false;
+ }
+ else if ((a_Entity->GetUniqueID() == m_UniqueID) || (a_Entity->GetUniqueID() == m_AttacheeUniqueID))
+ {
+ return false;
+ }
+
+ cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
+ cBoundingBox bbMinecart(Vector3d(m_Pos.x, floor(m_Pos.y), m_Pos.z), m_Width / 2, m_Height);
+
+ if (bbEntity.DoesIntersect(bbMinecart))
+ {
+ m_CollidedEntityPos = a_Entity->GetPosition();
+ m_DoesInteserct = true;
+ return true;
+ }
+ return false;
+ }
+
+ bool FoundIntersection(void) const
+ {
+ return m_DoesInteserct;
+ }
+
+ Vector3d GetCollidedEntityPosition(void) const
+ {
+ return m_CollidedEntityPos;
+ }
+
+protected:
+ bool m_DoesInteserct;
+
+ Vector3d m_CollidedEntityPos;
+
+ Vector3d m_Pos;
+ double m_Height, m_Width;
+ int m_UniqueID;
+ int m_AttacheeUniqueID;
+};
+
+
+
+
cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7),
@@ -30,7 +94,7 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
SetMass(20.f);
SetMaxHealth(6);
SetHealth(6);
- SetWidth(1.2);
+ SetWidth(1);
SetHeight(0.9);
}
@@ -96,10 +160,17 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
if (IsBlockRail(InsideType)) AddPosY(1); // Push cart upwards
}
+ bool WasDetectorRail = false;
if (IsBlockRail(InsideType))
{
- bool WasDetectorRail = false;
- SnapToRail(InsideMeta);
+ if (InsideType == E_BLOCK_RAIL)
+ {
+ SnapToRail(InsideMeta);
+ }
+ else
+ {
+ SnapToRail(InsideMeta & 0x07);
+ }
switch (InsideType)
{
@@ -116,12 +187,6 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
AddPosition(GetSpeed() * (a_Dt / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp
-
- if (m_bIsOnDetectorRail && !WasDetectorRail)
- {
- m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
- m_bIsOnDetectorRail = false;
- }
}
else
{
@@ -129,6 +194,17 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
SetPosY(floor(GetPosY()) + 0.35); // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail
super::HandlePhysics(a_Dt, *Chunk);
}
+
+ if (m_bIsOnDetectorRail && !Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())).Equals(m_DetectorRailPosition))
+ {
+ m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
+ m_bIsOnDetectorRail = false;
+ }
+ else if (WasDetectorRail)
+ {
+ m_bIsOnDetectorRail = true;
+ m_DetectorRailPosition = Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
+ }
// Broadcast positioning changes to client
BroadcastMovementUpdate();
@@ -154,7 +230,9 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
SetSpeedY(0); // Don't move vertically as on ground
SetSpeedX(0); // Correct diagonal movement from curved rails
- if (TestBlockCollision(a_RailMeta)) return;
+ // Execute both the entity and block collision checks
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
if (GetSpeedZ() != 0) // Don't do anything if cart is stationary
{
@@ -178,7 +256,8 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
SetSpeedY(0);
SetSpeedZ(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
if (GetSpeedX() != 0)
{
@@ -281,94 +360,46 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
{
SetYaw(315); // Set correct rotation server side
SetPosY(floor(GetPosY()) + 0.55); // Levitate dat cart
+ SetSpeedY(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
+
+ // SnapToRail handles turning
- if (GetSpeedZ() > 0) // Cart moving south
- {
- int OldX = (int)floor(GetPosX());
- AddSpeedX(-GetSpeedZ() + 0.5); // See below
- AddPosX(-GetSpeedZ() * (a_Dt / 1000)); // Diagonally move southwest (which will make cart hit a southwest rail)
- // If we are already at southwest rail, set Z speed to zero as we can be moving so fast, MCS doesn't tick fast enough to active the handle for the rail...
- // ...and so we derail unexpectedly.
- if (GetPosX() <= OldX - 1) SetSpeedZ(0);
- }
- else if (GetSpeedX() > 0) // Cart moving east
- {
- int OldZ = (int)floor(GetPosZ());
- AddSpeedZ(-GetSpeedX() + 0.5);
- AddPosZ(-GetSpeedX() * (a_Dt / 1000)); // Diagonally move northeast
- if (GetPosZ() <= OldZ - 1) SetSpeedX(0);
- }
break;
}
case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST
{
SetYaw(225);
SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
- if (GetSpeedZ() > 0)
- {
- int OldX = (int)floor(GetPosX());
- AddSpeedX(GetSpeedZ() - 0.5);
- AddPosX(GetSpeedZ() * (a_Dt / 1000));
- if (GetPosX() >= OldX + 1) SetSpeedZ(0);
- }
- else if (GetSpeedX() < 0)
- {
- int OldZ = (int)floor(GetPosZ());
- AddSpeedZ(GetSpeedX() + 0.5);
- AddPosZ(GetSpeedX() * (a_Dt / 1000));
- if (GetPosZ() <= OldZ - 1) SetSpeedX(0);
- }
break;
}
case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST
{
SetYaw(135);
SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
- if (GetSpeedZ() < 0)
- {
- int OldX = (int)floor(GetPosX());
- AddSpeedX(GetSpeedZ() + 0.5);
- AddPosX(GetSpeedZ() * (a_Dt / 1000));
- if (GetPosX() <= OldX - 1) SetSpeedZ(0);
- }
- else if (GetSpeedX() > 0)
- {
- int OldZ = (int)floor(GetPosZ());
- AddSpeedZ(GetSpeedX() - 0.5);
- AddPosZ(GetSpeedX() * (a_Dt / 1000));
- if (GetPosZ() >= OldZ + 1) SetSpeedX(0);
- }
break;
}
case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST
{
SetYaw(45);
SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
- if (GetSpeedZ() < 0)
- {
- int OldX = (int)floor(GetPosX());
- AddSpeedX(-GetSpeedZ() - 0.5);
- AddPosX(-GetSpeedZ() * (a_Dt / 1000));
- if (GetPosX() >= OldX + 1) SetSpeedZ(0);
- }
- else if (GetSpeedX() < 0)
- {
- int OldZ = (int)floor(GetPosZ());
- AddSpeedZ(-GetSpeedX() - 0.5);
- AddPosZ(-GetSpeedX() * (a_Dt / 1000));
- if (GetPosZ() >= OldZ + 1) SetSpeedX(0);
- }
break;
}
default:
@@ -385,8 +416,8 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
{
// Initialise to 'slow down' values
- int AccelDecelSpeed = -1;
- int AccelDecelNegSpeed = 1;
+ int AccelDecelSpeed = -2;
+ int AccelDecelNegSpeed = 2;
if ((a_RailMeta & 0x8) == 0x8)
{
@@ -404,17 +435,18 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedY(0);
SetSpeedX(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
if (GetSpeedZ() != 0)
{
if (GetSpeedZ() > 0)
{
- AddSpeedZ(AccelDecelNegSpeed);
+ AddSpeedZ(AccelDecelSpeed);
}
else
{
- AddSpeedZ(AccelDecelSpeed);
+ AddSpeedZ(AccelDecelNegSpeed);
}
}
break;
@@ -426,7 +458,8 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedY(0);
SetSpeedZ(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
if (GetSpeedX() != 0)
{
@@ -441,6 +474,87 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
}
break;
}
+ case E_META_RAIL_ASCEND_XM: // ASCEND EAST
+ {
+ SetYaw(180);
+ SetSpeedZ(0);
+
+ if (GetSpeedX() >= 0)
+ {
+ if (GetSpeedX() <= MAX_SPEED)
+ {
+ AddSpeedX(AccelDecelSpeed);
+ SetSpeedY(-GetSpeedX());
+ }
+ }
+ else
+ {
+ AddSpeedX(AccelDecelNegSpeed);
+ SetSpeedY(-GetSpeedX());
+ }
+ break;
+ }
+ case E_META_RAIL_ASCEND_XP: // ASCEND WEST
+ {
+ SetYaw(180);
+ SetSpeedZ(0);
+
+ if (GetSpeedX() > 0)
+ {
+ AddSpeedX(AccelDecelSpeed);
+ SetSpeedY(GetSpeedX());
+ }
+ else
+ {
+ if (GetSpeedX() >= MAX_SPEED_NEGATIVE)
+ {
+ AddSpeedX(AccelDecelNegSpeed);
+ SetSpeedY(GetSpeedX());
+ }
+ }
+ break;
+ }
+ case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
+ {
+ SetYaw(270);
+ SetSpeedX(0);
+
+ if (GetSpeedZ() >= 0)
+ {
+ if (GetSpeedZ() <= MAX_SPEED)
+ {
+ AddSpeedZ(AccelDecelSpeed);
+ SetSpeedY(-GetSpeedZ());
+ }
+ }
+ else
+ {
+ AddSpeedZ(AccelDecelNegSpeed);
+ SetSpeedY(-GetSpeedZ());
+ }
+ break;
+ }
+ case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
+ {
+ SetYaw(270);
+ SetSpeedX(0);
+
+ if (GetSpeedZ() > 0)
+ {
+ AddSpeedZ(AccelDecelSpeed);
+ SetSpeedY(GetSpeedZ());
+ }
+ else
+ {
+ if (GetSpeedZ() >= MAX_SPEED_NEGATIVE)
+ {
+ AddSpeedZ(AccelDecelNegSpeed);
+ SetSpeedY(GetSpeedZ());
+ }
+ }
+ break;
+ }
+ default: ASSERT(!"Unhandled powered rail metadata!"); break;
}
}
@@ -450,11 +564,17 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
{
- m_bIsOnDetectorRail = true;
- m_DetectorRailPosition = Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
-
m_World->SetBlockMeta(m_DetectorRailPosition, a_RailMeta | 0x08);
+ // No special handling
+ HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
+}
+
+
+
+
+void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
+{
HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
}
@@ -482,6 +602,107 @@ void cMinecart::SnapToRail(NIBBLETYPE a_RailMeta)
SetPosX(floor(GetPosX()) + 0.5);
break;
}
+ // Curved rail physics: once minecart has reached more than half of the block in the direction that it is travelling in, jerk it in the direction of curvature
+ case E_META_RAIL_CURVED_ZM_XM:
+ {
+ if (GetPosZ() > floor(GetPosZ()) + 0.5)
+ {
+ if (GetSpeedZ() > 0)
+ {
+ SetSpeedX(-GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ }
+ else if (GetPosX() > floor(GetPosX()) + 0.5)
+ {
+ if (GetSpeedX() > 0)
+ {
+ SetSpeedZ(-GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
+ case E_META_RAIL_CURVED_ZM_XP:
+ {
+ if (GetPosZ() > floor(GetPosZ()) + 0.5)
+ {
+ if (GetSpeedZ() > 0)
+ {
+ SetSpeedX(GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ }
+ else if (GetPosX() < floor(GetPosX()) + 0.5)
+ {
+ if (GetSpeedX() < 0)
+ {
+ SetSpeedZ(GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
+ case E_META_RAIL_CURVED_ZP_XM:
+ {
+ if (GetPosZ() < floor(GetPosZ()) + 0.5)
+ {
+ if (GetSpeedZ() < 0)
+ {
+ SetSpeedX(GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ }
+ else if (GetPosX() > floor(GetPosX()) + 0.5)
+ {
+ if (GetSpeedX() > 0)
+ {
+ SetSpeedZ(GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
+ case E_META_RAIL_CURVED_ZP_XP:
+ {
+ if (GetPosZ() < floor(GetPosZ()) + 0.5)
+ {
+ if (GetSpeedZ() < 0)
+ {
+ SetSpeedX(-GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ }
+ else if (GetPosX() < floor(GetPosX()) + 0.5)
+ {
+ if (GetSpeedX() < 0)
+ {
+ SetSpeedZ(-GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
default: break;
}
}
@@ -513,7 +734,7 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
}
}
}
- else
+ else if (GetSpeedZ() < 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1);
if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
@@ -549,7 +770,7 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
}
}
}
- else
+ else if (GetSpeedX() < 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ()));
if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
@@ -597,10 +818,93 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
+bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
+{
+ cMinecartCollisionCallback MinecartCollisionCallback(GetPosition(), GetHeight(), GetWidth(), GetUniqueID(), ((m_Attachee == NULL) ? -1 : m_Attachee->GetUniqueID()));
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk((int)floor(GetPosX()), (int)floor(GetPosZ()), ChunkX, ChunkZ);
+ m_World->ForEachEntityInChunk(ChunkX, ChunkZ, MinecartCollisionCallback);
+
+ if (!MinecartCollisionCallback.FoundIntersection())
+ {
+ return false;
+ }
+
+ switch (a_RailMeta)
+ {
+ case E_META_RAIL_ZM_ZP:
+ {
+ if (MinecartCollisionCallback.GetCollidedEntityPosition().z >= GetPosZ())
+ {
+ if ((-GetSpeedZ() * 0.4) < 0.01)
+ {
+ AddSpeedZ(-4);
+ }
+ else
+ {
+ SetSpeedZ(-GetSpeedZ() * 0.4);
+ }
+ }
+ else
+ {
+ if ((GetSpeedZ() * 0.4) < 0.01)
+ {
+ AddSpeedZ(4);
+ }
+ else
+ {
+ SetSpeedZ(GetSpeedZ() * 0.4);
+ }
+ }
+ return true;
+ }
+ case E_META_RAIL_XM_XP:
+ {
+ if (MinecartCollisionCallback.GetCollidedEntityPosition().x >= GetPosX())
+ {
+ if ((-GetSpeedX() * 0.4) < 0.01)
+ {
+ AddSpeedX(-4);
+ }
+ else
+ {
+ SetSpeedX(-GetSpeedX() * 0.4);
+ }
+ }
+ else
+ {
+ if ((GetSpeedX() * 0.4) < 0.01)
+ {
+ AddSpeedX(4);
+ }
+ else
+ {
+ SetSpeedX(GetSpeedX() * 0.4);
+ }
+ }
+ return true;
+ }
+ case E_META_RAIL_CURVED_ZM_XM:
+ case E_META_RAIL_CURVED_ZM_XP:
+ case E_META_RAIL_CURVED_ZP_XM:
+ case E_META_RAIL_CURVED_ZP_XP:
+ {
+ // TODO - simply can't be bothered right now
+ break;
+ }
+ default: break;
+ }
+
+ return false;
+}
+
+
+
+
void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
{
- if (TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative())
+ if ((TDI.Attacker != NULL) && TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative())
{
Destroy();
TDI.FinalDamage = GetMaxHealth(); // Instant hit for creative
diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h
index 874d0204e..073e78953 100644
--- a/src/Entities/Minecart.h
+++ b/src/Entities/Minecart.h
@@ -15,20 +15,6 @@
-inline bool IsBlockRail(BLOCKTYPE a_BlockType)
- {
- return (
- (a_BlockType == E_BLOCK_RAIL) ||
- (a_BlockType == E_BLOCK_ACTIVATOR_RAIL) ||
- (a_BlockType == E_BLOCK_DETECTOR_RAIL) ||
- (a_BlockType == E_BLOCK_POWERED_RAIL)
- ) ;
- }
-
-
-
-
-
class cMinecart :
public cEntity
{
@@ -79,10 +65,16 @@ protected:
*/
void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
- /** Snaps a minecart to a rail's axis, resetting its speed */
+ /** Handles activator rails - placeholder for future implementation */
+ void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
+
+ /** Snaps a mincecart to a rail's axis, resetting its speed
+ For curved rails, it changes the cart's direction as well as snapping it to axis */
void SnapToRail(NIBBLETYPE a_RailMeta);
- /** Tests is a solid block is in front of a cart, and stops the cart (and returns true) if so; returns false if no obstruction*/
+ /** Tests if a solid block is in front of a cart, and stops the cart (and returns true) if so; returns false if no obstruction */
bool TestBlockCollision(NIBBLETYPE a_RailMeta);
+ /** Tests if this mincecart's bounding box is intersecting another entity's bounding box (collision) and pushes mincecart away */
+ bool TestEntityCollision(NIBBLETYPE a_RailMeta);
} ;
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index 001e386a7..bfe162b69 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -6,19 +6,58 @@
#endif
#include "Pickup.h"
+#include "Player.h"
#include "../ClientHandle.h"
-#include "../Inventory.h"
#include "../World.h"
-#include "../Simulator/FluidSimulator.h"
#include "../Server.h"
-#include "Player.h"
#include "../Bindings/PluginManager.h"
-#include "../Item.h"
#include "../Root.h"
#include "../Chunk.h"
-#include "../Vector3d.h"
-#include "../Vector3f.h"
+
+
+
+class cPickupCombiningCallback :
+ public cEntityCallback
+{
+public:
+ cPickupCombiningCallback(Vector3d a_Position, cPickup * a_Pickup) :
+ m_Position(a_Position),
+ m_Pickup(a_Pickup),
+ m_FoundMatchingPickup(false)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (!a_Entity->IsPickup() || (a_Entity->GetUniqueID() == m_Pickup->GetUniqueID()) || a_Entity->IsDestroyed())
+ {
+ return false;
+ }
+
+ Vector3d EntityPos = a_Entity->GetPosition();
+ double Distance = (EntityPos - m_Position).Length();
+
+ if ((Distance < 1.2) && ((cPickup *)a_Entity)->GetItem().IsEqual(m_Pickup->GetItem()))
+ {
+ m_Pickup->GetItem().AddCount(((cPickup *)a_Entity)->GetItem().m_ItemCount);
+ a_Entity->Destroy();
+ m_FoundMatchingPickup = true;
+ }
+ return false;
+ }
+
+ inline bool FoundMatchingPickup()
+ {
+ return m_FoundMatchingPickup;
+ }
+
+protected:
+ bool m_FoundMatchingPickup;
+
+ Vector3d m_Position;
+ cPickup * m_Pickup;
+};
@@ -26,10 +65,10 @@
cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */)
: cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2)
- , m_Timer( 0.f )
+ , m_Timer(0.f)
, m_Item(a_Item)
- , m_bCollected( false )
- , m_bIsPlayerCreated( IsPlayerCreated )
+ , m_bCollected(false)
+ , m_bIsPlayerCreated(IsPlayerCreated)
{
SetGravity(-10.5f);
SetMaxHealth(5);
@@ -89,6 +128,16 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
return;
}
}
+
+ if (!IsDestroyed()) // Don't try to combine if someone has tried to combine me
+ {
+ cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
+ m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
+ if (PickupCombiningCallback.FoundMatchingPickup())
+ {
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ }
}
}
}
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 9a935520f..8c37fdc8d 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -7,7 +7,6 @@
#include "../UI/Window.h"
#include "../UI/WindowOwner.h"
#include "../World.h"
-#include "Pickup.h"
#include "../Bindings/PluginManager.h"
#include "../BlockEntities/BlockEntity.h"
#include "../GroupManager.h"
@@ -27,8 +26,6 @@
#include "inifile/iniFile.h"
#include "json/json.h"
-#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x))
-
@@ -36,8 +33,6 @@
cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
: super(etPlayer, 0.6, 1.8)
- , m_AirLevel( MAX_AIR_LEVEL )
- , m_AirTickTimer(DROWNING_TICKS)
, m_bVisible(true)
, m_FoodLevel(MAX_FOOD_LEVEL)
, m_FoodSaturationLevel(5)
@@ -74,6 +69,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_IsChargingBow(false)
, m_BowCharge(0)
, m_FloaterID(-1)
+ , m_Team(NULL)
{
LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
@@ -107,9 +103,23 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
);
}
+
m_LastJumpHeight = (float)(GetPosY());
m_LastGroundHeight = (float)(GetPosY());
m_Stance = GetPosY() + 1.62;
+
+ if (m_GameMode == gmNotSet)
+ {
+ cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
+ if (World == NULL)
+ {
+ World = cRoot::Get()->GetDefaultWorld();
+ }
+ if (World->IsGameModeCreative())
+ {
+ m_CanFly = true;
+ }
+ }
cRoot::Get()->GetServer()->PlayerCreated(this);
}
@@ -198,12 +208,6 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
super::Tick(a_Dt, a_Chunk);
- // Set player swimming state
- SetSwimState(a_Chunk);
-
- // Handle air drowning stuff
- HandleAir();
-
// Handle charging the bow:
if (m_IsChargingBow)
{
@@ -436,7 +440,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
cWorld * World = GetWorld();
if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height))
{
- BLOCKTYPE BlockType = World->GetBlock(float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ()));
+ BLOCKTYPE BlockType = World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
if (BlockType != E_BLOCK_AIR)
{
m_bTouchGround = true;
@@ -461,12 +465,10 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
if (Damage > 0)
{
- if (!IsGameModeCreative())
- {
- TakeDamage(dtFalling, NULL, Damage, Damage, 0);
- }
+ // cPlayer makes sure damage isn't applied in creative, no need to check here
+ TakeDamage(dtFalling, NULL, Damage, Damage, 0);
- // Mojang uses floor() to get X and Z positions, instead of just casting it to an (int)
+ // Fall particles
GetWorld()->BroadcastSoundParticleEffect(2006, (int)floor(GetPosX()), (int)GetPosY() - 1, (int)floor(GetPosZ()), Damage /* Used as particle effect speed modifier */);
}
@@ -788,10 +790,24 @@ void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (IsGameModeCreative())
{
- // No damage / health in creative mode
+ // No damage / health in creative mode if not void damage
return;
}
}
+
+ if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer()))
+ {
+ cPlayer* Attacker = (cPlayer*) a_TDI.Attacker;
+
+ if ((m_Team != NULL) && (m_Team == Attacker->m_Team))
+ {
+ if (!m_Team->AllowsFriendlyFire())
+ {
+ // Friendly fire is disabled
+ return;
+ }
+ }
+ }
super::DoTakeDamage(a_TDI);
@@ -838,6 +854,25 @@ void cPlayer::KilledBy(cEntity * a_Killer)
GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by a %s", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str(), KillerClass.c_str()));
}
+
+ class cIncrementCounterCB
+ : public cObjectiveCallback
+ {
+ AString m_Name;
+ public:
+ cIncrementCounterCB(const AString & a_Name) : m_Name(a_Name) {}
+
+ virtual bool Item(cObjective * a_Objective) override
+ {
+ a_Objective->AddScore(m_Name, 1);
+ return true;
+ }
+ } IncrementCounter (GetName());
+
+ cScoreboard & Scoreboard = m_World->GetScoreBoard();
+
+ // Update scoreboard objectives
+ Scoreboard.ForEachObjectiveWith(cObjective::E_TYPE_DEATH_COUNT, IncrementCounter);
}
@@ -918,6 +953,50 @@ bool cPlayer::IsGameModeAdventure(void) const
+void cPlayer::SetTeam(cTeam * a_Team)
+{
+ if (m_Team == a_Team)
+ {
+ return;
+ }
+
+ if (m_Team)
+ {
+ m_Team->RemovePlayer(GetName());
+ }
+
+ m_Team = a_Team;
+
+ if (m_Team)
+ {
+ m_Team->AddPlayer(GetName());
+ }
+}
+
+
+
+
+
+cTeam * cPlayer::UpdateTeam(void)
+{
+ if (m_World == NULL)
+ {
+ SetTeam(NULL);
+ }
+ else
+ {
+ cScoreboard & Scoreboard = m_World->GetScoreBoard();
+
+ SetTeam(Scoreboard.QueryPlayerTeam(GetName()));
+ }
+
+ return m_Team;
+}
+
+
+
+
+
void cPlayer::OpenWindow(cWindow * a_Window)
{
if (a_Window != m_CurrentWindow)
@@ -1344,59 +1423,68 @@ AString cPlayer::GetColor(void) const
-void cPlayer::TossItem(
- bool a_bDraggingItem,
- char a_Amount /* = 1 */,
- short a_CreateType /* = 0 */,
- short a_CreateHealth /* = 0 */
-)
+void cPlayer::TossEquippedItem(char a_Amount)
{
cItems Drops;
- if (a_CreateType != 0)
+ cItem DroppedItem(GetInventory().GetEquippedItem());
+ if (!DroppedItem.IsEmpty())
{
- // Just create item without touching the inventory (used in creative mode)
- Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth));
+ char NewAmount = a_Amount;
+ if (NewAmount > GetInventory().GetEquippedItem().m_ItemCount)
+ {
+ NewAmount = GetInventory().GetEquippedItem().m_ItemCount; // Drop only what's there
+ }
+
+ GetInventory().GetHotbarGrid().ChangeSlotCount(GetInventory().GetEquippedSlotNum() /* Returns hotbar subslot, which HotbarGrid takes */, -a_Amount);
+
+ DroppedItem.m_ItemCount = NewAmount;
+ Drops.push_back(DroppedItem);
}
- else
+
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
+}
+
+
+
+
+
+void cPlayer::TossHeldItem(char a_Amount)
+{
+ cItems Drops;
+ cItem & Item = GetDraggingItem();
+ if (!Item.IsEmpty())
{
- // Drop an item from the inventory:
- if (a_bDraggingItem)
+ char OriginalItemAmount = Item.m_ItemCount;
+ Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount);
+ Drops.push_back(Item);
+ if (OriginalItemAmount > a_Amount)
{
- cItem & Item = GetDraggingItem();
- if (!Item.IsEmpty())
- {
- char OriginalItemAmount = Item.m_ItemCount;
- Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount);
- Drops.push_back(Item);
- if (OriginalItemAmount > a_Amount)
- {
- Item.m_ItemCount = OriginalItemAmount - (char)a_Amount;
- }
- else
- {
- Item.Empty();
- }
- }
+ Item.m_ItemCount = OriginalItemAmount - a_Amount;
}
else
{
- // Else drop equipped item
- cItem DroppedItem(GetInventory().GetEquippedItem());
- if (!DroppedItem.IsEmpty())
- {
- char NewAmount = a_Amount;
- if (NewAmount > GetInventory().GetEquippedItem().m_ItemCount)
- {
- NewAmount = GetInventory().GetEquippedItem().m_ItemCount; // Drop only what's there
- }
-
- GetInventory().GetHotbarGrid().ChangeSlotCount(GetInventory().GetEquippedSlotNum() /* Returns hotbar subslot, which HotbarGrid takes */, -a_Amount);
-
- DroppedItem.m_ItemCount = NewAmount;
- Drops.push_back(DroppedItem);
- }
+ Item.Empty();
}
}
+
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
+}
+
+
+
+
+
+void cPlayer::TossPickup(const cItem & a_Item)
+{
+ cItems Drops;
+ Drops.push_back(a_Item);
+
double vX = 0, vY = 0, vZ = 0;
EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
@@ -1545,27 +1633,12 @@ bool cPlayer::LoadFromDisk()
m_CurrentXp = (short) root.get("xpCurrent", 0).asInt();
m_IsFlying = root.get("isflying", 0).asBool();
- //SetExperience(root.get("experience", 0).asInt());
-
m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
if (m_GameMode == eGameMode_Creative)
{
m_CanFly = true;
}
- else if (m_GameMode == eGameMode_NotSet)
- {
- cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
- if (World == NULL)
- {
- World = cRoot::Get()->GetDefaultWorld();
- }
-
- if (World->GetGameMode() == eGameMode_Creative)
- {
- m_CanFly = true;
- }
- }
m_Inventory.LoadFromJson(root["inventory"]);
@@ -1682,85 +1755,6 @@ void cPlayer::UseEquippedItem(void)
-void cPlayer::SetSwimState(cChunk & a_Chunk)
-{
- int RelY = (int)floor(m_LastPosY + 0.1);
- if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
- {
- m_IsSwimming = false;
- m_IsSubmerged = false;
- return;
- }
-
- BLOCKTYPE BlockIn;
- int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
- int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
-
- // Check if the player is swimming:
- // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
- if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn))
- {
- // This sometimes happens on Linux machines
- // Ref.: http://forum.mc-server.org/showthread.php?tid=1244
- LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}",
- RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ()
- );
- m_IsSwimming = false;
- m_IsSubmerged = false;
- return;
- }
- m_IsSwimming = IsBlockWater(BlockIn);
-
- // Check if the player is submerged:
- VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn));
- m_IsSubmerged = IsBlockWater(BlockIn);
-}
-
-
-
-
-
-void cPlayer::HandleAir(void)
-{
- // Ref.: http://www.minecraftwiki.net/wiki/Chunk_format
- // see if the player is /submerged/ water (block above is water)
- // Get the type of block the player's standing in:
-
- if (IsSubmerged())
- {
- // either reduce air level or damage player
- if (m_AirLevel < 1)
- {
- if (m_AirTickTimer < 1)
- {
- // damage player
- TakeDamage(dtDrowning, NULL, 1, 1, 0);
- // reset timer
- m_AirTickTimer = DROWNING_TICKS;
- }
- else
- {
- m_AirTickTimer -= 1;
- }
- }
- else
- {
- // reduce air supply
- m_AirLevel -= 1;
- }
- }
- else
- {
- // set the air back to maximum
- m_AirLevel = MAX_AIR_LEVEL;
- m_AirTickTimer = DROWNING_TICKS;
- }
-}
-
-
-
-
-
void cPlayer::HandleFood(void)
{
// Ref.: http://www.minecraftwiki.net/wiki/Hunger
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index bf3ca08e8..50f7560d6 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -13,6 +13,7 @@
class cGroup;
class cWindow;
class cClientHandle;
+class cTeam;
@@ -30,8 +31,6 @@ public:
MAX_HEALTH = 20,
MAX_FOOD_LEVEL = 20,
EATING_TICKS = 30, ///< Number of ticks it takes to eat an item
- MAX_AIR_LEVEL = 300,
- DROWNING_TICKS = 10, //number of ticks per heart of damage
} ;
// tolua_end
@@ -153,6 +152,15 @@ public:
AString GetIP(void) const { return m_IP; } // tolua_export
+ /// Returns the associated team, NULL if none
+ cTeam * GetTeam(void) { return m_Team; } // tolua_export
+
+ /// Sets the player team, NULL if none
+ void SetTeam(cTeam * a_Team);
+
+ /// Forces the player to query the scoreboard for his team
+ cTeam * UpdateTeam(void);
+
// tolua_end
void SetIP(const AString & a_IP);
@@ -214,7 +222,14 @@ public:
/// Returns the full color code to use for this player, based on their primary group or set in m_Color
AString GetColor(void) const;
- void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0);
+ /** tosses the item in the selected hotbar slot */
+ void TossEquippedItem(char a_Amount = 1);
+
+ /** tosses the item held in hand (when in UI windows) */
+ void TossHeldItem(char a_Amount = 1);
+
+ /** tosses a pickup newly created from a_Item */
+ void TossPickup(const cItem & a_Item);
/// Heals the player by the specified amount of HPs (positive only); sends health update
void Heal(int a_Health);
@@ -224,8 +239,6 @@ public:
int GetFoodTickTimer (void) const { return m_FoodTickTimer; }
double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; }
int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; }
-
- int GetAirLevel (void) const { return m_AirLevel; }
/// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore
bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); }
@@ -336,12 +349,6 @@ public:
/// If true the player can fly even when he's not in creative.
void SetCanFly(bool a_CanFly);
- /// Returns whether the player is swimming or not
- virtual bool IsSwimming(void) const{ return m_IsSwimming; }
-
- /// Return whether the player is under water or not
- virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
-
/// Returns wheter the player can fly or not.
virtual bool CanFly(void) const { return m_CanFly; }
// tolua_end
@@ -372,12 +379,6 @@ protected:
XP_TO_LEVEL30 = 825
} ;
- /// Player's air level (for swimming)
- int m_AirLevel;
-
- /// used to time ticks between damage taken via drowning/suffocation
- int m_AirTickTimer;
-
bool m_bVisible;
// Food-related variables:
@@ -414,7 +415,7 @@ protected:
float m_LastBlockActionTime;
int m_LastBlockActionCnt;
eGameMode m_GameMode;
- std::string m_IP;
+ AString m_IP;
/// The item being dragged by the cursor while in a UI window
cItem m_DraggingItem;
@@ -456,6 +457,8 @@ protected:
int m_FloaterID;
+ cTeam* m_Team;
+
void ResolvePermissions(void);
@@ -463,7 +466,7 @@ protected:
virtual void Destroyed(void);
- /// Filters out damage for creative mode
+ /// Filters out damage for creative mode/friendly fire
virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
/// Called in each tick to handle food-related processing
@@ -471,12 +474,6 @@ protected:
/// Called in each tick if the player is fishing to make sure the floater dissapears when the player doesn't have a fishing rod as equipped item.
void HandleFloater(void);
-
- /// Called in each tick to handle air-related processing i.e. drowning
- void HandleAir();
-
- /// Called once per tick to set IsSwimming and IsSubmerged
- void SetSwimState(cChunk & a_Chunk);
/// Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block)
void ApplyFoodExhaustionFromMovement();
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 12ce9a303..bffa790a3 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -679,7 +679,8 @@ super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
- // TODO: Spawn experience orbs
+ // Spawn an experience orb with a reward between 3 and 11.
+ m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
Destroy();
}
@@ -710,8 +711,6 @@ void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
SetSpeed(0, 0, 0);
SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ());
- std::cout << a_HitPos.x << " " << a_HitPos.y << " " << a_HitPos.z << std::endl;
-
m_IsInGround = true;
BroadcastMovementUpdate();