Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1180 lines
37 KiB

5 years ago
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A base vehicle class
//
//=============================================================================//
#include "cbase.h"
#include "basetfvehicle.h"
#include "tf_movedata.h"
#include "in_buttons.h"
#include "baseplayer_shared.h"
#if defined( CLIENT_DLL )
#include "hud_vehicle_role.h"
#include "hud.h"
#include "hud_crosshair.h"
#else
#include "tf_team.h"
#include "tf_gamerules.h"
#include "tf_func_construction_yard.h"
#include "ndebugoverlay.h"
#endif
IMPLEMENT_NETWORKCLASS_ALIASED( BaseTFVehicle, DT_BaseTFVehicle );
BEGIN_NETWORK_TABLE( CBaseTFVehicle, DT_BaseTFVehicle )
#if !defined( CLIENT_DLL )
SendPropInt( SENDINFO(m_nMaxPassengers), CBaseTFVehicle::MAX_PASSENGER_BITS, SPROP_UNSIGNED ),
SendPropArray( SendPropEHandle(SENDINFO_ARRAY(m_hPassengers)), m_hPassengers ),
SendPropEHandle( SENDINFO(m_hDriverGun) ),
#else
RecvPropInt( RECVINFO(m_nMaxPassengers) ),
RecvPropArray( RecvPropEHandle(RECVINFO(m_hPassengers[0])), m_hPassengers ),
RecvPropEHandle( RECVINFO(m_hDriverGun) ),
#endif
END_NETWORK_TABLE()
BEGIN_PREDICTION_DATA( CBaseTFVehicle )
DEFINE_PRED_ARRAY( m_hPassengers, FIELD_EHANDLE, CBaseTFVehicle::MAX_PASSENGERS, FTYPEDESC_INSENDTABLE ),
END_PREDICTION_DATA()
extern float RemapAngleRange( float startInterval, float endInterval, float value );
extern ConVar road_feel;
ConVar vehicle_view_offset_forward( "vehicle_view_offset_forward", "-280", FCVAR_REPLICATED );
ConVar vehicle_view_offset_right( "vehicle_view_offset_right", "0", FCVAR_REPLICATED );
ConVar vehicle_view_offset_up( "vehicle_view_offset_up", "50", FCVAR_REPLICATED );
ConVar vehicle_thirdperson( "vehicle_thirdperson", "1", FCVAR_REPLICATED, "Enable/disable thirdperson camera view in vehicles" );
ConVar vehicle_attach_eye_angles( "vehicle_attach_eye_angles", "0", FCVAR_REPLICATED, "Attach player eye angles to vehicle attachments" );
#define PITCH_CURVE_ZERO 10 // pitch less than this is clamped to zero
#define PITCH_CURVE_LINEAR 45 // pitch greater than this is copied out
#define ROLL_CURVE_ZERO 5 // roll less than this is clamped to zero
#define ROLL_CURVE_LINEAR 45 // roll greater than this is copied out
#if defined( CLIENT_DLL )
ConVar road_feel( "road_feel", "0.1", FCVAR_NOTIFY | FCVAR_REPLICATED );
#else
// Deterioration
#define DETERIORATION_THINK_CONTEXT "VehicleDeteriorationThink"
#define PASSENGER_THINK_CONTEXT "VehiclePassengerThink"
ConVar vehicle_deterioration_start_time( "vehicle_deterioration_start_time", "90", 0, "Time it takes for a vehicle to start deteriorating after being left alone." );
#define DETERIORATION_DISTANCE (600 * 600) // Never deteriorate if team mates are within this distance
#endif // CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseTFVehicle::CBaseTFVehicle()
{
SetPredictionEligible( true );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::Spawn()
{
BaseClass::Spawn();
CollisionProp()->SetSurroundingBoundsType( USE_OBB_COLLISION_BOUNDS );
#if defined( CLIENT_DLL )
SetNextClientThink( CLIENT_THINK_ALWAYS );
m_pIconDefaultCrosshair = NULL;
#else
m_fObjectFlags |= OF_DOESNT_NEED_POWER | OF_MUST_BE_BUILT_ON_ATTACHMENT;
SetContextThink( VehiclePassengerThink, 2.0, PASSENGER_THINK_CONTEXT );
#endif
}
//-----------------------------------------------------------------------------
// Vehicle overrides
//-----------------------------------------------------------------------------
CBaseEntity* CBaseTFVehicle::GetVehicleEnt()
{
return this;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
{
// animate + update attachment points
#ifdef CLIENT_DLL
StudioFrameAdvance();
#else
StudioFrameAdvance();
// This calls StudioFrameAdvance, then we use the results from that to determine where to move.
DispatchAnimEvents( this );
#endif
CTFMoveData *pMoveData = (CTFMoveData*)move;
Assert( sizeof(VehicleBaseMoveData_t) <= pMoveData->VehicleDataMaxSize() );
VehicleBaseMoveData_t *pVehicleData = (VehicleBaseMoveData_t*)pMoveData->VehicleData();
pVehicleData->m_pVehicle = this;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
{
VehicleDriverGunThink();
}
//-----------------------------------------------------------------------------
// Purpose: Returns the driver as a tfplayer pointer
//-----------------------------------------------------------------------------
CBaseTFPlayer *CBaseTFVehicle::GetDriverPlayer()
{
return m_hPassengers[VEHICLE_DRIVER].Get();
}
//-----------------------------------------------------------------------------
// Purpose: Can we get into the vehicle?
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::CanGetInVehicle( CBaseTFPlayer *pPlayer )
{
if ( !IsPowered() )
return false;
if ( !InSameTeam( pPlayer ) )
return false;
// Player/Class-specific query.
return pPlayer->CanGetInVehicle();
}
//-----------------------------------------------------------------------------
// Purpose: Here's where we deal with weapons
//-----------------------------------------------------------------------------
void CBaseTFVehicle::OnItemPostFrame( CBaseTFPlayer *pDriver )
{
// If we have a gun for the driver, handle it
if ( m_hDriverGun )
{
if ( GetPassengerRole(pDriver) != VEHICLE_DRIVER )
return;
if ( pDriver->m_nButtons & (IN_ATTACK | IN_ATTACK2) )
{
// Time to fire?
if ( m_hDriverGun->CanFireNow() )
{
m_hDriverGun->Fire( pDriver );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseTFVehicle::GetPassengerRole( CBasePlayer *pEnt )
{
Assert( pEnt->IsPlayer() );
for ( int i = m_nMaxPassengers; --i >= 0; )
{
if (m_hPassengers[i] == pEnt)
{
return i;
}
}
return -1;
}
Vector CBaseTFVehicle::GetSoundEmissionOrigin() const
{
return WorldSpaceCenter() + Vector( 0, 0, 64 );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBasePlayer* CBaseTFVehicle::GetPassenger( int nRole )
{
return m_hPassengers[nRole].Get();
}
//-----------------------------------------------------------------------------
// Is a particular player in the vehicle?
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::IsPlayerInVehicle( CBaseTFPlayer *pPlayer )
{
return (GetPassengerRole( pPlayer ) >= 0);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseTFVehicle::GetPassengerCount() const
{
// FIXME: Cache this off!
int nCount = 0;
for (int i = m_nMaxPassengers; --i >= 0; )
{
if (m_hPassengers[i].Get())
{
++nCount;
}
}
return nCount;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseTFVehicle::GetMaxPassengerCount() const
{
return m_nMaxPassengers;
}
//-----------------------------------------------------------------------------
// Process input
//-----------------------------------------------------------------------------
void CBaseTFVehicle::ItemPostFrame( CBasePlayer *pPassenger )
{
#ifndef CLIENT_DLL
Assert( GetPassengerRole( pPassenger ) != -1 );
if (pPassenger->m_afButtonPressed & (IN_USE /*| IN_JUMP*/))
{
// Get the player out..
pPassenger->LeaveVehicle();
return;
}
#endif
OnItemPostFrame( static_cast<CBaseTFPlayer*>(pPassenger) );
}
//-----------------------------------------------------------------------------
// Purpose: Reset the time before this vehicle begins to deteriorate
//-----------------------------------------------------------------------------
void CBaseTFVehicle::ResetDeteriorationTime( void )
{
#if !defined (CLIENT_DLL)
SetContextThink( VehicleDeteriorationThink, gpGlobals->curtime + vehicle_deterioration_start_time.GetFloat(), DETERIORATION_THINK_CONTEXT );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Prevent driving in construction yards
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::IsReadyToDrive( void )
{
#if !defined( CLIENT_DLL )
return ( PointInConstructionYard( GetAbsOrigin() ) == false );
#else
return true;
#endif
}
//-----------------------------------------------------------------------------
// Process input
//-----------------------------------------------------------------------------
void CBaseTFVehicle::SetMaxPassengerCount( int nCount )
{
#if !defined( CLIENT_DLL )
Assert( (nCount >= 1) && (nCount <= MAX_PASSENGERS) );
m_nMaxPassengers = nCount;
#endif
}
//-----------------------------------------------------------------------------
//
// Server-only code here
//
//-----------------------------------------------------------------------------
#if !defined (CLIENT_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::FinishedBuilding( void )
{
BaseClass::FinishedBuilding();
// See if we've finished building on a vehicle that has a passenger slot assigned to my buildpoint.
CBaseObject *pParent = GetParentObject();
if ( pParent && pParent->IsAVehicle() )
{
CBaseTFVehicle *pVehicle = static_cast<CBaseTFVehicle*>(pParent);
int iRole = pVehicle->GetChildVehicleRole( this );
if ( iRole != -1 )
{
// Is there a player in the role assigned to this buildpoint?
CBaseTFPlayer *pExistingPlayer = static_cast<CBaseTFPlayer*>( pVehicle->GetPassenger( iRole ) );
if ( pExistingPlayer )
{
// Remove the player from my parent vehicle and put them in me
pExistingPlayer->LeaveVehicle();
// Get in the vehicle.
pExistingPlayer->GetInVehicle( this, VEHICLE_DRIVER );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::VehicleDeteriorationThink( void )
{
StartDeteriorating();
SetContextThink( NULL, gpGlobals->curtime + vehicle_deterioration_start_time.GetFloat(), DETERIORATION_THINK_CONTEXT );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::VehiclePassengerThink( void )
{
SetNextThink( gpGlobals->curtime + 10.0, PASSENGER_THINK_CONTEXT );
if ( IsPlacing() )
{
ResetDeteriorationTime();
return;
}
// If there are any passengers in the vehicle, push off deterioration time
if ( GetPassengerCount() )
{
ResetDeteriorationTime();
return;
}
// See if there are any team members nearby
if ( GetTeam() )
{
int iNumPlayers = GetTFTeam()->GetNumPlayers();
for ( int i = 0; i < iNumPlayers; i++ )
{
if ( GetTFTeam()->GetPlayer(i) )
{
Vector vecOrigin = GetTFTeam()->GetPlayer(i)->GetAbsOrigin();
if ( (vecOrigin - GetAbsOrigin()).LengthSqr() < DETERIORATION_DISTANCE )
{
// Found one nearby, reset our deterioration time
ResetDeteriorationTime();
return;
}
}
}
}
}
//-----------------------------------------------------------------------------
// Figure out which role of a vehicle a child vehicle is sitting in..
//-----------------------------------------------------------------------------
int CBaseTFVehicle::GetChildVehicleRole( CBaseTFVehicle *pChild )
{
int nBuildPoints = GetNumBuildPoints();
for( int i = 0; i < nBuildPoints; i++ )
{
CBaseObject* pObject = GetBuildPointObject(i);
if (pObject == pChild)
{
return GetBuildPointPassenger(i);
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: Vehicles are permanently oriented off angle for vphysics.
//-----------------------------------------------------------------------------
void CBaseTFVehicle::GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const
{
// This call is necessary to cause m_rgflCoordinateFrame to be recomputed
const matrix3x4_t &entityToWorld = EntityToWorldTransform();
if (pForward != NULL)
{
MatrixGetColumn( entityToWorld, 1, *pForward );
}
if (pRight != NULL)
{
MatrixGetColumn( entityToWorld, 0, *pRight );
}
if (pUp != NULL)
{
MatrixGetColumn( entityToWorld, 2, *pUp );
}
}
//-----------------------------------------------------------------------------
// Purpose: Get into the vehicle
//-----------------------------------------------------------------------------
void CBaseTFVehicle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
BaseClass::Use( pActivator, pCaller, useType, value );
if ( useType == USE_ON )
{
CBaseTFPlayer *pPlayer = dynamic_cast<CBaseTFPlayer*>(pActivator);
if ( pPlayer && InSameTeam(pPlayer) )
{
// Check to see if we are really using nearby build points:
if( !UseAttachedItem( pActivator, pCaller, useType, value ) )
{
// Attempt to board the vehicle:
AttemptToBoardVehicle( pPlayer );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Figure out if I should be using an attached item rather than this vehicle itself.
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::UseAttachedItem( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
CBaseTFPlayer* pPlayer = dynamic_cast<CBaseTFPlayer*>(pActivator);
if ( !pPlayer || !InSameTeam(pPlayer) )
return false;
Vector vecPlayerOrigin = pPlayer->GetAbsOrigin();
int nBestBuildPoint = -1;
float fBestDistance = FLT_MAX;
// Get the closest regular entry point:
int nRole = LocateEntryPoint( pPlayer, &fBestDistance );
// Iterate through each of the build points, if any, and see which we are closest to.
int nBuildPoints = GetNumBuildPoints();
for( int i = 0; i < nBuildPoints; i++ )
{
CBaseObject* pObject = GetBuildPointObject(i);
// If there's something in the build point that isn't in the process of being built or placed:
if( pObject && !pObject->IsPlacing() && !pObject->IsBuilding() )
{
Vector vecOrigin;
QAngle vecAngles;
// If the build point is the default point for this role, just take it
if (GetBuildPointPassenger(i) == nRole)
{
nBestBuildPoint = i;
break;
}
// And I can get the build point.
if( GetBuildPoint( i, vecOrigin, vecAngles ) )
{
float fLength2dSqr = (vecOrigin - vecPlayerOrigin).AsVector2D().LengthSqr();
if( fLength2dSqr < fBestDistance )
{
nBestBuildPoint = i;
fBestDistance = fLength2dSqr;
}
}
}
}
if( nBestBuildPoint >= 0 )
{
// They're using an item on me, so push out the deterioration time
ResetDeteriorationTime();
GetBuildPointObject(nBestBuildPoint)->Use( pActivator, pCaller, useType, value );
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Object has been removed...
//-----------------------------------------------------------------------------
void CBaseTFVehicle::DestroyObject( void )
{
for (int i = m_nMaxPassengers; --i >= 0; )
{
if (m_hPassengers[i])
{
m_hPassengers[i]->LeaveVehicle();
}
}
BaseClass::DestroyObject();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseTFVehicle::GetEmptyRole( void )
{
for ( int iPassenger = 0; iPassenger < m_nMaxPassengers; ++iPassenger )
{
if ( !m_hPassengers[iPassenger].Get() )
return iPassenger;
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: Try to board the vehicle
//-----------------------------------------------------------------------------
void CBaseTFVehicle::AttemptToBoardVehicle( CBaseTFPlayer *pPlayer )
{
if ( !CanGetInVehicle( pPlayer ) )
return;
// Locate the entry point.
int nRole = LocateEntryPoint( pPlayer );
if ( nRole != -1 )
{
// Set the owner flag.
bool bOwner = ( pPlayer == GetOwner() );
if ( bOwner )
{
// Check to see if a player exists at this location (role).
CBaseTFPlayer *pExistingPlayer = static_cast<CBaseTFPlayer*>( GetPassenger( nRole ) );
if ( pExistingPlayer )
{
pExistingPlayer->LeaveVehicle();
// Get in the vehicle.
pPlayer->GetInVehicle( this, nRole );
// Then see if we can move the other player to another slot in this vehicle
int nEmptyRole = GetEmptyRole();
if ( nEmptyRole != -1 )
{
pExistingPlayer->GetInVehicle( this, nEmptyRole );
}
return;
}
}
// Get in the vehicle.
pPlayer->GetInVehicle( this, nRole );
}
}
//-----------------------------------------------------------------------------
// Purpose: Handle commands sent from vgui panels on the client
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::ClientCommand( CBaseTFPlayer *pPlayer, const CCommand &args )
{
ResetDeteriorationTime();
if ( FStrEq( pCmd, "toggle_use" ) )
{
AttemptToBoardVehicle( pPlayer );
return true;
}
return BaseClass::ClientCommand( pPlayer, args );
}
//-----------------------------------------------------------------------------
// Get a position in *world space* inside the vehicle for the player to exit at
// NOTE: This doesn't check for obstructions
//-----------------------------------------------------------------------------
void CBaseTFVehicle::GetInitialPassengerExitPoint( int nRole, Vector *pAbsPoint, QAngle *pAbsAngles )
{
char pAttachmentName[32];
Q_snprintf( pAttachmentName, sizeof( pAttachmentName ), "vehicle_exit_passenger%d", nRole );
int exitAttachmentIndex = LookupAttachment(pAttachmentName);
if (exitAttachmentIndex <= 0)
{
// bad attachment, just return the origin
*pAbsPoint = GetAbsOrigin();
pAbsPoint->z += WorldAlignMaxs()[2] + 50.0f;
return;
}
QAngle vehicleExitAngles;
if( !pAbsAngles )
{
pAbsAngles = &vehicleExitAngles;
}
GetAttachment( exitAttachmentIndex, *pAbsPoint, *pAbsAngles );
}
//-----------------------------------------------------------------------------
// Get a point to leave the vehicle from
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::IsValidExitPoint( int nRole, Vector *pExitPoint, QAngle *pAngles )
{
GetInitialPassengerExitPoint( nRole, pExitPoint, pAngles );
// Check the exit point:
Vector vecStart = *pExitPoint;
Vector vecEnd = *pExitPoint + Vector(0,0,20);
trace_t tr;
UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
if ( (tr.fraction < 1.f) )
return false;
vecEnd = *pExitPoint + Vector(20,20,20);
UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
if ( (tr.fraction < 1.f) )
return false;
vecEnd = *pExitPoint + Vector(-20,-20,20);
UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
if ( (tr.fraction < 1.f) )
return false;
vecEnd = *pExitPoint + Vector(20,-20,20);
UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
if ( (tr.fraction < 1.f) )
return false;
vecEnd = *pExitPoint + Vector(-20,20,20);
UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
if ( (tr.fraction < 1.f) )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::GetPassengerExitPoint( CBasePlayer *pPlayer, int nRole, Vector *pAbsPosition, QAngle *pAbsAngles )
{
// Deal with vehicles built on other vehicles
CBaseTFVehicle *pParentVehicle = dynamic_cast<CBaseTFVehicle*>(GetMoveParent());
if (pParentVehicle)
{
int nParentVehicleRole = pParentVehicle->GetChildVehicleRole( this );
if (nParentVehicleRole >= 0)
{
pParentVehicle->GetPassengerExitPoint( pPlayer, nParentVehicleRole, pAbsPosition, pAbsAngles );
return;
}
}
// Deal with vehicles build on objects
IHasBuildPoints *pMount = dynamic_cast<IHasBuildPoints*>(GetMoveParent());
if (pMount)
{
int nBuildPoint = pMount->FindObjectOnBuildPoint( this );
if (nBuildPoint >= 0)
{
pMount->GetExitPoint( pPlayer, nBuildPoint, pAbsPosition, pAbsAngles );
return;
}
}
Vector vNewPos;
GetInitialPassengerExitPoint( nRole, pAbsPosition, pAbsAngles );
if ( EntityPlacementTest(pPlayer, *pAbsPosition, vNewPos, true) )
{
*pAbsPosition = vNewPos;
return;
}
// Find the first valid exit point
for( int iExitPoint = 0; iExitPoint < m_nMaxPassengers; ++iExitPoint )
{
if (iExitPoint == nRole)
continue;
GetInitialPassengerExitPoint( iExitPoint, pAbsPosition, pAbsAngles );
if ( EntityPlacementTest(pPlayer, *pAbsPosition, vNewPos, true) )
{
*pAbsPosition = vNewPos;
return;
}
}
// Worst case, we will be returning the vehicle's origin + 50z here
*pAbsPosition = GetAbsOrigin();
pAbsPosition->z = WorldAlignMaxs()[2] + 150.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::GetPassengerExitPoint( int nRole, Vector *pAbsPosition, QAngle *pAbsAngles )
{
// FIXME: Clean this up
CBasePlayer *pPlayer = GetPassenger(nRole);
GetPassengerExitPoint( pPlayer, nRole, pAbsPosition, pAbsAngles );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseTFVehicle::GetEntryAnimForPoint( const Vector &vecPoint )
{
return ACTIVITY_NOT_AVAILABLE;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseTFVehicle::GetExitAnimToUse( Vector &vecEyeExitEndpoint, bool &bAllPointsBlocked )
{
bAllPointsBlocked = false;
return ACTIVITY_NOT_AVAILABLE;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::HandleEntryExitFinish( bool bExitAnimOn, bool bResetAnim )
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
// false -
//-----------------------------------------------------------------------------
void CBaseTFVehicle::HandlePassengerEntry( CBasePlayer *pPlayer, bool bAllowEntryOutsideZone )
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPlayer -
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::HandlePassengerExit( CBasePlayer *pPlayer )
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Get and set the current driver.
//-----------------------------------------------------------------------------
void CBaseTFVehicle::SetPassenger( int nRole, CBasePlayer *pEnt )
{
Assert( !pEnt || pEnt->IsPlayer() );
Assert( nRole >= 0 && nRole < m_nMaxPassengers );
Assert( !m_hPassengers[nRole].Get() || !pEnt );
m_hPassengers.Set( nRole, dynamic_cast<CBaseTFPlayer*>(pEnt) );
// If the vehicle's deteriorating, I get to own it now
if ( IsDeteriorating() )
{
StopDeteriorating();
SetBuilder( (CBaseTFPlayer*)pEnt, true );
}
ResetDeteriorationTime();
}
#endif
//-----------------------------------------------------------------------------
// Get a position in *world space* inside the vehicle for the player to start at
//-----------------------------------------------------------------------------
void CBaseTFVehicle::GetPassengerStartPoint( int nRole, Vector *pAbsPoint, QAngle *pAbsAngles )
{
char pAttachmentName[32];
Q_snprintf( pAttachmentName, sizeof( pAttachmentName ), "vehicle_feet_passenger%d", nRole );
int nFeetAttachmentIndex = LookupAttachment(pAttachmentName);
GetAttachment( nFeetAttachmentIndex, *pAbsPoint, *pAbsAngles );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
#define INITIAL_MAX_DISTANCE 999999.0f
int CBaseTFVehicle::LocateEntryPoint( CBaseTFPlayer *pPlayer, float* fBest2dDistanceSqr )
{
// Get the players origin and compare it to all the entry points on the
// vehicle.
Vector vecPlayerPos = pPlayer->GetAbsOrigin();
Vector vecEntryPos;
QAngle vecEntryAngle;
int iMinEntry = -1;
float flMinDistance2 = INITIAL_MAX_DISTANCE;
// Is the player the owner of the vehicle?
bool bOwner = ( pPlayer == GetOwner() );
char szPassengerEyes[32];
for( int iEntryPoint = 0; iEntryPoint < m_nMaxPassengers; ++iEntryPoint )
{
// If not the owner, check to see if the entry point is available. The
// entry point is always available for the owner.
bool bOccupied = ( GetPassenger( iEntryPoint ) != NULL );
// Also check for child vehicles...
if ( bOccupied && !bOwner )
continue;
// FIXME: Cache off the entry point
Q_snprintf( szPassengerEyes, sizeof( szPassengerEyes ), "vehicle_feet_passenger%d", iEntryPoint );
int nAttachmentIndex = LookupAttachment( szPassengerEyes );
float flDistance2;
if (nAttachmentIndex > 0)
{
GetAttachment( nAttachmentIndex, vecEntryPos, vecEntryAngle );
Vector vecDelta = vecEntryPos - vecPlayerPos;
flDistance2 = vecDelta.AsVector2D().LengthSqr();
}
else
{
// No attachment? Choose it if we must as a last resort
flDistance2 = INITIAL_MAX_DISTANCE - 1;
}
if ( flDistance2 < flMinDistance2 )
{
flMinDistance2 = flDistance2;
iMinEntry = iEntryPoint;
}
}
if( fBest2dDistanceSqr )
{
*fBest2dDistanceSqr = flMinDistance2;
}
return iMinEntry;
}
//-----------------------------------------------------------------------------
// Purpose: Set a gun that the driver can control
//-----------------------------------------------------------------------------
void CBaseTFVehicle::SetDriverGun( CBaseObjectDriverGun *pGun )
{
m_hDriverGun = pGun;
}
//-----------------------------------------------------------------------------
// Purpose: Update the driver's gun
//-----------------------------------------------------------------------------
void CBaseTFVehicle::VehicleDriverGunThink( void )
{
if ( !m_hDriverGun )
return;
// No driver?
CBaseTFPlayer *pDriver = GetDriverPlayer();
if ( !pDriver )
return;
QAngle vecTargetAngles = m_hDriverGun->GetCurrentAngles();
// Cast a ray out of the view to see where the player is looking.
trace_t trace;
Vector vecForward;
Vector vecSrc;
QAngle angEyeAngles;
GetVehicleViewPosition( VEHICLE_DRIVER, &vecSrc, &angEyeAngles, NULL );
AngleVectors( angEyeAngles, &vecForward, NULL, NULL );
Vector vecEnd = vecSrc + (vecForward * 10000);
UTIL_TraceLine( vecSrc, vecEnd, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &trace );
//NDebugOverlay::Box( vecSrc, -Vector(10,10,10), Vector(10,10,10), 255,0,0,8, 5 );
//NDebugOverlay::Box( vecEnd, -Vector(10,10,10), Vector(10,10,10), 0,255,0,8, 5 );
//NDebugOverlay::Box( trace.endpos, -Vector(10,10,10), Vector(10,10,10), 255,255,255,8, 0.1 );
if ( trace.fraction < 1 )
{
// Figure out what angles our turret needs to be at in order to hit the target.
Vector vFireOrigin = m_hDriverGun->GetFireOrigin();
//NDebugOverlay::Box( vFireOrigin, -Vector(10,10,10), Vector(10,10,10), 0,255,0,8, 0.1 );
// Get a direction vector that points at the target.
Vector vTo = trace.endpos - vFireOrigin;
// Transform it into the tank's local space.
matrix3x4_t tankToWorld;
AngleMatrix( GetAbsAngles(), tankToWorld );
Vector vLocalTo;
VectorITransform( vTo, tankToWorld, vLocalTo );
// Now figure out what the angles are in local space.
QAngle localAngles;
VectorAngles( vLocalTo, localAngles );
vecTargetAngles[YAW] = localAngles[YAW] - 90;
vecTargetAngles[PITCH] = anglemod( localAngles[PITCH] );
}
// Set the gun's angles
m_hDriverGun->SetTargetAngles( vecTargetAngles );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::ShouldUseThirdPersonVehicleView()
{
return true;
}
//-----------------------------------------------------------------------------
// Returns the unperterbed view position for a particular role
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::GetRoleViewPosition( int nRole, Vector *pVehicleEyeOrigin, QAngle *pVehicleEyeAngles )
{
// Generate the view position in world space.
Vector vAbsOrigin;
QAngle vAbsAngle;
bool bUsingThirdPersonCamera = GetRoleAbsViewPosition( nRole, &vAbsOrigin, &vAbsAngle );
// Make a matrix for it.
matrix3x4_t absMatrix;
AngleMatrix( vAbsAngle, absMatrix );
MatrixSetColumn( vAbsOrigin, 3, absMatrix );
// Transform the matrix into local space.
matrix3x4_t worldToEntity, local;
MatrixInvert( EntityToWorldTransform(), worldToEntity );
ConcatTransforms( worldToEntity, absMatrix, local );
// Suck out the origin and angles.
pVehicleEyeOrigin->Init( local[0][3], local[1][3], local[2][3] );
MatrixAngles( local, *pVehicleEyeAngles );
return bUsingThirdPersonCamera;
}
bool CBaseTFVehicle::GetRoleAbsViewPosition( int nRole, Vector *pAbsVehicleEyeOrigin, QAngle *pAbsVehicleEyeAngles )
{
int iAttachment = LookupAttachment( "ThirdPersonCameraOrigin" );
if ( ShouldUseThirdPersonVehicleView() && vehicle_thirdperson.GetInt() && nRole == VEHICLE_DRIVER && iAttachment > 0 )
{
// Ok, we're using third person. Leave their angles intact but rotate theirt view around the
// ThirdPersonCameraOrigin attachment.
Vector vAttachOrigin;
QAngle vAttachAngles;
GetAttachment( iAttachment, vAttachOrigin, vAttachAngles );
Vector vForward, vRight, vUp;
AngleVectors( *pAbsVehicleEyeAngles, &vForward, &vRight, &vUp );
*pAbsVehicleEyeOrigin = vAttachOrigin + vForward * vehicle_view_offset_forward.GetFloat() +
vRight * vehicle_view_offset_right.GetFloat() +
vUp * vehicle_view_offset_up.GetFloat();
// Returning true tells the caller that we're using a third-person camera origin.
return true;
}
else
{
// Use the vehicle_eyes_passengerX attachments.
Assert( nRole >= 0 );
char pAttachmentName[32];
Q_snprintf( pAttachmentName, sizeof( pAttachmentName ), "vehicle_eyes_passenger%d", nRole );
int eyeAttachmentIndex = LookupAttachment(pAttachmentName);
QAngle vTempAngles;
GetAttachment( eyeAttachmentIndex, *pAbsVehicleEyeOrigin, vTempAngles );
if ( vehicle_attach_eye_angles.GetInt() )
*pAbsVehicleEyeAngles = vTempAngles;
return false;
}
}
//-----------------------------------------------------------------------------
// Purpose: Modify the player view/camera while in a vehicle
//-----------------------------------------------------------------------------
void CBaseTFVehicle::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles, float *pFOV /*= NULL*/ )
{
// UNDONE: Use attachment point on the vehicle, not hardcoded player eyes
Assert( nRole >= 0 );
CBasePlayer *pPlayer = GetPassenger( nRole );
Assert( pPlayer );
Vector vehicleEyeOrigin;
QAngle vehicleEyeAngles = pPlayer->LocalEyeAngles();
GetRoleAbsViewPosition( nRole, &vehicleEyeOrigin, &vehicleEyeAngles );
*pAbsOrigin = vehicleEyeOrigin;
*pAbsAngles = vehicleEyeAngles;
/*
if ( bUsingThirdPersonCamera )
{
*pAbsOrigin = vehicleEyeOrigin;
*pAbsAngles = vehicleEyeAngles;
}
else
{
matrix3x4_t vehicleEyePosToWorld;
AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld );
// Compute the relative rotation between the unperterbed eye attachment + the eye angles
matrix3x4_t cameraToWorld;
AngleMatrix( *pAbsAngles, cameraToWorld );
matrix3x4_t worldToEyePos;
MatrixInvert( vehicleEyePosToWorld, worldToEyePos );
matrix3x4_t vehicleCameraToEyePos;
ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos );
// Now perterb the attachment point
if( inv_demo.GetInt() )
{
vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * road_feel.GetFloat(), PITCH_CURVE_LINEAR, vehicleEyeAngles.x );
vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * road_feel.GetFloat(), ROLL_CURVE_LINEAR, vehicleEyeAngles.z );
}
else
{
vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO, PITCH_CURVE_LINEAR, vehicleEyeAngles.x );
vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO, ROLL_CURVE_LINEAR, vehicleEyeAngles.z );
}
AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld );
// Now treat the relative eye angles as being relative to this new, perterbed view position...
matrix3x4_t newCameraToWorld;
ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld );
// output new view abs angles
MatrixAngles( newCameraToWorld, *pAbsAngles );
// UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics
MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin );
}
*/
}
//-----------------------------------------------------------------------------
//
// Client-only code here
//
//-----------------------------------------------------------------------------
#if defined (CLIENT_DLL)
void CBaseTFVehicle::ClientThink()
{
BaseClass::ClientThink();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseTFVehicle::ShouldPredict( void )
{
// Only predict vehicles driven by local players
return GetDriverPlayer() ? GetDriverPlayer()->IsLocalPlayer() : false;
}
//-----------------------------------------------------------------------------
// Purpose: Get the angles that a player in the specified role should be using for visuals
//-----------------------------------------------------------------------------
QAngle CBaseTFVehicle::GetPassengerAngles( QAngle angCurrent, int nRole )
{
// Just use your current angles
return angCurrent;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::DrawHudElements( void )
{
// If we've got a driver gun, tell it to draw it's elements
if ( m_hDriverGun )
{
m_hDriverGun->DrawHudElements();
}
DrawHudBoostData();
SetupCrosshair();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseTFVehicle::DrawHudBoostData( void )
{
#define HUD_IMAGE_LEFT XRES( 568 )
// Boostable vehicle
if ( IsBoostable() )
{
// Set our color
CHudTexture *pVehicleBoostLabel = gHUD.GetIcon( "no2" );
if ( pVehicleBoostLabel )
{
int nScreenY = ScreenHeight() - YRES( 12 );
float nOneHudHeight = ( YRES(10) + pVehicleBoostLabel->Height() );
nScreenY -= ( nOneHudHeight * 3 );
pVehicleBoostLabel->DrawSelf( HUD_IMAGE_LEFT, nScreenY - pVehicleBoostLabel->Height(), gHUD.m_clrNormal );
gHUD.DrawProgressBar( HUD_IMAGE_LEFT, nScreenY + YRES( 4 ), XRES( 70 ), YRES( 4 ), m_nBoostTimeLeft / 100.0f, gHUD.m_clrNormal, CHud::HUDPB_HORIZONTAL_INV );
}
}
#undef HUD_IMAGE_LEFT
}
//-----------------------------------------------------------------------------
// Purpose: Set a crosshair when in a vehicle and we don't have a proper
// crosshair sprite (ie. a commando laser rifle).
//-----------------------------------------------------------------------------
void CBaseTFVehicle::SetupCrosshair( void )
{
if ( !m_pIconDefaultCrosshair )
{
// Init the default crosshair the first time.
CHudTexture newTexture;
Q_strncpy( newTexture.szTextureFile, "sprites/crosshairs", sizeof( newTexture.szTextureFile ) );
newTexture.rc.left = 0;
newTexture.rc.top = 48;
newTexture.rc.right = newTexture.rc.left + 24;
newTexture.rc.bottom = newTexture.rc.top + 24;
m_pIconDefaultCrosshair = gHUD.AddUnsearchableHudIconToList( newTexture );
}
CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair );
if ( crosshair )
{
if ( !crosshair->HasCrosshair() && m_pIconDefaultCrosshair )
{
crosshair->SetCrosshair( m_pIconDefaultCrosshair, gHUD.m_clrNormal );
}
}
}
#endif