summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Entities/Boat.cpp22
-rw-r--r--src/Entities/Effects.h2
-rw-r--r--src/Entities/EnderCrystal.cpp56
-rw-r--r--src/Entities/EnderCrystal.h33
-rw-r--r--src/Entities/Entity.cpp494
-rw-r--r--src/Entities/Entity.h79
-rw-r--r--src/Entities/ExpOrb.cpp31
-rw-r--r--src/Entities/ExpOrb.h24
-rw-r--r--src/Entities/FallingBlock.cpp19
-rw-r--r--src/Entities/Floater.cpp8
-rw-r--r--src/Entities/Floater.h8
-rw-r--r--src/Entities/HangingEntity.cpp53
-rw-r--r--src/Entities/HangingEntity.h49
-rw-r--r--src/Entities/ItemFrame.cpp91
-rw-r--r--src/Entities/ItemFrame.h48
-rw-r--r--src/Entities/Minecart.cpp500
-rw-r--r--src/Entities/Minecart.h28
-rw-r--r--src/Entities/Painting.cpp54
-rw-r--r--src/Entities/Painting.h42
-rw-r--r--src/Entities/Pickup.cpp69
-rw-r--r--src/Entities/Pickup.h30
-rw-r--r--src/Entities/Player.cpp444
-rw-r--r--src/Entities/Player.h250
-rw-r--r--src/Entities/ProjectileEntity.cpp158
-rw-r--r--src/Entities/ProjectileEntity.h29
-rw-r--r--src/Entities/TNTEntity.cpp33
-rw-r--r--src/Entities/TNTEntity.h23
27 files changed, 1843 insertions, 834 deletions
diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp
index 184aeeeeb..921252253 100644
--- a/src/Entities/Boat.cpp
+++ b/src/Entities/Boat.cpp
@@ -89,10 +89,20 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
BroadcastMovementUpdate();
- SetSpeed(GetSpeed() * 0.97); // Slowly decrease the speed.
- if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())))
+
+ SetSpeed(GetSpeed() * 0.97); // Slowly decrease the speed
+
+ if ((POSY_TOINT < 0) || (POSY_TOINT > cChunkDef::Height))
+ {
+ return;
+ }
+
+ if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT)))
{
- SetSpeedY(1);
+ if (GetSpeedY() < 2)
+ {
+ AddSpeedY(0.2);
+ }
}
}
@@ -102,15 +112,13 @@ void cBoat::Tick(float a_Dt, cChunk & a_Chunk)
void cBoat::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
{
- if (GetSpeed().Length() > 7)
+ if (GetSpeed().Length() > 7.5)
{
return;
}
- Vector3d ToAddSpeed(m_Attachee->GetLookVector() * (a_Sideways * 1.5));
+ Vector3d ToAddSpeed = m_Attachee->GetLookVector() * (a_Sideways * 0.4) ;
ToAddSpeed.y = 0;
AddSpeed(ToAddSpeed);
}
-
- \ No newline at end of file
diff --git a/src/Entities/Effects.h b/src/Entities/Effects.h
index e7611847d..baf3302fb 100644
--- a/src/Entities/Effects.h
+++ b/src/Entities/Effects.h
@@ -27,4 +27,4 @@ enum ENUM_ENTITY_EFFECT
E_EFFECT_ABSORPTION = 22,
E_EFFECT_SATURATION = 23,
} ;
-// tolua_end \ No newline at end of file
+// tolua_end
diff --git a/src/Entities/EnderCrystal.cpp b/src/Entities/EnderCrystal.cpp
new file mode 100644
index 000000000..a640b236c
--- /dev/null
+++ b/src/Entities/EnderCrystal.cpp
@@ -0,0 +1,56 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "EnderCrystal.h"
+#include "ClientHandle.h"
+#include "Player.h"
+#include "../Chunk.h"
+
+
+
+
+
+cEnderCrystal::cEnderCrystal(double a_X, double a_Y, double a_Z)
+ : cEntity(etEnderCrystal, a_X, a_Y, a_Z, 1.0, 1.0)
+{
+ SetMaxHealth(5);
+}
+
+
+
+
+
+void cEnderCrystal::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ a_ClientHandle.SendSpawnObject(*this, 51, 0, (Byte)GetYaw(), (Byte)GetPitch());
+}
+
+
+
+
+
+void cEnderCrystal::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+
+ a_Chunk.SetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT, E_BLOCK_FIRE, 0);
+
+ // No further processing (physics e.t.c.) is needed
+}
+
+
+
+
+
+void cEnderCrystal::KilledBy(cEntity * a_Killer)
+{
+ super::KilledBy(a_Killer);
+
+ m_World->DoExplosionAt(6.0, GetPosX(), GetPosY(), GetPosZ(), true, esEnderCrystal, this);
+
+ Destroy();
+}
+
+
+
+
diff --git a/src/Entities/EnderCrystal.h b/src/Entities/EnderCrystal.h
new file mode 100644
index 000000000..5b86df987
--- /dev/null
+++ b/src/Entities/EnderCrystal.h
@@ -0,0 +1,33 @@
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+// tolua_begin
+class cEnderCrystal :
+ public cEntity
+{
+ // tolua_end
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cEnderCrystal);
+
+ cEnderCrystal(double a_X, double a_Y, double a_Z);
+
+private:
+
+ // cEntity overrides:
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void KilledBy(cEntity * a_Killer) override;
+
+}; // tolua_export
+
+
+
+
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 565c78dfd..8ef45f1a5 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 "../Matrix4.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)
@@ -54,6 +50,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_TicksSinceLastFireDamage(0)
, m_TicksLeftBurning(0)
, m_TicksSinceLastVoidDamage(0)
+ , m_IsSwimming(false)
+ , m_IsSubmerged(false)
, m_HeadYaw( 0.0 )
, m_Rot(0.0, 0.0, 0.0)
, m_Pos(a_X, a_Y, a_Z)
@@ -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;
}
@@ -400,6 +397,7 @@ int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_Dama
case dtPotionOfHarming:
case dtFalling:
case dtLightning:
+ case dtPlugin:
{
return 0;
}
@@ -476,7 +474,7 @@ void cEntity::KilledBy(cEntity * a_Killer)
return;
}
- // Drop loot:
+ // Drop loot:
cItems Drops;
GetDrops(Drops, a_Killer);
m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ());
@@ -523,18 +521,36 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
if (a_Chunk.IsValid())
{
- HandlePhysics(a_Dt, a_Chunk);
+ cChunk * NextChunk = a_Chunk.GetNeighborChunk(POSX_TOINT, POSZ_TOINT);
+
+ if ((NextChunk == NULL) || !NextChunk->IsValid())
+ {
+ return;
+ }
+
+ TickBurning(*NextChunk);
+
+ if (GetPosY() < VOID_BOUNDARY)
+ {
+ TickInVoid(*NextChunk);
+ }
+ else
+ {
+ m_TicksSinceLastVoidDamage = 0;
+ }
+
+ if (IsMob() || IsPlayer())
+ {
+ // Set swimming state
+ SetSwimState(*NextChunk);
+
+ // Handle drowning
+ HandleAir();
+ }
+
+ HandlePhysics(a_Dt, *NextChunk);
}
}
- if (a_Chunk.IsValid())
- {
- TickBurning(a_Chunk);
- }
- if ((a_Chunk.IsValid()) && (GetPosY() < -46))
- {
- TickInVoid(a_Chunk);
- }
- else { m_TicksSinceLastVoidDamage = 0; }
}
@@ -554,7 +570,7 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
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)
@@ -563,210 +579,205 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
NextPos += (NextSpeed * a_Dt);
SetPosition(NextPos);
}
+
return;
}
- // Make sure we got the correct chunk and a valid one. No one ever knows...
- cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
- if (NextChunk != NULL)
+ 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;
+ if (!cBlockInfo::IsSolid(BlockIn)) // Making sure we are not inside a solid block
{
- 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 (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block
+ if (m_bOnGround) // check if it's still on the ground
{
- if (m_bOnGround) // check if it's still on the ground
+ if (!cBlockInfo::IsSolid(BlockBelow)) // Check if block below is air or water.
{
- if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water.
- {
- m_bOnGround = false;
- }
+ m_bOnGround = false;
}
}
- else
- {
- // Push out entity.
- BLOCKTYPE GotBlock;
+ }
+ else
+ {
+ // Push out entity.
+ BLOCKTYPE GotBlock;
- static const struct
- {
- int x, y, z;
- } gCrossCoords[] =
- {
- { 1, 0, 0},
- {-1, 0, 0},
- { 0, 0, 1},
- { 0, 0, -1},
- } ;
-
- bool IsNoAirSurrounding = true;
- for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ static const struct
+ {
+ int x, y, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0, 0},
+ {-1, 0, 0},
+ { 0, 0, 1},
+ { 0, 0, -1},
+ } ;
+
+ 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;
- }
- if (!g_BlockIsSolid[GotBlock])
- {
- NextPos.x += gCrossCoords[i].x;
- NextPos.z += gCrossCoords[i].z;
- IsNoAirSurrounding = false;
- break;
- }
- } // for i - gCrossCoords[]
-
- if (IsNoAirSurrounding)
+ // The pickup is too close to an unloaded chunk, bail out of any physics handling
+ return;
+ }
+ if (!cBlockInfo::IsSolid(GotBlock))
{
- NextPos.y += 0.5;
+ NextPos.x += gCrossCoords[i].x;
+ NextPos.z += gCrossCoords[i].z;
+ IsNoAirSurrounding = false;
+ break;
}
+ } // for i - gCrossCoords[]
+
+ if (IsNoAirSurrounding)
+ {
+ NextPos.y += 0.5;
+ }
- m_bOnGround = true;
+ m_bOnGround = true;
- /*
- // DEBUG:
- LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
- m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
- );
- */
- }
+ /*
+ // DEBUG:
+ LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
+ m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
+ );
+ */
+ }
- if (!m_bOnGround)
+ if (!m_bOnGround)
+ {
+ float fallspeed;
+ if (IsBlockWater(BlockIn))
{
- float fallspeed;
- if (IsBlockWater(BlockIn))
- {
- fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water.
- }
- else if (BlockIn == E_BLOCK_COBWEB)
- {
- NextSpeed.y *= 0.05; // Reduce overall falling speed
- fallspeed = 0; // No falling.
- }
- else
- {
- // Normal gravity
- fallspeed = m_Gravity * a_Dt;
- }
- NextSpeed.y += fallspeed;
+ fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water.
+ }
+ else if (BlockIn == E_BLOCK_COBWEB)
+ {
+ NextSpeed.y *= 0.05; // Reduce overall falling speed
+ fallspeed = 0; // No falling.
}
else
{
- // Friction
- if (NextSpeed.SqrLength() > 0.0004f)
+ // Normal gravity
+ fallspeed = m_Gravity * a_Dt;
+ }
+ NextSpeed.y += fallspeed;
+ }
+ else
+ {
+ // Friction
+ if (NextSpeed.SqrLength() > 0.0004f)
+ {
+ NextSpeed.x *= 0.7f / (1 + a_Dt);
+ if (fabs(NextSpeed.x) < 0.05)
{
- NextSpeed.x *= 0.7f / (1 + a_Dt);
- if (fabs(NextSpeed.x) < 0.05)
- {
- NextSpeed.x = 0;
- }
- NextSpeed.z *= 0.7f / (1 + a_Dt);
- if (fabs(NextSpeed.z) < 0.05)
- {
- NextSpeed.z = 0;
- }
+ NextSpeed.x = 0;
+ }
+ NextSpeed.z *= 0.7f / (1 + a_Dt);
+ if (fabs(NextSpeed.z) < 0.05)
+ {
+ NextSpeed.z = 0;
}
}
+ }
- // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
- // might have different speed modifiers according to terrain.
- if (BlockIn == E_BLOCK_COBWEB)
- {
- NextSpeed.x *= 0.25;
- NextSpeed.z *= 0.25;
- }
+ // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
+ // might have different speed modifiers according to terrain.
+ if (BlockIn == E_BLOCK_COBWEB)
+ {
+ NextSpeed.x *= 0.25;
+ NextSpeed.z *= 0.25;
+ }
- //Get water direction
- Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);
+ //Get water direction
+ Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);
- m_WaterSpeed *= 0.9f; //Reduce speed each tick
+ m_WaterSpeed *= 0.9f; //Reduce speed each tick
- switch(WaterDir)
- {
- case X_PLUS:
- m_WaterSpeed.x = 0.2f;
- m_bOnGround = false;
- break;
- case X_MINUS:
- m_WaterSpeed.x = -0.2f;
- m_bOnGround = false;
- break;
- case Z_PLUS:
- m_WaterSpeed.z = 0.2f;
- m_bOnGround = false;
- break;
- case Z_MINUS:
- m_WaterSpeed.z = -0.2f;
- m_bOnGround = false;
- break;
-
- default:
+ switch(WaterDir)
+ {
+ case X_PLUS:
+ m_WaterSpeed.x = 0.2f;
+ m_bOnGround = false;
break;
- }
+ case X_MINUS:
+ m_WaterSpeed.x = -0.2f;
+ m_bOnGround = false;
+ break;
+ case Z_PLUS:
+ m_WaterSpeed.z = 0.2f;
+ m_bOnGround = false;
+ break;
+ case Z_MINUS:
+ m_WaterSpeed.z = -0.2f;
+ m_bOnGround = false;
+ break;
+
+ default:
+ break;
+ }
- if (fabs(m_WaterSpeed.x) < 0.05)
- {
- m_WaterSpeed.x = 0;
- }
+ if (fabs(m_WaterSpeed.x) < 0.05)
+ {
+ m_WaterSpeed.x = 0;
+ }
- if (fabs(m_WaterSpeed.z) < 0.05)
- {
- m_WaterSpeed.z = 0;
- }
+ if (fabs(m_WaterSpeed.z) < 0.05)
+ {
+ m_WaterSpeed.z = 0;
+ }
- NextSpeed += m_WaterSpeed;
+ NextSpeed += m_WaterSpeed;
- if( NextSpeed.SqrLength() > 0.f )
+ if( NextSpeed.SqrLength() > 0.f )
+ {
+ cTracer Tracer( GetWorld() );
+ bool HasHit = Tracer.Trace( NextPos, NextSpeed, 2 );
+ if (HasHit) // Oh noez! we hit something
{
- cTracer Tracer( GetWorld() );
- int Ret = Tracer.Trace( NextPos, NextSpeed, 2 );
- if( Ret ) // Oh noez! we hit something
+ // Set to hit position
+ if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength())
{
- // Set to hit position
- if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() )
- {
- if( Ret == 1 )
- {
- if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f;
- if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f;
- if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f;
-
- if( Tracer.HitNormal.y > 0 ) // means on ground
- {
- m_bOnGround = true;
- }
- }
- NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
- NextPos.x += Tracer.HitNormal.x * 0.3f;
- NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot
- NextPos.z += Tracer.HitNormal.z * 0.3f;
- }
- else
+ if (Tracer.HitNormal.x != 0.f) NextSpeed.x = 0.f;
+ if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f;
+ if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f;
+
+ if (Tracer.HitNormal.y > 0) // means on ground
{
- NextPos += (NextSpeed * a_Dt);
+ m_bOnGround = true;
}
+ NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
+ NextPos.x += Tracer.HitNormal.x * 0.3f;
+ NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot
+ NextPos.z += Tracer.HitNormal.z * 0.3f;
}
else
{
- // We didn't hit anything, so move =]
NextPos += (NextSpeed * a_Dt);
}
}
- BlockX = (int) floor(NextPos.x);
- BlockZ = (int) floor(NextPos.z);
- NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ);
- // See if we can commit our changes. If not, we will discard them.
- if (NextChunk != NULL)
+ else
{
- 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);
+ // We didn't hit anything, so move =]
+ NextPos += (NextSpeed * a_Dt);
}
}
+
+ 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);
+ }
}
@@ -807,14 +818,13 @@ void cEntity::TickBurning(cChunk & a_Chunk)
{
int RelX = x;
int RelZ = z;
- cChunk * CurChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ);
- if (CurChunk == NULL)
- {
- continue;
- }
+
for (int y = MinY; y <= MaxY; y++)
{
- switch (CurChunk->GetBlock(RelX, y, RelZ))
+ BLOCKTYPE Block;
+ a_Chunk.UnboundedRelGetBlockType(RelX, y, RelZ, Block);
+
+ switch (Block)
{
case E_BLOCK_FIRE:
{
@@ -912,6 +922,86 @@ void cEntity::TickInVoid(cChunk & a_Chunk)
+void cEntity::SetSwimState(cChunk & a_Chunk)
+{
+ int RelY = (int)floor(GetPosY() + 0.1);
+ if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
+ {
+ m_IsSwimming = false;
+ m_IsSubmerged = false;
+ return;
+ }
+
+ BLOCKTYPE BlockIn;
+ int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
+
+ // Check if the player is swimming:
+ 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)
{
@@ -1379,7 +1469,7 @@ void cEntity::SteerVehicle(float a_Forward, float a_Sideways)
Vector3d cEntity::GetLookVector(void) const
{
Matrix4d m;
- m.Init(Vector3f(), 0, m_Rot.x, -m_Rot.y);
+ m.Init(Vector3d(), 0, m_Rot.x, -m_Rot.y);
Vector3d Look = m.Transform(Vector3d(0, 0, 1));
return Look;
}
@@ -1429,33 +1519,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..6e3f8292b 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -2,8 +2,7 @@
#pragma once
#include "../Item.h"
-#include "../Vector3d.h"
-#include "../Vector3f.h"
+#include "../Vector3.h"
@@ -28,12 +27,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;
@@ -66,6 +69,7 @@ public:
enum eEntityType
{
etEntity, // For all other types
+ etEnderCrystal,
etPlayer,
etPickup,
etMonster,
@@ -76,6 +80,8 @@ public:
etProjectile,
etExpOrb,
etFloater,
+ etItemFrame,
+ etPainting,
// Common variations
etMob = etMonster, // DEPRECATED, use etMonster instead!
@@ -110,6 +116,9 @@ 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
+ VOID_BOUNDARY = -46 ///< At what position Y to begin applying void damage
} ;
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
@@ -122,16 +131,19 @@ public:
eEntityType GetEntityType(void) const { return m_EntityType; }
- bool IsPlayer (void) const { return (m_EntityType == etPlayer); }
- bool IsPickup (void) const { return (m_EntityType == etPickup); }
- bool IsMob (void) const { return (m_EntityType == etMonster); }
+ bool IsEnderCrystal(void) const { return (m_EntityType == etEnderCrystal); }
+ bool IsPlayer (void) const { return (m_EntityType == etPlayer); }
+ bool IsPickup (void) const { return (m_EntityType == etPickup); }
+ bool IsMob (void) const { return (m_EntityType == etMonster); }
bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); }
- bool IsMinecart (void) const { return (m_EntityType == etMinecart); }
- bool IsBoat (void) const { return (m_EntityType == etBoat); }
- bool IsTNT (void) const { return (m_EntityType == etTNT); }
- bool IsProjectile (void) const { return (m_EntityType == etProjectile); }
- bool IsExpOrb (void) const { return (m_EntityType == etExpOrb); }
- bool IsFloater (void) const { return (m_EntityType == etFloater); }
+ bool IsMinecart (void) const { return (m_EntityType == etMinecart); }
+ bool IsBoat (void) const { return (m_EntityType == etBoat); }
+ bool IsTNT (void) const { return (m_EntityType == etTNT); }
+ bool IsProjectile (void) const { return (m_EntityType == etProjectile); }
+ bool IsExpOrb (void) const { return (m_EntityType == etExpOrb); }
+ bool IsFloater (void) const { return (m_EntityType == etFloater); }
+ bool IsItemFrame (void) const { return (m_EntityType == etItemFrame); }
+ bool IsPainting (void) const { return (m_EntityType == etPainting); }
/// Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true)
virtual bool IsA(const char * a_ClassName) const;
@@ -147,7 +159,7 @@ public:
cWorld * GetWorld(void) const { return m_World; }
- double GetHeadYaw (void) const { return m_HeadYaw; }
+ double GetHeadYaw (void) const { return m_HeadYaw; } // In degrees
double GetHeight (void) const { return m_Height; }
double GetMass (void) const { return m_Mass; }
const Vector3d & GetPosition (void) const { return m_Pos; }
@@ -155,9 +167,9 @@ public:
double GetPosY (void) const { return m_Pos.y; }
double GetPosZ (void) const { return m_Pos.z; }
const Vector3d & GetRot (void) const { return m_Rot; } // OBSOLETE, use individual GetYaw(), GetPitch, GetRoll() components
- double GetYaw (void) const { return m_Rot.x; }
- double GetPitch (void) const { return m_Rot.y; }
- double GetRoll (void) const { return m_Rot.z; }
+ double GetYaw (void) const { return m_Rot.x; } // In degrees, [-180, +180)
+ double GetPitch (void) const { return m_Rot.y; } // In degrees, [-180, +180), but normal client clips to [-90, +90]
+ double GetRoll (void) const { return m_Rot.z; } // In degrees, unused in current client
Vector3d GetLookVector(void) const;
const Vector3d & GetSpeed (void) const { return m_Speed; }
double GetSpeedX (void) const { return m_Speed.x; }
@@ -177,9 +189,9 @@ public:
void SetPosition(double a_PosX, double a_PosY, double a_PosZ);
void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); }
void SetRot (const Vector3f & a_Rot); // OBSOLETE, use individual SetYaw(), SetPitch(), SetRoll() components
- void SetYaw (double a_Yaw);
- void SetPitch (double a_Pitch);
- void SetRoll (double a_Roll);
+ void SetYaw (double a_Yaw); // In degrees, normalizes to [-180, +180)
+ void SetPitch (double a_Pitch); // In degrees, normalizes to [-180, +180)
+ void SetRoll (double a_Roll); // In degrees, normalizes to [-180, +180)
void SetSpeed (double a_SpeedX, double a_SpeedY, double a_SpeedZ);
void SetSpeed (const Vector3d & a_Speed) { SetSpeed(a_Speed.x, a_Speed.y, a_Speed.z); }
void SetSpeedX (double a_SpeedX);
@@ -344,7 +356,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 +392,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 +431,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/ExpOrb.cpp b/src/Entities/ExpOrb.cpp
index 04ee85823..3623c869a 100644
--- a/src/Entities/ExpOrb.cpp
+++ b/src/Entities/ExpOrb.cpp
@@ -5,20 +5,26 @@
#include "../ClientHandle.h"
-cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward) :
- cEntity(etExpOrb, a_X, a_Y, a_Z, 0.98, 0.98),
- m_Reward(a_Reward)
+cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward)
+ : cEntity(etExpOrb, a_X, a_Y, a_Z, 0.98, 0.98)
+ , m_Reward(a_Reward)
+ , m_Timer(0.f)
{
+ SetMaxHealth(5);
+ SetHealth(5);
}
-cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward) :
- cEntity(etExpOrb, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98),
- m_Reward(a_Reward)
+cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward)
+ : cEntity(etExpOrb, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98)
+ , m_Reward(a_Reward)
+ , m_Timer(0.f)
{
+ SetMaxHealth(5);
+ SetHealth(5);
}
@@ -51,7 +57,10 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk)
{
LOGD("Player %s picked up an ExpOrb. His reward is %i", a_ClosestPlayer->GetName().c_str(), m_Reward);
a_ClosestPlayer->DeltaExperience(m_Reward);
- Destroy(true);
+
+ m_World->BroadcastSoundEffect("random.orb", (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+
+ Destroy();
}
a_Distance.Normalize();
a_Distance *= ((float) (5.5 - Distance));
@@ -61,4 +70,10 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk)
BroadcastMovementUpdate();
}
HandlePhysics(a_Dt, a_Chunk);
-} \ No newline at end of file
+
+ m_Timer += a_Dt;
+ if (m_Timer >= 1000 * 60 * 5) // 5 minutes
+ {
+ Destroy(true);
+ }
+}
diff --git a/src/Entities/ExpOrb.h b/src/Entities/ExpOrb.h
index a062eedd3..e76274ac9 100644
--- a/src/Entities/ExpOrb.h
+++ b/src/Entities/ExpOrb.h
@@ -7,13 +7,17 @@
+// tolua_begin
class cExpOrb :
public cEntity
{
typedef cExpOrb super;
public:
-
+ // tolua_end
+
+ CLASS_PROTODEF(cExpOrb);
+
cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward);
cExpOrb(const Vector3d & a_Pos, int a_Reward);
@@ -21,9 +25,21 @@ public:
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void SpawnOn(cClientHandle & a_Client) override;
- // cExpOrb functions
- int GetReward(void) const { return m_Reward; }
+ /** Returns the number of ticks that this entity has existed */
+ int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export
+
+ /** Set the number of ticks that this entity has existed */
+ void SetAge(int a_Age) { m_Timer = (float)(a_Age * 50); } // tolua_export
+
+ /** Get the exp amount */
+ int GetReward(void) const { return m_Reward; } // tolua_export
+
+ /** Set the exp amount */
+ void SetReward(int a_Reward) { m_Reward = a_Reward; } // tolua_export
protected:
int m_Reward;
-} ; \ No newline at end of file
+
+ /** The number of ticks that the entity has existed / timer between collect and destroy; in msec */
+ float m_Timer;
+} ; // tolua_export
diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp
index 9fcd9ac80..a66c7e4ae 100644
--- a/src/Entities/FallingBlock.cpp
+++ b/src/Entities/FallingBlock.cpp
@@ -33,20 +33,16 @@ void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle)
void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
{
- float MilliDt = a_Dt * 0.001f;
- AddSpeedY(MilliDt * -9.8f);
- AddPosY(GetSpeedY() * MilliDt);
-
// GetWorld()->BroadcastTeleportEntity(*this); // Test position
- int BlockX = m_OriginalPosition.x;
+ int BlockX = POSX_TOINT;
int BlockY = (int)(GetPosY() - 0.5);
- int BlockZ = m_OriginalPosition.z;
+ int BlockZ = POSZ_TOINT;
if (BlockY < 0)
{
// Fallen out of this world, just continue falling until out of sight, then destroy:
- if (BlockY < 100)
+ if (BlockY < VOID_BOUNDARY)
{
Destroy(true);
}
@@ -86,6 +82,15 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
Destroy(true);
return;
}
+
+ float MilliDt = a_Dt * 0.001f;
+ AddSpeedY(MilliDt * -9.8f);
+ AddPosition(GetSpeed() * MilliDt);
+
+ if ((GetSpeedX() != 0) || (GetSpeedZ() != 0))
+ {
+ BroadcastMovementUpdate();
+ }
}
diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp
index dfe77f059..b910c3769 100644
--- a/src/Entities/Floater.cpp
+++ b/src/Entities/Floater.cpp
@@ -35,7 +35,7 @@ public:
cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
double LineCoeff;
- char Face;
+ eBlockFace Face;
EntBox.Expand(m_Floater->GetWidth() / 2, m_Floater->GetHeight() / 2, m_Floater->GetWidth() / 2);
if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face))
{
@@ -103,10 +103,10 @@ protected:
cFloater::cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime) :
cEntity(etFloater, a_X, a_Y, a_Z, 0.2, 0.2),
- m_PickupCountDown(0),
- m_PlayerID(a_PlayerID),
m_CanPickupItem(false),
+ m_PickupCountDown(0),
m_CountDownTime(a_CountDownTime),
+ m_PlayerID(a_PlayerID),
m_AttachedMobID(-1)
{
SetSpeed(a_Speed);
@@ -215,4 +215,4 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk)
SetSpeedZ(GetSpeedZ() * 0.95);
BroadcastMovementUpdate();
-} \ No newline at end of file
+}
diff --git a/src/Entities/Floater.h b/src/Entities/Floater.h
index 162b74e75..547d503f1 100644
--- a/src/Entities/Floater.h
+++ b/src/Entities/Floater.h
@@ -11,11 +11,13 @@
class cFloater :
public cEntity
{
- typedef cFloater super;
+ typedef cEntity super;
public:
-
//tolua_end
+
+ CLASS_PROTODEF(cFloater);
+
cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime);
virtual void SpawnOn(cClientHandle & a_Client) override;
@@ -41,4 +43,4 @@ protected:
// Entity IDs
int m_PlayerID;
int m_AttachedMobID;
-} ; // tolua_export \ No newline at end of file
+} ; // tolua_export
diff --git a/src/Entities/HangingEntity.cpp b/src/Entities/HangingEntity.cpp
new file mode 100644
index 000000000..41ac86268
--- /dev/null
+++ b/src/Entities/HangingEntity.cpp
@@ -0,0 +1,53 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "HangingEntity.h"
+#include "ClientHandle.h"
+#include "Player.h"
+
+
+
+
+
+cHangingEntity::cHangingEntity(eEntityType a_EntityType, eBlockFace a_BlockFace, double a_X, double a_Y, double a_Z)
+ : cEntity(a_EntityType, a_X, a_Y, a_Z, 0.8, 0.8)
+ , m_BlockFace(a_BlockFace)
+{
+ SetMaxHealth(1);
+ SetHealth(1);
+}
+
+
+
+
+
+void cHangingEntity::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ int Dir = 0;
+
+ // The client uses different values for item frame directions and block faces. Our constants are for the block faces, so we convert them here to item frame faces
+ switch (m_BlockFace)
+ {
+ case BLOCK_FACE_ZP: break; // Initialised to zero
+ case BLOCK_FACE_ZM: Dir = 2; break;
+ case BLOCK_FACE_XM: Dir = 1; break;
+ case BLOCK_FACE_XP: Dir = 3; break;
+ default: ASSERT(!"Unhandled block face when trying to spawn item frame!"); return;
+ }
+
+ if ((Dir == 0) || (Dir == 2)) // Probably a client bug, but two directions are flipped and contrary to the norm, so we do -180
+ {
+ SetYaw((Dir * 90) - 180);
+ }
+ else
+ {
+ SetYaw(Dir * 90);
+ }
+
+ a_ClientHandle.SendSpawnObject(*this, 71, Dir, (Byte)GetYaw(), (Byte)GetPitch());
+ a_ClientHandle.SendEntityMetadata(*this);
+}
+
+
+
+
diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h
new file mode 100644
index 000000000..6498e4b5b
--- /dev/null
+++ b/src/Entities/HangingEntity.h
@@ -0,0 +1,49 @@
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+// tolua_begin
+class cHangingEntity :
+ public cEntity
+{
+ // tolua_end
+ typedef cEntity super;
+
+public:
+
+ CLASS_PROTODEF(cHangingEntity);
+
+ cHangingEntity(eEntityType a_EntityType, eBlockFace a_BlockFace, double a_X, double a_Y, double a_Z);
+
+ /** Returns the orientation from the hanging entity */
+ eBlockFace GetDirection() const { return m_BlockFace; } // tolua_export
+
+ /** Set the orientation from the hanging entity */
+ void SetDirection(eBlockFace a_BlockFace) { m_BlockFace = a_BlockFace; } // tolua_export
+
+ /** Returns the X coord. */
+ int GetTileX() const { return POSX_TOINT; } // tolua_export
+
+ /** Returns the Y coord. */
+ int GetTileY() const { return POSY_TOINT; } // tolua_export
+
+ /** Returns the Z coord. */
+ int GetTileZ() const { return POSZ_TOINT; } // tolua_export
+
+private:
+
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override {};
+
+ eBlockFace m_BlockFace;
+
+}; // tolua_export
+
+
+
+
diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp
new file mode 100644
index 000000000..9dd909880
--- /dev/null
+++ b/src/Entities/ItemFrame.cpp
@@ -0,0 +1,91 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "ItemFrame.h"
+#include "ClientHandle.h"
+#include "Player.h"
+
+
+
+
+
+cItemFrame::cItemFrame(eBlockFace a_BlockFace, double a_X, double a_Y, double a_Z)
+ : cHangingEntity(etItemFrame, a_BlockFace, a_X, a_Y, a_Z)
+ , m_Item(E_BLOCK_AIR)
+ , m_Rotation(0)
+{
+}
+
+
+
+
+
+void cItemFrame::OnRightClicked(cPlayer & a_Player)
+{
+ if (!m_Item.IsEmpty())
+ {
+ // Item not empty, rotate, clipping values to zero to three inclusive
+ m_Rotation++;
+ if (m_Rotation >= 4)
+ {
+ m_Rotation = 0;
+ }
+ }
+ else if (!a_Player.GetEquippedItem().IsEmpty())
+ {
+ // Item empty, and player held item not empty - add this item to self
+ m_Item = a_Player.GetEquippedItem();
+ m_Item.m_ItemCount = 1;
+
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ }
+
+ GetWorld()->BroadcastEntityMetadata(*this); // Update clients
+}
+
+
+
+
+
+
+void cItemFrame::KilledBy(cEntity * a_Killer)
+{
+ if (m_Item.IsEmpty())
+ {
+ super::KilledBy(a_Killer);
+ Destroy();
+ return;
+ }
+
+ if ((a_Killer != NULL) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative())
+ {
+ cItems Item;
+ Item.push_back(m_Item);
+
+ GetWorld()->SpawnItemPickups(Item, GetPosX(), GetPosY(), GetPosZ());
+ }
+
+ SetHealth(GetMaxHealth());
+ m_Item.Clear();
+ m_Rotation = 0;
+ GetWorld()->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cItemFrame::GetDrops(cItems & a_Items, cEntity * a_Killer)
+{
+ if ((a_Killer != NULL) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative())
+ {
+ a_Items.push_back(cItem(E_ITEM_ITEM_FRAME));
+ }
+}
+
+
+
+
diff --git a/src/Entities/ItemFrame.h b/src/Entities/ItemFrame.h
new file mode 100644
index 000000000..6577e7d94
--- /dev/null
+++ b/src/Entities/ItemFrame.h
@@ -0,0 +1,48 @@
+
+#pragma once
+
+#include "HangingEntity.h"
+
+
+
+
+
+// tolua_begin
+class cItemFrame :
+ public cHangingEntity
+{
+ // tolua_end
+ typedef cHangingEntity super;
+
+public:
+
+ CLASS_PROTODEF(cItemFrame);
+
+ cItemFrame(eBlockFace a_BlockFace, double a_X, double a_Y, double a_Z);
+
+ /** Returns the item in the frame */
+ const cItem & GetItem(void) { return m_Item; } // tolua_export
+
+ /** Set the item in the frame */
+ void SetItem(cItem & a_Item) { m_Item = a_Item; }; // tolua_export
+
+ /** Returns the rotation from the item in the frame */
+ Byte GetRotation(void) const { return m_Rotation; } // tolua_export
+
+ /** Set the rotation from the item in the frame */
+ void SetRotation(Byte a_Rotation) { m_Rotation = a_Rotation; } // tolua_export
+
+private:
+
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ virtual void KilledBy(cEntity * a_Killer) override;
+ virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override;
+
+ cItem m_Item;
+ Byte m_Rotation;
+
+}; // tolua_export
+
+
+
+
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index d0d384481..7f38aa35a 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_DoesInteserct(false),
+ m_CollidedEntityPos(0, 0, 0),
+ m_Pos(a_Pos),
+ m_Height(a_Height),
+ m_Width(a_Width),
+ 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;
}
}
@@ -499,7 +720,7 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
if (GetSpeedZ() > 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)ceil(GetPosZ()));
- if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
+ if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
{
// We could try to detect a block in front based purely on coordinates, but xoft made a bounding box system - why not use? :P
cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()), (int)floor(GetPosY()), (int)ceil(GetPosZ())), 0.5, 1);
@@ -513,10 +734,10 @@ 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])
+ if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
{
cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1), 0.5, 1);
cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ() - 1), GetWidth() / 2, GetHeight());
@@ -536,7 +757,7 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
if (GetSpeedX() > 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)ceil(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
- if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
+ if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
{
cBoundingBox bbBlock(Vector3d((int)ceil(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())), 0.5, 1);
cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());
@@ -549,10 +770,10 @@ 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])
+ if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
{
cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ())), 0.5, 1);
cBoundingBox bbMinecart(Vector3d(GetPosX() - 1, floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());
@@ -577,10 +798,10 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
BLOCKTYPE BlockZM = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) + 1);
BLOCKTYPE BlockZP = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) + 1);
if (
- (!IsBlockRail(BlockXM) && g_BlockIsSolid[BlockXM]) ||
- (!IsBlockRail(BlockXP) && g_BlockIsSolid[BlockXP]) ||
- (!IsBlockRail(BlockZM) && g_BlockIsSolid[BlockZM]) ||
- (!IsBlockRail(BlockZP) && g_BlockIsSolid[BlockZP])
+ (!IsBlockRail(BlockXM) && cBlockInfo::IsSolid(BlockXM)) ||
+ (!IsBlockRail(BlockXP) && cBlockInfo::IsSolid(BlockXP)) ||
+ (!IsBlockRail(BlockZM) && cBlockInfo::IsSolid(BlockZM)) ||
+ (!IsBlockRail(BlockZP) && cBlockInfo::IsSolid(BlockZP))
)
{
SetSpeed(0, 0, 0);
@@ -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
@@ -727,9 +1031,9 @@ cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) :
-void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item)
+void cMinecartWithChest::SetSlot(size_t a_Idx, const cItem & a_Item)
{
- ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items)));
+ ASSERT(a_Idx < ARRAYCOUNT(m_Items));
m_Items[a_Idx] = a_Item;
}
@@ -753,8 +1057,8 @@ void cMinecartWithChest::OnRightClicked(cPlayer & a_Player)
cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) :
super(mpFurnace, a_X, a_Y, a_Z),
- m_IsFueled(false),
- m_FueledTimeLeft(-1)
+ m_FueledTimeLeft(-1),
+ m_IsFueled(false)
{
}
@@ -833,4 +1137,4 @@ cMinecartWithHopper::cMinecartWithHopper(double a_X, double a_Y, double a_Z) :
}
// TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks
-// AND AVARYTHING!! \ No newline at end of file
+// AND AVARYTHING!!
diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h
index 874d0204e..ebdb576e0 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);
} ;
@@ -130,7 +122,7 @@ public:
const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; }
cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; }
- void SetSlot(int a_Idx, const cItem & a_Item);
+ void SetSlot(size_t a_Idx, const cItem & a_Item);
protected:
@@ -201,4 +193,4 @@ public:
CLASS_PROTODEF(cMinecartWithHopper);
cMinecartWithHopper(double a_X, double a_Y, double a_Z);
-} ; \ No newline at end of file
+} ;
diff --git a/src/Entities/Painting.cpp b/src/Entities/Painting.cpp
new file mode 100644
index 000000000..e217556c7
--- /dev/null
+++ b/src/Entities/Painting.cpp
@@ -0,0 +1,54 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Painting.h"
+#include "ClientHandle.h"
+#include "Player.h"
+#include "../Chunk.h"
+
+
+
+
+
+cPainting::cPainting(const AString & a_Name, int a_Direction, double a_X, double a_Y, double a_Z)
+ : cEntity(etPainting, a_X, a_Y, a_Z, 1, 1),
+ m_Name(a_Name),
+ m_Direction(a_Direction)
+{
+}
+
+
+
+
+
+
+void cPainting::SpawnOn(cClientHandle & a_Client)
+{
+ a_Client.SendPaintingSpawn(*this);
+}
+
+
+
+
+
+void cPainting::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+}
+
+
+
+
+
+void cPainting::GetDrops(cItems & a_Items, cEntity * a_Killer)
+{
+ if ((a_Killer != NULL) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative())
+ {
+ a_Items.push_back(cItem(E_ITEM_PAINTING));
+ }
+}
+
+
+
+
diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h
new file mode 100644
index 000000000..c1024bd1b
--- /dev/null
+++ b/src/Entities/Painting.h
@@ -0,0 +1,42 @@
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+// tolua_begin
+class cPainting :
+ public cEntity
+{
+ // tolua_end
+ typedef cEntity super;
+
+public:
+ CLASS_PROTODEF(cPainting);
+
+ cPainting(const AString & a_Name, int a_Direction, double a_X, double a_Y, double a_Z);
+ const AString & GetName(void) const { return m_Name; } // tolua_export
+ int GetDirection(void) const { return m_Direction; } // tolua_export
+
+private:
+
+ virtual void SpawnOn(cClientHandle & a_Client) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override;
+ virtual void KilledBy(cEntity * a_Killer) override
+ {
+ super::KilledBy(a_Killer);
+ Destroy();
+ }
+
+ AString m_Name;
+ int m_Direction;
+
+}; // tolua_export
+
+
+
+
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index 001e386a7..7fc89b62b 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_FoundMatchingPickup(false),
+ m_Position(a_Position),
+ m_Pickup(a_Pickup)
+ {
+ }
+
+ 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);
@@ -43,7 +82,7 @@ cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_It
void cPickup::SpawnOn(cClientHandle & a_Client)
{
- a_Client.SendPickupSpawn(*this);
+ a_Client.SendPickupSpawn(*this);
}
@@ -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/Pickup.h b/src/Entities/Pickup.h
index d39eda298..74b917bce 100644
--- a/src/Entities/Pickup.h
+++ b/src/Entities/Pickup.h
@@ -18,38 +18,42 @@ class cPlayer;
class cPickup :
public cEntity
{
- // tolua_end
typedef cEntity super;
public:
+ // tolua_end
+
CLASS_PROTODEF(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); // tolua_export
-
- cItem & GetItem(void) {return m_Item; } // tolua_export
+ 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);
+
+ cItem & GetItem(void) {return m_Item; } // tolua_export
const cItem & GetItem(void) const {return m_Item; }
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
-
+
bool CollectedBy(cPlayer * a_Dest); // tolua_export
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
-
- /// Returns the number of ticks that this entity has existed
- int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export
-
- /// Returns true if the pickup has already been collected
+
+ /** Returns the number of ticks that this entity has existed */
+ int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export
+
+ /** Set the number of ticks that this entity has existed */
+ void SetAge(int a_Age) { m_Timer = (float)(a_Age * 50); } // tolua_export
+
+ /** Returns true if the pickup has already been collected */
bool IsCollected(void) const { return m_bCollected; } // tolua_export
- /// Returns true if created by player (i.e. vomiting), used for determining picking-up delay time
+ /** Returns true if created by player (i.e. vomiting), used for determining picking-up delay time */
bool IsPlayerCreated(void) const { return m_bIsPlayerCreated; } // tolua_export
-
+
private:
Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;)
Vector3d m_WaterSpeed;
- /// The number of ticks that the entity has existed / timer between collect and destroy; in msec
+ /** The number of ticks that the entity has existed / timer between collect and destroy; in msec */
float m_Timer;
cItem m_Item;
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index c1f2456eb..7f2e5b4c2 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -3,32 +3,22 @@
#include "Player.h"
#include "../Server.h"
-#include "../ClientHandle.h"
#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"
#include "../Group.h"
-#include "../ChatColor.h"
-#include "../Item.h"
-#include "../Tracer.h"
#include "../Root.h"
#include "../OSSupport/Timer.h"
-#include "../MersenneTwister.h"
#include "../Chunk.h"
#include "../Items/ItemHandler.h"
-
-#include "../Vector3d.h"
-#include "../Vector3f.h"
+#include "../Vector3.h"
#include "inifile/iniFile.h"
#include "json/json.h"
-#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x))
-
@@ -36,8 +26,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)
@@ -51,15 +39,13 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_Inventory(*this)
, m_CurrentWindow(NULL)
, m_InventoryWindow(NULL)
- , m_TimeLastPickupCheck(0.f)
, m_Color('-')
- , m_LastBlockActionTime(0)
- , m_LastBlockActionCnt(0)
, m_GameMode(eGameMode_NotSet)
, m_IP("")
, m_ClientHandle(a_Client)
- , m_NormalMaxSpeed(0.1)
- , m_SprintingMaxSpeed(0.13)
+ , m_NormalMaxSpeed(1.0)
+ , m_SprintingMaxSpeed(1.3)
+ , m_FlyingMaxSpeed(1.0)
, m_IsCrouched(false)
, m_IsSprinting(false)
, m_IsFlying(false)
@@ -74,6 +60,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(),
@@ -91,7 +78,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
m_LastPlayerListTime = t1.GetNowTime();
m_TimeLastTeleportPacket = 0;
- m_TimeLastPickupCheck = 0;
m_PlayerName = a_PlayerName;
m_bDirtyPosition = true; // So chunks are streamed to player at spawn
@@ -107,9 +93,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);
}
@@ -120,7 +120,13 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
cPlayer::~cPlayer(void)
{
- LOGD("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID());
+ if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this))
+ {
+ cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str()));
+ LOGINFO("Player %s has left the game.", GetName().c_str());
+ }
+
+ LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), this, GetUniqueID());
// Notify the server that the player is being destroyed
cRoot::Get()->GetServer()->PlayerDestroying(this);
@@ -196,12 +202,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)
{
@@ -245,6 +245,9 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
HandleFloater();
}
+ // Update items (e.g. Maps)
+ m_Inventory.UpdateItems();
+
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
cTimer t1;
if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
@@ -434,7 +437,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;
@@ -459,12 +462,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 */);
}
@@ -684,7 +685,21 @@ const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const
double cPlayer::GetMaxSpeed(void) const
{
- return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed;
+ if (m_IsFlying)
+ {
+ return m_FlyingMaxSpeed;
+ }
+ else
+ {
+ if (m_IsSprinting)
+ {
+ return m_SprintingMaxSpeed;
+ }
+ else
+ {
+ return m_NormalMaxSpeed;
+ }
+ }
}
@@ -694,7 +709,7 @@ double cPlayer::GetMaxSpeed(void) const
void cPlayer::SetNormalMaxSpeed(double a_Speed)
{
m_NormalMaxSpeed = a_Speed;
- if (!m_IsSprinting)
+ if (!m_IsSprinting && !m_IsFlying)
{
m_ClientHandle->SendPlayerMaxSpeed();
}
@@ -707,7 +722,7 @@ void cPlayer::SetNormalMaxSpeed(double a_Speed)
void cPlayer::SetSprintingMaxSpeed(double a_Speed)
{
m_SprintingMaxSpeed = a_Speed;
- if (m_IsSprinting)
+ if (m_IsSprinting && !m_IsFlying)
{
m_ClientHandle->SendPlayerMaxSpeed();
}
@@ -717,6 +732,18 @@ void cPlayer::SetSprintingMaxSpeed(double a_Speed)
+void cPlayer::SetFlyingMaxSpeed(double a_Speed)
+{
+ m_FlyingMaxSpeed = a_Speed;
+
+ // Update the flying speed, always:
+ m_ClientHandle->SendPlayerAbilities();
+}
+
+
+
+
+
void cPlayer::SetCrouch(bool a_IsCrouched)
{
// Set the crouch status, broadcast to all visible players
@@ -782,14 +809,28 @@ void cPlayer::SetFlying(bool a_IsFlying)
void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
{
- if (a_TDI.DamageType != dtInVoid)
+ if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin))
{
if (IsGameModeCreative())
{
- // No damage / health in creative mode
+ // No damage / health in creative mode if not void or plugin 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);
@@ -818,24 +859,34 @@ void cPlayer::KilledBy(cEntity * a_Killer)
cItems Pickups;
m_Inventory.CopyToItems(Pickups);
m_Inventory.Clear();
+
+ if (GetName() == "Notch")
+ {
+ Pickups.Add(cItem(E_ITEM_RED_APPLE));
+ }
+
m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
SaveToDisk(); // Save it, yeah the world is a tough place !
if (a_Killer == NULL)
{
- GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by environmental damage", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str()));
+ GetWorld()->BroadcastChatDeath(Printf("%s was killed by environmental damage", GetName().c_str()));
}
else if (a_Killer->IsPlayer())
{
- GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by %s", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str(), ((cPlayer *)a_Killer)->GetName().c_str()));
+ GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), ((cPlayer *)a_Killer)->GetName().c_str()));
+
+ m_World->GetScoreBoard().AddPlayerScore(((cPlayer *)a_Killer)->GetName(), cObjective::otPlayerKillCount, 1);
}
else
{
AString KillerClass = a_Killer->GetClass();
KillerClass.erase(KillerClass.begin()); // Erase the 'c' of the class (e.g. "cWitch" -> "Witch")
- 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()));
+ GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str()));
}
+
+ m_World->GetScoreBoard().AddPlayerScore(GetName(), cObjective::otDeathCount, 1);
}
@@ -916,6 +967,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)
@@ -969,27 +1064,6 @@ void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse)
-void cPlayer::SetLastBlockActionTime()
-{
- if (m_World != NULL)
- {
- m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f;
- }
-}
-
-
-
-
-
-void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt )
-{
- m_LastBlockActionCnt = a_LastBlockActionCnt;
-}
-
-
-
-
-
void cPlayer::SetGameMode(eGameMode a_GameMode)
{
if ((a_GameMode < gmMin) || (a_GameMode >= gmMax))
@@ -1036,21 +1110,24 @@ void cPlayer::SetIP(const AString & a_IP)
-void cPlayer::SendMessage(const AString & a_Message)
+void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
{
- m_ClientHandle->SendChat(a_Message);
+ SetPosition(a_PosX, a_PosY, a_PosZ);
+ m_LastGroundHeight = (float)a_PosY;
+ m_LastJumpHeight = (float)a_PosY;
+
+ m_World->BroadcastTeleportEntity(*this, GetClientHandle());
+ m_ClientHandle->SendPlayerMoveLook();
}
-void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
+void cPlayer::SendRotation(double a_YawDegrees, double a_PitchDegrees)
{
- SetPosition( a_PosX, a_PosY, a_PosZ );
- m_LastGroundHeight = (float)a_PosY;
-
- m_World->BroadcastTeleportEntity(*this, GetClientHandle());
+ SetYaw(a_YawDegrees);
+ SetPitch(a_PitchDegrees);
m_ClientHandle->SendPlayerMoveLook();
}
@@ -1186,19 +1263,6 @@ void cPlayer::RemoveFromGroup( const AString & a_GroupName )
-bool cPlayer::CanUseCommand( const AString & a_Command )
-{
- for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr )
- {
- if( (*itr)->HasCommand( a_Command ) ) return true;
- }
- return false;
-}
-
-
-
-
-
bool cPlayer::HasPermission(const AString & a_Permission)
{
if (a_Permission.empty())
@@ -1342,59 +1406,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;
@@ -1427,6 +1500,7 @@ bool cPlayer::MoveToWorld(const char * a_WorldName)
// Add player to all the necessary parts of the new world
SetWorld(World);
+ m_ClientHandle->StreamChunks();
World->AddEntity(this);
World->AddPlayer(this);
@@ -1448,10 +1522,14 @@ void cPlayer::LoadPermissionsFromDisk()
std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", "");
if (!Groups.empty())
{
- AStringVector Split = StringSplit( Groups, "," );
- for( unsigned int i = 0; i < Split.size(); i++ )
+ AStringVector Split = StringSplitAndTrim(Groups, ",");
+ for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr)
{
- AddToGroup( Split[i].c_str() );
+ if (!cRoot::Get()->GetGroupManager()->ExistsGroup(*itr))
+ {
+ LOGWARNING("The group %s for player %s was not found!", itr->c_str(), m_PlayerName.c_str());
+ }
+ AddToGroup(*itr);
}
}
else
@@ -1459,16 +1537,15 @@ void cPlayer::LoadPermissionsFromDisk()
AddToGroup("Default");
}
- m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0];
+ AString Color = IniFile.GetValue(m_PlayerName, "Color", "-");
+ if (!Color.empty())
+ {
+ m_Color = Color[0];
+ }
}
else
{
- LOGWARN("Regenerating users.ini, player %s will be added to the \"Default\" group", m_PlayerName.c_str());
- IniFile.AddHeaderComment(" This is the file in which the group the player belongs to is stored");
- IniFile.AddHeaderComment(" The format is: [PlayerName] | Groups=GroupName");
-
- IniFile.SetValue(m_PlayerName, "Groups", "Default");
- IniFile.WriteFile("users.ini");
+ cGroupManager::GenerateDefaultUsersIni(IniFile);
AddToGroup("Default");
}
ResolvePermissions();
@@ -1543,27 +1620,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"]);
@@ -1679,79 +1741,17 @@ 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)
+void cPlayer::TickBurning(cChunk & a_Chunk)
{
- // 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())
+ // Don't burn in creative and stop burning in creative if necessary
+ if (!IsGameModeCreative())
{
- // 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;
- }
+ super::TickBurning(a_Chunk);
}
- else
+ else if (IsOnFire())
{
- // set the air back to maximum
- m_AirLevel = MAX_AIR_LEVEL;
- m_AirTickTimer = DROWNING_TICKS;
+ m_TicksLeftBurning = 0;
+ OnFinishedBurning();
}
}
@@ -1763,6 +1763,12 @@ 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;
@@ -1780,7 +1786,7 @@ void cPlayer::HandleFood(void)
Heal(1);
m_FoodExhaustionLevel += 3;
}
- else if (m_FoodLevel <= 0)
+ else if ((m_FoodLevel <= 0) && (m_Health > 1))
{
// Damage from starving
TakeDamage(dtStarving, NULL, 1, 1, 0);
@@ -1906,7 +1912,7 @@ void cPlayer::Detach()
{
for (int z = PosZ - 2; z <= (PosZ + 2); ++z)
{
- if (!g_BlockIsSolid[m_World->GetBlock(x, y, z)] && g_BlockIsSolid[m_World->GetBlock(x, y - 1, z)])
+ if (!cBlockInfo::IsSolid(m_World->GetBlock(x, y, z)) && cBlockInfo::IsSolid(m_World->GetBlock(x, y - 1, z)))
{
TeleportToCoords(x, y, z);
return;
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index bf3ca08e8..05377a117 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -5,6 +5,7 @@
#include "../Inventory.h"
#include "../Defines.h"
#include "../World.h"
+#include "../ClientHandle.h"
@@ -13,6 +14,7 @@
class cGroup;
class cWindow;
class cClientHandle;
+class cTeam;
@@ -30,8 +32,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
@@ -47,19 +47,19 @@ public:
virtual void HandlePhysics(float a_Dt, cChunk &) override { UNUSED(a_Dt); };
- /// Returns the curently equipped weapon; empty item if none
+ /** Returns the curently equipped weapon; empty item if none */
virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }
- /// Returns the currently equipped helmet; empty item if nonte
+ /** Returns the currently equipped helmet; empty item if none */
virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); }
- /// Returns the currently equipped chestplate; empty item if none
+ /** Returns the currently equipped chestplate; empty item if none */
virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); }
- /// Returns the currently equipped leggings; empty item if none
+ /** Returns the currently equipped leggings; empty item if none */
virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); }
- /// Returns the currently equipped boots; empty item if none
+ /** Returns the currently equipped boots; empty item if none */
virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); }
@@ -77,36 +77,41 @@ public:
*/
short DeltaExperience(short a_Xp_delta);
- /// Gets the experience total - XpTotal for score on death
+ /** Gets the experience total - XpTotal for score on death */
inline short GetXpLifetimeTotal(void) { return m_LifetimeTotalXp; }
- /// Gets the currrent experience
+ /** Gets the currrent experience */
inline short GetCurrentXp(void) { return m_CurrentXp; }
- /// Gets the current level - XpLevel
+ /** Gets the current level - XpLevel */
short GetXpLevel(void);
- /// Gets the experience bar percentage - XpP
+ /** Gets the experience bar percentage - XpP */
float GetXpPercentage(void);
- /// Caculates the amount of XP needed for a given level, ref: http://minecraft.gamepedia.com/XP
+ /** Caculates the amount of XP needed for a given level
+ Ref: http://minecraft.gamepedia.com/XP
+ */
static short XpForLevel(short int a_Level);
- /// inverse of XpForLevel, ref: http://minecraft.gamepedia.com/XP values are as per this with pre-calculations
+ /** Inverse of XpForLevel
+ Ref: http://minecraft.gamepedia.com/XP
+ values are as per this with pre-calculations
+ */
static short CalcLevelFromXp(short int a_CurrentXp);
// tolua_end
- /// Starts charging the equipped bow
+ /** Starts charging the equipped bow */
void StartChargingBow(void);
- /// Finishes charging the current bow. Returns the number of ticks for which the bow has been charged
+ /** Finishes charging the current bow. Returns the number of ticks for which the bow has been charged */
int FinishChargingBow(void);
- /// Cancels the current bow charging
+ /** Cancels the current bow charging */
void CancelChargingBow(void);
- /// Returns true if the player is currently charging the bow
+ /** Returns true if the player is currently charging the bow */
bool IsChargingBow(void) const { return m_IsChargingBow; }
void SetTouchGround( bool a_bTouchGround );
@@ -124,16 +129,22 @@ public:
// tolua_begin
- /// Returns the position where projectiles thrown by this player should start, player eye position + adjustment
+ /** Sends the "look" packet to the player, forcing them to set their rotation to the specified values.
+ a_YawDegrees is clipped to range [-180, +180),
+ a_PitchDegrees is clipped to range [-180, +180) but the client only uses [-90, +90]
+ */
+ void SendRotation(double a_YawDegrees, double a_PitchDegrees);
+
+ /** Returns the position where projectiles thrown by this player should start, player eye position + adjustment */
Vector3d GetThrowStartPos(void) const;
- /// Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff.
+ /** Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. */
Vector3d GetThrowSpeed(double a_SpeedCoeff) const;
- /// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
+ /** Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable */
eGameMode GetGameMode(void) const { return m_GameMode; }
- /// Returns the current effective gamemode (inherited gamemode is resolved before returning)
+ /** Returns the current effective gamemode (inherited gamemode is resolved before returning) */
eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; }
/** Sets the gamemode for the player.
@@ -142,52 +153,63 @@ public:
*/
void SetGameMode(eGameMode a_GameMode);
- /// Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world
+ /** Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world */
bool IsGameModeCreative(void) const;
- /// Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world
+ /** Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world */
bool IsGameModeSurvival(void) const;
- /// Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world
+ /** Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world */
bool IsGameModeAdventure(void) const;
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);
-
- float GetLastBlockActionTime() { return m_LastBlockActionTime; }
- int GetLastBlockActionCnt() { return m_LastBlockActionCnt; }
- void SetLastBlockActionCnt( int );
- void SetLastBlockActionTime();
// Sets the current gamemode, doesn't check validity, doesn't send update packets to client
void LoginSetGameMode(eGameMode a_GameMode);
- /// Forces the player to move in the given direction.
+ /** Forces the player to move in the given direction. */
void ForceSetSpeed(Vector3d a_Direction); // tolua_export
- /// Tries to move to a new position, with attachment-related checks (y == -999)
+ /** Tries to move to a new position, with attachment-related checks (y == -999) */
void MoveTo(const Vector3d & a_NewPos); // tolua_export
cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export
const cWindow * GetWindow(void) const { return m_CurrentWindow; }
- /// Opens the specified window; closes the current one first using CloseWindow()
+ /** Opens the specified window; closes the current one first using CloseWindow() */
void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp
// tolua_begin
- /// Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true
+ /** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */
void CloseWindow(bool a_CanRefuse = true);
- /// Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow
+ /** Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow */
void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true);
cClientHandle * GetClientHandle(void) const { return m_ClientHandle; }
- void SendMessage(const AString & a_Message);
+ void SendMessage (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtCustom); }
+ void SendMessageInfo (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtInformation); }
+ void SendMessageFailure (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtFailure); }
+ void SendMessageSuccess (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtSuccess); }
+ void SendMessageWarning (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtWarning); }
+ void SendMessageFatal (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtFailure); }
+ void SendMessagePrivateMsg(const AString & a_Message, const AString & a_Sender) { m_ClientHandle->SendChat(a_Message, mtPrivateMessage, a_Sender); }
+ void SendMessage (const cCompositeChat & a_Message) { m_ClientHandle->SendChat(a_Message); }
const AString & GetName(void) const { return m_PlayerName; }
void SetName(const AString & a_Name) { m_PlayerName = a_Name; }
@@ -197,13 +219,12 @@ public:
typedef std::list< cGroup* > GroupList;
typedef std::list< std::string > StringList;
- /// Adds a player to existing group or creates a new group when it doesn't exist
+ /** Adds a player to existing group or creates a new group when it doesn't exist */
void AddToGroup( const AString & a_GroupName ); // tolua_export
- /// Removes a player from the group, resolves permissions and group inheritance (case sensitive)
+ /** Removes a player from the group, resolves permissions and group inheritance (case sensitive) */
void RemoveFromGroup( const AString & a_GroupName ); // tolua_export
- bool CanUseCommand( const AString & a_Command ); // tolua_export
bool HasPermission( const AString & a_Permission ); // tolua_export
const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS <<
StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS <<
@@ -211,12 +232,20 @@ public:
// tolua_begin
- /// Returns the full color code to use for this player, based on their primary group or set in m_Color
+ /** Returns the full color code to use for this player, based on their primary group or set in m_Color.
+ The returned value includes the cChatColor::Delimiter. */
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);
- /// Heals the player by the specified amount of HPs (positive only); sends health update
+ /** 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);
int GetFoodLevel (void) const { return m_FoodLevel; }
@@ -224,10 +253,8 @@ 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
+ /** 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); }
void SetFoodLevel (int a_FoodLevel);
@@ -236,25 +263,28 @@ public:
void SetFoodExhaustionLevel (double a_FoodExhaustionLevel);
void SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining);
- /// Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full"
+ /** Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full" */
bool Feed(int a_Food, double a_Saturation);
- /// Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values.
+ /** Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. */
void AddFoodExhaustion(double a_Exhaustion)
{
m_FoodExhaustionLevel += a_Exhaustion;
}
- /// Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two
+ /** Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two */
void FoodPoison(int a_NumTicks);
- /// Returns true if the player is currently in the process of eating the currently equipped item
+ /** Returns true if the player is currently in the process of eating the currently equipped item */
bool IsEating(void) const { return (m_EatingFinishTick >= 0); }
- /// Returns true if the player is currently flying.
+ /** Returns true if the player is currently flying. */
bool IsFlying(void) const { return m_IsFlying; }
- /// returns true if the player has thrown out a floater.
+ /** Returns if a player is sleeping in a bed */
+ bool IsInBed(void) const { return m_bIsInBed; }
+
+ /** returns true if the player has thrown out a floater. */
bool IsFishing(void) const { return m_IsFishing; }
void SetIsFishing(bool a_IsFishing, int a_FloaterID = -1) { m_IsFishing = a_IsFishing; m_FloaterID = a_FloaterID; }
@@ -262,14 +292,17 @@ public:
int GetFloaterID(void) const { return m_FloaterID; }
// tolua_end
+
+ /** Sets a player's in-bed state; we can't be sure plugins will keep this value updated, so no exporting */
+ void SetIsInBed(bool a_Flag) { m_bIsInBed = a_Flag; }
- /// Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet
+ /** Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet */
void StartEating(void);
- /// Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets
+ /** Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets */
void FinishEating(void);
- /// Aborts the current eating operation
+ /** Aborts the current eating operation */
void AbortEating(void);
virtual void KilledBy(cEntity * a_Killer) override;
@@ -298,51 +331,51 @@ public:
cItem & GetDraggingItem(void) {return m_DraggingItem; }
// In UI windows, when inventory-painting:
- /// Clears the list of slots that are being inventory-painted. To be used by cWindow only
+ /** Clears the list of slots that are being inventory-painted. To be used by cWindow only */
void ClearInventoryPaintSlots(void);
- /// Adds a slot to the list for inventory painting. To be used by cWindow only
+ /** Adds a slot to the list for inventory painting. To be used by cWindow only */
void AddInventoryPaintSlot(int a_SlotNum);
- /// Returns the list of slots currently stored for inventory painting. To be used by cWindow only
+ /** Returns the list of slots currently stored for inventory painting. To be used by cWindow only */
const cSlotNums & GetInventoryPaintSlots(void) const;
// tolua_begin
- /// Returns the current maximum speed, as reported in the 1.6.1+ protocol (takes current sprinting state into account)
+ /** Returns the current relative maximum speed (takes current sprinting / flying state into account) */
double GetMaxSpeed(void) const;
- /// Gets the normal maximum speed, as reported in the 1.6.1+ protocol, in the protocol units
+ /** Gets the normal relative maximum speed */
double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; }
- /// Gets the sprinting maximum speed, as reported in the 1.6.1+ protocol, in the protocol units
+ /** Gets the sprinting relative maximum speed */
double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; }
- /// Sets the normal maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed.
+ /** Gets the flying relative maximum speed */
+ double GetFlyingMaxSpeed(void) const { return m_FlyingMaxSpeed; }
+
+ /** Sets the normal relative maximum speed. Sends the update to player, if needed. */
void SetNormalMaxSpeed(double a_Speed);
- /// Sets the sprinting maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed.
+ /** Sets the sprinting relative maximum speed. Sends the update to player, if needed. */
void SetSprintingMaxSpeed(double a_Speed);
- /// Sets the crouch status, broadcasts to all visible players
+ /** Sets the flying relative maximum speed. Sends the update to player, if needed. */
+ void SetFlyingMaxSpeed(double a_Speed);
+
+ /** Sets the crouch status, broadcasts to all visible players */
void SetCrouch(bool a_IsCrouched);
- /// Starts or stops sprinting, sends the max speed update to the client, if needed
+ /** Starts or stops sprinting, sends the max speed update to the client, if needed */
void SetSprint(bool a_IsSprinting);
- /// Flags the player as flying
+ /** Flags the player as flying */
void SetFlying(bool a_IsFlying);
- /// If true the player can fly even when he's not in creative.
+ /** 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.
+ /** Returns wheter the player can fly or not. */
virtual bool CanFly(void) const { return m_CanFly; }
// tolua_end
@@ -361,10 +394,10 @@ protected:
GroupList m_ResolvedGroups;
GroupList m_Groups;
- std::string m_PlayerName;
- std::string m_LoadedWorldName;
+ AString m_PlayerName;
+ AString m_LoadedWorldName;
- /// Xp Level stuff
+ /** Xp Level stuff */
enum
{
XP_TO_LEVEL15 = 255,
@@ -372,31 +405,25 @@ 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:
- /// Represents the food bar, one point equals half a "drumstick"
+ /** Represents the food bar, one point equals half a "drumstick" */
int m_FoodLevel;
- /// "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel
+ /** "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel */
double m_FoodSaturationLevel;
- /// Count-up to the healing or damaging action, based on m_FoodLevel
+ /** Count-up to the healing or damaging action, based on m_FoodLevel */
int m_FoodTickTimer;
- /// A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little
+ /** A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little */
double m_FoodExhaustionLevel;
- /// Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned
+ /** Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned */
int m_FoodPoisonedTicksRemaining;
- /// Last position that has been recorded for food-related processing:
+ /** Last position that has been recorded for food-related processing: */
Vector3d m_LastFoodPos;
float m_LastJumpHeight;
@@ -407,16 +434,12 @@ protected:
cWindow * m_CurrentWindow;
cWindow * m_InventoryWindow;
- float m_TimeLastPickupCheck;
-
char m_Color;
- 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
+ /** The item being dragged by the cursor while in a UI window */
cItem m_DraggingItem;
long long m_LastPlayerListTime;
@@ -426,12 +449,21 @@ protected:
cSlotNums m_InventoryPaintSlots;
- /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is walking. 0.1 by default
+ /** Max speed, relative to the game default.
+ 1 means regular speed, 2 means twice as fast, 0.5 means half-speed.
+ Default value is 1. */
double m_NormalMaxSpeed;
- /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is sprinting. 0.13 by default
+ /** Max speed, relative to the game default max speed, when sprinting.
+ 1 means regular speed, 2 means twice as fast, 0.5 means half-speed.
+ Default value is 1.3. */
double m_SprintingMaxSpeed;
+ /** Max speed, relative to the game default flying max speed, when flying.
+ 1 means regular speed, 2 means twice as fast, 0.5 means half-speed.
+ Default value is 1. */
+ double m_FlyingMaxSpeed;
+
bool m_IsCrouched;
bool m_IsSprinting;
bool m_IsFlying;
@@ -441,10 +473,10 @@ protected:
bool m_CanFly; // If this is true the player can fly. Even if he is not in creative.
- /// The world tick in which eating will be finished. -1 if not eating
+ /** The world tick in which eating will be finished. -1 if not eating */
Int64 m_EatingFinishTick;
- /// Player Xp level
+ /** Player Xp level */
short int m_LifetimeTotalXp;
short int m_CurrentXp;
@@ -456,6 +488,8 @@ protected:
int m_FloaterID;
+ cTeam * m_Team;
+
void ResolvePermissions(void);
@@ -463,23 +497,25 @@ 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;
+
+ /** Stops players from burning in creative mode */
+ virtual void TickBurning(cChunk & a_Chunk) override;
- /// Called in each tick to handle food-related processing
+ /** Called in each tick to handle food-related processing */
void HandleFood(void);
- /// 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.
+ /** 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)
+ /** Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) */
void ApplyFoodExhaustionFromMovement();
+
+ /** Flag representing whether the player is currently in a bed
+ Set by a right click on unoccupied bed, unset by a time fast forward or teleport */
+ bool m_bIsInBed;
+
} ; // tolua_export
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 12ce9a303..e86bb48bd 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -4,6 +4,7 @@
// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
#include "Globals.h"
+#include "../Bindings/PluginManager.h"
#include "ProjectileEntity.h"
#include "../ClientHandle.h"
#include "Player.h"
@@ -50,12 +51,12 @@ protected:
LOGD("Hit block %d:%d at {%d, %d, %d} face %d, %s (%s)",
a_BlockType, a_BlockMeta,
a_BlockX, a_BlockY, a_BlockZ, a_EntryFace,
- g_BlockIsSolid[a_BlockType] ? "solid" : "non-solid",
+ cBlockInfo::IsSolid(a_BlockType) ? "solid" : "non-solid",
ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str()
);
*/
- if (g_BlockIsSolid[a_BlockType])
+ if (cBlockInfo::IsSolid(a_BlockType))
{
// The projectile hit a solid block
// Calculate the exact hit coords:
@@ -63,9 +64,14 @@ protected:
Vector3d Line1 = m_Projectile->GetPosition();
Vector3d Line2 = Line1 + m_Projectile->GetSpeed();
double LineCoeff = 0;
- char Face;
+ eBlockFace Face;
if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face))
{
+ if (cPluginManager::Get()->CallHookProjectileHitBlock(*m_Projectile))
+ {
+ return false;
+ }
+
Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff;
m_Projectile->OnHitSolidBlock(Intersection, Face);
return true;
@@ -138,7 +144,7 @@ public:
// Instead of colliding the bounding box with another bounding box in motion, we collide an enlarged bounding box with a hairline.
// The results should be good enough for our purposes
double LineCoeff;
- char Face;
+ eBlockFace Face;
EntBox.Expand(m_Projectile->GetWidth() / 2, m_Projectile->GetHeight() / 2, m_Projectile->GetWidth() / 2);
if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face))
{
@@ -147,7 +153,11 @@ public:
}
// TODO: Some entities don't interact with the projectiles (pickups, falling blocks)
- // TODO: Allow plugins to interfere about which entities can be hit
+ if (cPluginManager::Get()->CallHookProjectileHitEntity(*m_Projectile, *a_Entity))
+ {
+ // A plugin disagreed.
+ return false;
+ }
if (LineCoeff < m_MinCoeff)
{
@@ -214,7 +224,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve
-cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed)
+cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed)
{
Vector3d Speed;
if (a_Speed != NULL)
@@ -231,8 +241,15 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator,
case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed);
- case pkFirework: return new cFireworkEntity (a_Creator, a_X, a_Y, a_Z );
- // TODO: the rest
+ case pkFirework:
+ {
+ if (a_Item.m_FireworkItem.m_Colours.empty())
+ {
+ return NULL;
+ }
+
+ return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, a_Item);
+ }
}
LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind);
@@ -243,7 +260,7 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator,
-void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// Set the position based on what face was hit:
SetPosition(a_HitPos);
@@ -276,6 +293,7 @@ AString cProjectileEntity::GetMCAClassName(void) const
case pkExpBottle: return "ThrownExpBottle";
case pkSplashPotion: return "ThrownPotion";
case pkWitherSkull: return "WitherSkull";
+ case pkFirework: return "Firework";
case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this?
}
ASSERT(!"Unhandled projectile entity kind!");
@@ -353,13 +371,14 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
SetYawFromSpeed();
SetPitchFromSpeed();
- // DEBUG:
+ /*
LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}",
m_UniqueID,
GetPosX(), GetPosY(), GetPosZ(),
GetSpeedX(), GetSpeedY(), GetSpeedZ(),
GetYaw(), GetPitch()
);
+ */
}
@@ -446,7 +465,7 @@ bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
-void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
if (a_HitFace == BLOCK_FACE_NONE) { return; }
@@ -590,7 +609,7 @@ cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y,
-void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
if (m_World->GetTickRandomNumber(7) == 1)
{
@@ -623,7 +642,7 @@ cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X
-void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// Teleport the creator here, make them take 5 damage:
if (m_Creator != NULL)
@@ -653,10 +672,8 @@ cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, do
-void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
- // TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs
-
Destroy();
}
@@ -664,6 +681,30 @@ void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_Hi
+void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
+{
+ int TotalDamage = 0;
+ if (a_EntityHit.IsMob())
+ {
+ cMonster::eType MobType = ((cMonster &) a_EntityHit).GetMobType();
+ if (MobType == cMonster::mtBlaze)
+ {
+ TotalDamage = 3;
+ }
+ else if (MobType == cMonster::mtEnderDragon)
+ {
+ TotalDamage = 1;
+ }
+ }
+ a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
+
+ Destroy(true);
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBottleOEnchantingEntity :
@@ -677,9 +718,11 @@ super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
-void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
- // TODO: Spawn experience orbs
+ // Spawn an experience orb with a reward between 3 and 11.
+ m_World->BroadcastSoundParticleEffect(2002, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0);
+ m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
Destroy();
}
@@ -691,8 +734,10 @@ void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireworkEntity :
-cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z) :
-super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) :
+super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
+ m_ExplodeTimer(0),
+ m_FireworkItem(a_Item)
{
}
@@ -700,32 +745,20 @@ super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
-void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
- if ((a_HitFace != BLOCK_FACE_BOTTOM) && (a_HitFace != BLOCK_FACE_NONE))
+ int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
+ int PosY = POSY_TOINT;
+
+ if ((PosY < 0) || (PosY >= cChunkDef::Height))
{
- return;
+ goto setspeed;
}
- 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();
-}
-
-
-
-
-
-void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
-{
if (m_IsInGround)
{
- if (a_Chunk.GetBlock((int)GetPosX(), (int)GetPosY() + 1, (int)GetPosZ()) == E_BLOCK_AIR)
+ if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR)
{
m_IsInGround = false;
}
@@ -734,28 +767,35 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
return;
}
}
+ else
+ {
+ if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR)
+ {
+ OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM);
+ return;
+ }
+ }
- Vector3d PerTickSpeed = GetSpeed() / 20;
- Vector3d Pos = GetPosition();
+setspeed:
+ AddSpeedY(1);
+ AddPosition(GetSpeed() * (a_Dt / 1000));
+}
- // 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))
+
+
+
+
+void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks)
{
- // Something has been hit, abort all other processing
- return;
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_FIREWORK_EXPLODE);
+ Destroy();
}
- // 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);
- // Add slowdown and gravity effect to the speed:
- Vector3d NewSpeed(GetSpeed());
- NewSpeed.y += 2;
- NewSpeed *= TracerCallback.GetSlowdownCoeff();
- SetSpeed(NewSpeed);
+ m_ExplodeTimer++;
}
@@ -785,7 +825,7 @@ void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
-void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
@@ -831,7 +871,7 @@ void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
-void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
+void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
index 4721409ee..efb7ae783 100644
--- a/src/Entities/ProjectileEntity.h
+++ b/src/Entities/ProjectileEntity.h
@@ -46,10 +46,10 @@ public:
cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height);
- static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL);
+ static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed = NULL);
/// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace);
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace);
/// Called by the physics blocktracer when the entity hits another entity
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
@@ -174,7 +174,7 @@ protected:
Vector3i m_HitBlockPos;
// cProjectileEntity overrides:
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
virtual void CollectedBy(cPlayer * a_Player) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
@@ -204,7 +204,7 @@ protected:
// tolua_end
// cProjectileEntity overrides:
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
// tolua_begin
@@ -232,7 +232,7 @@ protected:
// tolua_end
// cProjectileEntity overrides:
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
// tolua_begin
@@ -258,7 +258,8 @@ public:
protected:
// cProjectileEntity overrides:
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
+ virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// tolua_begin
@@ -284,7 +285,7 @@ public:
protected:
// cProjectileEntity overrides:
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
// tolua_begin
@@ -305,13 +306,19 @@ public:
CLASS_PROTODEF(cFireworkEntity);
- cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z);
+ cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item);
+ const cItem & GetItem(void) const { return m_FireworkItem; }
protected:
// cProjectileEntity overrides:
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+private:
+
+ int m_ExplodeTimer;
+ cItem m_FireworkItem;
// tolua_begin
@@ -339,7 +346,7 @@ protected:
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
// cProjectileEntity overrides:
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// TODO: Deflecting the fireballs by arrow- or sword- hits
@@ -370,7 +377,7 @@ protected:
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
// cProjectileEntity overrides:
- virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override;
+ virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// tolua_begin
diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp
index 339107b2e..02f31f5bb 100644
--- a/src/Entities/TNTEntity.cpp
+++ b/src/Entities/TNTEntity.cpp
@@ -8,10 +8,9 @@
-cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec) :
+cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, int a_FuseTicks) :
super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98),
- m_Counter(0),
- m_MaxFuseTime(a_FuseTimeInSec)
+ m_FuseTicks(a_FuseTicks)
{
}
@@ -19,10 +18,9 @@ cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSe
-cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) :
+cTNTEntity::cTNTEntity(const Vector3d & a_Pos, int a_FuseTicks) :
super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98),
- m_Counter(0),
- m_MaxFuseTime(a_FuseTimeInSec)
+ m_FuseTicks(a_FuseTicks)
{
}
@@ -42,18 +40,27 @@ void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
+void cTNTEntity::Explode(void)
+{
+ m_FuseTicks = 0;
+ Destroy(true);
+ LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ());
+ m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this);
+}
+
+
+
+
+
void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
BroadcastMovementUpdate();
- float delta_time = a_Dt / 1000; // Convert miliseconds to seconds
- m_Counter += delta_time;
- if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM
+
+ m_FuseTicks -= 1;
+ if (m_FuseTicks <= 0)
{
- Destroy(true);
- LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ());
- m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this);
- return;
+ Explode();
}
}
diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h
index d1fcae766..116f5a8cb 100644
--- a/src/Entities/TNTEntity.h
+++ b/src/Entities/TNTEntity.h
@@ -16,19 +16,28 @@ public:
// tolua_end
CLASS_PROTODEF(cTNTEntity);
- cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec);
- cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec);
+ cTNTEntity(double a_X, double a_Y, double a_Z, int a_FuseTicks = 80);
+ cTNTEntity(const Vector3d & a_Pos, int a_FuseTicks = 80);
// cEntity overrides:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
-
- double GetCounterTime(void) const { return m_Counter; } // tolua_export
- double GetMaxFuseTime(void) const { return m_MaxFuseTime; } // tolua_export
+
+ // tolua_begin
+
+ /** Explode the tnt */
+ void Explode(void);
+
+ /** Returns the fuse ticks until the tnt will explode */
+ int GetFuseTicks(void) const { return m_FuseTicks; }
+
+ /** Set the fuse ticks until the tnt will explode */
+ void SetFuseTicks(int a_FuseTicks) { m_FuseTicks = a_FuseTicks; }
+
+ // tolua_end
protected:
- double m_Counter; ///< How much time has elapsed since the object was created, in seconds
- double m_MaxFuseTime; ///< How long the fuse is, in seconds
+ int m_FuseTicks; ///< How much ticks is left, while the tnt will explode
}; // tolua_export