1
0
cuberite-2a/source/cPickup.cpp
faketruth f43b65cf53 cClientHandles have a unique ID now to distinguish them
cAuthenticator uses unique client ID for authentication
Changed the kick function used by cAuthenticator to take a client ID instead of name, so the correct user is kicked
Using callback reference instead of pointer in GetChunkData and affiliates
GetChunkData returns false when failed, and true when succeeded
Renamed entity type enums to something prettier
Exposed some functions to Lua

git-svn-id: http://mc-server.googlecode.com/svn/trunk@388 0a769ca7-a7f5-676a-18bf-c427514a06d6
2012-03-09 13:42:28 +00:00

318 lines
7.2 KiB
C++

#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#ifndef _WIN32
#include <cstdlib>
#endif
#include "cPickup.h"
#include "cClientHandle.h"
#include "cInventory.h"
#include "cWorld.h"
#include "cWaterSimulator.h"
#include "cServer.h"
#include "cPlayer.h"
#include "cPluginManager.h"
#include "cItem.h"
#include "cRoot.h"
#include "cTracer.h"
#include "cChunk.h"
#include "packets/cPacket_TeleportEntity.h"
#include "packets/cPacket_PickupSpawn.h"
#include "packets/cPacket_CollectItem.h"
#include "Vector3d.h"
#include "Vector3f.h"
CLASS_DEFINITION( cPickup, cEntity )
cPickup::~cPickup()
{
delete m_Item;
}
cPickup::cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */)
: cEntity( ((double)(a_X))/32, ((double)(a_Y))/32, ((double)(a_Z))/32 )
, m_Speed( a_SpeedX, a_SpeedY, a_SpeedZ )
, m_bOnGround( false )
, m_bReplicated( false )
, m_Timer( 0.f )
, m_Item( new cItem( a_Item ) )
, m_bCollected( false )
{
//LOG("New pickup: ID(%i) Amount(%i) Health(%i)", m_Item.m_ItemID, m_Item.m_ItemCount, m_Item.m_ItemHealth );
// Spawn it on clients
if (!a_Item.IsEmpty())
{
std::auto_ptr<cPacket> PickupSpawn(GetSpawnPacket());
if (PickupSpawn.get() != NULL)
{
cRoot::Get()->GetServer()->Broadcast( PickupSpawn.get() );
}
}
m_EntityType = eEntityType_Pickup;
}
cPickup::cPickup(cPacket_PickupSpawn* a_PickupSpawnPacket)
: cEntity( ((double)a_PickupSpawnPacket->m_PosX)/32, ((double)a_PickupSpawnPacket->m_PosY)/32, ((double)a_PickupSpawnPacket->m_PosZ)/32 )
, m_Speed( new Vector3f() )
, m_ResultingSpeed(new Vector3f())
, m_WaterSpeed(new Vector3f())
, m_bOnGround( false )
, m_bReplicated( false )
, m_Timer( 0.f )
, m_bCollected( false )
{
a_PickupSpawnPacket->m_UniqueID = m_UniqueID;
m_Item = new cItem();
m_Item->m_ItemID = (ENUM_ITEM_ID)a_PickupSpawnPacket->m_Item;
m_Item->m_ItemCount = a_PickupSpawnPacket->m_Count;
m_Item->m_ItemHealth = 0x0;
m_Speed.x = (float)(a_PickupSpawnPacket->m_Rotation) / 8;
m_Speed.y = (float)(a_PickupSpawnPacket->m_Pitch) / 8;
m_Speed.z = (float)(a_PickupSpawnPacket->m_Roll) / 8;
// Spawn it on clients
if (a_PickupSpawnPacket->m_Item != E_ITEM_EMPTY)
{
cRoot::Get()->GetServer()->Broadcast( *a_PickupSpawnPacket );
}
m_EntityType = eEntityType_Pickup;
}
cPacket * cPickup::GetSpawnPacket(void) const
{
if (m_Item->IsEmpty())
{
return NULL;
}
cPacket_PickupSpawn * PickupSpawn = new cPacket_PickupSpawn;
PickupSpawn->m_UniqueID = m_UniqueID;
PickupSpawn->m_Item = (short)m_Item->m_ItemID;
PickupSpawn->m_Count = m_Item->m_ItemCount;
PickupSpawn->m_Health = m_Item->m_ItemHealth;
PickupSpawn->m_PosX = (int) (m_Pos.x * 32);
PickupSpawn->m_PosY = (int) (m_Pos.y * 32);
PickupSpawn->m_PosZ = (int) (m_Pos.z * 32);
PickupSpawn->m_Rotation = (char)(m_Speed.x * 8);
PickupSpawn->m_Pitch = (char)(m_Speed.y * 8);
PickupSpawn->m_Roll = (char)(m_Speed.z * 8);
return PickupSpawn;
}
void cPickup::Tick(float a_Dt)
{
m_Timer += a_Dt;
a_Dt = a_Dt / 1000.f;
if(m_bCollected)
{
if(m_Timer > 500.f) // 0.5 second
{
Destroy();
return;
}
}
if( m_Timer > 1000*60*5 ) // 5 minutes
{
Destroy();
return;
}
if( m_Pos.y < 0 ) // Out of this world!
{
Destroy();
return;
}
if(!m_bCollected)
{
HandlePhysics( a_Dt );
}
if( !m_bReplicated || m_bDirtyPosition )
{
MoveToCorrectChunk();
m_bReplicated = true;
m_bDirtyPosition = false;
cPacket_TeleportEntity TeleportEntity( this );
GetWorld()->BroadcastToChunk( m_ChunkX, m_ChunkY, m_ChunkZ, TeleportEntity );
}
}
void cPickup::HandlePhysics(float a_Dt)
{
m_ResultingSpeed.Set(0.f, 0.f, 0.f);
cWorld * World = GetWorld();
if( m_bOnGround ) // check if it's still on the ground
{
int BlockX = (m_Pos.x)<0 ? (int)m_Pos.x-1 : (int)m_Pos.x;
int BlockZ = (m_Pos.z)<0 ? (int)m_Pos.z-1 : (int)m_Pos.z;
char BlockBelow = World->GetBlock( BlockX, (int)m_Pos.y -1, BlockZ );
//Not only air, falls through water ;)
if( BlockBelow == E_BLOCK_AIR || IsBlockWater(BlockBelow))
{
m_bOnGround = false;
}
char Block = World->GetBlock( BlockX, (int)m_Pos.y - (int)m_bOnGround, BlockZ );
char BlockIn = World->GetBlock( BlockX, (int)m_Pos.y, 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;
m_Pos.y += 0.2;
m_bReplicated = false;
}
m_Speed.x *= 0.7f/(1+a_Dt);
if( fabs(m_Speed.x) < 0.05 ) m_Speed.x = 0;
m_Speed.z *= 0.7f/(1+a_Dt);
if( fabs(m_Speed.z) < 0.05 ) m_Speed.z = 0;
}
//get flowing direction
Direction WaterDir = World->GetWaterSimulator()->GetFlowingDirection((int) m_Pos.x - 1, (int) m_Pos.y, (int) m_Pos.z - 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;
m_Speed.y += Gravity;
// Set to hit position
m_ResultingSpeed += m_Speed;
cTracer Tracer( GetWorld() );
int Ret = Tracer.Trace( m_Pos, m_Speed, 2 );
if( Ret ) // Oh noez! we hit something
{
if( (Tracer.RealHit - Vector3f(m_Pos)).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 ) m_Speed.x = 0.f;
if( Tracer.HitNormal.y != 0.f ) m_Speed.y = 0.f;
if( Tracer.HitNormal.z != 0.f ) m_Speed.z = 0.f;
if( Tracer.HitNormal.y > 0 ) // means on ground
{
m_bOnGround = true;
}
}
m_Pos = Tracer.RealHit;
m_Pos += Tracer.HitNormal * 0.2f;
}
else
m_Pos += m_ResultingSpeed*a_Dt;
}
else
{ // We didn't hit anything, so move =]
m_Pos += m_ResultingSpeed * a_Dt;
}
}
//Usable for debugging
//SetPosition(m_Pos.x, m_Pos.y, m_Pos.z);
}
bool cPickup::CollectedBy( cPlayer* a_Dest )
{
if(m_bCollected) return false; // It's already collected!
// 800 is to long
if(m_Timer < 500.f) return false; // Not old enough
if( cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_COLLECT_ITEM, 2, this, a_Dest ) ) return false;
if( a_Dest->GetInventory().AddItem( *m_Item ) )
{
cPacket_CollectItem CollectItem;
CollectItem.m_CollectedID = m_UniqueID;
CollectItem.m_CollectorID = a_Dest->GetUniqueID();
cRoot::Get()->GetServer()->Broadcast( CollectItem );
m_bCollected = true;
m_Timer = 0;
return true;
}
return false;
}