2013-07-29 12:13:03 +01:00
// Minecart.cpp
// Implements the cMinecart class representing a minecart in the world
2014-01-13 22:37:09 +00:00
// Handles physics when a minecart is on any type of rail (overrides simulator in Entity.cpp)
2013-08-27 20:38:11 +01:00
// Indiana Jones!
2013-07-29 12:13:03 +01:00
#include "Globals.h"
#include "Minecart.h"
2013-08-19 11:39:13 +02:00
#include "../World.h"
#include "../ClientHandle.h"
2013-09-22 21:43:00 +02:00
#include "../Chunk.h"
2013-07-29 12:13:03 +01:00
#include "Player.h"
2014-01-13 22:37:09 +00:00
#include "../BoundingBox.h"
2013-07-29 12:13:03 +01:00
2014-01-12 13:28:37 +00:00
#define MAX_SPEED 8
#define MAX_SPEED_NEGATIVE -MAX_SPEED
2013-07-29 12:13:03 +01:00
2014-01-18 20:58:26 +00:00
class cMinecartCollisionCallback :
public cEntityCallback
{
public :
cMinecartCollisionCallback ( Vector3d a_Pos , double a_Height , double a_Width , int a_UniqueID , int a_AttacheeUniqueID ) :
2014-02-05 09:43:49 -08:00
m_DoesInteserct ( false ),
m_CollidedEntityPos ( 0 , 0 , 0 ),
2014-01-18 20:58:26 +00:00
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 ;
};
2013-07-29 12:13:03 +01:00
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 ),
2013-10-08 19:20:49 +01:00
m_Payload ( a_Payload ),
2014-01-13 22:37:09 +00:00
m_LastDamage ( 0 ),
m_DetectorRailPosition ( 0 , 0 , 0 ),
m_bIsOnDetectorRail ( false )
2013-07-29 12:13:03 +01:00
{
2013-08-28 22:13:27 +01:00
SetMass ( 20.f );
SetMaxHealth ( 6 );
2013-09-02 12:01:49 +01:00
SetHealth ( 6 );
2014-01-18 20:58:26 +00:00
SetWidth ( 1 );
2014-01-13 22:37:09 +00:00
SetHeight ( 0.9 );
2013-07-29 12:13:03 +01:00
}
void cMinecart :: SpawnOn ( cClientHandle & a_ClientHandle )
{
2013-08-29 13:47:22 +01:00
char SubType = 0 ;
2013-08-28 22:13:27 +01:00
switch ( m_Payload )
2013-07-29 12:13:03 +01:00
{
2013-08-29 13:47:22 +01:00
case mpNone : SubType = 0 ; break ;
case mpChest : SubType = 1 ; break ;
case mpFurnace : SubType = 2 ; break ;
case mpTNT : SubType = 3 ; break ;
case mpHopper : SubType = 5 ; break ;
2013-07-29 12:13:03 +01:00
default :
{
ASSERT ( ! "Unknown payload, cannot spawn on client" );
return ;
}
}
2013-08-29 14:00:39 +01:00
a_ClientHandle . SendSpawnVehicle ( * this , 10 , SubType ); // 10 = Minecarts, SubType = What type of Minecart
2014-01-12 18:04:41 +01:00
a_ClientHandle . SendEntityMetadata ( * this );
2013-07-29 12:13:03 +01:00
}
2013-09-02 12:01:49 +01:00
void cMinecart :: HandlePhysics ( float a_Dt , cChunk & a_Chunk )
2013-07-29 12:13:03 +01:00
{
2014-01-13 22:37:09 +00:00
if ( IsDestroyed ()) // Mainly to stop detector rails triggering again after minecart is dead
{
return ;
}
2013-09-22 21:43:00 +02:00
int PosY = ( int ) floor ( GetPosY ());
if (( PosY <= 0 ) || ( PosY >= cChunkDef :: Height ))
2013-09-02 12:01:49 +01:00
{
2013-09-22 21:43:00 +02:00
// Outside the world, just process normal falling physics
super :: HandlePhysics ( a_Dt , a_Chunk );
BroadcastMovementUpdate ();
return ;
}
int RelPosX = ( int ) floor ( GetPosX ()) - a_Chunk . GetPosX () * cChunkDef :: Width ;
int RelPosZ = ( int ) floor ( GetPosZ ()) - a_Chunk . GetPosZ () * cChunkDef :: Width ;
cChunk * Chunk = a_Chunk . GetRelNeighborChunkAdjustCoords ( RelPosX , RelPosZ );
if ( Chunk == NULL )
{
// Inside an unloaded chunk, bail out all processing
return ;
}
2013-09-02 12:01:49 +01:00
2014-01-12 13:28:37 +00:00
BLOCKTYPE InsideType ;
NIBBLETYPE InsideMeta ;
Chunk -> GetBlockTypeMeta ( RelPosX , PosY , RelPosZ , InsideType , InsideMeta );
if ( ! IsBlockRail ( InsideType ))
2013-09-22 21:43:00 +02:00
{
2014-01-13 22:37:09 +00:00
Chunk -> GetBlockTypeMeta ( RelPosX , PosY + 1 , RelPosZ , InsideType , InsideMeta ); // When an descending minecart hits a flat rail, it goes through the ground; check for this
if ( IsBlockRail ( InsideType )) AddPosY ( 1 ); // Push cart upwards
2013-09-22 21:43:00 +02:00
}
2014-01-12 13:28:37 +00:00
2014-01-19 18:27:06 +00:00
bool WasDetectorRail = false ;
2014-01-12 13:28:37 +00:00
if ( IsBlockRail ( InsideType ))
2013-09-22 21:43:00 +02:00
{
2014-01-19 18:27:06 +00:00
if ( InsideType == E_BLOCK_RAIL )
{
SnapToRail ( InsideMeta );
}
else
{
SnapToRail ( InsideMeta & 0x07 );
}
2014-01-12 13:28:37 +00:00
switch ( InsideType )
2013-09-02 12:01:49 +01:00
{
2014-01-13 22:37:09 +00:00
case E_BLOCK_RAIL : HandleRailPhysics ( InsideMeta , a_Dt ); break ;
2014-01-12 13:28:37 +00:00
case E_BLOCK_ACTIVATOR_RAIL : break ;
case E_BLOCK_POWERED_RAIL : HandlePoweredRailPhysics ( InsideMeta ); break ;
2014-01-13 22:37:09 +00:00
case E_BLOCK_DETECTOR_RAIL :
{
HandleDetectorRailPhysics ( InsideMeta , a_Dt );
WasDetectorRail = true ;
break ;
}
2014-01-12 13:28:37 +00:00
default : VERIFY ( ! "Unhandled rail type despite checking if block was rail!" ); break ;
2013-09-02 12:01:49 +01:00
}
2014-01-12 13:28:37 +00:00
2014-01-13 22:37:09 +00:00
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
2014-01-12 13:28:37 +00:00
}
else
{
2014-01-13 22:37:09 +00:00
// Not on rail, default physics
2014-01-12 13:28:37 +00:00
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 );
2013-09-02 12:01:49 +01:00
}
2014-01-19 18:27:06 +00:00
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 ()));
}
2014-01-12 13:28:37 +00:00
2014-01-13 22:37:09 +00:00
// Broadcast positioning changes to client
2014-01-12 13:28:37 +00:00
BroadcastMovementUpdate ();
2013-09-02 12:01:49 +01:00
}
2014-01-13 22:37:09 +00:00
void cMinecart :: HandleRailPhysics ( NIBBLETYPE a_RailMeta , float a_Dt )
2013-09-02 12:01:49 +01:00
{
2013-08-27 20:38:11 +01:00
/*
NOTE: Please bear in mind that taking away from negatives make them even more negative,
2013-08-29 13:47:22 +01:00
adding to negatives make them positive, etc.
2013-08-27 20:38:11 +01:00
*/
2014-01-12 13:28:37 +00:00
switch ( a_RailMeta )
2013-08-27 20:38:11 +01:00
{
2013-09-03 12:33:54 +01:00
case E_META_RAIL_ZM_ZP : // NORTHSOUTH
2013-08-27 20:38:11 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 270 );
2014-01-12 13:28:37 +00:00
SetPosY ( floor ( GetPosY ()) + 0.55 );
SetSpeedY ( 0 ); // Don't move vertically as on ground
SetSpeedX ( 0 ); // Correct diagonal movement from curved rails
2014-01-13 22:37:09 +00:00
2014-01-18 20:58:26 +00:00
// Execute both the entity and block collision checks
bool BlckCol = TestBlockCollision ( a_RailMeta ), EntCol = TestEntityCollision ( a_RailMeta );
if ( EntCol || BlckCol ) return ;
2013-09-02 12:01:49 +01:00
2014-01-12 13:28:37 +00:00
if ( GetSpeedZ () != 0 ) // Don't do anything if cart is stationary
2013-08-27 20:38:11 +01:00
{
2014-01-12 13:28:37 +00:00
if ( GetSpeedZ () > 0 )
2013-09-02 12:01:49 +01:00
{
// Going SOUTH, slow down
2014-01-12 13:28:37 +00:00
AddSpeedZ ( - 0.1 );
2013-09-02 12:01:49 +01:00
}
else
2013-08-27 20:38:11 +01:00
{
2013-09-02 12:01:49 +01:00
// Going NORTH, slow down
2014-01-12 13:28:37 +00:00
AddSpeedZ ( 0.1 );
2013-08-27 20:38:11 +01:00
}
}
2013-09-02 12:01:49 +01:00
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_XM_XP : // EASTWEST
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 180 );
2014-01-12 13:28:37 +00:00
SetPosY ( floor ( GetPosY ()) + 0.55 );
SetSpeedY ( 0 );
SetSpeedZ ( 0 );
2013-08-27 20:38:11 +01:00
2014-01-18 20:58:26 +00:00
bool BlckCol = TestBlockCollision ( a_RailMeta ), EntCol = TestEntityCollision ( a_RailMeta );
if ( EntCol || BlckCol ) return ;
2014-01-13 22:37:09 +00:00
2014-01-12 13:28:37 +00:00
if ( GetSpeedX () != 0 )
2013-09-02 12:01:49 +01:00
{
2014-01-12 13:28:37 +00:00
if ( GetSpeedX () > 0 )
2013-08-27 20:38:11 +01:00
{
2014-01-12 13:28:37 +00:00
AddSpeedX ( - 0.1 );
2013-09-02 12:01:49 +01:00
}
else
{
2014-01-12 13:28:37 +00:00
AddSpeedX ( 0.1 );
2013-08-27 20:38:11 +01:00
}
}
2013-09-02 12:01:49 +01:00
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_ASCEND_ZM : // ASCEND NORTH
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 270 );
2014-01-12 13:28:37 +00:00
SetSpeedX ( 0 );
2013-08-27 20:38:11 +01:00
2014-01-12 13:28:37 +00:00
if ( GetSpeedZ () >= 0 )
2013-08-27 20:38:11 +01:00
{
2013-09-02 12:01:49 +01:00
// SpeedZ POSITIVE, going SOUTH
2014-01-12 13:28:37 +00:00
if ( GetSpeedZ () <= MAX_SPEED ) // Speed limit
2013-08-27 20:38:11 +01:00
{
2014-01-12 13:28:37 +00:00
AddSpeedZ ( 0.5 ); // Speed up
SetSpeedY ( - GetSpeedZ ()); // Downward movement is negative (0 minus positive numbers is negative)
2013-08-27 20:38:11 +01:00
}
}
2013-09-02 12:01:49 +01:00
else
{
// SpeedZ NEGATIVE, going NORTH
2014-01-12 13:28:37 +00:00
AddSpeedZ ( 1 ); // Slow down
SetSpeedY ( - GetSpeedZ ()); // Upward movement is positive (0 minus negative number is positive number)
2013-09-02 12:01:49 +01:00
}
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_ASCEND_ZP : // ASCEND SOUTH
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 270 );
2014-01-12 13:28:37 +00:00
SetSpeedX ( 0 );
2013-08-27 20:38:11 +01:00
2014-01-12 13:28:37 +00:00
if ( GetSpeedZ () > 0 )
2013-08-27 20:38:11 +01:00
{
2013-09-02 12:01:49 +01:00
// SpeedZ POSITIVE, going SOUTH
2014-01-12 13:28:37 +00:00
AddSpeedZ ( - 1 ); // Slow down
SetSpeedY ( GetSpeedZ ()); // Upward movement positive
2013-09-02 12:01:49 +01:00
}
else
{
2014-01-12 13:28:37 +00:00
if ( GetSpeedZ () >= MAX_SPEED_NEGATIVE ) // Speed limit
2013-08-27 20:38:11 +01:00
{
2013-09-02 12:01:49 +01:00
// SpeedZ NEGATIVE, going NORTH
2014-01-12 13:28:37 +00:00
AddSpeedZ ( - 0.5 ); // Speed up
SetSpeedY ( GetSpeedZ ()); // Downward movement negative
2013-08-27 20:38:11 +01:00
}
}
2013-09-02 12:01:49 +01:00
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_ASCEND_XM : // ASCEND EAST
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 180 );
2014-01-12 13:28:37 +00:00
SetSpeedZ ( 0 );
2013-08-27 20:38:11 +01:00
2014-01-12 13:28:37 +00:00
if ( GetSpeedX () >= 0 )
2013-08-27 20:38:11 +01:00
{
2014-01-12 13:28:37 +00:00
if ( GetSpeedX () <= MAX_SPEED )
2013-08-27 20:38:11 +01:00
{
2014-01-12 13:28:37 +00:00
AddSpeedX ( 0.5 );
SetSpeedY ( - GetSpeedX ());
2013-08-27 20:38:11 +01:00
}
}
2013-09-02 12:01:49 +01:00
else
{
2014-01-12 13:28:37 +00:00
AddSpeedX ( 1 );
SetSpeedY ( - GetSpeedX ());
2013-09-02 12:01:49 +01:00
}
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_ASCEND_XP : // ASCEND WEST
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 180 );
2014-01-12 13:28:37 +00:00
SetSpeedZ ( 0 );
2013-08-27 20:38:11 +01:00
2014-01-12 13:28:37 +00:00
if ( GetSpeedX () > 0 )
2013-08-27 20:38:11 +01:00
{
2014-01-12 13:28:37 +00:00
AddSpeedX ( - 1 );
SetSpeedY ( GetSpeedX ());
2013-09-02 12:01:49 +01:00
}
else
{
2014-01-12 13:28:37 +00:00
if ( GetSpeedX () >= MAX_SPEED_NEGATIVE )
2013-08-27 20:38:11 +01:00
{
2014-01-12 13:28:37 +00:00
AddSpeedX ( - 0.5 );
SetSpeedY ( GetSpeedX ());
2013-08-27 20:38:11 +01:00
}
}
2013-09-02 12:01:49 +01:00
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_CURVED_ZM_XM : // Ends pointing NORTH and WEST
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 315 ); // Set correct rotation server side
2014-01-13 22:37:09 +00:00
SetPosY ( floor ( GetPosY ()) + 0.55 ); // Levitate dat cart
2014-01-19 19:31:17 +00:00
SetSpeedY ( 0 );
2014-01-13 22:37:09 +00:00
2014-01-18 20:58:26 +00:00
TestBlockCollision ( a_RailMeta );
TestEntityCollision ( a_RailMeta );
// SnapToRail handles turning
2013-09-02 12:01:49 +01:00
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_CURVED_ZM_XP : // Curved NORTH EAST
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 225 );
2014-01-13 22:37:09 +00:00
SetPosY ( floor ( GetPosY ()) + 0.55 );
2014-01-19 19:31:17 +00:00
SetSpeedY ( 0 );
2014-01-13 22:37:09 +00:00
2014-01-18 20:58:26 +00:00
TestBlockCollision ( a_RailMeta );
TestEntityCollision ( a_RailMeta );
2013-09-02 12:01:49 +01:00
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_CURVED_ZP_XM : // Curved SOUTH WEST
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 135 );
2014-01-13 22:37:09 +00:00
SetPosY ( floor ( GetPosY ()) + 0.55 );
2014-01-19 19:31:17 +00:00
SetSpeedY ( 0 );
2014-01-13 22:37:09 +00:00
2014-01-18 20:58:26 +00:00
TestBlockCollision ( a_RailMeta );
TestEntityCollision ( a_RailMeta );
2013-09-02 12:01:49 +01:00
break ;
}
2013-09-03 12:33:54 +01:00
case E_META_RAIL_CURVED_ZP_XP : // Curved SOUTH EAST
2013-09-02 12:01:49 +01:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 45 );
2014-01-13 22:37:09 +00:00
SetPosY ( floor ( GetPosY ()) + 0.55 );
2014-01-19 19:31:17 +00:00
SetSpeedY ( 0 );
2014-01-13 22:37:09 +00:00
2014-01-18 20:58:26 +00:00
TestBlockCollision ( a_RailMeta );
TestEntityCollision ( a_RailMeta );
2013-09-02 12:01:49 +01:00
break ;
}
default :
{
ASSERT ( ! "Unhandled rail meta!" ); // Dun dun DUN!
break ;
2013-08-27 20:38:11 +01:00
}
}
2014-01-12 13:28:37 +00:00
}
2013-09-02 12:01:49 +01:00
2013-08-27 20:38:11 +01:00
2013-09-02 12:01:49 +01:00
2014-01-12 13:28:37 +00:00
void cMinecart :: HandlePoweredRailPhysics ( NIBBLETYPE a_RailMeta )
{
2014-01-13 22:37:09 +00:00
// Initialise to 'slow down' values
2014-01-25 20:33:23 +00:00
int AccelDecelSpeed = - 2 ;
int AccelDecelNegSpeed = 2 ;
2014-01-13 22:37:09 +00:00
2014-01-12 13:28:37 +00:00
if (( a_RailMeta & 0x8 ) == 0x8 )
{
2014-01-13 22:37:09 +00:00
// Rail powered - set variables to 'speed up' values
AccelDecelSpeed = 1 ;
AccelDecelNegSpeed = - 1 ;
}
switch ( a_RailMeta & 0x07 )
{
case E_META_RAIL_ZM_ZP : // NORTHSOUTH
2014-01-12 13:28:37 +00:00
{
2014-01-16 19:00:49 +00:00
SetYaw ( 270 );
2014-01-13 22:37:09 +00:00
SetPosY ( floor ( GetPosY ()) + 0.55 );
SetSpeedY ( 0 );
SetSpeedX ( 0 );
2014-01-19 19:31:17 +00:00
bool BlckCol = TestBlockCollision ( a_RailMeta ), EntCol = TestEntityCollision ( a_RailMeta );
if ( EntCol || BlckCol ) return ;
2014-01-12 13:28:37 +00:00
2014-01-13 22:37:09 +00:00
if ( GetSpeedZ () != 0 )
{
if ( GetSpeedZ () > 0 )
2014-01-12 13:28:37 +00:00
{
2014-01-19 18:27:06 +00:00
AddSpeedZ ( AccelDecelSpeed );
2014-01-13 22:37:09 +00:00
}
else
{
2014-01-19 18:27:06 +00:00
AddSpeedZ ( AccelDecelNegSpeed );
2014-01-12 13:28:37 +00:00
}
}
2014-01-13 22:37:09 +00:00
break ;
}
case E_META_RAIL_XM_XP : // EASTWEST
{
2014-01-16 19:00:49 +00:00
SetYaw ( 180 );
2014-01-13 22:37:09 +00:00
SetPosY ( floor ( GetPosY ()) + 0.55 );
SetSpeedY ( 0 );
SetSpeedZ ( 0 );
2014-01-12 13:28:37 +00:00
2014-01-19 19:31:17 +00:00
bool BlckCol = TestBlockCollision ( a_RailMeta ), EntCol = TestEntityCollision ( a_RailMeta );
if ( EntCol || BlckCol ) return ;
2014-01-13 22:37:09 +00:00
if ( GetSpeedX () != 0 )
{
if ( GetSpeedX () > 0 )
2014-01-12 13:28:37 +00:00
{
2014-01-13 22:37:09 +00:00
AddSpeedX ( AccelDecelSpeed );
}
else
{
AddSpeedX ( AccelDecelNegSpeed );
2014-01-12 13:28:37 +00:00
}
}
2014-01-13 22:37:09 +00:00
break ;
2014-01-12 13:28:37 +00:00
}
2014-01-24 19:39:39 +00:00
case E_META_RAIL_ASCEND_XM : // ASCEND EAST
2014-01-19 19:31:17 +00:00
{
SetYaw ( 180 );
SetSpeedZ ( 0 );
if ( GetSpeedX () >= 0 )
{
if ( GetSpeedX () <= MAX_SPEED )
{
2014-01-24 19:39:39 +00:00
AddSpeedX ( AccelDecelSpeed );
2014-01-19 19:31:17 +00:00
SetSpeedY ( - GetSpeedX ());
}
}
else
{
2014-01-24 19:39:39 +00:00
AddSpeedX ( AccelDecelNegSpeed );
2014-01-19 19:31:17 +00:00
SetSpeedY ( - GetSpeedX ());
}
break ;
2014-01-24 19:39:39 +00:00
}
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 ;
2014-01-19 19:31:17 +00:00
}
default : ASSERT ( ! "Unhandled powered rail metadata!" ); break ;
2014-01-12 13:28:37 +00:00
}
}
2014-01-13 22:37:09 +00:00
void cMinecart :: HandleDetectorRailPhysics ( NIBBLETYPE a_RailMeta , float a_Dt )
{
m_World -> SetBlockMeta ( m_DetectorRailPosition , a_RailMeta | 0x08 );
2014-01-19 19:31:17 +00:00
// No special handling
HandleRailPhysics ( a_RailMeta & 0x07 , a_Dt );
}
void cMinecart :: HandleActivatorRailPhysics ( NIBBLETYPE a_RailMeta , float a_Dt )
{
2014-01-13 22:37:09 +00:00
HandleRailPhysics ( a_RailMeta & 0x07 , a_Dt );
}
2014-01-12 13:28:37 +00:00
void cMinecart :: SnapToRail ( NIBBLETYPE a_RailMeta )
{
switch ( a_RailMeta )
{
case E_META_RAIL_ASCEND_XM :
case E_META_RAIL_ASCEND_XP :
case E_META_RAIL_XM_XP :
{
SetSpeedZ ( 0 );
2014-01-13 22:37:09 +00:00
SetPosZ ( floor ( GetPosZ ()) + 0.5 );
2014-01-12 13:28:37 +00:00
break ;
}
case E_META_RAIL_ASCEND_ZM :
case E_META_RAIL_ASCEND_ZP :
case E_META_RAIL_ZM_ZP :
{
SetSpeedX ( 0 );
2014-01-13 22:37:09 +00:00
SetPosX ( floor ( GetPosX ()) + 0.5 );
2014-01-12 13:28:37 +00:00
break ;
}
2014-01-19 18:27:06 +00:00
// 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
2014-01-18 20:58:26 +00:00
case E_META_RAIL_CURVED_ZM_XM :
{
2014-01-19 18:27:06 +00:00
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 )
2014-01-18 20:58:26 +00:00
{
if ( GetSpeedX () > 0 )
{
SetSpeedZ ( - GetSpeedX () * 0.7 );
}
SetSpeedX ( 0 );
SetPosX ( floor ( GetPosX ()) + 0.5 );
}
2014-01-19 19:31:17 +00:00
SetSpeedY ( 0 );
2014-01-19 18:27:06 +00:00
break ;
}
case E_META_RAIL_CURVED_ZM_XP :
{
if ( GetPosZ () > floor ( GetPosZ ()) + 0.5 )
2014-01-18 20:58:26 +00:00
{
if ( GetSpeedZ () > 0 )
{
2014-01-19 18:27:06 +00:00
SetSpeedX ( GetSpeedZ () * 0.7 );
2014-01-18 20:58:26 +00:00
}
SetSpeedZ ( 0 );
SetPosZ ( floor ( GetPosZ ()) + 0.5 );
}
2014-01-19 18:27:06 +00:00
else if ( GetPosX () < floor ( GetPosX ()) + 0.5 )
2014-01-18 20:58:26 +00:00
{
if ( GetSpeedX () < 0 )
{
SetSpeedZ ( GetSpeedX () * 0.7 );
}
SetSpeedX ( 0 );
SetPosX ( floor ( GetPosX ()) + 0.5 );
}
2014-01-19 19:31:17 +00:00
SetSpeedY ( 0 );
2014-01-18 20:58:26 +00:00
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 );
}
2014-01-19 18:27:06 +00:00
else if ( GetPosX () > floor ( GetPosX ()) + 0.5 )
2014-01-18 20:58:26 +00:00
{
if ( GetSpeedX () > 0 )
{
SetSpeedZ ( GetSpeedX () * 0.7 );
}
SetSpeedX ( 0 );
SetPosX ( floor ( GetPosX ()) + 0.5 );
}
2014-01-19 19:31:17 +00:00
SetSpeedY ( 0 );
2014-01-18 20:58:26 +00:00
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 );
}
2014-01-19 18:27:06 +00:00
else if ( GetPosX () < floor ( GetPosX ()) + 0.5 )
2014-01-18 20:58:26 +00:00
{
if ( GetSpeedX () < 0 )
{
SetSpeedZ ( - GetSpeedX () * 0.7 );
}
SetSpeedX ( 0 );
SetPosX ( floor ( GetPosX ()) + 0.5 );
}
2014-01-19 19:31:17 +00:00
SetSpeedY ( 0 );
2014-01-18 20:58:26 +00:00
break ;
}
2014-01-12 13:28:37 +00:00
default : break ;
}
2013-09-02 12:01:49 +01:00
}
2013-08-27 20:38:11 +01:00
2013-09-02 12:01:49 +01:00
2014-01-13 22:37:09 +00:00
bool cMinecart :: TestBlockCollision ( NIBBLETYPE a_RailMeta )
{
switch ( a_RailMeta )
{
case E_META_RAIL_ZM_ZP :
{
if ( GetSpeedZ () > 0 )
{
BLOCKTYPE Block = m_World -> GetBlock (( int ) floor ( GetPosX ()), ( int ) floor ( GetPosY ()), ( int ) ceil ( GetPosZ ()));
2014-03-01 21:34:19 +02:00
if ( ! IsBlockRail ( Block ) && cBlockInfo :: IsSolid ( Block ))
2014-01-13 22:37:09 +00:00
{
// 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 );
cBoundingBox bbMinecart ( Vector3d ( GetPosX (), floor ( GetPosY ()), GetPosZ ()), GetWidth () / 2 , GetHeight ());
if ( bbBlock . DoesIntersect ( bbMinecart ))
{
SetSpeed ( 0 , 0 , 0 );
SetPosZ ( floor ( GetPosZ ()) + 0.4 );
return true ;
}
}
}
2014-01-18 20:58:26 +00:00
else if ( GetSpeedZ () < 0 )
2014-01-13 22:37:09 +00:00
{
BLOCKTYPE Block = m_World -> GetBlock (( int ) floor ( GetPosX ()), ( int ) floor ( GetPosY ()), ( int ) floor ( GetPosZ ()) - 1 );
2014-03-01 21:34:19 +02:00
if ( ! IsBlockRail ( Block ) && cBlockInfo :: IsSolid ( Block ))
2014-01-13 22:37:09 +00:00
{
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 ());
if ( bbBlock . DoesIntersect ( bbMinecart ))
{
SetSpeed ( 0 , 0 , 0 );
SetPosZ ( floor ( GetPosZ ()) + 0.65 );
return true ;
}
}
}
break ;
}
case E_META_RAIL_XM_XP :
{
if ( GetSpeedX () > 0 )
{
BLOCKTYPE Block = m_World -> GetBlock (( int ) ceil ( GetPosX ()), ( int ) floor ( GetPosY ()), ( int ) floor ( GetPosZ ()));
2014-03-01 21:34:19 +02:00
if ( ! IsBlockRail ( Block ) && cBlockInfo :: IsSolid ( Block ))
2014-01-13 22:37:09 +00:00
{
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 ());
if ( bbBlock . DoesIntersect ( bbMinecart ))
{
SetSpeed ( 0 , 0 , 0 );
SetPosX ( floor ( GetPosX ()) + 0.4 );
return true ;
}
}
}
2014-01-18 20:58:26 +00:00
else if ( GetSpeedX () < 0 )
2014-01-13 22:37:09 +00:00
{
BLOCKTYPE Block = m_World -> GetBlock (( int ) floor ( GetPosX ()) - 1 , ( int ) floor ( GetPosY ()), ( int ) floor ( GetPosZ ()));
2014-03-01 21:34:19 +02:00
if ( ! IsBlockRail ( Block ) && cBlockInfo :: IsSolid ( Block ))
2014-01-13 22:37:09 +00:00
{
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 ());
if ( bbBlock . DoesIntersect ( bbMinecart ))
{
SetSpeed ( 0 , 0 , 0 );
SetPosX ( floor ( GetPosX ()) + 0.65 );
return true ;
}
}
}
break ;
}
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 :
{
BLOCKTYPE BlockXM = m_World -> GetBlock (( int ) floor ( GetPosX ()) - 1 , ( int ) floor ( GetPosY ()), ( int ) floor ( GetPosZ ()));
BLOCKTYPE BlockXP = m_World -> GetBlock (( int ) floor ( GetPosX ()) + 1 , ( int ) floor ( GetPosY ()), ( int ) floor ( GetPosZ ()));
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 (
2014-03-01 21:34:19 +02:00
( ! IsBlockRail ( BlockXM ) && cBlockInfo :: IsSolid ( BlockXM )) ||
( ! IsBlockRail ( BlockXP ) && cBlockInfo :: IsSolid ( BlockXP )) ||
( ! IsBlockRail ( BlockZM ) && cBlockInfo :: IsSolid ( BlockZM )) ||
( ! IsBlockRail ( BlockZP ) && cBlockInfo :: IsSolid ( BlockZP ))
2014-01-13 22:37:09 +00:00
)
{
SetSpeed ( 0 , 0 , 0 );
SetPosition (( int ) floor ( GetPosX ()) + 0.5 , GetPosY (), ( int ) floor ( GetPosZ ()) + 0.5 );
return true ;
}
break ;
}
default : break ;
}
return false ;
}
2014-01-18 20:58:26 +00:00
bool cMinecart :: TestEntityCollision ( NIBBLETYPE a_RailMeta )
{
2014-01-19 18:42:05 +00:00
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 ;
}
2014-01-18 20:58:26 +00:00
switch ( a_RailMeta )
{
case E_META_RAIL_ZM_ZP :
{
2014-01-19 18:42:05 +00:00
if ( MinecartCollisionCallback . GetCollidedEntityPosition (). z >= GetPosZ ())
2014-01-18 20:58:26 +00:00
{
2014-01-19 18:42:05 +00:00
if (( - GetSpeedZ () * 0.4 ) < 0.01 )
2014-01-18 20:58:26 +00:00
{
2014-01-19 18:42:05 +00:00
AddSpeedZ ( - 4 );
2014-01-18 20:58:26 +00:00
}
else
{
2014-01-19 18:42:05 +00:00
SetSpeedZ ( - GetSpeedZ () * 0.4 );
2014-01-18 20:58:26 +00:00
}
}
2014-01-19 18:42:05 +00:00
else
{
if (( GetSpeedZ () * 0.4 ) < 0.01 )
{
AddSpeedZ ( 4 );
}
else
{
SetSpeedZ ( GetSpeedZ () * 0.4 );
}
}
return true ;
2014-01-18 20:58:26 +00:00
}
case E_META_RAIL_XM_XP :
{
2014-01-19 18:42:05 +00:00
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 ;
2014-01-18 20:58:26 +00:00
}
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 :
{
2014-01-19 18:42:05 +00:00
// TODO - simply can't be bothered right now
2014-01-18 20:58:26 +00:00
break ;
}
default : break ;
}
2014-01-19 18:42:05 +00:00
2014-01-18 20:58:26 +00:00
return false ;
}
2014-01-13 22:37:09 +00:00
2013-09-02 12:01:49 +01:00
void cMinecart :: DoTakeDamage ( TakeDamageInfo & TDI )
{
2014-01-18 20:58:26 +00:00
if (( TDI . Attacker != NULL ) && TDI . Attacker -> IsPlayer () && (( cPlayer * ) TDI . Attacker ) -> IsGameModeCreative ())
2014-01-12 13:28:37 +00:00
{
Destroy ();
TDI . FinalDamage = GetMaxHealth (); // Instant hit for creative
super :: DoTakeDamage ( TDI );
return ; // No drops for creative
}
2013-10-08 19:20:49 +01:00
m_LastDamage = TDI . FinalDamage ;
2013-09-02 12:01:49 +01:00
super :: DoTakeDamage ( TDI );
2013-10-08 19:20:49 +01:00
m_World -> BroadcastEntityMetadata ( * this );
2013-09-09 18:55:42 +01:00
if ( GetHealth () <= 0 )
2013-09-02 12:01:49 +01:00
{
2014-01-12 13:28:37 +00:00
Destroy ();
2013-10-08 19:20:49 +01:00
cItems Drops ;
switch ( m_Payload )
{
case mpNone :
{
Drops . push_back ( cItem ( E_ITEM_MINECART , 1 , 0 ));
break ;
}
case mpChest :
{
Drops . push_back ( cItem ( E_ITEM_CHEST_MINECART , 1 , 0 ));
break ;
}
case mpFurnace :
{
Drops . push_back ( cItem ( E_ITEM_FURNACE_MINECART , 1 , 0 ));
break ;
}
case mpTNT :
{
2013-10-09 21:02:59 +01:00
Drops . push_back ( cItem ( E_ITEM_MINECART_WITH_TNT , 1 , 0 ));
2013-10-08 19:20:49 +01:00
break ;
}
case mpHopper :
{
2013-10-09 21:02:59 +01:00
Drops . push_back ( cItem ( E_ITEM_MINECART_WITH_HOPPER , 1 , 0 ));
2013-10-08 19:20:49 +01:00
break ;
}
default :
{
ASSERT ( ! "Unhandled minecart type when spawning pickup!" );
return ;
}
}
m_World -> SpawnItemPickups ( Drops , GetPosX (), GetPosY (), GetPosZ ());
2013-09-02 12:01:49 +01:00
}
2013-07-29 12:13:03 +01:00
}
2014-01-13 22:37:09 +00:00
void cMinecart :: Destroyed ()
{
if ( m_bIsOnDetectorRail )
{
m_World -> SetBlock ( m_DetectorRailPosition . x , m_DetectorRailPosition . y , m_DetectorRailPosition . z , E_BLOCK_DETECTOR_RAIL , m_World -> GetBlockMeta ( m_DetectorRailPosition ) & 0x07 );
}
}
2013-07-29 12:13:03 +01:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2014-01-12 18:04:41 +01:00
// cRideableMinecart:
2013-07-29 12:13:03 +01:00
2014-01-12 18:04:41 +01:00
cRideableMinecart :: cRideableMinecart ( double a_X , double a_Y , double a_Z , const cItem & a_Content , int a_Height ) :
2014-01-12 15:27:50 +01:00
super ( mpNone , a_X , a_Y , a_Z ),
m_Content ( a_Content ),
m_Height ( a_Height )
2013-07-29 12:13:03 +01:00
{
}
2014-01-12 18:04:41 +01:00
void cRideableMinecart :: OnRightClicked ( cPlayer & a_Player )
2013-07-29 12:13:03 +01:00
{
if ( m_Attachee != NULL )
{
if ( m_Attachee -> GetUniqueID () == a_Player . GetUniqueID ())
{
// This player is already sitting in, they want out.
a_Player . Detach ();
return ;
}
if ( m_Attachee -> IsPlayer ())
{
// Another player is already sitting in here, cannot attach
return ;
}
// Detach whatever is sitting in this minecart now:
m_Attachee -> Detach ();
}
// Attach the player to this minecart
a_Player . AttachTo ( this );
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithChest:
cMinecartWithChest :: cMinecartWithChest ( double a_X , double a_Y , double a_Z ) :
super ( mpChest , a_X , a_Y , a_Z )
{
}
void cMinecartWithChest :: SetSlot ( int a_Idx , const cItem & a_Item )
{
ASSERT (( a_Idx >= 0 ) && ( a_Idx < ARRAYCOUNT ( m_Items )));
m_Items [ a_Idx ] = a_Item ;
}
void cMinecartWithChest :: OnRightClicked ( cPlayer & a_Player )
{
// Show the chest UI window to the player
// TODO
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithFurnace:
cMinecartWithFurnace :: cMinecartWithFurnace ( double a_X , double a_Y , double a_Z ) :
2013-10-08 19:20:49 +01:00
super ( mpFurnace , a_X , a_Y , a_Z ),
2014-02-05 09:43:49 -08:00
m_FueledTimeLeft ( - 1 ),
m_IsFueled ( false )
2013-07-29 12:13:03 +01:00
{
}
void cMinecartWithFurnace :: OnRightClicked ( cPlayer & a_Player )
{
2013-10-08 19:20:49 +01:00
if ( a_Player . GetEquippedItem (). m_ItemType == E_ITEM_COAL )
{
if ( ! a_Player . IsGameModeCreative ())
{
a_Player . GetInventory (). RemoveOneEquippedItem ();
}
2014-01-15 14:03:09 +01:00
if ( ! m_IsFueled ) // We don't want to change the direction by right clicking it.
{
AddSpeed ( a_Player . GetLookVector (). x , 0 , a_Player . GetLookVector (). z );
}
2013-10-08 19:20:49 +01:00
m_IsFueled = true ;
2014-01-15 14:38:54 +01:00
m_FueledTimeLeft = m_FueledTimeLeft + 600 ; // The minecart will be active 600 more ticks.
2013-10-08 19:20:49 +01:00
m_World -> BroadcastEntityMetadata ( * this );
}
2013-07-29 12:13:03 +01:00
}
2014-01-15 14:03:09 +01:00
void cMinecartWithFurnace :: Tick ( float a_Dt , cChunk & a_Chunk )
{
super :: Tick ( a_Dt , a_Chunk );
if ( m_IsFueled )
{
2014-01-15 14:38:54 +01:00
m_FueledTimeLeft -- ;
if ( m_FueledTimeLeft < 0 )
{
m_IsFueled = false ;
m_World -> BroadcastEntityMetadata ( * this );
return ;
}
2014-01-15 14:03:09 +01:00
if ( GetSpeed (). Length () > 6 )
{
return ;
}
AddSpeed ( GetSpeed () / 4 );
}
}
2013-08-16 11:23:24 +01:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithTNT:
cMinecartWithTNT :: cMinecartWithTNT ( double a_X , double a_Y , double a_Z ) :
super ( mpTNT , a_X , a_Y , a_Z )
{
}
// TODO: Make it activate when passing over activator rail
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithHopper:
cMinecartWithHopper :: cMinecartWithHopper ( double a_X , double a_Y , double a_Z ) :
super ( mpHopper , a_X , a_Y , a_Z )
{
}
// TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks
2014-02-05 09:43:49 -08:00
// AND AVARYTHING!!