summaryrefslogtreecommitdiffstats
path: root/source/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'source/Entities')
-rw-r--r--source/Entities/Entity.cpp49
-rw-r--r--source/Entities/Entity.h8
-rw-r--r--source/Entities/FallingBlock.cpp14
-rw-r--r--source/Entities/FallingBlock.h1
-rw-r--r--source/Entities/Minecart.cpp14
-rw-r--r--source/Entities/Minecart.h1
-rw-r--r--source/Entities/Pickup.cpp21
-rw-r--r--source/Entities/Pickup.h2
-rw-r--r--source/Entities/Player.cpp80
-rw-r--r--source/Entities/Player.h21
-rw-r--r--source/Entities/ProjectileEntity.cpp351
-rw-r--r--source/Entities/ProjectileEntity.h238
-rw-r--r--source/Entities/TNTEntity.cpp14
-rw-r--r--source/Entities/TNTEntity.h1
14 files changed, 728 insertions, 87 deletions
diff --git a/source/Entities/Entity.cpp b/source/Entities/Entity.cpp
index b9810aabb..56fd36a05 100644
--- a/source/Entities/Entity.cpp
+++ b/source/Entities/Entity.cpp
@@ -144,6 +144,10 @@ bool cEntity::Initialize(cWorld * a_World)
m_World->AddEntity(this);
cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this);
+
+ // Spawn the entity on the clients:
+ a_World->BroadcastSpawnEntity(*this);
+
return true;
}
@@ -477,7 +481,7 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
// TODO Add collision detection with entities.
- a_Dt /= 1000;
+ a_Dt /= 1000; // Convert from msec to sec
Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ());
Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ());
int BlockX = (int) floor(NextPos.x);
@@ -493,7 +497,7 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
// Make sure we got the correct chunk and a valid one. No one ever knows...
- cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX,BlockZ);
+ cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
if (NextChunk != NULL)
{
int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
@@ -513,11 +517,12 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
else
{
- //Push out entity.
+ // Push out entity.
m_bOnGround = true;
NextPos.y += 0.2;
- LOGD("Entity #%d (%s) is inside a block at {%d,%d,%d}",
- m_UniqueID, GetClass(), BlockX, BlockY, BlockZ);
+ LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
+ m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
+ );
}
if (!m_bOnGround)
@@ -525,16 +530,16 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
float fallspeed;
if (IsBlockWater(BlockIn))
{
- fallspeed = -3.0f * a_Dt; //Fall slower in water.
+ 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.
+ NextSpeed.y *= 0.05; // Reduce overall falling speed
+ fallspeed = 0; // No falling.
}
else
{
- //Normal gravity
+ // Normal gravity
fallspeed = m_Gravity * a_Dt;
}
NextSpeed.y += fallspeed;
@@ -548,19 +553,25 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
(BlockBelow != E_BLOCK_ACTIVATOR_RAIL)
)
{
- //Friction
+ // Friction
if (NextSpeed.SqrLength() > 0.0004f)
{
- 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.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;
+ }
}
}
}
- //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.
+ // 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;
@@ -1037,9 +1048,9 @@ void cEntity::SetMass(double a_Mass)
}
else
{
- //Make sure that mass is not zero. 1g is the default because we
- //have to choose a number. It's perfectly legal to have a mass
- //less than 1g as long as is NOT equal or less than zero.
+ // Make sure that mass is not zero. 1g is the default because we
+ // have to choose a number. It's perfectly legal to have a mass
+ // less than 1g as long as is NOT equal or less than zero.
m_Mass = 0.001;
}
}
diff --git a/source/Entities/Entity.h b/source/Entities/Entity.h
index 119cb2fe5..2d058abae 100644
--- a/source/Entities/Entity.h
+++ b/source/Entities/Entity.h
@@ -90,12 +90,13 @@ public:
etPlayer,
etPickup,
etMonster,
- etMob = etMonster, // DEPRECATED, use etMonster instead!
etFallingBlock,
etMinecart,
etTNT,
+ etProjectile,
// DEPRECATED older constants, left over for compatibility reasons (plugins)
+ etMob = etMonster, // DEPRECATED, use etMonster instead!
eEntityType_Entity = etEntity,
eEntityType_Player = etPlayer,
eEntityType_Pickup = etPickup,
@@ -162,7 +163,7 @@ public:
void SetPosY (double a_PosY);
void SetPosZ (double a_PosZ);
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 SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); }
void SetRot (const Vector3f & a_Rot);
void SetRotation(double a_Rotation);
void SetPitch (double a_Pitch);
@@ -276,9 +277,8 @@ public:
/** Descendants override this function to send a command to the specified client to spawn the entity on the client.
To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity()
- Needs to have a default implementation due to Lua bindings.
*/
- virtual void SpawnOn(cClientHandle & a_Client) {ASSERT(!"SpawnOn() unimplemented!"); }
+ virtual void SpawnOn(cClientHandle & a_Client) = 0;
// tolua_begin
diff --git a/source/Entities/FallingBlock.cpp b/source/Entities/FallingBlock.cpp
index 237327975..9fcd9ac80 100644
--- a/source/Entities/FallingBlock.cpp
+++ b/source/Entities/FallingBlock.cpp
@@ -22,20 +22,6 @@ cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_Block
-bool cFallingBlock::Initialize(cWorld * a_World)
-{
- if (super::Initialize(a_World))
- {
- a_World->BroadcastSpawnEntity(*this);
- return true;
- }
- return false;
-}
-
-
-
-
-
void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle)
{
a_ClientHandle.SendSpawnFallingBlock(*this);
diff --git a/source/Entities/FallingBlock.h b/source/Entities/FallingBlock.h
index 13931f061..5ba9909bb 100644
--- a/source/Entities/FallingBlock.h
+++ b/source/Entities/FallingBlock.h
@@ -29,7 +29,6 @@ public:
NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; }
// cEntity overrides:
- virtual bool Initialize(cWorld * a_World) override;
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
diff --git a/source/Entities/Minecart.cpp b/source/Entities/Minecart.cpp
index 9a92df38e..685067e79 100644
--- a/source/Entities/Minecart.cpp
+++ b/source/Entities/Minecart.cpp
@@ -26,20 +26,6 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
-bool cMinecart::Initialize(cWorld * a_World)
-{
- if (super::Initialize(a_World))
- {
- a_World->BroadcastSpawnEntity(*this);
- return true;
- }
- return false;
-}
-
-
-
-
-
void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
{
char SubType = 0;
diff --git a/source/Entities/Minecart.h b/source/Entities/Minecart.h
index b3386fbc9..f98b02bb5 100644
--- a/source/Entities/Minecart.h
+++ b/source/Entities/Minecart.h
@@ -35,7 +35,6 @@ public:
} ;
// cEntity overrides:
- virtual bool Initialize(cWorld * a_World) override;
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
void HandleRailPhysics(float a_Dt, cChunk & a_Chunk);
diff --git a/source/Entities/Pickup.cpp b/source/Entities/Pickup.cpp
index 0417b861d..9b388366a 100644
--- a/source/Entities/Pickup.cpp
+++ b/source/Entities/Pickup.cpp
@@ -40,20 +40,6 @@ cPickup::cPickup(int a_MicroPosX, int a_MicroPosY, int a_MicroPosZ, const cItem
-bool cPickup::Initialize(cWorld * a_World)
-{
- if (super::Initialize(a_World))
- {
- a_World->BroadcastSpawnEntity(*this);
- return true;
- }
- return false;
-}
-
-
-
-
-
void cPickup::SpawnOn(cClientHandle & a_Client)
{
a_Client.SendPickupSpawn(*this);
@@ -73,18 +59,19 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
if (!m_bCollected)
{
int BlockY = (int) floor(GetPosY());
- if (BlockY < cChunkDef::Height) // Don't do anything except for falling when above the world
+ if ((BlockY >= 0) && (BlockY < cChunkDef::Height)) // Don't do anything except for falling when outside the world
{
int BlockX = (int) floor(GetPosX());
int BlockZ = (int) floor(GetPosZ());
- //Position might have changed due to physics. So we have to make sure we have the correct chunk.
+ // Position might have changed due to physics. So we have to make sure we have the correct chunk.
cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
if (CurrentChunk != NULL) // Make sure the chunk is loaded
{
int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width);
int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width);
- BLOCKTYPE BlockBelow = CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ);
+ // If the pickup is on the bottommost block position, make it think the void is made of air: (#131)
+ BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ);
if (
diff --git a/source/Entities/Pickup.h b/source/Entities/Pickup.h
index b0323dd92..af6eaf3bb 100644
--- a/source/Entities/Pickup.h
+++ b/source/Entities/Pickup.h
@@ -26,8 +26,6 @@ public:
cPickup(int a_MicroPosX, int a_MicroPosY, int a_MicroPosZ, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export
- virtual bool Initialize(cWorld * a_World) override;
-
cItem & GetItem(void) {return m_Item; } // tolua_export
const cItem & GetItem(void) const {return m_Item; }
diff --git a/source/Entities/Player.cpp b/source/Entities/Player.cpp
index 3ccb4ca1d..0943f61ff 100644
--- a/source/Entities/Player.cpp
+++ b/source/Entities/Player.cpp
@@ -64,6 +64,8 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_IsSwimming(false)
, m_IsSubmerged(false)
, m_EatingFinishTick(-1)
+ , m_IsChargingBow(false)
+ , m_BowCharge(0)
{
LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
@@ -200,6 +202,12 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
}
}
+ if (!a_Chunk.IsValid())
+ {
+ // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83)
+ return;
+ }
+
super::Tick(a_Dt, a_Chunk);
// Set player swimming state
@@ -207,6 +215,13 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
// Handle air drowning stuff
HandleAir();
+
+ // Handle charging the bow:
+ if (m_IsChargingBow)
+ {
+ m_BowCharge += 1;
+ LOGD("Player \"%s\" charging bow: %d", m_PlayerName.c_str(), m_BowCharge);
+ }
if (m_bDirtyPosition)
{
@@ -247,6 +262,41 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
+void cPlayer::StartChargingBow(void)
+{
+ LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str());
+ m_IsChargingBow = true;
+ m_BowCharge = 0;
+}
+
+
+
+
+
+int cPlayer::FinishChargingBow(void)
+{
+ LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
+ int res = m_BowCharge;
+ m_IsChargingBow = false;
+ m_BowCharge = 0;
+ return res;
+}
+
+
+
+
+
+void cPlayer::CancelChargingBow(void)
+{
+ LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
+ m_IsChargingBow = false;
+ m_BowCharge = 0;
+}
+
+
+
+
+
void cPlayer::SetTouchGround(bool a_bTouchGround)
{
// If just
@@ -807,6 +857,36 @@ void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
+Vector3d cPlayer::GetThrowStartPos(void) const
+{
+ Vector3d res = GetEyePosition();
+
+ // Adjust the position to be just outside the player's bounding box:
+ res.x += 0.16 * cos(GetPitch());
+ res.y += -0.1;
+ res.z += 0.16 * sin(GetPitch());
+
+ return res;
+}
+
+
+
+
+
+Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const
+{
+ Vector3d res = GetLookVector();
+ res.Normalize();
+
+ // TODO: Add a slight random change (+-0.0075 in each direction)
+
+ return res * a_SpeedCoeff;
+}
+
+
+
+
+
void cPlayer::MoveTo( const Vector3d & a_NewPos )
{
if ((a_NewPos.y < -990) && (GetPosY() > -100))
diff --git a/source/Entities/Player.h b/source/Entities/Player.h
index 5dcce8421..82ff48954 100644
--- a/source/Entities/Player.h
+++ b/source/Entities/Player.h
@@ -62,6 +62,18 @@ public:
/// Returns the currently equipped boots; empty item if none
virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); }
+
+ /// 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
+ int FinishChargingBow(void);
+
+ /// Cancels the current bow charging
+ void CancelChargingBow(void);
+
+ /// Returns true if the player is currently charging the bow
+ bool IsChargingBow(void) const { return m_IsChargingBow; }
void SetTouchGround( bool a_bTouchGround );
inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; }
@@ -78,6 +90,12 @@ public:
// tolua_begin
+ /// 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.
+ Vector3d GetThrowSpeed(double a_SpeedCoeff) const;
+
/// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
eGameMode GetGameMode(void) const { return m_GameMode; }
@@ -351,6 +369,9 @@ protected:
/// The world tick in which eating will be finished. -1 if not eating
Int64 m_EatingFinishTick;
+
+ bool m_IsChargingBow;
+ int m_BowCharge;
virtual void Destroyed(void);
diff --git a/source/Entities/ProjectileEntity.cpp b/source/Entities/ProjectileEntity.cpp
new file mode 100644
index 000000000..91b2c97a8
--- /dev/null
+++ b/source/Entities/ProjectileEntity.cpp
@@ -0,0 +1,351 @@
+
+// ProjectileEntity.cpp
+
+// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
+
+#include "Globals.h"
+#include "ProjectileEntity.h"
+#include "../ClientHandle.h"
+#include "Player.h"
+#include "../LineBlockTracer.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProjectileTracerCallback:
+
+class cProjectileTracerCallback :
+ public cBlockTracer::cCallbacks
+{
+public:
+ cProjectileTracerCallback(cProjectileEntity * a_Projectile) :
+ m_Projectile(a_Projectile)
+ {
+ }
+
+protected:
+ cProjectileEntity * m_Projectile;
+
+ virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override
+ {
+ if (g_BlockIsSolid[a_BlockType])
+ {
+ // The projectile hit a solid block
+ m_Projectile->OnHitSolidBlock(a_BlockX, a_BlockY, a_BlockZ, a_EntryFace);
+ return true;
+ }
+
+ // Convey some special effects from special blocks:
+ switch (a_BlockType)
+ {
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ m_Projectile->StartBurning(30);
+ break;
+ }
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ m_Projectile->StopBurning();
+ break;
+ }
+ } // switch (a_BlockType)
+
+ // Continue tracing
+ return false;
+ }
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProjectileEntity:
+
+cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) :
+ super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height),
+ m_ProjectileKind(a_Kind),
+ m_Creator(a_Creator),
+ m_IsInGround(false)
+{
+}
+
+
+
+
+
+cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) :
+ super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height),
+ m_ProjectileKind(a_Kind),
+ m_Creator(a_Creator),
+ m_IsInGround(false)
+{
+ SetSpeed(a_Speed);
+}
+
+
+
+
+
+cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed)
+{
+ Vector3d Speed;
+ if (a_Speed != NULL)
+ {
+ Speed = *a_Speed;
+ }
+
+ switch (a_Kind)
+ {
+ case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed);
+ case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
+ // TODO: the rest
+ }
+
+ LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind);
+ return NULL;
+}
+
+
+
+
+
+void cProjectileEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+{
+ // TODO: Set proper position based on what face was hit
+ switch (a_BlockFace)
+ {
+ case BLOCK_FACE_TOP: SetPosition(0.5 + a_BlockX, 1.0 + a_BlockY, 0.5 + a_BlockZ); break;
+ case BLOCK_FACE_BOTTOM: SetPosition(0.5 + a_BlockX, a_BlockY, 0.5 + a_BlockZ); break;
+ case BLOCK_FACE_EAST: SetPosition( a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
+ case BLOCK_FACE_WEST: SetPosition(1.0 + a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
+ case BLOCK_FACE_NORTH: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, 1.0 + a_BlockZ); break;
+ case BLOCK_FACE_SOUTH: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, a_BlockZ); break;
+ case BLOCK_FACE_NONE: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
+ }
+ SetSpeed(0, 0, 0);
+
+ // DEBUG:
+ LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, hit solid block at face %d",
+ m_UniqueID,
+ GetPosX(), GetPosY(), GetPosZ(),
+ a_BlockFace
+ );
+
+ m_IsInGround = true;
+}
+
+
+
+
+
+AString cProjectileEntity::GetMCAClassName(void) const
+{
+ switch (m_ProjectileKind)
+ {
+ case pkArrow: return "Arrow";
+ case pkSnowball: return "Snowball";
+ case pkEgg: return "Egg";
+ case pkGhastFireball: return "Fireball";
+ case pkFireCharge: return "SmallFireball";
+ case pkEnderPearl: return "ThrownEnderPearl";
+ case pkExpBottle: return "ThrownExpBottle";
+ case pkSplashPotion: return "ThrownPotion";
+ case pkWitherSkull: return "WitherSkull";
+ case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this?
+ }
+ ASSERT(!"Unhandled projectile entity kind!");
+ return "";
+}
+
+
+
+
+
+void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+ BroadcastMovementUpdate();
+}
+
+
+
+
+
+void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
+{
+ if (m_IsInGround)
+ {
+ // Already-grounded projectiles don't move at all
+ return;
+ }
+
+ Vector3d PerTickSpeed = GetSpeed() / 20;
+ Vector3d Pos = GetPosition();
+
+ // Trace the tick's worth of movement as a line:
+ Vector3d NextPos = Pos + PerTickSpeed;
+ cProjectileTracerCallback TracerCallback(this);
+ if (cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
+ {
+ // Nothing in the way, update the position
+ SetPosition(NextPos);
+ }
+
+ // Add gravity effect to the vertical speed component:
+ SetSpeedY(GetSpeedY() + m_Gravity / 20);
+
+ // DEBUG:
+ LOGD("Arrow %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}",
+ m_UniqueID,
+ GetPosX(), GetPosY(), GetPosZ(),
+ GetSpeedX(), GetSpeedY(), GetSpeedZ()
+ );
+}
+
+
+
+
+
+void cProjectileEntity::SpawnOn(cClientHandle & a_Client)
+{
+ // Default spawning - use the projectile kind to spawn an object:
+ a_Client.SendSpawnObject(*this, m_ProjectileKind, 0, 0, 0);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cArrowEntity:
+
+cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5),
+ m_PickupState(psNoPickup),
+ m_DamageCoeff(2)
+{
+ SetSpeed(a_Speed);
+ SetMass(0.1);
+ LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f}",
+ m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ()
+ );
+}
+
+
+
+
+
+cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
+ super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5),
+ m_PickupState(psInSurvivalOrCreative),
+ m_DamageCoeff(2)
+{
+}
+
+
+
+
+
+bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
+{
+ switch (m_PickupState)
+ {
+ case psNoPickup: return false;
+ case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative());
+ case psInCreative: return a_Player.IsGameModeCreative();
+ }
+ ASSERT(!"Unhandled pickup state");
+ return false;
+}
+
+
+
+
+
+void cArrowEntity::SpawnOn(cClientHandle & a_Client)
+{
+ a_Client.SendSpawnObject(*this, pkArrow, 0, 0, 0);
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cThrownEggEntity:
+
+cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+{
+ SetSpeed(a_Speed);
+}
+
+
+
+
+
+void cThrownEggEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+{
+ // TODO: Random-spawn a chicken or four
+
+ Destroy();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cThrownEnderPearlEntity :
+
+cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+{
+ SetSpeed(a_Speed);
+}
+
+
+
+
+
+void cThrownEnderPearlEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+{
+ // TODO: Teleport the creator here, make them take 5 damage
+
+ Destroy();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cThrownSnowballEntity :
+
+cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
+ super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
+{
+ SetSpeed(a_Speed);
+}
+
+
+
+
+
+void cThrownSnowballEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+{
+ // TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs
+
+ Destroy();
+}
+
+
+
+
+
diff --git a/source/Entities/ProjectileEntity.h b/source/Entities/ProjectileEntity.h
new file mode 100644
index 000000000..95dc00abc
--- /dev/null
+++ b/source/Entities/ProjectileEntity.h
@@ -0,0 +1,238 @@
+
+// ProjectileEntity.h
+
+// Declares the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
+
+
+
+
+
+#pragma once
+
+#include "Entity.h"
+
+
+
+
+
+// tolua_begin
+
+class cProjectileEntity :
+ public cEntity
+{
+ typedef cEntity super;
+
+public:
+ /// The kind of the projectile. The numbers correspond to the network type ID used for spawning via the 0x17 packet.
+ enum eKind
+ {
+ pkArrow = 60,
+ pkSnowball = 61,
+ pkEgg = 62,
+ pkGhastFireball = 63,
+ pkFireCharge = 64,
+ pkEnderPearl = 65,
+ pkExpBottle = 75,
+ pkSplashPotion = 73,
+ pkWitherSkull = 66,
+ pkFishingFloat = 90,
+ } ;
+
+ // tolua_end
+
+ CLASS_PROTODEF(cProjectileEntity);
+
+ 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);
+
+ /// Called by the physics blocktracer when the entity hits a solid block, the block's coords and the face hit is given
+ virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
+
+ // tolua_begin
+
+ /// Returns the kind of the projectile (fast class identification)
+ eKind GetProjectileKind(void) const { return m_ProjectileKind; }
+
+ /// Returns the entity who created this projectile; may be NULL
+ cEntity * GetCreator(void) { return m_Creator; }
+
+ /// Returns the string that is used as the entity type (class name) in MCA files
+ AString GetMCAClassName(void) const;
+
+ /// Returns true if the projectile has hit the ground and is stuck there
+ bool IsInGround(void) const { return m_IsInGround; }
+
+protected:
+ eKind m_ProjectileKind;
+
+ /// The entity who has created this projectile; may be NULL (e. g. for dispensers)
+ cEntity * m_Creator;
+
+ /// True if the projectile has hit the ground and is stuck there
+ bool m_IsInGround;
+
+ // tolua_end
+
+ // cEntity overrides:
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
+ virtual void SpawnOn(cClientHandle & a_Client) override;
+
+ // tolua_begin
+} ;
+
+
+
+
+
+class cArrowEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+ /// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field
+ enum ePickupState
+ {
+ psNoPickup = 0,
+ psInSurvivalOrCreative = 1,
+ psInCreative = 2,
+ } ;
+
+ // tolua_end
+
+ CLASS_PROTODEF(cArrowEntity);
+
+ /// Creates a new arrow with psNoPickup state and default damage modifier coeff
+ cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+ /// Creates a new arrow as shot by a player, initializes it from the player object
+ cArrowEntity(cPlayer & a_Player, double a_Force);
+
+ // tolua_begin
+
+ /// Returns whether the arrow can be picked up by players
+ ePickupState GetPickupState(void) const { return m_PickupState; }
+
+ /// Sets a new pickup state
+ void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; }
+
+ /// Returns the damage modifier coeff.
+ double GetDamageCoeff(void) const { return m_DamageCoeff; }
+
+ /// Sets the damage modifier coeff
+ void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
+
+ /// Returns true if the specified player can pick the arrow up
+ bool CanPickup(const cPlayer & a_Player) const;
+
+ // tolua_end
+
+protected:
+
+ /// Determines when the arrow can be picked up by players
+ ePickupState m_PickupState;
+
+ /// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow
+ double m_DamageCoeff;
+
+ // cProjectileEntity overrides:
+ virtual void SpawnOn(cClientHandle & a_Client) override;
+
+ // tolua_begin
+} ;
+
+
+
+
+
+class cThrownEggEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cThrownEggEntity);
+
+ cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ // tolua_end
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
+
+ // tolua_begin
+
+} ;
+
+
+
+
+
+class cThrownEnderPearlEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cThrownEnderPearlEntity);
+
+ cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ // tolua_end
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
+
+ // tolua_begin
+
+} ;
+
+
+
+
+
+class cThrownSnowballEntity :
+ public cProjectileEntity
+{
+ typedef cProjectileEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cThrownSnowballEntity);
+
+ cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
+
+protected:
+
+ // tolua_end
+
+ // cProjectileEntity overrides:
+ virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
+
+ // tolua_begin
+
+} ;
+
+
+
+
+
+// tolua_end
+
+
+
diff --git a/source/Entities/TNTEntity.cpp b/source/Entities/TNTEntity.cpp
index 43a0dea09..ad3d9ae0c 100644
--- a/source/Entities/TNTEntity.cpp
+++ b/source/Entities/TNTEntity.cpp
@@ -29,20 +29,6 @@ cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) :
-bool cTNTEntity::Initialize(cWorld * a_World)
-{
- if (super::Initialize(a_World))
- {
- a_World->BroadcastSpawnEntity(*this);
- return true;
- }
- return false;
-}
-
-
-
-
-
void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
{
a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT
diff --git a/source/Entities/TNTEntity.h b/source/Entities/TNTEntity.h
index ae6fc75e2..eb5040e8a 100644
--- a/source/Entities/TNTEntity.h
+++ b/source/Entities/TNTEntity.h
@@ -19,7 +19,6 @@ public:
cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec);
// cEntity overrides:
- virtual bool Initialize(cWorld * a_World) override;
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;