diff options
Diffstat (limited to 'game/shared/tf2/basetfvehicle.cpp')
| -rw-r--r-- | game/shared/tf2/basetfvehicle.cpp | 1179 |
1 files changed, 1179 insertions, 0 deletions
diff --git a/game/shared/tf2/basetfvehicle.cpp b/game/shared/tf2/basetfvehicle.cpp new file mode 100644 index 0000000..1825753 --- /dev/null +++ b/game/shared/tf2/basetfvehicle.cpp @@ -0,0 +1,1179 @@ +//========= 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 |