diff --git a/source/Entity.cpp b/source/Entity.cpp index 59d07e33f..61d0da026 100644 --- a/source/Entity.cpp +++ b/source/Entity.cpp @@ -9,6 +9,9 @@ #include "Matrix4f.h" #include "ReferenceManager.h" #include "ClientHandle.h" +#include "Tracer.h" +#include "Chunk.h" +#include "Simulator/FluidSimulator.h" @@ -33,6 +36,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z) , m_bDirtyOrientation(true) , m_bDirtyPosition(true) , m_bDirtySpeed(true) + , m_bOnGround( false ) + , m_Gravity( -9.81f ) , m_IsInitialized(false) , m_LastPosX( 0.0 ) , m_LastPosY( 0.0 ) @@ -44,6 +49,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z) , m_World(NULL) , m_FireDamageInterval(0.f) , m_BurnPeriod(0.f) + , m_WaterSpeed( 0.0 , 0.0 , 0.0 ) { cCSLock Lock(m_CSCount); m_EntityCount++; @@ -201,6 +207,160 @@ 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; + Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ()); + Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ()); + int BlockX = (int) floor(NextPos.x); + int BlockY = (int) floor(NextPos.y); + int BlockZ = (int) floor(NextPos.z); + //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 - (NextChunk->GetPosX() * cChunkDef::Width); + int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width); + BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ ); + if( BlockIn == E_BLOCK_AIR || IsBlockWater(BlockIn) || BlockIn == E_BLOCK_FIRE || IsBlockLava(BlockIn) ) // If not in ground itself or in water or in fire or in lava + { + if( m_bOnGround ) // check if it's still on the ground + { + BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ ); + if(BlockBelow == E_BLOCK_AIR || IsBlockWater(BlockBelow) || BlockBelow == E_BLOCK_FIRE || IsBlockLava(BlockBelow)) //Check if block below is air or water. + { + m_bOnGround = false; + } + } + } + else + { + //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); + } + + if (!m_bOnGround) + { + float fallspeed; + if (!IsBlockWater(BlockIn)) + { + fallspeed = m_Gravity * a_Dt; + } + else + { + fallspeed = -3.0f * a_Dt; //Fall slower in water. + } + 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; + NextSpeed.z *= 0.7f/(1+a_Dt); + if ( fabs(NextSpeed.z) < 0.05 ) NextSpeed.z = 0; + } + } + + //Get water direction + Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ); + + m_WaterSpeed *= 0.9f; //Reduce speed each tick + + switch(WaterDir) + { + case X_PLUS: + m_WaterSpeed.x = 1.f; + m_bOnGround = false; + break; + case X_MINUS: + m_WaterSpeed.x = -1.f; + m_bOnGround = false; + break; + case Z_PLUS: + m_WaterSpeed.z = 1.f; + m_bOnGround = false; + break; + case Z_MINUS: + m_WaterSpeed.z = -1.f; + m_bOnGround = false; + break; + + default: + break; + } + + if (fabs(m_WaterSpeed.x) < 0.05) + { + m_WaterSpeed.x = 0; + } + + if (fabs(m_WaterSpeed.z) < 0.05) + { + m_WaterSpeed.z = 0; + } + + NextSpeed += m_WaterSpeed; + + if( NextSpeed.SqrLength() > 0.f ) + { + 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() ) + { + 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.5f; + NextPos.z += Tracer.HitNormal.z * 0.5f; + } + else + NextPos += (NextSpeed * a_Dt); + } + 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) + { + 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); + } + } +} + + + + + void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) { //We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks diff --git a/source/Entity.h b/source/Entity.h index 5871ce89c..3009bc611 100644 --- a/source/Entity.h +++ b/source/Entity.h @@ -159,7 +159,7 @@ public: // tolua_end virtual void Tick(float a_Dt, cChunk & a_Chunk); - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) {} + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk); /** 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() @@ -220,6 +220,9 @@ protected: bool m_bDirtyPosition; bool m_bDirtySpeed; + bool m_bOnGround; + float m_Gravity; + // Last Position. double m_LastPosX, m_LastPosY, m_LastPosZ; @@ -248,6 +251,7 @@ private: Vector3d m_Speed; Vector3d m_Rot; Vector3d m_Pos; + Vector3d m_WaterSpeed; } ; // tolua_export typedef std::list cEntityList; diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp index 8781858a6..232143579 100644 --- a/source/Mobs/Monster.cpp +++ b/source/Mobs/Monster.cpp @@ -14,8 +14,8 @@ #include "../Vector3f.h" #include "../Vector3i.h" #include "../Vector3d.h" - #include "../Tracer.h" + // #include "../../iniFile/iniFile.h" @@ -27,8 +27,6 @@ cMonster::cMonster(const AString & a_ConfigName, char a_ProtocolMobType, const A , m_Target(NULL) , m_bMovingToDestination(false) , m_DestinationTime( 0 ) - , m_Gravity( -9.81f) - , m_bOnGround( false ) , m_DestroyTimer( 0 ) , m_Jump(0) , m_MobType(a_ProtocolMobType) @@ -102,6 +100,9 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) return; } + HandlePhysics(a_Dt,a_Chunk); + BroadcastMovementUpdate(); + a_Dt /= 1000; if (m_bMovingToDestination) @@ -143,9 +144,6 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) } } - HandlePhysics(a_Dt); - BroadcastMovementUpdate(); - Vector3d Distance = m_Destination - GetPosition(); if (Distance.SqrLength() > 0.1f) { @@ -154,7 +152,7 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch ); SetHeadYaw (Rotation); SetRotation( Rotation ); - SetPitch( Pitch ); + SetPitch( -Pitch ); } switch (m_EMState) @@ -184,120 +182,6 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) -//Not used. Will remove later when we implement the AI. -void cMonster::ReplicateMovement() -{ - if (m_bDirtyOrientation && !m_bDirtyPosition) - { - m_World->BroadcastEntLook(*this); - m_bDirtyOrientation = false; - } - - if (m_bDirtyPosition) - { - float DiffX = (float)(GetPosX() - m_LastPosX); - float DiffY = (float)(GetPosY() - m_LastPosY); - float DiffZ = (float)(GetPosZ() - m_LastPosZ); - float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; - if ( - (SqrDist > 4 * 4) // 4 blocks is max Relative Move - || (m_World->GetWorldAge() - m_TimeLastTeleportPacket > 40) // Send an absolute position every 2 seconds - ) - { - // LOGD("Teleported %f", sqrtf(SqrDist) ); - m_World->BroadcastTeleportEntity(*this); - m_TimeLastTeleportPacket = m_World->GetWorldAge(); - } - else - { - // Relative move sucks balls! It's always wrong wtf! - if (m_bDirtyOrientation) - { - m_World->BroadcastEntRelMoveLook(*this, (char)(DiffX * 32), (char)(DiffY * 32), (char)(DiffZ * 32)); - m_bDirtyOrientation = false; - } - else - { - m_World->BroadcastEntRelMove(*this, (char)(DiffX * 32), (char)(DiffY * 32), (char)(DiffZ * 32)); - } - } - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_bDirtyPosition = false; - } -} - - - - - -void cMonster::HandlePhysics(float a_Dt) -{ - if( m_bOnGround ) // check if it's still on the ground - { - cWorld* World = GetWorld(); - if( World->GetBlock( (int)GetPosX(), (int)GetPosY() -1, (int)GetPosZ() ) == E_BLOCK_AIR ) - { - m_bOnGround = false; - } - if( World->GetBlock( (int)GetPosX(), (int)GetPosY(), (int)GetPosZ() ) != E_BLOCK_AIR ) // If in ground itself, push it out - { - m_bOnGround = true; - SetPosY(GetPosY() + 0.2); - m_bDirtyPosition = true; - } - SetSpeedX(GetSpeedX() * 0.7f/(1+a_Dt)); - if( fabs(GetSpeedX()) < 0.05 ) SetSpeedX(0); - SetSpeedZ(GetSpeedZ() * 0.7f/(1+a_Dt)); - if( fabs(GetSpeedZ()) < 0.05 ) SetSpeedZ(0); - } - - if( !m_bOnGround ) - { - float Gravity = -9.81f*a_Dt; - SetSpeedY(GetSpeedY() + Gravity); - } - - if( GetSpeed().SqrLength() > 0.f ) - { - cTracer Tracer( GetWorld() ); - int Ret = Tracer.Trace( GetPosition(), GetSpeed(), 2 ); - if( Ret ) // Oh noez! we hit something - { - // Set to hit position - if( (Tracer.RealHit - GetPosition()).SqrLength() <= ( GetSpeed() * a_Dt ).SqrLength() ) - { - if( Ret == 1 ) - { - - if( Tracer.HitNormal.x != 0.f ) SetSpeedX(0.f); - if( Tracer.HitNormal.y != 0.f ) SetSpeedY(0.f); - if( Tracer.HitNormal.z != 0.f ) SetSpeedZ(0.f); - - if( Tracer.HitNormal.y > 0 ) // means on ground - { - m_bOnGround = true; - } - } - SetPosition(Tracer.RealHit); - SetPosX(GetPosX() + (Tracer.HitNormal.x * 0.5f)); - SetPosZ(GetPosZ() + (Tracer.HitNormal.z * 0.5f)); - } - else - SetPosition(GetPosition() + (GetSpeed()*a_Dt)); - } - else - { // We didn't hit anything, so move =] - SetPosition(GetPosition() + (GetSpeed()*a_Dt)); - } - - m_bDirtyPosition = true; - } -} - - - void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) diff --git a/source/Mobs/Monster.h b/source/Mobs/Monster.h index c7abeb243..ec27df38a 100644 --- a/source/Mobs/Monster.h +++ b/source/Mobs/Monster.h @@ -38,9 +38,6 @@ public: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - virtual void HandlePhysics(float a_Dt); - virtual void ReplicateMovement(void); virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; @@ -91,9 +88,6 @@ protected: float m_DestinationTime; - float m_Gravity; - bool m_bOnGround; - float m_DestroyTimer; float m_Jump; diff --git a/source/Pickup.cpp b/source/Pickup.cpp index 7cee53cb7..438216266 100644 --- a/source/Pickup.cpp +++ b/source/Pickup.cpp @@ -16,6 +16,7 @@ #include "Item.h" #include "Root.h" #include "Tracer.h" +#include "Chunk.h" #include "Vector3d.h" #include "Vector3f.h" @@ -27,13 +28,12 @@ cPickup::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 */) : cEntity(etPickup, ((double)(a_MicroPosX)) / 32, ((double)(a_MicroPosY)) / 32, ((double)(a_MicroPosZ)) / 32) , m_Health(5) - , m_bOnGround( false ) - , m_bReplicated( false ) , m_Timer( 0.f ) , m_Item(a_Item) , m_bCollected( false ) { SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); + m_Gravity = -3.0; } @@ -62,10 +62,40 @@ void cPickup::SpawnOn(cClientHandle & a_Client) void cPickup::Tick(float a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - + BroadcastMovementUpdate(); //Notify clients of position + m_Timer += a_Dt; - a_Dt = a_Dt / 1000.f; - if (m_bCollected) + + if (!m_bCollected) + { + int BlockX = (int) floor(GetPosX()); + int BlockY = (int) floor(GetPosY()); + int BlockZ = (int) floor(GetPosZ()); + //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) //Making 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 ); + BLOCKTYPE BlockIn = CurrentChunk->GetBlock( RelBlockX, BlockY, RelBlockZ ); + + if( IsBlockLava(BlockBelow) || BlockBelow == E_BLOCK_FIRE + || IsBlockLava(BlockIn) || BlockIn == E_BLOCK_FIRE ) + { + m_bCollected = true; + m_Timer = 0; //We have to reset the timer. + m_Timer += a_Dt; //In case we have to destroy the pickup in the same tick. + if (m_Timer > 500.f) + { + Destroy(); + return; + } + } + } + } + else { if (m_Timer > 500.f) // 0.5 second { @@ -85,144 +115,6 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk) Destroy(); return; } - - if (!m_bReplicated || m_bDirtyPosition) - { - m_bReplicated = true; - m_bDirtyPosition = false; - GetWorld()->BroadcastTeleportEntity(*this); - } -} - - - - - -void cPickup::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - // TODO: Rewrite this function to use a_Chunk instead of m_World - - m_ResultingSpeed.Set(0.f, 0.f, 0.f); - cWorld * World = GetWorld(); - - a_Dt /= 1000; // Go from msec to sec, so that physics formulas work - - if (m_bOnGround) // check if it's still on the ground - { - int BlockX = (GetPosX() < 0) ? (int)GetPosX() - 1 : (int)GetPosX(); - int BlockZ = (GetPosZ() < 0) ? (int)GetPosZ() - 1 : (int)GetPosZ(); - char BlockBelow = World->GetBlock(BlockX, (int)GetPosY() - 1, BlockZ); - if (BlockBelow == E_BLOCK_AIR || IsBlockWater(BlockBelow)) - { - m_bOnGround = false; - } - char Block = World->GetBlock(BlockX, (int)GetPosY() - (int)m_bOnGround, BlockZ ); - char BlockIn = World->GetBlock(BlockX, (int)GetPosY(), BlockZ ); - - if( IsBlockLava(Block) || Block == E_BLOCK_FIRE - || IsBlockLava(BlockIn) || BlockIn == E_BLOCK_FIRE) - { - m_bCollected = true; - m_Timer = 0; - return; - } - - if( BlockIn != E_BLOCK_AIR && !IsBlockWater(BlockIn) ) // If in ground itself, push it out - { - m_bOnGround = true; - AddPosY(0.2); - m_bReplicated = false; - } - SetSpeedX(GetSpeedX() * 0.7f/(1+a_Dt)); - if( fabs(GetSpeedX()) < 0.05 ) SetSpeedX(0); - SetSpeedZ(GetSpeedZ() * 0.7f/(1+a_Dt)); - if( fabs(GetSpeedZ()) < 0.05 ) SetSpeedZ(0); - } - - // get flowing direction - Direction WaterDir = World->GetWaterSimulator()->GetFlowingDirection((int) GetSpeedX() - 1, (int) GetSpeedY(), (int) GetSpeedZ() - 1); - - m_WaterSpeed *= 0.9f; //Keep old speed but lower it - - switch(WaterDir) - { - case X_PLUS: - m_WaterSpeed.x = 1.f; - m_bOnGround = false; - break; - case X_MINUS: - m_WaterSpeed.x = -1.f; - m_bOnGround = false; - break; - case Z_PLUS: - m_WaterSpeed.z = 1.f; - m_bOnGround = false; - break; - case Z_MINUS: - m_WaterSpeed.z = -1.f; - m_bOnGround = false; - break; - - default: - break; - } - - m_ResultingSpeed += m_WaterSpeed; - - if (!m_bOnGround) - { - - float Gravity = -9.81f * a_Dt; - if (Gravity < -3) // Cap gravity-caused speed at 3 m / sec - { - Gravity = -3; - } - AddSpeedY(Gravity); - - // Set to hit position - m_ResultingSpeed += GetSpeed(); - - /* - LOGD("Pickup #%d speed: {%.03f, %.03f, %.03f}, pos {%.02f, %.02f, %.02f}", - m_UniqueID, - m_ResultingSpeed.x, m_ResultingSpeed.y, m_ResultingSpeed.z, - m_Pos.x, m_Pos.y, m_Pos.z - ); - */ - - cTracer Tracer(GetWorld()); - int Ret = Tracer.Trace(GetPosition(), GetSpeed(), 2); - if (Ret) // Oh noez! we hit something - { - if ((Tracer.RealHit - Vector3f(GetPosition())).SqrLength() <= ( m_ResultingSpeed * a_Dt ).SqrLength()) - { - m_bReplicated = false; // It's only interesting to replicate when we actually hit something... - if (Ret == 1) - { - - if( Tracer.HitNormal.x != 0.f ) SetSpeedX(0.f); - if( Tracer.HitNormal.y != 0.f ) SetSpeedY(0.f); - if( Tracer.HitNormal.z != 0.f ) SetSpeedZ(0.f); - - if (Tracer.HitNormal.y > 0) // means on ground - { - m_bOnGround = true; - } - } - SetPosition(Tracer.RealHit); - AddPosition(Tracer.HitNormal * 0.2f); - - } - else - AddPosition(m_ResultingSpeed*a_Dt); - } - else - { // We didn't hit anything, so move =] - AddPosition(m_ResultingSpeed*a_Dt); - } - } - // Usable for debugging - //SetPosition(m_Pos.x, m_Pos.y, m_Pos.z); } diff --git a/source/Pickup.h b/source/Pickup.h index b6a8130da..74dceae4b 100644 --- a/source/Pickup.h +++ b/source/Pickup.h @@ -36,7 +36,6 @@ public: virtual bool CollectedBy(cPlayer * a_Dest); // tolua_export virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; short GetHealth(void) const { return m_Health; } @@ -49,8 +48,6 @@ private: Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) Vector3d m_WaterSpeed; - bool m_bOnGround; - bool m_bReplicated; float m_Timer; diff --git a/source/Player.h b/source/Player.h index d64cc7581..866798159 100644 --- a/source/Player.h +++ b/source/Player.h @@ -41,6 +41,8 @@ public: virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override { }; + /// Returns the curently equipped weapon; empty item if none virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }