diff --git a/source/Entities/Entity.cpp b/source/Entities/Entity.cpp index b936d8d73..a07811a96 100644 --- a/source/Entities/Entity.cpp +++ b/source/Entities/Entity.cpp @@ -253,6 +253,39 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R +void cEntity::SetRotationFromSpeed(void) +{ + const double EPS = 0.0000001; + if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS)) + { + // atan2() may overflow or is undefined, pick any number + SetRotation(0); + return; + } + SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI); +} + + + + + +void cEntity::SetPitchFromSpeed(void) +{ + const double EPS = 0.0000001; + double xz = sqrt(m_Speed.x * m_Speed.x + m_Speed.z * m_Speed.z); // Speed XZ-plane component + if ((abs(xz) < EPS) && (abs(m_Speed.y) < EPS)) + { + // atan2() may overflow or is undefined, pick any number + SetPitch(0); + return; + } + SetPitch(atan2(m_Speed.y, xz) * 180 / PI); +} + + + + + void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) { if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI)) diff --git a/source/Entities/Entity.h b/source/Entities/Entity.h index aa5d8fbd2..0e30f230c 100644 --- a/source/Entities/Entity.h +++ b/source/Entities/Entity.h @@ -205,6 +205,12 @@ public: void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; } + /// Sets the rotation to match the speed vector (entity goes "face-forward") + void SetRotationFromSpeed(void); + + /// Sets the pitch to match the speed vector (entity gies "face-forward") + void SetPitchFromSpeed(void); + // tolua_end /// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied diff --git a/source/Entities/ProjectileEntity.cpp b/source/Entities/ProjectileEntity.cpp index b97f72884..81ac336f0 100644 --- a/source/Entities/ProjectileEntity.cpp +++ b/source/Entities/ProjectileEntity.cpp @@ -16,6 +16,13 @@ +/// Converts an angle in radians into a byte representation used by the network protocol +#define ANGLE_TO_PROTO(X) (Byte)(X * 255 / 360) + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cProjectileTracerCallback: @@ -189,6 +196,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve m_IsInGround(false) { SetSpeed(a_Speed); + SetRotationFromSpeed(); + SetPitchFromSpeed(); } @@ -321,12 +330,15 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) NewSpeed.y += m_Gravity / 20; NewSpeed *= TracerCallback.GetSlowdownCoeff(); SetSpeed(NewSpeed); + SetRotationFromSpeed(); + SetPitchFromSpeed(); // DEBUG: - LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}", + LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}", m_UniqueID, GetPosX(), GetPosY(), GetPosZ(), - GetSpeedX(), GetSpeedY(), GetSpeedZ() + GetSpeedX(), GetSpeedY(), GetSpeedZ(), + GetRotation(), GetPitch() ); } @@ -337,7 +349,8 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) 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); + a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch())); + a_Client.SendEntityMetadata(*this); } @@ -355,8 +368,11 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a { SetSpeed(a_Speed); SetMass(0.1); - LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f}", - m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ() + SetRotationFromSpeed(); + SetPitchFromSpeed(); + LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", + m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), + GetRotation(), GetPitch() ); } @@ -392,14 +408,21 @@ bool cArrowEntity::CanPickup(const cPlayer & a_Player) const -void cArrowEntity::SpawnOn(cClientHandle & a_Client) +void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) { - a_Client.SendSpawnObject(*this, pkArrow, 0, 0, 0); + super::OnHitSolidBlock(a_HitPos, a_HitFace); + + // Broadcast the position and speed packets before teleporting: + BroadcastMovementUpdate(); + + // Teleport the entity to the exact hit coords: + m_World->BroadcastTeleportEntity(*this); } + void cArrowEntity::OnHitEntity(cEntity & a_EntityHit) { if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer()) diff --git a/source/Entities/ProjectileEntity.h b/source/Entities/ProjectileEntity.h index 5863b4f10..43aecac0e 100644 --- a/source/Entities/ProjectileEntity.h +++ b/source/Entities/ProjectileEntity.h @@ -151,7 +151,7 @@ protected: bool m_IsCritical; // cProjectileEntity overrides: - virtual void SpawnOn(cClientHandle & a_Client) override; + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit) override; // tolua_begin