aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/ai_behavior_passenger.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/ai_behavior_passenger.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/ai_behavior_passenger.cpp')
-rw-r--r--mp/src/game/server/ai_behavior_passenger.cpp3472
1 files changed, 1736 insertions, 1736 deletions
diff --git a/mp/src/game/server/ai_behavior_passenger.cpp b/mp/src/game/server/ai_behavior_passenger.cpp
index 7d154025..d5d4439d 100644
--- a/mp/src/game/server/ai_behavior_passenger.cpp
+++ b/mp/src/game/server/ai_behavior_passenger.cpp
@@ -1,1736 +1,1736 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Behavior for NPCs riding in cars (with boys)
-//
-//=============================================================================
-
-#include "cbase.h"
-#include "ai_playerally.h"
-#include "ai_motor.h"
-#include "bone_setup.h"
-#include "vehicle_base.h"
-#include "entityblocker.h"
-#include "ai_behavior_passenger.h"
-#include "ai_pathfinder.h"
-#include "ai_network.h"
-#include "ai_node.h"
-#include "ai_moveprobe.h"
-#include "env_debughistory.h"
-
-// Custom activities
-int ACT_PASSENGER_IDLE;
-int ACT_PASSENGER_RANGE_ATTACK1;
-
-ConVar passenger_debug_transition( "passenger_debug_transition", "0" );
-ConVar passenger_impact_response_threshold( "passenger_impact_response_threshold", "-350.0" );
-
-#define ORIGIN_KEYNAME "origin"
-#define ANGLES_KEYNAME "angles"
-
-BEGIN_DATADESC( CAI_PassengerBehavior )
-
- DEFINE_EMBEDDED( m_vehicleState ),
- DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_PassengerIntent, FIELD_INTEGER ),
- DEFINE_FIELD( m_PassengerState, FIELD_INTEGER ),
- DEFINE_FIELD( m_hVehicle, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hBlocker, FIELD_EHANDLE ),
- DEFINE_FIELD( m_vecTargetPosition, FIELD_POSITION_VECTOR ),
- DEFINE_FIELD( m_vecTargetAngles, FIELD_VECTOR ),
- DEFINE_FIELD( m_flOriginStartFrame, FIELD_FLOAT ),
- DEFINE_FIELD( m_flOriginEndFrame, FIELD_FLOAT ),
- DEFINE_FIELD( m_flAnglesStartFrame, FIELD_FLOAT ),
- DEFINE_FIELD( m_flAnglesEndFrame, FIELD_FLOAT ),
- DEFINE_FIELD( m_nTransitionSequence,FIELD_INTEGER ),
-
-END_DATADESC();
-
-BEGIN_SIMPLE_DATADESC( passengerVehicleState_t )
-
- DEFINE_FIELD( m_bWasBoosting, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bWasOverturned, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_vecLastLocalVelocity, FIELD_VECTOR ),
- DEFINE_FIELD( m_vecDeltaVelocity, FIELD_VECTOR ),
- DEFINE_FIELD( m_vecLastAngles, FIELD_VECTOR ),
- DEFINE_FIELD( m_flNextWarningTime, FIELD_TIME ),
- DEFINE_FIELD( m_flLastSpeedSqr, FIELD_FLOAT ),
- DEFINE_FIELD( m_bPlayerInVehicle, FIELD_BOOLEAN ),
-
-END_DATADESC();
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CAI_PassengerBehavior::CAI_PassengerBehavior( void ) :
-m_bEnabled( false ),
-m_hVehicle( NULL ),
-m_PassengerState( PASSENGER_STATE_OUTSIDE ),
-m_PassengerIntent( PASSENGER_INTENT_NONE ),
-m_nTransitionSequence( -1 )
-{
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Enables the behavior to run
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::Enable( CPropJeepEpisodic *pVehicle, bool bImmediateEnter /*= false*/ )
-{
- if ( m_bEnabled && m_hVehicle.Get() )
- return;
-
- m_bEnabled = true;
- m_hVehicle = pVehicle;
- SetPassengerState( PASSENGER_STATE_OUTSIDE );
-
- // Init our starting information about the vehicle
- InitVehicleState();
-}
-
-void CAI_PassengerBehavior::OnRestore()
-{
- if ( m_bEnabled && !m_hVehicle.Get() )
- {
- Disable();
- }
-
- BaseClass::OnRestore();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Stops the behavior from being run
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::Disable( void )
-{
- m_bEnabled = false;
- m_hVehicle = NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Starts the process of entering a vehicle
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::EnterVehicle( void )
-{
- // If we're already in the vehicle, simply cancel out our intents
- if ( GetPassengerState() == PASSENGER_STATE_INSIDE || GetPassengerState() == PASSENGER_STATE_ENTERING )
- {
- // Clear out an exit
- if ( m_PassengerIntent == PASSENGER_INTENT_EXIT )
- {
- m_PassengerIntent = PASSENGER_INTENT_NONE;
- ClearCondition( COND_PASSENGER_ENTERING );
- ClearCondition( COND_PASSENGER_EXITING );
- }
-
- return;
- }
-
- // Update our internal state
- m_PassengerIntent = PASSENGER_INTENT_ENTER;
-
- // Otherwise set this condition and go!
- SetCondition( COND_PASSENGER_ENTERING );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Starts the process of exiting a vehicle
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::ExitVehicle( void )
-{
- // Must be in the seat
- if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE || GetPassengerState() == PASSENGER_STATE_EXITING )
- {
- // Clear out an entrance
- if ( m_PassengerIntent == PASSENGER_INTENT_ENTER )
- {
- m_PassengerIntent = PASSENGER_INTENT_NONE;
- SetCondition( COND_PASSENGER_CANCEL_ENTER );
- ClearCondition( COND_PASSENGER_ENTERING );
- ClearCondition( COND_PASSENGER_EXITING );
- }
- return;
- }
-
- // Update our internal state
- m_PassengerIntent = PASSENGER_INTENT_EXIT;
-
- //
- // Everything below this point will still attempt to exit the vehicle, once able
- //
-
- // Must have a valid vehicle
- if ( m_hVehicle == NULL )
- return;
-
- // Cannot exit while we're upside down
- if ( m_hVehicle->IsOverturned() )
- return;
-
- // Interrupt what we're doing
- SetCondition( COND_PASSENGER_EXITING );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: FIXME - This should move into something a bit more flexible
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::AddPhysicsPush( float force )
-{
- /*
- // Kick the vehicle so the player knows we've arrived
- Vector impulse = m_hVehicle->GetAbsOrigin() - GetOuter()->GetAbsOrigin();
- VectorNormalize( impulse );
- impulse.z = -0.75;
- VectorNormalize( impulse );
- Vector vecForce = impulse * force;
-
- m_hVehicle->VPhysicsGetObject()->ApplyForceOffset( vecForce, GetOuter()->GetAbsOrigin() );
- */
-
- Vector vecDir;
-
- IPhysicsObject *pObject = GetOuter()->VPhysicsGetObject();
- Vector vecVelocity;
- pObject->GetVelocity( &vecVelocity, NULL );
- GetOuter()->GetVectors( NULL, NULL, &vecDir );
- vecDir.Negate();
-
- Vector vecForce = vecDir * force;
-
- m_hVehicle->VPhysicsGetObject()->ApplyForceOffset( vecForce, GetOuter()->GetAbsOrigin() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::IsPassengerHostile( void )
-{
- CBaseEntity *pPlayer = AI_GetSinglePlayer();
-
- // If the player hates or fears the passenger, they're hostile
- if ( GetOuter()->IRelationType( pPlayer ) == D_HT || GetOuter()->IRelationType( pPlayer ) == D_FR )
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::InitVehicleState( void )
-{
- // Set the player's state
- CBasePlayer *pPlayer = AI_GetSinglePlayer();
- m_vehicleState.m_bPlayerInVehicle = ( pPlayer && pPlayer->IsInAVehicle() && pPlayer->GetServerVehicle() == m_hVehicle->GetServerVehicle() );
-
- // Update our vehicle state so we don't confuse our previous velocity on the first frame!
- m_vehicleState.m_bWasBoosting = false;
- m_vehicleState.m_bWasOverturned = false;
- m_vehicleState.m_flNextWarningTime = 0.0f;
- m_vehicleState.m_vecDeltaVelocity = vec3_origin;
- m_vehicleState.m_flNextWarningTime = gpGlobals->curtime;
- m_vehicleState.m_vecLastAngles = m_hVehicle->GetAbsAngles();
-
- Vector localVelocity;
- GetLocalVehicleVelocity( &m_vehicleState.m_vecLastLocalVelocity );
-
- m_vehicleState.m_flLastSpeedSqr = localVelocity.LengthSqr();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Puts the NPC in hierarchy with the vehicle and makes them intangible
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::FinishEnterVehicle( void )
-{
- if ( m_hVehicle == NULL )
- return;
-
- // Get the ultimate position we want to be in
- Vector vecFinalPos;
- QAngle vecFinalAngles;
- GetEntryTarget( &vecFinalPos, &vecFinalAngles );
-
- // Make sure we're exactly where we need to be
- GetOuter()->SetLocalOrigin( vecFinalPos );
- GetOuter()->SetLocalAngles( vecFinalAngles );
- GetOuter()->SetMoveType( MOVETYPE_NONE );
- GetOuter()->GetMotor()->SetYawLocked( true );
-
- // We're now riding inside the vehicle
- SetPassengerState( PASSENGER_STATE_INSIDE );
-
- // If we've not been told to leave immediately, we're done
- if ( m_PassengerIntent == PASSENGER_INTENT_ENTER )
- {
- m_PassengerIntent = PASSENGER_INTENT_NONE;
- }
-
- // Tell the vehicle we've succeeded
- m_hVehicle->NPC_FinishedEnterVehicle( GetOuter(), (IsPassengerHostile()==false) );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Removes the NPC from the car
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::FinishExitVehicle( void )
-{
- if ( m_hVehicle == NULL )
- return;
-
- // Destroy the blocker
- if ( m_hBlocker != NULL )
- {
- UTIL_Remove( m_hBlocker );
- m_hBlocker = NULL;
- }
-
- // To do this, we need to be very sure we're in a good spot
- GetOuter()->SetCondition( COND_PROVOKED );
- GetOuter()->SetMoveType( MOVETYPE_STEP );
- GetOuter()->RemoveFlag( FL_FLY );
- GetOuter()->GetMotor()->SetYawLocked( false );
-
- // Re-enable the physical collisions for this NPC
- IPhysicsObject *pPhysObj = GetOuter()->VPhysicsGetObject();
- if ( pPhysObj != NULL )
- {
- pPhysObj->EnableCollisions( true );
- }
-
- m_hVehicle->NPC_RemovePassenger( GetOuter() );
- m_hVehicle->NPC_FinishedExitVehicle( GetOuter(), (IsPassengerHostile()==false) );
-
- SetPassengerState( PASSENGER_STATE_OUTSIDE );
-
- // Stop our custom move sequence
- GetOuter()->m_iszSceneCustomMoveSeq = NULL_STRING;
-
- // If we've not been told to enter immediately, we're done
- if ( m_PassengerIntent == PASSENGER_INTENT_EXIT )
- {
- m_PassengerIntent = PASSENGER_INTENT_NONE;
- Disable();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Build our custom interrupt cases for the behavior
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::BuildScheduleTestBits( void )
-{
- // Always interrupt when we need to get in or out
- if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE || GetPassengerState() == PASSENGER_STATE_INSIDE )
- {
- GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_ENTERING ) );
- GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_EXITING ) );
- GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_CANCEL_ENTER ) );
- }
-
- BaseClass::BuildScheduleTestBits();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Dictates whether or not the behavior is active and working
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::CanSelectSchedule( void )
-{
- return m_bEnabled;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::CanExitVehicle( void )
-{
- // Vehicle must not be overturned
- if ( m_hVehicle->IsOverturned() )
- return false;
-
- // Vehicle must be at rest
- Vector vecVelocity;
- m_hVehicle->GetVelocity( &vecVelocity, NULL );
- if ( vecVelocity.LengthSqr() > Square( 8.0f ) )
- return false;
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Handles passengers deciding to enter or exit the vehicle
-// Output : int
-//-----------------------------------------------------------------------------
-int CAI_PassengerBehavior::SelectTransitionSchedule( void )
-{
- // Handle our various states
- if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
- {
- // Exiting schedule
- if ( HasCondition( COND_PASSENGER_EXITING ) || m_PassengerIntent == PASSENGER_INTENT_EXIT )
- {
- if ( CanExitVehicle( ) )
- {
- ClearCondition( COND_PASSENGER_EXITING );
- return SCHED_PASSENGER_EXIT_VEHICLE;
- }
- }
- }
- else if ( GetPassengerState() == PASSENGER_STATE_ENTERING || GetPassengerState() == PASSENGER_STATE_EXITING )
- {
- // The following code attempts to fix up a passenger being interrupted mid-transition
- Warning( "SelectSchedule() called on transitioning passenger!\n" );
- ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs( "%s(%d): SelectSchedule() called on transitioning passenger!\n", GetOuter()->GetDebugName(), GetOuter()->entindex() ) );
- Assert( 0 );
-
- // Correct this issue immediately
- if ( GetPassengerState() == PASSENGER_STATE_EXITING )
- {
- // Force them out of the vehicle to where they want to be
- // The teleport function is overridden for passengers, meaning that they will clean up properly when called to do so
- GetOuter()->Teleport( &m_vecTargetPosition, &m_vecTargetAngles, NULL );
- }
- else if ( GetPassengerState() == PASSENGER_STATE_ENTERING )
- {
- // Force them into the proper position
- GetOuter()->SetLocalOrigin( m_vecTargetPosition );
- GetOuter()->SetLocalAngles( m_vecTargetAngles );
- FinishEnterVehicle();
- }
-
- // Stop playing our animation
- SetActivity( ACT_RESET );
- }
-
- return SCHED_NONE;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Overrides the schedule selection
-// Output : int - Schedule to play
-//-----------------------------------------------------------------------------
-int CAI_PassengerBehavior::SelectSchedule( void )
-{
- // Protect from this rare occurrence happening
- if ( m_hVehicle == NULL )
- {
- Assert( m_hVehicle != NULL );
- Warning( "Entity %s running passenger behavior without a valid vehicle!\n", GetName() );
-
- Disable();
- return BaseClass::SelectSchedule();
- }
-
- // See if we're transitioning in / out of the vehicle
- int nSchedule = SelectTransitionSchedule();
- if ( nSchedule != SCHED_NONE )
- return nSchedule;
-
- return SCHED_PASSENGER_IDLE;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CAI_PassengerBehavior::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
-{
- switch( failedTask )
- {
- // For now, just sit back down
- case TASK_PASSENGER_DETACH_FROM_VEHICLE:
- return SCHED_PASSENGER_IDLE;
- break;
- }
-
- return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds a ground position at a given location with some delta up and down to check
-// Input : &in - position to check at
-// delta - amount of distance up and down to check
-// *out - ground position
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::FindGroundAtPosition( const Vector &in, float flUpDelta, float flDownDelta, Vector *out )
-{
- Vector startPos = in + Vector( 0, 0, flUpDelta ); // Look up by delta
- Vector endPos = in - Vector( 0, 0, flDownDelta ); // Look down by delta
- Vector hullMin = GetOuter()->GetHullMins();
- Vector hullMax = GetOuter()->GetHullMaxs();
-
- // Ignore ourself and the vehicle we're referencing
- CTraceFilterVehicleTransition ignoreFilter( m_hVehicle, GetOuter(), COLLISION_GROUP_NONE );
-
- trace_t tr;
- UTIL_TraceHull( startPos, endPos, hullMin, hullMax, MASK_NPCSOLID, &ignoreFilter, &tr );
-
- // Must not have ended up in solid space
- if ( tr.allsolid )
- {
- // Debug
- if ( passenger_debug_transition.GetBool() )
- {
- NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 255, 255, 0, 255, 1.0f );
- }
-
- return false;
- }
-
- // Must have ended up with feet on the ground
- if ( tr.DidHitWorld() || ( tr.m_pEnt && tr.m_pEnt->IsStandable() ) )
- {
- // Debug
- if ( passenger_debug_transition.GetBool() )
- {
- NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 0, 255, 0, 255, 1.0f );
- }
-
- *out = tr.endpos;
- return true;
- }
-
- // Ended up in the air
- if ( passenger_debug_transition.GetBool() )
- {
- NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 255, 0, 0, 255, 1.0f );
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Attempt to verify that a test position is on the node graph
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::PointIsNavigable( const Vector &vecTargetPos )
-{
- // Attempt to local move between this point and the nearest node, ignoring anything but world architecture
- AIMoveTrace_t moveTrace;
- int iNearestNode = GetOuter()->GetPathfinder()->NearestNodeToPoint( vecTargetPos );
- if ( iNearestNode != NO_NODE )
- {
- // Try a movement trace between the test position and the node
- GetOuter()->GetMoveProbe()->MoveLimit( NAV_GROUND,
- g_pBigAINet->GetNodePosition(GetOuter()->GetHullType(), iNearestNode ),
- vecTargetPos,
- MASK_SOLID_BRUSHONLY,
- NULL,
- 0,
- &moveTrace );
-
- // See if we got close enough to call it arrived
- if ( ( moveTrace.vEndPosition - vecTargetPos ).LengthSqr() < Square( GetHullWidth() ) &&
- GetOuter()->GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, MASK_SOLID_BRUSHONLY ) )
- {
- // NDebugOverlay::HorzArrow( vecTargetPos, moveTrace.vEndPosition, 8.0f, 255, 0, 0, 16, true, 4.0f );
- // NDebugOverlay::HorzArrow( vecTargetPos, g_pBigAINet->GetNodePosition(GetOuter()->GetHullType(), iNearestNode ), 8.0f, 255, 255, 0, 16, true, 4.0f );
- return true;
- }
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets the exit point for the passenger (on the ground)
-// Input : &vecOut - position the entity should be at when finished exiting
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::GetExitPoint( int nSequence, Vector *vecExitPoint, QAngle *vecExitAngles )
-{
- bool bSucceeded = true;
-
- // Get the delta to the final position as will be dictated by this animation's auto movement
- Vector vecDeltaPos;
- QAngle vecDeltaAngles;
- GetOuter()->GetSequenceMovement( nSequence, 0.0f, 1.0f, vecDeltaPos, vecDeltaAngles );
-
- // Rotate the delta position by our starting angles
- Vector vecRotPos = vecDeltaPos;
- VectorRotate( vecRotPos, GetOuter()->GetAbsAngles(), vecDeltaPos );
-
- if ( vecExitPoint != NULL )
- {
- float flDownDelta = 64.0f;
- float flUpDelta = 16.0f;
- Vector vecGroundPos;
-
- bool bFoundGround = FindGroundAtPosition( GetOuter()->GetAbsOrigin() + vecDeltaPos, flUpDelta, flDownDelta, &vecGroundPos );
- if ( bFoundGround )
- {
- if ( PointIsNavigable( vecGroundPos ) == false )
- {
- bSucceeded = false;
- }
- }
- else
- {
- bSucceeded = false;
- }
-
- *vecExitPoint = vecGroundPos;
- }
-
- if ( vecExitAngles != NULL )
- {
- QAngle newAngles = GetOuter()->GetAbsAngles() + vecDeltaAngles;
- newAngles.x = UTIL_AngleMod( newAngles.x );
- newAngles.y = UTIL_AngleMod( newAngles.y );
- newAngles.z = UTIL_AngleMod( newAngles.z );
- *vecExitAngles = newAngles;
- }
-
- return bSucceeded;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Reserve our entry point
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::ReserveEntryPoint( VehicleSeatQuery_e eSeatSearchType )
-{
- // FIXME: Move all this logic into the NPC_EnterVehicle function?
- // Find any seat to get into
- int nSeatID = m_hVehicle->GetServerVehicle()->NPC_GetAvailableSeat( GetOuter(), GetRoleName(), eSeatSearchType );
- if ( nSeatID != VEHICLE_SEAT_INVALID )
- return m_hVehicle->NPC_AddPassenger( GetOuter(), GetRoleName(), nSeatID );
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Determines whether the NPC can move between a start and end position of a transition
-// Input : &vecStartPos - start position
-// &vecEndPos - end position
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::IsValidTransitionPoint( const Vector &vecStartPos, const Vector &vecEndPos )
-{
- // Now sweep a hull through space to see if we can validly exit there
- Vector vecHullMins = GetOuter()->GetHullMins() + Vector( 0, 0, GetOuter()->StepHeight()*2.0f );
- Vector vecHullMaxs = GetOuter()->GetHullMaxs() - Vector( 0, 0, GetOuter()->StepHeight() );
-
- trace_t tr;
- CTraceFilterVehicleTransition skipFilter( GetOuter(), m_hVehicle, COLLISION_GROUP_NONE );
- UTIL_TraceHull( vecStartPos, vecEndPos, vecHullMins, vecHullMaxs, MASK_NPCSOLID, &skipFilter, &tr );
-
- // If we're blocked, we can't get out there
- if ( tr.fraction < 1.0f || tr.allsolid )
- {
- if ( passenger_debug_transition.GetBool() )
- {
- NDebugOverlay::SweptBox( vecStartPos, vecEndPos, vecHullMins, vecHullMaxs, vec3_angle, 255, 0, 0, 64, 2.0f );
- }
- return false;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Find the proper sequence to use (weighted by priority or distance from current position)
-// to enter the vehicle.
-// Input : bNearest - Use distance as the criteria for a "best" sequence. Otherwise the order of the
-// seats is their priority.
-// Output : int - sequence index
-//-----------------------------------------------------------------------------
-int CAI_PassengerBehavior::FindEntrySequence( bool bNearest /*= false*/ )
-{
- // Get a list of all our animations
- const PassengerSeatAnims_t *pEntryAnims = m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatAnims( GetOuter(), PASSENGER_SEAT_ENTRY );
- if ( pEntryAnims == NULL )
- return -1;
-
- // Get the ultimate position we'll end up at
- Vector vecStartPos, vecEndPos;
- if ( m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPosition( GetOuter(), &vecEndPos, NULL ) == false )
- return -1;
-
- const CPassengerSeatTransition *pTransition;
- Vector vecSeatDir;
- float flNearestDist = FLT_MAX;
- float flSeatDist;
- int nNearestSequence = -1;
- int nSequence;
-
- // Test each animation (sorted by priority) for the best match
- for ( int i = 0; i < pEntryAnims->Count(); i++ )
- {
- // Find the activity for this animation name
- pTransition = &pEntryAnims->Element(i);
- nSequence = GetOuter()->LookupSequence( STRING( pTransition->GetAnimationName() ) );
- if ( nSequence == -1 )
- continue;
-
- // Test this entry for validity
- if ( GetEntryPoint( nSequence, &vecStartPos ) == false )
- continue;
-
- // Check to see if we can use this
- if ( IsValidTransitionPoint( vecStartPos, vecEndPos ) )
- {
- // If we're just looking for the first, we're done
- if ( bNearest == false )
- return nSequence;
-
- // Otherwise distance is the deciding factor
- vecSeatDir = ( vecStartPos - GetOuter()->GetAbsOrigin() );
- flSeatDist = VectorNormalize( vecSeatDir );
-
- // Closer, take it
- if ( flSeatDist < flNearestDist )
- {
- flNearestDist = flSeatDist;
- nNearestSequence = nSequence;
- }
- }
-
- }
-
- return nNearestSequence;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-int CAI_PassengerBehavior::FindExitSequence( void )
-{
- // Get a list of all our animations
- const PassengerSeatAnims_t *pExitAnims = m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatAnims( GetOuter(), PASSENGER_SEAT_EXIT );
- if ( pExitAnims == NULL )
- return -1;
-
- // Get the ultimate position we'll end up at
- Vector vecStartPos, vecEndPos;
- if ( m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPosition( GetOuter(), &vecStartPos, NULL ) == false )
- return -1;
-
- // Test each animation (sorted by priority) for the best match
- for ( int i = 0; i < pExitAnims->Count(); i++ )
- {
- // Find the activity for this animation name
- int nSequence = GetOuter()->LookupSequence( STRING( pExitAnims->Element(i).GetAnimationName() ) );
- if ( nSequence == -1 )
- continue;
-
- // Test this entry for validity
- if ( GetExitPoint( nSequence, &vecEndPos ) == false )
- continue;
-
- // Check to see if we can use this
- if ( IsValidTransitionPoint( vecStartPos, vecEndPos ) )
- return nSequence;
- }
-
- return -1;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Reserve our exit point so nothing moves into it while we're moving
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::ReserveExitPoint( void )
-{
- // Cannot exit while we're upside down
- // FIXME: This is probably redundant!
- if ( m_hVehicle->IsOverturned() )
- return false;
-
- // Find the exit activity to use
- int nSequence = FindExitSequence();
- if ( nSequence == -1 )
- return false;
-
- // Get the exit position
- Vector vecGroundPos;
- if ( GetExitPoint( nSequence, &vecGroundPos, &m_vecTargetAngles ) == false )
- return false;
-
- // We have to do this specially because the activities are not named
- SetTransitionSequence( nSequence );
-
- // Reserve this space
- Vector hullMin = GetOuter()->GetHullMins();
- Vector hullMax = GetOuter()->GetHullMaxs();
- m_hBlocker = CEntityBlocker::Create( vecGroundPos, hullMin, hullMax, GetOuter(), true );
-
- // Save this destination position so we can interpolate towards it
- m_vecTargetPosition = vecGroundPos;
-
- // Pitch and roll must be zero when we finish!
- m_vecTargetAngles.x = m_vecTargetAngles.z = 0.0f;
-
- if ( passenger_debug_transition.GetBool() )
- {
- Vector vecForward;
- AngleVectors( m_vecTargetAngles, &vecForward, NULL, NULL );
- Vector vecArrowEnd = m_vecTargetPosition + ( vecForward * 64.0f );
- NDebugOverlay::HorzArrow( m_vecTargetPosition, vecArrowEnd, 8.0f, 255, 255, 0, 64, true, 4.0f );
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Find the exact point we'd like to start our animation from to enter
-// the vehicle.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::GetEntryPoint( int nSequence, Vector *vecEntryPoint, QAngle *vecEntryAngles )
-{
- bool bSucceeded = true;
-
- // Get the delta to the final position as will be dictated by this animation's auto movement
- Vector vecDeltaPos;
- QAngle vecDeltaAngles;
- GetOuter()->GetSequenceMovement( nSequence, 1.0f, 0.0f, vecDeltaPos, vecDeltaAngles );
-
- // Get the final position we're trying to end up at
- Vector vecTargetPos;
- QAngle vecTargetAngles;
- GetEntryTarget( &vecTargetPos, &vecTargetAngles );
-
- // Rotate it to match
- Vector vecPreDelta = vecDeltaPos;
- VectorRotate( vecPreDelta, vecTargetAngles, vecDeltaPos );
-
- // Offset this into the proper worldspace position
- vecTargetPos = vecTargetPos + vecDeltaPos;
-
- // Output the position, if requested
- if ( vecEntryPoint != NULL )
- {
- m_hVehicle->EntityToWorldSpace( vecTargetPos, vecEntryPoint );
-
- // Trace down to the ground to see where we'll stand
- Vector vecGroundPos;
- if ( FindGroundAtPosition( (*vecEntryPoint), 16.0f, 64.0f, &vecGroundPos ) == false )
- {
- // We failed
- if ( passenger_debug_transition.GetBool() )
- {
- NDebugOverlay::SweptBox( (*vecEntryPoint), vecGroundPos, GetOuter()->GetHullMins(), GetOuter()->GetHullMaxs(), vec3_angle, 255, 0, 0, 64, 2.0f );
- }
-
- // The floor could not be found
- bSucceeded = false;
- }
-
- // Take this position
- *vecEntryPoint = vecGroundPos;
- }
-
- // Output the angles, if requested
- if ( vecEntryAngles != NULL )
- {
- // Add our delta angles to find what angles to start at
- *vecEntryAngles = vecTargetAngles;
- vecEntryAngles->y = UTIL_AngleMod( vecTargetAngles.y + vecDeltaAngles.y );
-
- //Transform those angles to worldspace
- matrix3x4_t angToParent, angToWorld;
- AngleMatrix( (*vecEntryAngles), angToParent );
- ConcatTransforms( m_hVehicle->EntityToWorldTransform(), angToParent, angToWorld );
- MatrixAngles( angToWorld, (*vecEntryAngles) );
- }
-
- // Debug info
- if ( passenger_debug_transition.GetBool() && vecEntryPoint && vecEntryAngles )
- {
- NDebugOverlay::Axis( *vecEntryPoint, vecTargetAngles, 16, true, 4.0f );
- NDebugOverlay::Cross3D( *vecEntryPoint, 4, 255, 255, 0, true, 4.0f );
-
- if ( vecEntryAngles != NULL )
- {
- Vector vecForward;
- AngleVectors( (*vecEntryAngles), &vecForward, NULL, NULL );
- Vector vecArrowEnd = (*vecEntryPoint ) + ( vecForward * 64.0f );
- NDebugOverlay::HorzArrow( (*vecEntryPoint), vecArrowEnd, 8.0f, 0, 255, 0, 64, true, 4.0f );
- }
- }
-
- return bSucceeded;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Do the low-level work to detach us from our vehicle
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::DetachFromVehicle( void )
-{
- // Detach from the parent
- GetOuter()->SetParent( NULL );
- GetOuter()->SetMoveType( MOVETYPE_STEP );
- GetOuter()->AddFlag( FL_FLY );
- GetOuter()->SetGroundEntity( NULL );
- GetOuter()->SetCollisionGroup( COLLISION_GROUP_NPC );
- m_hVehicle->RemovePhysicsChild( GetOuter() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::AttachToVehicle( void )
-{
- // Parent to the vehicle
- GetOuter()->ClearForceCrouch();
- GetOuter()->SetParent( m_hVehicle );
- GetOuter()->AddFlag( FL_FLY );
- GetOuter()->SetGroundEntity( NULL );
- GetOuter()->SetCollisionGroup( COLLISION_GROUP_IN_VEHICLE );
-
- // Turn off physical interactions while we're in the vehicle
- IPhysicsObject *pPhysObj = GetOuter()->VPhysicsGetObject();
- if ( pPhysObj != NULL )
- {
- pPhysObj->EnableCollisions( false );
- }
-
- // Set our destination target
- GetEntryTarget( &m_vecTargetPosition, &m_vecTargetAngles );
-
- // Get physics messages from our attached physics object
- m_hVehicle->AddPhysicsChild( GetOuter() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Handle task starting
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::StartTask( const Task_t *pTask )
-{
- switch ( pTask->iTask )
- {
- case TASK_PASSENGER_ENTER_VEHICLE:
- {
- // You must have set your entrance animation before this point!
- Assert( m_nTransitionSequence != -1 );
-
- // Start us playing the correct sequence
- GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
- SetPassengerState( PASSENGER_STATE_ENTERING );
-
- // Overlaying any gestures will mess us up, so don't allow it
- GetOuter()->RemoveAllGestures();
- }
- break;
-
- case TASK_PASSENGER_EXIT_VEHICLE:
- {
- // You must have set your entrance animation before this point!
- Assert( m_nTransitionSequence != -1 );
-
- // Start us playing the correct sequence
- GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
-
- // Overlaying any gestures will mess us up, so don't allow it
- GetOuter()->RemoveAllGestures();
- }
- break;
-
- case TASK_PASSENGER_ATTACH_TO_VEHICLE:
- {
- AttachToVehicle();
- TaskComplete();
- }
- break;
-
- case TASK_PASSENGER_DETACH_FROM_VEHICLE:
- {
- // Place an entity blocker where we're going to go
- if ( ReserveExitPoint() == false )
- {
- OnExitVehicleFailed();
- TaskFail("Failed to find valid exit point\n");
- return;
- }
-
- // Physically detach from the vehicle
- DetachFromVehicle();
-
- // Mark that we're now disembarking
- SetPassengerState( PASSENGER_STATE_EXITING );
-
- TaskComplete();
- }
- break;
-
- case TASK_PASSENGER_SET_IDEAL_ENTRY_YAW:
- {
- // Get the ideal facing to enter the vehicle
- QAngle vecEntryAngles;
- GetEntryPoint( m_nTransitionSequence, NULL, &vecEntryAngles );
- GetOuter()->GetMotor()->SetIdealYaw( vecEntryAngles.y );
-
- TaskComplete();
- return;
- }
- break;
-
- default:
- BaseClass::StartTask( pTask );
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Handle task running
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::RunTask( const Task_t *pTask )
-{
- switch ( pTask->iTask )
- {
- case TASK_PASSENGER_ENTER_VEHICLE:
- {
- // Correct for angular/spatial deviation
- Assert( GetSequence() == m_nTransitionSequence );
- if ( GetSequence() != m_nTransitionSequence )
- {
- Warning("Corrected entrance animation on vehicle enter!\n");
- GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
- GetOuter()->GetNavigator()->ClearGoal();
- SetTransitionSequence( m_nTransitionSequence );
- }
-
- bool corrected = DoTransitionMovement();
-
- // We must be done with the animation and in the correct position
- if ( corrected == false )
- {
- FinishEnterVehicle();
- TaskComplete();
- }
- }
- break;
-
- case TASK_PASSENGER_EXIT_VEHICLE:
- {
- // Correct for angular/spatial deviation
- Assert( GetSequence() == m_nTransitionSequence );
- if ( GetSequence() != m_nTransitionSequence )
- {
- Warning("Corrected exit animation on vehicle exit!\n");
- GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
- GetOuter()->GetNavigator()->ClearGoal();
- SetTransitionSequence( m_nTransitionSequence );
- }
-
- // Correct for angular/spatial deviation
- bool corrected = DoTransitionMovement();
-
- // We must be done with the animation and in the correct position
- if ( corrected == false )
- {
- FinishExitVehicle();
- TaskComplete();
- }
- }
- break;
-
- default:
- BaseClass::RunTask( pTask );
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Find the blend amounts for position and angles, given a point in
-// time within a sequence
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::GetSequenceBlendAmount( float flCycle, float *posBlend, float *angBlend )
-{
- // Find positional blend, if requested
- if ( posBlend != NULL )
- {
- float flFrac = RemapValClamped( flCycle, m_flOriginStartFrame, m_flOriginEndFrame, 0.0f, 1.0f );
- (*posBlend) = SimpleSpline( flFrac );
- }
-
- // Find angular blend, if requested
- if ( angBlend != NULL )
- {
- float flFrac = RemapValClamped( flCycle, m_flAnglesStartFrame, m_flAnglesEndFrame, 0.0f, 1.0f );
- (*angBlend) = SimpleSpline( flFrac );
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the target destination for the entry animation
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::GetEntryTarget( Vector *vecOrigin, QAngle *vecAngles )
-{
- // Get the ultimate position we'll end up at
- m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPositionLocal( GetOuter(), vecOrigin, vecAngles );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the ideal position to be in to end up at the target at the
-// end of the animation.
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::GetTransitionAnimationIdeal( float flCycle, const Vector &vecTargetPos, const QAngle &vecTargetAngles, Vector *idealOrigin, QAngle *idealAngles )
-{
- // Get the position in time working backwards from our goal
- Vector vecDeltaPos;
- QAngle vecDeltaAngles;
- GetOuter()->GetSequenceMovement( GetSequence(), 1.0f, flCycle, vecDeltaPos, vecDeltaAngles );
-
- // Rotate the delta by our local angles
- Vector vecPreDelta = vecDeltaPos;
- VectorRotate( vecPreDelta, vecTargetAngles, vecDeltaPos );
-
- // Ideal origin
- if ( idealOrigin != NULL )
- {
- *idealOrigin = ( vecTargetPos + vecDeltaPos );
- }
-
- // Ideal angles
- if ( idealAngles != NULL )
- {
- (*idealAngles).x = anglemod( vecTargetAngles.x + vecDeltaAngles.x );
- (*idealAngles).y = anglemod( vecTargetAngles.y + vecDeltaAngles.y );
- (*idealAngles).z = anglemod( vecTargetAngles.z + vecDeltaAngles.z );
- }
-}
-
-//-----------------------------------------------------------------------------
-// FIXME: This is basically a complete duplication of GetIntervalMovement
-// which doesn't remove the x and z components of the angles. This
-// should be consolidated to not replicate so much code! -- jdw
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::LocalIntervalMovement( float flInterval, bool &bMoveSeqFinished, Vector &newPosition, QAngle &newAngles )
-{
- CStudioHdr *pstudiohdr = GetOuter()->GetModelPtr();
- if ( pstudiohdr == NULL )
- return false;
-
- // Get our next cycle point
- float flNextCycle = GetNextCycleForInterval( GetSequence(), flInterval );
-
- // Fix-up loops
- if ( ( GetOuter()->SequenceLoops() == false ) && flNextCycle > 1.0f )
- {
- flInterval = GetOuter()->GetCycle() / ( GetOuter()->GetSequenceCycleRate( GetSequence() ) * GetOuter()->GetPlaybackRate() );
- flNextCycle = 1.0f;
- bMoveSeqFinished = true;
- }
- else
- {
- bMoveSeqFinished = false;
- }
-
- Vector deltaPos;
- QAngle deltaAngles;
-
- // Find the delta position and delta angles for this sequence
- if ( Studio_SeqMovement( pstudiohdr, GetOuter()->GetSequence(), GetOuter()->GetCycle(), flNextCycle, GetOuter()->GetPoseParameterArray(), deltaPos, deltaAngles ))
- {
- Vector vecPreDelta = deltaPos;
- VectorRotate( vecPreDelta, GetOuter()->GetLocalAngles(), deltaPos );
-
- newPosition = GetLocalOrigin() + deltaPos;
- newAngles = GetLocalAngles() + deltaAngles;
-
- return true;
- }
- else
- {
- newPosition = GetLocalOrigin();
- newAngles = GetLocalAngles();
-
- return false;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the next cycle point in a sequence for a given interval
-//-----------------------------------------------------------------------------
-float CAI_PassengerBehavior::GetNextCycleForInterval( int nSequence, float flInterval )
-{
- return GetOuter()->GetCycle() + flInterval * GetOuter()->GetSequenceCycleRate( GetSequence() ) * GetOuter()->GetPlaybackRate();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw debug information for the transitional movement
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::DrawDebugTransitionInfo( const Vector &vecIdealPos, const QAngle &vecIdealAngles, const Vector &vecAnimPos, const QAngle &vecAnimAngles )
-{
- // Debug info
- if ( GetPassengerState() == PASSENGER_STATE_ENTERING )
- {
- // Green - Ideal location
- Vector foo;
- m_hVehicle->EntityToWorldSpace( vecIdealPos, &foo );
- NDebugOverlay::Cross3D( foo, 2, 0, 255, 0, true, 0.1f );
- NDebugOverlay::Axis( foo, vecIdealAngles, 8, true, 0.1f );
-
- // Blue - Actual location
- m_hVehicle->EntityToWorldSpace( vecAnimPos, &foo );
- NDebugOverlay::Cross3D( foo, 2, 0, 0, 255, true, 0.1f );
- NDebugOverlay::Axis( foo, vecAnimAngles, 8, true, 0.1f );
- }
- else
- {
- // Green - Ideal location
- NDebugOverlay::Cross3D( vecIdealPos, 4, 0, 255, 0, true, 0.1f );
- NDebugOverlay::Axis( vecIdealPos, vecIdealAngles, 8, true, 0.1f );
-
- // Blue - Actual location
- NDebugOverlay::Cross3D( vecAnimPos, 2, 0, 0, 255, true, 0.1f );
- NDebugOverlay::Axis( vecAnimPos, vecAnimAngles, 8, true, 0.1f );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Local movement to enter or exit the vehicle
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::DoTransitionMovement( void )
-{
- // Get our animation's extrapolated end position
- Vector vecAnimPos;
- QAngle vecAnimAngles;
- float flInterval = GetOuter()->GetAnimTimeInterval();
- bool bSequenceFinished;
-
- // Get the position we're moving to for this frame with our animation's motion
- if ( LocalIntervalMovement( flInterval, bSequenceFinished, vecAnimPos, vecAnimAngles ) )
- {
- // Get the position we'd ideally be in
- Vector vecIdealPos;
- QAngle vecIdealAngles;
- float flNextCycle = GetNextCycleForInterval( GetOuter()->GetSequence(), flInterval );
- flNextCycle = clamp( flNextCycle, 0.0f, 1.0f );
- GetTransitionAnimationIdeal( flNextCycle, m_vecTargetPosition, m_vecTargetAngles, &vecIdealPos, &vecIdealAngles );
-
- // Get the amount of error to blend out
- float flPosBlend = 1.0f;
- float flAngBlend = 1.0f;
- GetSequenceBlendAmount( flNextCycle, &flPosBlend, &flAngBlend );
-
- // Find the error between our position and our ideal
- Vector vecDelta = ( vecIdealPos - vecAnimPos ) * flPosBlend;
-
- QAngle vecDeltaAngles;
- vecDeltaAngles.x = AngleDiff( vecIdealAngles.x, vecAnimAngles.x ) * flAngBlend;
- vecDeltaAngles.y = AngleDiff( vecIdealAngles.y, vecAnimAngles.y ) * flAngBlend;
- vecDeltaAngles.z = AngleDiff( vecIdealAngles.z, vecAnimAngles.z ) * flAngBlend;
-
- // Factor in the error
- GetOuter()->SetLocalOrigin( vecAnimPos + vecDelta );
- GetOuter()->SetLocalAngles( vecAnimAngles + vecDeltaAngles );
-
- // Draw our debug information
- if ( passenger_debug_transition.GetBool() )
- {
- DrawDebugTransitionInfo( vecIdealPos, vecIdealAngles, vecAnimPos, vecAnimAngles );
- }
-
- // We're done moving
- if ( bSequenceFinished )
- return false;
-
- // We're still correcting out the error
- return true;
- }
-
- // There was no movement in the animation
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Translate normal schedules into vehicle schedules
-//-----------------------------------------------------------------------------
-int CAI_PassengerBehavior::TranslateSchedule( int scheduleType )
-{
- if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
- {
- // Always be seated when riding in the car!
- if ( scheduleType == SCHED_IDLE_STAND )
- return SCHED_PASSENGER_IDLE;
- }
-
- return BaseClass::TranslateSchedule( scheduleType );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the velocity of the vehicle with respect to its orientation
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::GetLocalVehicleVelocity( Vector *pOut )
-{
- Vector velocity;
- m_hVehicle->GetVelocity( &velocity, NULL );
- m_hVehicle->WorldToEntitySpace( m_hVehicle->GetAbsOrigin() + velocity, pOut );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Gather conditions we can comment on or react to while riding in the vehicle
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::GatherVehicleStateConditions( void )
-{
- // Must have a vehicle to bother with this
- if ( m_hVehicle == NULL )
- return;
-
- // Clear out transient conditions
- ClearCondition( COND_PASSENGER_HARD_IMPACT );
- ClearCondition( COND_PASSENGER_ERRATIC_DRIVING );
- ClearCondition( COND_PASSENGER_JOSTLE_SMALL );
- ClearCondition( COND_PASSENGER_VEHICLE_STARTED );
- ClearCondition( COND_PASSENGER_VEHICLE_STOPPED );
- ClearCondition( COND_PASSENGER_PLAYER_ENTERED_VEHICLE );
- ClearCondition( COND_PASSENGER_PLAYER_EXITED_VEHICLE );
-
- CBasePlayer *pPlayer = AI_GetSinglePlayer();
- if ( pPlayer )
- {
- if ( pPlayer->IsInAVehicle() && pPlayer->GetVehicle() == m_hVehicle->GetServerVehicle() )
- {
- if ( m_vehicleState.m_bPlayerInVehicle == false )
- {
- SetCondition( COND_PASSENGER_PLAYER_ENTERED_VEHICLE );
- m_vehicleState.m_bPlayerInVehicle = true;
- }
- }
- else
- {
- if ( m_vehicleState.m_bPlayerInVehicle )
- {
- SetCondition( COND_PASSENGER_PLAYER_EXITED_VEHICLE );
- m_vehicleState.m_bPlayerInVehicle = false;
- }
- }
- }
-
- // Get the vehicle's boost state
- if ( m_hVehicle->m_nBoostTimeLeft < 100.0f )
- {
- if ( m_vehicleState.m_bWasBoosting == false )
- {
- m_vehicleState.m_bWasBoosting = true;
- }
- }
- else
- {
- m_vehicleState.m_bWasBoosting = false;
- }
-
- // Detect being overturned
- if ( m_hVehicle->IsOverturned() )
- {
- SetCondition( COND_PASSENGER_OVERTURNED );
-
- if ( m_vehicleState.m_bWasOverturned == false )
- {
- m_vehicleState.m_bWasOverturned = true;
- }
- }
- else
- {
- ClearCondition( COND_PASSENGER_OVERTURNED );
- m_vehicleState.m_bWasOverturned = false;
- }
-
- // Get our local velocity
- Vector localVelocity;
- GetLocalVehicleVelocity( &localVelocity );
-
- Vector deltaVelocity = ( localVelocity - m_vehicleState.m_vecLastLocalVelocity );
-
- // Detect a sudden stop!
- if ( deltaVelocity.y < passenger_impact_response_threshold.GetFloat() )
- {
- SetCondition( COND_PASSENGER_HARD_IMPACT );
- }
- else if ( fabs( deltaVelocity.x ) > 200.0f || fabs( deltaVelocity.z ) > 75.0f )
- {
- // The X axis represents lateral movement and the Z axis represents vertical movement{
- SetCondition( COND_PASSENGER_ERRATIC_DRIVING );
- }
- else if ( fabs( deltaVelocity.x ) > 50.0f || fabs( deltaVelocity.z ) > 25.0f )
- {
- // Lightly jostled
- SetCondition( COND_PASSENGER_JOSTLE_SMALL );
- }
-
- // Get our speed
- float flSpeedSqr = localVelocity.LengthSqr();
-
- // See if we've crossed over the threshold between movement to stillness
- if ( m_vehicleState.m_flLastSpeedSqr > STOPPED_VELOCITY_THRESHOLD_SQR && flSpeedSqr < STOPPED_VELOCITY_THRESHOLD_SQR )
- {
- SetCondition( COND_PASSENGER_VEHICLE_STOPPED );
- }
- else if ( m_vehicleState.m_flLastSpeedSqr < STARTED_VELOCITY_THRESHOLD_SQR && flSpeedSqr > STARTED_VELOCITY_THRESHOLD_SQR )
- {
- // See if we've crossed over the threshold between stillness to movement
- SetCondition( COND_PASSENGER_VEHICLE_STARTED );
- }
-
- // Save this as our last speed
- m_vehicleState.m_flLastSpeedSqr = flSpeedSqr;
-
- // Find our delta velocity from the last frame
- m_vehicleState.m_vecDeltaVelocity = ( localVelocity - m_vehicleState.m_vecLastLocalVelocity );
- m_vehicleState.m_vecLastLocalVelocity = localVelocity;
-
- // Get our angular velocity
- Vector vecVelocity;
- AngularImpulse angVelocty;
- m_hVehicle->GetVelocity( &vecVelocity, &angVelocty );
- QAngle angVel( angVelocty.x, angVelocty.y, angVelocty.z );
-
- // Blend this into the old values
- m_vehicleState.m_vecLastAngles = ( m_vehicleState.m_vecLastAngles * 0.2f ) + ( angVel * 0.8f );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Do some pre-schedule clean-up
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::PrescheduleThink( void )
-{
- BaseClass::PrescheduleThink();
-
- // If we're outside the vehicle, we need to turn this behavior off immediately
- if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE && HasCondition( COND_PASSENGER_CANCEL_ENTER ) )
- {
- // Clear out our passenger intent
- m_PassengerIntent = PASSENGER_INTENT_NONE;
- ClearCondition( COND_PASSENGER_CANCEL_ENTER );
-
- // Stop pathfinding
- GetOuter()->GetNavigator()->ClearGoal();
-
- // We're outside and have no intent to enter, so we're done
- Disable();
-
- // This must be stomped to cause our behavior to relinquish control
- GetOuter()->ClearSchedule("Passenger enter canceled");
- }
-
-#ifdef DEBUG
- if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
- {
- Vector vecSeatOrigin;
- QAngle vecSeatAngles;
- if ( m_hVehicle && m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPositionLocal( GetOuter(), &vecSeatOrigin, &vecSeatAngles ) )
- {
- if ( ( GetLocalOrigin() - vecSeatOrigin ).LengthSqr() > Square( 0.1f ) )
- {
- Warning( "Passenger has strayed from seat position!\n" );
- // GetOuter()->SetLocalOrigin( vecSeatOrigin );
- // GetOuter()->SetLocalAngles( vecSeatAngles );
- }
- }
- else
- {
- Warning( "Passenger is in vehicle without a valid seat position! -- EJECTED\n" );
- GetOuter()->SetParent( NULL );
- Disable();
-
- return;
- }
- }
-#endif // DEBUG
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Gather conditions for our use in making decisions
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::GatherConditions( void )
-{
- if ( IsEnabled() == false )
- return BaseClass::GatherConditions();
-
- // Sense the state of the car
- GatherVehicleStateConditions();
-
- BaseClass::GatherConditions();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
-{
- if ( m_hVehicle == NULL )
- return;
-
- // Mark whether we're overturned or not
- bool bOverturned = m_hVehicle->IsOverturned();
- criteriaSet.AppendCriteria( "vehicle_overturned", bOverturned ? "1" : "0" );
-
- // Denote whether we're in the vehicle or not
- bool bInsideVehicle = ( GetPassengerState() == PASSENGER_STATE_INSIDE );
- criteriaSet.AppendCriteria( "vehicle_inside", bInsideVehicle ? "1" : "0" );
-
- // Note what angle we're at (extreme or normal)
- Vector vecUp( 0.0f, 0.0f, 1.0f );
- Vector vecVehicleUp;
- m_hVehicle->GetVectors( NULL, NULL, &vecVehicleUp );
-
- float flVehicleUp = DotProduct( vecVehicleUp, vecUp );
- criteriaSet.AppendCriteria( "vehicle_tilt", UTIL_VarArgs( "%.2f", flVehicleUp ) );
-
- // Set the vehicle's speed (necessary for certain types of movement judgments)
- float flVehicleSpeed = sqrt( m_vehicleState.m_flLastSpeedSqr );
- criteriaSet.AppendCriteria( "vehicle_speed", UTIL_VarArgs( "%f", flVehicleSpeed ) );
-
- // Whether or not the passenger is currently able to enter the vehicle (only accounts for locking really)
- bool bCanExitVehicle = ( m_hVehicle->NPC_CanExitVehicle( GetOuter(), true ) );
- criteriaSet.AppendCriteria( "vehicle_can_exit", bCanExitVehicle ? "1" : "0" );
-
- // Whether or not the passenger is currently able to exit the vehicle (only accounts for locking really)
- bool bCanEnterVehicle = ( m_hVehicle->NPC_CanEnterVehicle( GetOuter(), true ) );
- criteriaSet.AppendCriteria( "vehicle_can_enter", bCanEnterVehicle ? "1" : "0" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Cache off our frame numbers from the sequence keyvalue blocks
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::CacheBlendTargets( void )
-{
- // Get the keyvalues for this sequence
- KeyValues *seqValues = GetOuter()->GetSequenceKeyValues( m_nTransitionSequence );
- if ( seqValues == NULL )
- {
- Assert( 0 );
- return;
- }
-
- // Get the entry/exit subkeys
- KeyValues *blendValues = seqValues->FindKey( "entryexit_blend" );
- if ( blendValues == NULL )
- {
- Assert( 0 );
- return;
- }
-
- // Find our frame range on this sequence
- int nMaxFrames = Studio_MaxFrame( GetOuter()->GetModelPtr(), m_nTransitionSequence, GetOuter()->GetPoseParameterArray() );
-
- // Find a key by this name
- KeyValues *subKeys = blendValues->FindKey( ORIGIN_KEYNAME );
- if ( subKeys )
- {
- // Retrieve our frame numbers
- m_flOriginStartFrame = subKeys->GetFloat( "startframe", 0.0f );
- m_flOriginEndFrame = subKeys->GetFloat( "endframe", nMaxFrames );
-
- // Convert to normalized values
- m_flOriginStartFrame = RemapValClamped( m_flOriginStartFrame, 0, nMaxFrames, 0.0f, 1.0f );
- m_flOriginEndFrame = RemapValClamped( m_flOriginEndFrame, 0, nMaxFrames, 0.0f, 1.0f );
- }
-
- // Find a key by this name
- subKeys = blendValues->FindKey( ANGLES_KEYNAME );
- if ( subKeys )
- {
- // Retrieve our frame numbers
- m_flAnglesStartFrame = subKeys->GetFloat( "startframe", 0.0f );
- m_flAnglesEndFrame = subKeys->GetFloat( "endframe", nMaxFrames );
-
- // Convert to normalized values
- m_flAnglesStartFrame = RemapValClamped( m_flAnglesStartFrame, 0, nMaxFrames, 0.0f, 1.0f );
- m_flAnglesEndFrame = RemapValClamped( m_flAnglesEndFrame, 0, nMaxFrames, 0.0f, 1.0f );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::SetTransitionSequence( int nSequence )
-{
- // We need to use the ACT_SCRIPT_CUSTOM_MOVE scenario for this type of custom anim
- m_nTransitionSequence = nSequence;
- GetOuter()->m_iszSceneCustomMoveSeq = AllocPooledString( GetOuter()->GetSequenceName( m_nTransitionSequence ) );
-
- // Cache off our blending information at this point
- CacheBlendTargets();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::SpeakIfAllowed( AIConcept_t concept, const char *modifiers /*= NULL*/, bool bRespondingToPlayer /*= false*/, char *pszOutResponseChosen /*= NULL*/, size_t bufsize /*= 0*/ )
-{
- // FIXME: Store this cast off?
- CAI_PlayerAlly *pAlly = dynamic_cast<CAI_PlayerAlly *>(GetOuter());
- if ( pAlly != NULL )
- return pAlly->SpeakIfAllowed( concept, modifiers, bRespondingToPlayer, pszOutResponseChosen, bufsize );
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Forces us to begin a dynamic scripted scene
-// Input : *lpszInteractionName - Name of the sequence we'll play
-// *pOther -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::ForceVehicleInteraction( const char *lpszInteractionName, CBaseCombatCharacter *pOther )
-{
- // Don't do this unless we're sitting in the cabin of the vehicle!
- if ( GetPassengerState() != PASSENGER_STATE_INSIDE )
- return false;
-
- // Set a sequence and fire it off!
- GetOuter()->m_iszSceneCustomMoveSeq = AllocPooledString( lpszInteractionName );
- GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
-
- // Slam our schedule (very unsafe!)
- GetOuter()->SetSchedule( SCHED_PASSENGER_PLAY_SCRIPTED_ANIM );
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Fix up teleport event when in the vehicle
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
-{
- //First, safely remove me from the vehicle
- if ( GetPassengerState() != PASSENGER_STATE_OUTSIDE )
- {
- // Detach from the vehicle
- DetachFromVehicle();
- FinishExitVehicle();
-
- // Turn the behavior off
- GetOuter()->ClearSchedule( "ai_behavior_passenger: teleport while in vehicle" );
- Disable();
- }
-
- //Then allow the teleportation
- BaseClass::Teleport( newPosition, newAngles, newVelocity );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: We override this function because it can completely wreak havoc if
-// we're in the middle of a transition
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::ClearSchedule( const char *szReason )
-{
- // Cannot do this while we're transitioning, but it's also a bug because the code that called it was probably relying on this to work!
- if ( GetPassengerState() == PASSENGER_STATE_ENTERING || GetPassengerState() == PASSENGER_STATE_EXITING )
- {
- Warning("ClearSchedule rejected due to transitioning passenger: %s\n", szReason );
- return;
- }
-
- // TODO: Even this will probably need more crafting depending on what we're doing in the vehicle
- // Otherwise allow it
- GetOuter()->ClearSchedule( szReason );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Dictate the terms for being interrupted by scripted schedules or scenes
-//-----------------------------------------------------------------------------
-bool CAI_PassengerBehavior::IsInterruptable( void )
-{
- // NOTE: We should never be interrupted this way when in a car. This would effectively makes us go comatose if we
- // start a FACETO, MOVETO, or SEQUENCE command from a VCD.
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAI_PassengerBehavior::CancelEnterVehicle( void )
-{
- // Stop!
- if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE )
- {
- SetCondition( COND_PASSENGER_CANCEL_ENTER );
- }
-}
-
-// ----------------------------------------------
-// Custom AI declarations
-// ----------------------------------------------
-
-AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_PassengerBehavior )
-{
- DECLARE_ACTIVITY( ACT_PASSENGER_IDLE )
- DECLARE_ACTIVITY( ACT_PASSENGER_RANGE_ATTACK1 )
-
- DECLARE_CONDITION( COND_PASSENGER_HARD_IMPACT )
- DECLARE_CONDITION( COND_PASSENGER_ENTERING )
- DECLARE_CONDITION( COND_PASSENGER_EXITING )
- DECLARE_CONDITION( COND_PASSENGER_VEHICLE_STARTED )
- DECLARE_CONDITION( COND_PASSENGER_VEHICLE_STOPPED )
- DECLARE_CONDITION( COND_PASSENGER_OVERTURNED )
- DECLARE_CONDITION( COND_PASSENGER_CANCEL_ENTER )
- DECLARE_CONDITION( COND_PASSENGER_ERRATIC_DRIVING )
- DECLARE_CONDITION( COND_PASSENGER_PLAYER_ENTERED_VEHICLE )
- DECLARE_CONDITION( COND_PASSENGER_PLAYER_EXITED_VEHICLE )
- DECLARE_CONDITION( COND_PASSENGER_JOSTLE_SMALL )
-
- DECLARE_TASK( TASK_PASSENGER_ENTER_VEHICLE )
- DECLARE_TASK( TASK_PASSENGER_EXIT_VEHICLE )
- DECLARE_TASK( TASK_PASSENGER_ATTACH_TO_VEHICLE )
- DECLARE_TASK( TASK_PASSENGER_DETACH_FROM_VEHICLE )
- DECLARE_TASK( TASK_PASSENGER_SET_IDEAL_ENTRY_YAW )
-
- // FIXME: Move to companion
- DEFINE_SCHEDULE
- (
- SCHED_PASSENGER_ENTER_VEHICLE,
-
- " Tasks"
- " TASK_PASSENGER_SET_IDEAL_ENTRY_YAW 0"
- " TASK_FACE_IDEAL 0"
- " TASK_PASSENGER_ATTACH_TO_VEHICLE 0"
- " TASK_PASSENGER_ENTER_VEHICLE 0"
- ""
- " Interrupts"
- " COND_NO_CUSTOM_INTERRUPTS"
- )
-
- DEFINE_SCHEDULE
- (
- SCHED_PASSENGER_EXIT_VEHICLE,
-
- " Tasks"
- " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_PASSENGER_IDLE"
- " TASK_PASSENGER_DETACH_FROM_VEHICLE 0"
- " TASK_WAIT 0.1" // We must wait one tick for us to start being updated
- " TASK_PASSENGER_EXIT_VEHICLE 0"
- ""
- " Interrupts"
- " COND_NO_CUSTOM_INTERRUPTS"
- " COND_TASK_FAILED"
- )
-
- DEFINE_SCHEDULE
- (
- SCHED_PASSENGER_IDLE,
-
- " Tasks"
- " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
- " TASK_WAIT 2"
- ""
- " Interrupts"
- " COND_PROVOKED"
- " COND_NEW_ENEMY"
- " COND_CAN_RANGE_ATTACK1"
- " COND_CAN_MELEE_ATTACK1"
- " COND_PASSENGER_EXITING"
- " COND_HEAR_DANGER"
- )
-
- DEFINE_SCHEDULE
- (
- SCHED_PASSENGER_PLAY_SCRIPTED_ANIM,
-
- " Tasks"
- " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SCRIPT_CUSTOM_MOVE"
- ""
- " Interrupts"
- " COND_PASSENGER_HARD_IMPACT"
- )
-
- AI_END_CUSTOM_SCHEDULE_PROVIDER()
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Behavior for NPCs riding in cars (with boys)
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "ai_playerally.h"
+#include "ai_motor.h"
+#include "bone_setup.h"
+#include "vehicle_base.h"
+#include "entityblocker.h"
+#include "ai_behavior_passenger.h"
+#include "ai_pathfinder.h"
+#include "ai_network.h"
+#include "ai_node.h"
+#include "ai_moveprobe.h"
+#include "env_debughistory.h"
+
+// Custom activities
+int ACT_PASSENGER_IDLE;
+int ACT_PASSENGER_RANGE_ATTACK1;
+
+ConVar passenger_debug_transition( "passenger_debug_transition", "0" );
+ConVar passenger_impact_response_threshold( "passenger_impact_response_threshold", "-350.0" );
+
+#define ORIGIN_KEYNAME "origin"
+#define ANGLES_KEYNAME "angles"
+
+BEGIN_DATADESC( CAI_PassengerBehavior )
+
+ DEFINE_EMBEDDED( m_vehicleState ),
+ DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_PassengerIntent, FIELD_INTEGER ),
+ DEFINE_FIELD( m_PassengerState, FIELD_INTEGER ),
+ DEFINE_FIELD( m_hVehicle, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hBlocker, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_vecTargetPosition, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_vecTargetAngles, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flOriginStartFrame, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flOriginEndFrame, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flAnglesStartFrame, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flAnglesEndFrame, FIELD_FLOAT ),
+ DEFINE_FIELD( m_nTransitionSequence,FIELD_INTEGER ),
+
+END_DATADESC();
+
+BEGIN_SIMPLE_DATADESC( passengerVehicleState_t )
+
+ DEFINE_FIELD( m_bWasBoosting, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bWasOverturned, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_vecLastLocalVelocity, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vecDeltaVelocity, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vecLastAngles, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flNextWarningTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flLastSpeedSqr, FIELD_FLOAT ),
+ DEFINE_FIELD( m_bPlayerInVehicle, FIELD_BOOLEAN ),
+
+END_DATADESC();
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CAI_PassengerBehavior::CAI_PassengerBehavior( void ) :
+m_bEnabled( false ),
+m_hVehicle( NULL ),
+m_PassengerState( PASSENGER_STATE_OUTSIDE ),
+m_PassengerIntent( PASSENGER_INTENT_NONE ),
+m_nTransitionSequence( -1 )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Enables the behavior to run
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::Enable( CPropJeepEpisodic *pVehicle, bool bImmediateEnter /*= false*/ )
+{
+ if ( m_bEnabled && m_hVehicle.Get() )
+ return;
+
+ m_bEnabled = true;
+ m_hVehicle = pVehicle;
+ SetPassengerState( PASSENGER_STATE_OUTSIDE );
+
+ // Init our starting information about the vehicle
+ InitVehicleState();
+}
+
+void CAI_PassengerBehavior::OnRestore()
+{
+ if ( m_bEnabled && !m_hVehicle.Get() )
+ {
+ Disable();
+ }
+
+ BaseClass::OnRestore();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Stops the behavior from being run
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::Disable( void )
+{
+ m_bEnabled = false;
+ m_hVehicle = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Starts the process of entering a vehicle
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::EnterVehicle( void )
+{
+ // If we're already in the vehicle, simply cancel out our intents
+ if ( GetPassengerState() == PASSENGER_STATE_INSIDE || GetPassengerState() == PASSENGER_STATE_ENTERING )
+ {
+ // Clear out an exit
+ if ( m_PassengerIntent == PASSENGER_INTENT_EXIT )
+ {
+ m_PassengerIntent = PASSENGER_INTENT_NONE;
+ ClearCondition( COND_PASSENGER_ENTERING );
+ ClearCondition( COND_PASSENGER_EXITING );
+ }
+
+ return;
+ }
+
+ // Update our internal state
+ m_PassengerIntent = PASSENGER_INTENT_ENTER;
+
+ // Otherwise set this condition and go!
+ SetCondition( COND_PASSENGER_ENTERING );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Starts the process of exiting a vehicle
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::ExitVehicle( void )
+{
+ // Must be in the seat
+ if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE || GetPassengerState() == PASSENGER_STATE_EXITING )
+ {
+ // Clear out an entrance
+ if ( m_PassengerIntent == PASSENGER_INTENT_ENTER )
+ {
+ m_PassengerIntent = PASSENGER_INTENT_NONE;
+ SetCondition( COND_PASSENGER_CANCEL_ENTER );
+ ClearCondition( COND_PASSENGER_ENTERING );
+ ClearCondition( COND_PASSENGER_EXITING );
+ }
+ return;
+ }
+
+ // Update our internal state
+ m_PassengerIntent = PASSENGER_INTENT_EXIT;
+
+ //
+ // Everything below this point will still attempt to exit the vehicle, once able
+ //
+
+ // Must have a valid vehicle
+ if ( m_hVehicle == NULL )
+ return;
+
+ // Cannot exit while we're upside down
+ if ( m_hVehicle->IsOverturned() )
+ return;
+
+ // Interrupt what we're doing
+ SetCondition( COND_PASSENGER_EXITING );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: FIXME - This should move into something a bit more flexible
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::AddPhysicsPush( float force )
+{
+ /*
+ // Kick the vehicle so the player knows we've arrived
+ Vector impulse = m_hVehicle->GetAbsOrigin() - GetOuter()->GetAbsOrigin();
+ VectorNormalize( impulse );
+ impulse.z = -0.75;
+ VectorNormalize( impulse );
+ Vector vecForce = impulse * force;
+
+ m_hVehicle->VPhysicsGetObject()->ApplyForceOffset( vecForce, GetOuter()->GetAbsOrigin() );
+ */
+
+ Vector vecDir;
+
+ IPhysicsObject *pObject = GetOuter()->VPhysicsGetObject();
+ Vector vecVelocity;
+ pObject->GetVelocity( &vecVelocity, NULL );
+ GetOuter()->GetVectors( NULL, NULL, &vecDir );
+ vecDir.Negate();
+
+ Vector vecForce = vecDir * force;
+
+ m_hVehicle->VPhysicsGetObject()->ApplyForceOffset( vecForce, GetOuter()->GetAbsOrigin() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::IsPassengerHostile( void )
+{
+ CBaseEntity *pPlayer = AI_GetSinglePlayer();
+
+ // If the player hates or fears the passenger, they're hostile
+ if ( GetOuter()->IRelationType( pPlayer ) == D_HT || GetOuter()->IRelationType( pPlayer ) == D_FR )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::InitVehicleState( void )
+{
+ // Set the player's state
+ CBasePlayer *pPlayer = AI_GetSinglePlayer();
+ m_vehicleState.m_bPlayerInVehicle = ( pPlayer && pPlayer->IsInAVehicle() && pPlayer->GetServerVehicle() == m_hVehicle->GetServerVehicle() );
+
+ // Update our vehicle state so we don't confuse our previous velocity on the first frame!
+ m_vehicleState.m_bWasBoosting = false;
+ m_vehicleState.m_bWasOverturned = false;
+ m_vehicleState.m_flNextWarningTime = 0.0f;
+ m_vehicleState.m_vecDeltaVelocity = vec3_origin;
+ m_vehicleState.m_flNextWarningTime = gpGlobals->curtime;
+ m_vehicleState.m_vecLastAngles = m_hVehicle->GetAbsAngles();
+
+ Vector localVelocity;
+ GetLocalVehicleVelocity( &m_vehicleState.m_vecLastLocalVelocity );
+
+ m_vehicleState.m_flLastSpeedSqr = localVelocity.LengthSqr();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Puts the NPC in hierarchy with the vehicle and makes them intangible
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::FinishEnterVehicle( void )
+{
+ if ( m_hVehicle == NULL )
+ return;
+
+ // Get the ultimate position we want to be in
+ Vector vecFinalPos;
+ QAngle vecFinalAngles;
+ GetEntryTarget( &vecFinalPos, &vecFinalAngles );
+
+ // Make sure we're exactly where we need to be
+ GetOuter()->SetLocalOrigin( vecFinalPos );
+ GetOuter()->SetLocalAngles( vecFinalAngles );
+ GetOuter()->SetMoveType( MOVETYPE_NONE );
+ GetOuter()->GetMotor()->SetYawLocked( true );
+
+ // We're now riding inside the vehicle
+ SetPassengerState( PASSENGER_STATE_INSIDE );
+
+ // If we've not been told to leave immediately, we're done
+ if ( m_PassengerIntent == PASSENGER_INTENT_ENTER )
+ {
+ m_PassengerIntent = PASSENGER_INTENT_NONE;
+ }
+
+ // Tell the vehicle we've succeeded
+ m_hVehicle->NPC_FinishedEnterVehicle( GetOuter(), (IsPassengerHostile()==false) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes the NPC from the car
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::FinishExitVehicle( void )
+{
+ if ( m_hVehicle == NULL )
+ return;
+
+ // Destroy the blocker
+ if ( m_hBlocker != NULL )
+ {
+ UTIL_Remove( m_hBlocker );
+ m_hBlocker = NULL;
+ }
+
+ // To do this, we need to be very sure we're in a good spot
+ GetOuter()->SetCondition( COND_PROVOKED );
+ GetOuter()->SetMoveType( MOVETYPE_STEP );
+ GetOuter()->RemoveFlag( FL_FLY );
+ GetOuter()->GetMotor()->SetYawLocked( false );
+
+ // Re-enable the physical collisions for this NPC
+ IPhysicsObject *pPhysObj = GetOuter()->VPhysicsGetObject();
+ if ( pPhysObj != NULL )
+ {
+ pPhysObj->EnableCollisions( true );
+ }
+
+ m_hVehicle->NPC_RemovePassenger( GetOuter() );
+ m_hVehicle->NPC_FinishedExitVehicle( GetOuter(), (IsPassengerHostile()==false) );
+
+ SetPassengerState( PASSENGER_STATE_OUTSIDE );
+
+ // Stop our custom move sequence
+ GetOuter()->m_iszSceneCustomMoveSeq = NULL_STRING;
+
+ // If we've not been told to enter immediately, we're done
+ if ( m_PassengerIntent == PASSENGER_INTENT_EXIT )
+ {
+ m_PassengerIntent = PASSENGER_INTENT_NONE;
+ Disable();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Build our custom interrupt cases for the behavior
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::BuildScheduleTestBits( void )
+{
+ // Always interrupt when we need to get in or out
+ if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE || GetPassengerState() == PASSENGER_STATE_INSIDE )
+ {
+ GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_ENTERING ) );
+ GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_EXITING ) );
+ GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_CANCEL_ENTER ) );
+ }
+
+ BaseClass::BuildScheduleTestBits();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dictates whether or not the behavior is active and working
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::CanSelectSchedule( void )
+{
+ return m_bEnabled;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::CanExitVehicle( void )
+{
+ // Vehicle must not be overturned
+ if ( m_hVehicle->IsOverturned() )
+ return false;
+
+ // Vehicle must be at rest
+ Vector vecVelocity;
+ m_hVehicle->GetVelocity( &vecVelocity, NULL );
+ if ( vecVelocity.LengthSqr() > Square( 8.0f ) )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handles passengers deciding to enter or exit the vehicle
+// Output : int
+//-----------------------------------------------------------------------------
+int CAI_PassengerBehavior::SelectTransitionSchedule( void )
+{
+ // Handle our various states
+ if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
+ {
+ // Exiting schedule
+ if ( HasCondition( COND_PASSENGER_EXITING ) || m_PassengerIntent == PASSENGER_INTENT_EXIT )
+ {
+ if ( CanExitVehicle( ) )
+ {
+ ClearCondition( COND_PASSENGER_EXITING );
+ return SCHED_PASSENGER_EXIT_VEHICLE;
+ }
+ }
+ }
+ else if ( GetPassengerState() == PASSENGER_STATE_ENTERING || GetPassengerState() == PASSENGER_STATE_EXITING )
+ {
+ // The following code attempts to fix up a passenger being interrupted mid-transition
+ Warning( "SelectSchedule() called on transitioning passenger!\n" );
+ ADD_DEBUG_HISTORY( HISTORY_AI_DECISIONS, UTIL_VarArgs( "%s(%d): SelectSchedule() called on transitioning passenger!\n", GetOuter()->GetDebugName(), GetOuter()->entindex() ) );
+ Assert( 0 );
+
+ // Correct this issue immediately
+ if ( GetPassengerState() == PASSENGER_STATE_EXITING )
+ {
+ // Force them out of the vehicle to where they want to be
+ // The teleport function is overridden for passengers, meaning that they will clean up properly when called to do so
+ GetOuter()->Teleport( &m_vecTargetPosition, &m_vecTargetAngles, NULL );
+ }
+ else if ( GetPassengerState() == PASSENGER_STATE_ENTERING )
+ {
+ // Force them into the proper position
+ GetOuter()->SetLocalOrigin( m_vecTargetPosition );
+ GetOuter()->SetLocalAngles( m_vecTargetAngles );
+ FinishEnterVehicle();
+ }
+
+ // Stop playing our animation
+ SetActivity( ACT_RESET );
+ }
+
+ return SCHED_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Overrides the schedule selection
+// Output : int - Schedule to play
+//-----------------------------------------------------------------------------
+int CAI_PassengerBehavior::SelectSchedule( void )
+{
+ // Protect from this rare occurrence happening
+ if ( m_hVehicle == NULL )
+ {
+ Assert( m_hVehicle != NULL );
+ Warning( "Entity %s running passenger behavior without a valid vehicle!\n", GetName() );
+
+ Disable();
+ return BaseClass::SelectSchedule();
+ }
+
+ // See if we're transitioning in / out of the vehicle
+ int nSchedule = SelectTransitionSchedule();
+ if ( nSchedule != SCHED_NONE )
+ return nSchedule;
+
+ return SCHED_PASSENGER_IDLE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CAI_PassengerBehavior::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
+{
+ switch( failedTask )
+ {
+ // For now, just sit back down
+ case TASK_PASSENGER_DETACH_FROM_VEHICLE:
+ return SCHED_PASSENGER_IDLE;
+ break;
+ }
+
+ return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds a ground position at a given location with some delta up and down to check
+// Input : &in - position to check at
+// delta - amount of distance up and down to check
+// *out - ground position
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::FindGroundAtPosition( const Vector &in, float flUpDelta, float flDownDelta, Vector *out )
+{
+ Vector startPos = in + Vector( 0, 0, flUpDelta ); // Look up by delta
+ Vector endPos = in - Vector( 0, 0, flDownDelta ); // Look down by delta
+ Vector hullMin = GetOuter()->GetHullMins();
+ Vector hullMax = GetOuter()->GetHullMaxs();
+
+ // Ignore ourself and the vehicle we're referencing
+ CTraceFilterVehicleTransition ignoreFilter( m_hVehicle, GetOuter(), COLLISION_GROUP_NONE );
+
+ trace_t tr;
+ UTIL_TraceHull( startPos, endPos, hullMin, hullMax, MASK_NPCSOLID, &ignoreFilter, &tr );
+
+ // Must not have ended up in solid space
+ if ( tr.allsolid )
+ {
+ // Debug
+ if ( passenger_debug_transition.GetBool() )
+ {
+ NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 255, 255, 0, 255, 1.0f );
+ }
+
+ return false;
+ }
+
+ // Must have ended up with feet on the ground
+ if ( tr.DidHitWorld() || ( tr.m_pEnt && tr.m_pEnt->IsStandable() ) )
+ {
+ // Debug
+ if ( passenger_debug_transition.GetBool() )
+ {
+ NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 0, 255, 0, 255, 1.0f );
+ }
+
+ *out = tr.endpos;
+ return true;
+ }
+
+ // Ended up in the air
+ if ( passenger_debug_transition.GetBool() )
+ {
+ NDebugOverlay::SweptBox( tr.startpos, tr.endpos, hullMin, hullMax, vec3_angle, 255, 0, 0, 255, 1.0f );
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Attempt to verify that a test position is on the node graph
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::PointIsNavigable( const Vector &vecTargetPos )
+{
+ // Attempt to local move between this point and the nearest node, ignoring anything but world architecture
+ AIMoveTrace_t moveTrace;
+ int iNearestNode = GetOuter()->GetPathfinder()->NearestNodeToPoint( vecTargetPos );
+ if ( iNearestNode != NO_NODE )
+ {
+ // Try a movement trace between the test position and the node
+ GetOuter()->GetMoveProbe()->MoveLimit( NAV_GROUND,
+ g_pBigAINet->GetNodePosition(GetOuter()->GetHullType(), iNearestNode ),
+ vecTargetPos,
+ MASK_SOLID_BRUSHONLY,
+ NULL,
+ 0,
+ &moveTrace );
+
+ // See if we got close enough to call it arrived
+ if ( ( moveTrace.vEndPosition - vecTargetPos ).LengthSqr() < Square( GetHullWidth() ) &&
+ GetOuter()->GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, MASK_SOLID_BRUSHONLY ) )
+ {
+ // NDebugOverlay::HorzArrow( vecTargetPos, moveTrace.vEndPosition, 8.0f, 255, 0, 0, 16, true, 4.0f );
+ // NDebugOverlay::HorzArrow( vecTargetPos, g_pBigAINet->GetNodePosition(GetOuter()->GetHullType(), iNearestNode ), 8.0f, 255, 255, 0, 16, true, 4.0f );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the exit point for the passenger (on the ground)
+// Input : &vecOut - position the entity should be at when finished exiting
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::GetExitPoint( int nSequence, Vector *vecExitPoint, QAngle *vecExitAngles )
+{
+ bool bSucceeded = true;
+
+ // Get the delta to the final position as will be dictated by this animation's auto movement
+ Vector vecDeltaPos;
+ QAngle vecDeltaAngles;
+ GetOuter()->GetSequenceMovement( nSequence, 0.0f, 1.0f, vecDeltaPos, vecDeltaAngles );
+
+ // Rotate the delta position by our starting angles
+ Vector vecRotPos = vecDeltaPos;
+ VectorRotate( vecRotPos, GetOuter()->GetAbsAngles(), vecDeltaPos );
+
+ if ( vecExitPoint != NULL )
+ {
+ float flDownDelta = 64.0f;
+ float flUpDelta = 16.0f;
+ Vector vecGroundPos;
+
+ bool bFoundGround = FindGroundAtPosition( GetOuter()->GetAbsOrigin() + vecDeltaPos, flUpDelta, flDownDelta, &vecGroundPos );
+ if ( bFoundGround )
+ {
+ if ( PointIsNavigable( vecGroundPos ) == false )
+ {
+ bSucceeded = false;
+ }
+ }
+ else
+ {
+ bSucceeded = false;
+ }
+
+ *vecExitPoint = vecGroundPos;
+ }
+
+ if ( vecExitAngles != NULL )
+ {
+ QAngle newAngles = GetOuter()->GetAbsAngles() + vecDeltaAngles;
+ newAngles.x = UTIL_AngleMod( newAngles.x );
+ newAngles.y = UTIL_AngleMod( newAngles.y );
+ newAngles.z = UTIL_AngleMod( newAngles.z );
+ *vecExitAngles = newAngles;
+ }
+
+ return bSucceeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reserve our entry point
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::ReserveEntryPoint( VehicleSeatQuery_e eSeatSearchType )
+{
+ // FIXME: Move all this logic into the NPC_EnterVehicle function?
+ // Find any seat to get into
+ int nSeatID = m_hVehicle->GetServerVehicle()->NPC_GetAvailableSeat( GetOuter(), GetRoleName(), eSeatSearchType );
+ if ( nSeatID != VEHICLE_SEAT_INVALID )
+ return m_hVehicle->NPC_AddPassenger( GetOuter(), GetRoleName(), nSeatID );
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Determines whether the NPC can move between a start and end position of a transition
+// Input : &vecStartPos - start position
+// &vecEndPos - end position
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::IsValidTransitionPoint( const Vector &vecStartPos, const Vector &vecEndPos )
+{
+ // Now sweep a hull through space to see if we can validly exit there
+ Vector vecHullMins = GetOuter()->GetHullMins() + Vector( 0, 0, GetOuter()->StepHeight()*2.0f );
+ Vector vecHullMaxs = GetOuter()->GetHullMaxs() - Vector( 0, 0, GetOuter()->StepHeight() );
+
+ trace_t tr;
+ CTraceFilterVehicleTransition skipFilter( GetOuter(), m_hVehicle, COLLISION_GROUP_NONE );
+ UTIL_TraceHull( vecStartPos, vecEndPos, vecHullMins, vecHullMaxs, MASK_NPCSOLID, &skipFilter, &tr );
+
+ // If we're blocked, we can't get out there
+ if ( tr.fraction < 1.0f || tr.allsolid )
+ {
+ if ( passenger_debug_transition.GetBool() )
+ {
+ NDebugOverlay::SweptBox( vecStartPos, vecEndPos, vecHullMins, vecHullMaxs, vec3_angle, 255, 0, 0, 64, 2.0f );
+ }
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the proper sequence to use (weighted by priority or distance from current position)
+// to enter the vehicle.
+// Input : bNearest - Use distance as the criteria for a "best" sequence. Otherwise the order of the
+// seats is their priority.
+// Output : int - sequence index
+//-----------------------------------------------------------------------------
+int CAI_PassengerBehavior::FindEntrySequence( bool bNearest /*= false*/ )
+{
+ // Get a list of all our animations
+ const PassengerSeatAnims_t *pEntryAnims = m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatAnims( GetOuter(), PASSENGER_SEAT_ENTRY );
+ if ( pEntryAnims == NULL )
+ return -1;
+
+ // Get the ultimate position we'll end up at
+ Vector vecStartPos, vecEndPos;
+ if ( m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPosition( GetOuter(), &vecEndPos, NULL ) == false )
+ return -1;
+
+ const CPassengerSeatTransition *pTransition;
+ Vector vecSeatDir;
+ float flNearestDist = FLT_MAX;
+ float flSeatDist;
+ int nNearestSequence = -1;
+ int nSequence;
+
+ // Test each animation (sorted by priority) for the best match
+ for ( int i = 0; i < pEntryAnims->Count(); i++ )
+ {
+ // Find the activity for this animation name
+ pTransition = &pEntryAnims->Element(i);
+ nSequence = GetOuter()->LookupSequence( STRING( pTransition->GetAnimationName() ) );
+ if ( nSequence == -1 )
+ continue;
+
+ // Test this entry for validity
+ if ( GetEntryPoint( nSequence, &vecStartPos ) == false )
+ continue;
+
+ // Check to see if we can use this
+ if ( IsValidTransitionPoint( vecStartPos, vecEndPos ) )
+ {
+ // If we're just looking for the first, we're done
+ if ( bNearest == false )
+ return nSequence;
+
+ // Otherwise distance is the deciding factor
+ vecSeatDir = ( vecStartPos - GetOuter()->GetAbsOrigin() );
+ flSeatDist = VectorNormalize( vecSeatDir );
+
+ // Closer, take it
+ if ( flSeatDist < flNearestDist )
+ {
+ flNearestDist = flSeatDist;
+ nNearestSequence = nSequence;
+ }
+ }
+
+ }
+
+ return nNearestSequence;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+int CAI_PassengerBehavior::FindExitSequence( void )
+{
+ // Get a list of all our animations
+ const PassengerSeatAnims_t *pExitAnims = m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatAnims( GetOuter(), PASSENGER_SEAT_EXIT );
+ if ( pExitAnims == NULL )
+ return -1;
+
+ // Get the ultimate position we'll end up at
+ Vector vecStartPos, vecEndPos;
+ if ( m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPosition( GetOuter(), &vecStartPos, NULL ) == false )
+ return -1;
+
+ // Test each animation (sorted by priority) for the best match
+ for ( int i = 0; i < pExitAnims->Count(); i++ )
+ {
+ // Find the activity for this animation name
+ int nSequence = GetOuter()->LookupSequence( STRING( pExitAnims->Element(i).GetAnimationName() ) );
+ if ( nSequence == -1 )
+ continue;
+
+ // Test this entry for validity
+ if ( GetExitPoint( nSequence, &vecEndPos ) == false )
+ continue;
+
+ // Check to see if we can use this
+ if ( IsValidTransitionPoint( vecStartPos, vecEndPos ) )
+ return nSequence;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reserve our exit point so nothing moves into it while we're moving
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::ReserveExitPoint( void )
+{
+ // Cannot exit while we're upside down
+ // FIXME: This is probably redundant!
+ if ( m_hVehicle->IsOverturned() )
+ return false;
+
+ // Find the exit activity to use
+ int nSequence = FindExitSequence();
+ if ( nSequence == -1 )
+ return false;
+
+ // Get the exit position
+ Vector vecGroundPos;
+ if ( GetExitPoint( nSequence, &vecGroundPos, &m_vecTargetAngles ) == false )
+ return false;
+
+ // We have to do this specially because the activities are not named
+ SetTransitionSequence( nSequence );
+
+ // Reserve this space
+ Vector hullMin = GetOuter()->GetHullMins();
+ Vector hullMax = GetOuter()->GetHullMaxs();
+ m_hBlocker = CEntityBlocker::Create( vecGroundPos, hullMin, hullMax, GetOuter(), true );
+
+ // Save this destination position so we can interpolate towards it
+ m_vecTargetPosition = vecGroundPos;
+
+ // Pitch and roll must be zero when we finish!
+ m_vecTargetAngles.x = m_vecTargetAngles.z = 0.0f;
+
+ if ( passenger_debug_transition.GetBool() )
+ {
+ Vector vecForward;
+ AngleVectors( m_vecTargetAngles, &vecForward, NULL, NULL );
+ Vector vecArrowEnd = m_vecTargetPosition + ( vecForward * 64.0f );
+ NDebugOverlay::HorzArrow( m_vecTargetPosition, vecArrowEnd, 8.0f, 255, 255, 0, 64, true, 4.0f );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the exact point we'd like to start our animation from to enter
+// the vehicle.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::GetEntryPoint( int nSequence, Vector *vecEntryPoint, QAngle *vecEntryAngles )
+{
+ bool bSucceeded = true;
+
+ // Get the delta to the final position as will be dictated by this animation's auto movement
+ Vector vecDeltaPos;
+ QAngle vecDeltaAngles;
+ GetOuter()->GetSequenceMovement( nSequence, 1.0f, 0.0f, vecDeltaPos, vecDeltaAngles );
+
+ // Get the final position we're trying to end up at
+ Vector vecTargetPos;
+ QAngle vecTargetAngles;
+ GetEntryTarget( &vecTargetPos, &vecTargetAngles );
+
+ // Rotate it to match
+ Vector vecPreDelta = vecDeltaPos;
+ VectorRotate( vecPreDelta, vecTargetAngles, vecDeltaPos );
+
+ // Offset this into the proper worldspace position
+ vecTargetPos = vecTargetPos + vecDeltaPos;
+
+ // Output the position, if requested
+ if ( vecEntryPoint != NULL )
+ {
+ m_hVehicle->EntityToWorldSpace( vecTargetPos, vecEntryPoint );
+
+ // Trace down to the ground to see where we'll stand
+ Vector vecGroundPos;
+ if ( FindGroundAtPosition( (*vecEntryPoint), 16.0f, 64.0f, &vecGroundPos ) == false )
+ {
+ // We failed
+ if ( passenger_debug_transition.GetBool() )
+ {
+ NDebugOverlay::SweptBox( (*vecEntryPoint), vecGroundPos, GetOuter()->GetHullMins(), GetOuter()->GetHullMaxs(), vec3_angle, 255, 0, 0, 64, 2.0f );
+ }
+
+ // The floor could not be found
+ bSucceeded = false;
+ }
+
+ // Take this position
+ *vecEntryPoint = vecGroundPos;
+ }
+
+ // Output the angles, if requested
+ if ( vecEntryAngles != NULL )
+ {
+ // Add our delta angles to find what angles to start at
+ *vecEntryAngles = vecTargetAngles;
+ vecEntryAngles->y = UTIL_AngleMod( vecTargetAngles.y + vecDeltaAngles.y );
+
+ //Transform those angles to worldspace
+ matrix3x4_t angToParent, angToWorld;
+ AngleMatrix( (*vecEntryAngles), angToParent );
+ ConcatTransforms( m_hVehicle->EntityToWorldTransform(), angToParent, angToWorld );
+ MatrixAngles( angToWorld, (*vecEntryAngles) );
+ }
+
+ // Debug info
+ if ( passenger_debug_transition.GetBool() && vecEntryPoint && vecEntryAngles )
+ {
+ NDebugOverlay::Axis( *vecEntryPoint, vecTargetAngles, 16, true, 4.0f );
+ NDebugOverlay::Cross3D( *vecEntryPoint, 4, 255, 255, 0, true, 4.0f );
+
+ if ( vecEntryAngles != NULL )
+ {
+ Vector vecForward;
+ AngleVectors( (*vecEntryAngles), &vecForward, NULL, NULL );
+ Vector vecArrowEnd = (*vecEntryPoint ) + ( vecForward * 64.0f );
+ NDebugOverlay::HorzArrow( (*vecEntryPoint), vecArrowEnd, 8.0f, 0, 255, 0, 64, true, 4.0f );
+ }
+ }
+
+ return bSucceeded;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Do the low-level work to detach us from our vehicle
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::DetachFromVehicle( void )
+{
+ // Detach from the parent
+ GetOuter()->SetParent( NULL );
+ GetOuter()->SetMoveType( MOVETYPE_STEP );
+ GetOuter()->AddFlag( FL_FLY );
+ GetOuter()->SetGroundEntity( NULL );
+ GetOuter()->SetCollisionGroup( COLLISION_GROUP_NPC );
+ m_hVehicle->RemovePhysicsChild( GetOuter() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::AttachToVehicle( void )
+{
+ // Parent to the vehicle
+ GetOuter()->ClearForceCrouch();
+ GetOuter()->SetParent( m_hVehicle );
+ GetOuter()->AddFlag( FL_FLY );
+ GetOuter()->SetGroundEntity( NULL );
+ GetOuter()->SetCollisionGroup( COLLISION_GROUP_IN_VEHICLE );
+
+ // Turn off physical interactions while we're in the vehicle
+ IPhysicsObject *pPhysObj = GetOuter()->VPhysicsGetObject();
+ if ( pPhysObj != NULL )
+ {
+ pPhysObj->EnableCollisions( false );
+ }
+
+ // Set our destination target
+ GetEntryTarget( &m_vecTargetPosition, &m_vecTargetAngles );
+
+ // Get physics messages from our attached physics object
+ m_hVehicle->AddPhysicsChild( GetOuter() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle task starting
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::StartTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ case TASK_PASSENGER_ENTER_VEHICLE:
+ {
+ // You must have set your entrance animation before this point!
+ Assert( m_nTransitionSequence != -1 );
+
+ // Start us playing the correct sequence
+ GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
+ SetPassengerState( PASSENGER_STATE_ENTERING );
+
+ // Overlaying any gestures will mess us up, so don't allow it
+ GetOuter()->RemoveAllGestures();
+ }
+ break;
+
+ case TASK_PASSENGER_EXIT_VEHICLE:
+ {
+ // You must have set your entrance animation before this point!
+ Assert( m_nTransitionSequence != -1 );
+
+ // Start us playing the correct sequence
+ GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
+
+ // Overlaying any gestures will mess us up, so don't allow it
+ GetOuter()->RemoveAllGestures();
+ }
+ break;
+
+ case TASK_PASSENGER_ATTACH_TO_VEHICLE:
+ {
+ AttachToVehicle();
+ TaskComplete();
+ }
+ break;
+
+ case TASK_PASSENGER_DETACH_FROM_VEHICLE:
+ {
+ // Place an entity blocker where we're going to go
+ if ( ReserveExitPoint() == false )
+ {
+ OnExitVehicleFailed();
+ TaskFail("Failed to find valid exit point\n");
+ return;
+ }
+
+ // Physically detach from the vehicle
+ DetachFromVehicle();
+
+ // Mark that we're now disembarking
+ SetPassengerState( PASSENGER_STATE_EXITING );
+
+ TaskComplete();
+ }
+ break;
+
+ case TASK_PASSENGER_SET_IDEAL_ENTRY_YAW:
+ {
+ // Get the ideal facing to enter the vehicle
+ QAngle vecEntryAngles;
+ GetEntryPoint( m_nTransitionSequence, NULL, &vecEntryAngles );
+ GetOuter()->GetMotor()->SetIdealYaw( vecEntryAngles.y );
+
+ TaskComplete();
+ return;
+ }
+ break;
+
+ default:
+ BaseClass::StartTask( pTask );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle task running
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::RunTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ case TASK_PASSENGER_ENTER_VEHICLE:
+ {
+ // Correct for angular/spatial deviation
+ Assert( GetSequence() == m_nTransitionSequence );
+ if ( GetSequence() != m_nTransitionSequence )
+ {
+ Warning("Corrected entrance animation on vehicle enter!\n");
+ GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
+ GetOuter()->GetNavigator()->ClearGoal();
+ SetTransitionSequence( m_nTransitionSequence );
+ }
+
+ bool corrected = DoTransitionMovement();
+
+ // We must be done with the animation and in the correct position
+ if ( corrected == false )
+ {
+ FinishEnterVehicle();
+ TaskComplete();
+ }
+ }
+ break;
+
+ case TASK_PASSENGER_EXIT_VEHICLE:
+ {
+ // Correct for angular/spatial deviation
+ Assert( GetSequence() == m_nTransitionSequence );
+ if ( GetSequence() != m_nTransitionSequence )
+ {
+ Warning("Corrected exit animation on vehicle exit!\n");
+ GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
+ GetOuter()->GetNavigator()->ClearGoal();
+ SetTransitionSequence( m_nTransitionSequence );
+ }
+
+ // Correct for angular/spatial deviation
+ bool corrected = DoTransitionMovement();
+
+ // We must be done with the animation and in the correct position
+ if ( corrected == false )
+ {
+ FinishExitVehicle();
+ TaskComplete();
+ }
+ }
+ break;
+
+ default:
+ BaseClass::RunTask( pTask );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the blend amounts for position and angles, given a point in
+// time within a sequence
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::GetSequenceBlendAmount( float flCycle, float *posBlend, float *angBlend )
+{
+ // Find positional blend, if requested
+ if ( posBlend != NULL )
+ {
+ float flFrac = RemapValClamped( flCycle, m_flOriginStartFrame, m_flOriginEndFrame, 0.0f, 1.0f );
+ (*posBlend) = SimpleSpline( flFrac );
+ }
+
+ // Find angular blend, if requested
+ if ( angBlend != NULL )
+ {
+ float flFrac = RemapValClamped( flCycle, m_flAnglesStartFrame, m_flAnglesEndFrame, 0.0f, 1.0f );
+ (*angBlend) = SimpleSpline( flFrac );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the target destination for the entry animation
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::GetEntryTarget( Vector *vecOrigin, QAngle *vecAngles )
+{
+ // Get the ultimate position we'll end up at
+ m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPositionLocal( GetOuter(), vecOrigin, vecAngles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the ideal position to be in to end up at the target at the
+// end of the animation.
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::GetTransitionAnimationIdeal( float flCycle, const Vector &vecTargetPos, const QAngle &vecTargetAngles, Vector *idealOrigin, QAngle *idealAngles )
+{
+ // Get the position in time working backwards from our goal
+ Vector vecDeltaPos;
+ QAngle vecDeltaAngles;
+ GetOuter()->GetSequenceMovement( GetSequence(), 1.0f, flCycle, vecDeltaPos, vecDeltaAngles );
+
+ // Rotate the delta by our local angles
+ Vector vecPreDelta = vecDeltaPos;
+ VectorRotate( vecPreDelta, vecTargetAngles, vecDeltaPos );
+
+ // Ideal origin
+ if ( idealOrigin != NULL )
+ {
+ *idealOrigin = ( vecTargetPos + vecDeltaPos );
+ }
+
+ // Ideal angles
+ if ( idealAngles != NULL )
+ {
+ (*idealAngles).x = anglemod( vecTargetAngles.x + vecDeltaAngles.x );
+ (*idealAngles).y = anglemod( vecTargetAngles.y + vecDeltaAngles.y );
+ (*idealAngles).z = anglemod( vecTargetAngles.z + vecDeltaAngles.z );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// FIXME: This is basically a complete duplication of GetIntervalMovement
+// which doesn't remove the x and z components of the angles. This
+// should be consolidated to not replicate so much code! -- jdw
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::LocalIntervalMovement( float flInterval, bool &bMoveSeqFinished, Vector &newPosition, QAngle &newAngles )
+{
+ CStudioHdr *pstudiohdr = GetOuter()->GetModelPtr();
+ if ( pstudiohdr == NULL )
+ return false;
+
+ // Get our next cycle point
+ float flNextCycle = GetNextCycleForInterval( GetSequence(), flInterval );
+
+ // Fix-up loops
+ if ( ( GetOuter()->SequenceLoops() == false ) && flNextCycle > 1.0f )
+ {
+ flInterval = GetOuter()->GetCycle() / ( GetOuter()->GetSequenceCycleRate( GetSequence() ) * GetOuter()->GetPlaybackRate() );
+ flNextCycle = 1.0f;
+ bMoveSeqFinished = true;
+ }
+ else
+ {
+ bMoveSeqFinished = false;
+ }
+
+ Vector deltaPos;
+ QAngle deltaAngles;
+
+ // Find the delta position and delta angles for this sequence
+ if ( Studio_SeqMovement( pstudiohdr, GetOuter()->GetSequence(), GetOuter()->GetCycle(), flNextCycle, GetOuter()->GetPoseParameterArray(), deltaPos, deltaAngles ))
+ {
+ Vector vecPreDelta = deltaPos;
+ VectorRotate( vecPreDelta, GetOuter()->GetLocalAngles(), deltaPos );
+
+ newPosition = GetLocalOrigin() + deltaPos;
+ newAngles = GetLocalAngles() + deltaAngles;
+
+ return true;
+ }
+ else
+ {
+ newPosition = GetLocalOrigin();
+ newAngles = GetLocalAngles();
+
+ return false;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the next cycle point in a sequence for a given interval
+//-----------------------------------------------------------------------------
+float CAI_PassengerBehavior::GetNextCycleForInterval( int nSequence, float flInterval )
+{
+ return GetOuter()->GetCycle() + flInterval * GetOuter()->GetSequenceCycleRate( GetSequence() ) * GetOuter()->GetPlaybackRate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw debug information for the transitional movement
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::DrawDebugTransitionInfo( const Vector &vecIdealPos, const QAngle &vecIdealAngles, const Vector &vecAnimPos, const QAngle &vecAnimAngles )
+{
+ // Debug info
+ if ( GetPassengerState() == PASSENGER_STATE_ENTERING )
+ {
+ // Green - Ideal location
+ Vector foo;
+ m_hVehicle->EntityToWorldSpace( vecIdealPos, &foo );
+ NDebugOverlay::Cross3D( foo, 2, 0, 255, 0, true, 0.1f );
+ NDebugOverlay::Axis( foo, vecIdealAngles, 8, true, 0.1f );
+
+ // Blue - Actual location
+ m_hVehicle->EntityToWorldSpace( vecAnimPos, &foo );
+ NDebugOverlay::Cross3D( foo, 2, 0, 0, 255, true, 0.1f );
+ NDebugOverlay::Axis( foo, vecAnimAngles, 8, true, 0.1f );
+ }
+ else
+ {
+ // Green - Ideal location
+ NDebugOverlay::Cross3D( vecIdealPos, 4, 0, 255, 0, true, 0.1f );
+ NDebugOverlay::Axis( vecIdealPos, vecIdealAngles, 8, true, 0.1f );
+
+ // Blue - Actual location
+ NDebugOverlay::Cross3D( vecAnimPos, 2, 0, 0, 255, true, 0.1f );
+ NDebugOverlay::Axis( vecAnimPos, vecAnimAngles, 8, true, 0.1f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Local movement to enter or exit the vehicle
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::DoTransitionMovement( void )
+{
+ // Get our animation's extrapolated end position
+ Vector vecAnimPos;
+ QAngle vecAnimAngles;
+ float flInterval = GetOuter()->GetAnimTimeInterval();
+ bool bSequenceFinished;
+
+ // Get the position we're moving to for this frame with our animation's motion
+ if ( LocalIntervalMovement( flInterval, bSequenceFinished, vecAnimPos, vecAnimAngles ) )
+ {
+ // Get the position we'd ideally be in
+ Vector vecIdealPos;
+ QAngle vecIdealAngles;
+ float flNextCycle = GetNextCycleForInterval( GetOuter()->GetSequence(), flInterval );
+ flNextCycle = clamp( flNextCycle, 0.0f, 1.0f );
+ GetTransitionAnimationIdeal( flNextCycle, m_vecTargetPosition, m_vecTargetAngles, &vecIdealPos, &vecIdealAngles );
+
+ // Get the amount of error to blend out
+ float flPosBlend = 1.0f;
+ float flAngBlend = 1.0f;
+ GetSequenceBlendAmount( flNextCycle, &flPosBlend, &flAngBlend );
+
+ // Find the error between our position and our ideal
+ Vector vecDelta = ( vecIdealPos - vecAnimPos ) * flPosBlend;
+
+ QAngle vecDeltaAngles;
+ vecDeltaAngles.x = AngleDiff( vecIdealAngles.x, vecAnimAngles.x ) * flAngBlend;
+ vecDeltaAngles.y = AngleDiff( vecIdealAngles.y, vecAnimAngles.y ) * flAngBlend;
+ vecDeltaAngles.z = AngleDiff( vecIdealAngles.z, vecAnimAngles.z ) * flAngBlend;
+
+ // Factor in the error
+ GetOuter()->SetLocalOrigin( vecAnimPos + vecDelta );
+ GetOuter()->SetLocalAngles( vecAnimAngles + vecDeltaAngles );
+
+ // Draw our debug information
+ if ( passenger_debug_transition.GetBool() )
+ {
+ DrawDebugTransitionInfo( vecIdealPos, vecIdealAngles, vecAnimPos, vecAnimAngles );
+ }
+
+ // We're done moving
+ if ( bSequenceFinished )
+ return false;
+
+ // We're still correcting out the error
+ return true;
+ }
+
+ // There was no movement in the animation
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Translate normal schedules into vehicle schedules
+//-----------------------------------------------------------------------------
+int CAI_PassengerBehavior::TranslateSchedule( int scheduleType )
+{
+ if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
+ {
+ // Always be seated when riding in the car!
+ if ( scheduleType == SCHED_IDLE_STAND )
+ return SCHED_PASSENGER_IDLE;
+ }
+
+ return BaseClass::TranslateSchedule( scheduleType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the velocity of the vehicle with respect to its orientation
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::GetLocalVehicleVelocity( Vector *pOut )
+{
+ Vector velocity;
+ m_hVehicle->GetVelocity( &velocity, NULL );
+ m_hVehicle->WorldToEntitySpace( m_hVehicle->GetAbsOrigin() + velocity, pOut );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gather conditions we can comment on or react to while riding in the vehicle
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::GatherVehicleStateConditions( void )
+{
+ // Must have a vehicle to bother with this
+ if ( m_hVehicle == NULL )
+ return;
+
+ // Clear out transient conditions
+ ClearCondition( COND_PASSENGER_HARD_IMPACT );
+ ClearCondition( COND_PASSENGER_ERRATIC_DRIVING );
+ ClearCondition( COND_PASSENGER_JOSTLE_SMALL );
+ ClearCondition( COND_PASSENGER_VEHICLE_STARTED );
+ ClearCondition( COND_PASSENGER_VEHICLE_STOPPED );
+ ClearCondition( COND_PASSENGER_PLAYER_ENTERED_VEHICLE );
+ ClearCondition( COND_PASSENGER_PLAYER_EXITED_VEHICLE );
+
+ CBasePlayer *pPlayer = AI_GetSinglePlayer();
+ if ( pPlayer )
+ {
+ if ( pPlayer->IsInAVehicle() && pPlayer->GetVehicle() == m_hVehicle->GetServerVehicle() )
+ {
+ if ( m_vehicleState.m_bPlayerInVehicle == false )
+ {
+ SetCondition( COND_PASSENGER_PLAYER_ENTERED_VEHICLE );
+ m_vehicleState.m_bPlayerInVehicle = true;
+ }
+ }
+ else
+ {
+ if ( m_vehicleState.m_bPlayerInVehicle )
+ {
+ SetCondition( COND_PASSENGER_PLAYER_EXITED_VEHICLE );
+ m_vehicleState.m_bPlayerInVehicle = false;
+ }
+ }
+ }
+
+ // Get the vehicle's boost state
+ if ( m_hVehicle->m_nBoostTimeLeft < 100.0f )
+ {
+ if ( m_vehicleState.m_bWasBoosting == false )
+ {
+ m_vehicleState.m_bWasBoosting = true;
+ }
+ }
+ else
+ {
+ m_vehicleState.m_bWasBoosting = false;
+ }
+
+ // Detect being overturned
+ if ( m_hVehicle->IsOverturned() )
+ {
+ SetCondition( COND_PASSENGER_OVERTURNED );
+
+ if ( m_vehicleState.m_bWasOverturned == false )
+ {
+ m_vehicleState.m_bWasOverturned = true;
+ }
+ }
+ else
+ {
+ ClearCondition( COND_PASSENGER_OVERTURNED );
+ m_vehicleState.m_bWasOverturned = false;
+ }
+
+ // Get our local velocity
+ Vector localVelocity;
+ GetLocalVehicleVelocity( &localVelocity );
+
+ Vector deltaVelocity = ( localVelocity - m_vehicleState.m_vecLastLocalVelocity );
+
+ // Detect a sudden stop!
+ if ( deltaVelocity.y < passenger_impact_response_threshold.GetFloat() )
+ {
+ SetCondition( COND_PASSENGER_HARD_IMPACT );
+ }
+ else if ( fabs( deltaVelocity.x ) > 200.0f || fabs( deltaVelocity.z ) > 75.0f )
+ {
+ // The X axis represents lateral movement and the Z axis represents vertical movement{
+ SetCondition( COND_PASSENGER_ERRATIC_DRIVING );
+ }
+ else if ( fabs( deltaVelocity.x ) > 50.0f || fabs( deltaVelocity.z ) > 25.0f )
+ {
+ // Lightly jostled
+ SetCondition( COND_PASSENGER_JOSTLE_SMALL );
+ }
+
+ // Get our speed
+ float flSpeedSqr = localVelocity.LengthSqr();
+
+ // See if we've crossed over the threshold between movement to stillness
+ if ( m_vehicleState.m_flLastSpeedSqr > STOPPED_VELOCITY_THRESHOLD_SQR && flSpeedSqr < STOPPED_VELOCITY_THRESHOLD_SQR )
+ {
+ SetCondition( COND_PASSENGER_VEHICLE_STOPPED );
+ }
+ else if ( m_vehicleState.m_flLastSpeedSqr < STARTED_VELOCITY_THRESHOLD_SQR && flSpeedSqr > STARTED_VELOCITY_THRESHOLD_SQR )
+ {
+ // See if we've crossed over the threshold between stillness to movement
+ SetCondition( COND_PASSENGER_VEHICLE_STARTED );
+ }
+
+ // Save this as our last speed
+ m_vehicleState.m_flLastSpeedSqr = flSpeedSqr;
+
+ // Find our delta velocity from the last frame
+ m_vehicleState.m_vecDeltaVelocity = ( localVelocity - m_vehicleState.m_vecLastLocalVelocity );
+ m_vehicleState.m_vecLastLocalVelocity = localVelocity;
+
+ // Get our angular velocity
+ Vector vecVelocity;
+ AngularImpulse angVelocty;
+ m_hVehicle->GetVelocity( &vecVelocity, &angVelocty );
+ QAngle angVel( angVelocty.x, angVelocty.y, angVelocty.z );
+
+ // Blend this into the old values
+ m_vehicleState.m_vecLastAngles = ( m_vehicleState.m_vecLastAngles * 0.2f ) + ( angVel * 0.8f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Do some pre-schedule clean-up
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::PrescheduleThink( void )
+{
+ BaseClass::PrescheduleThink();
+
+ // If we're outside the vehicle, we need to turn this behavior off immediately
+ if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE && HasCondition( COND_PASSENGER_CANCEL_ENTER ) )
+ {
+ // Clear out our passenger intent
+ m_PassengerIntent = PASSENGER_INTENT_NONE;
+ ClearCondition( COND_PASSENGER_CANCEL_ENTER );
+
+ // Stop pathfinding
+ GetOuter()->GetNavigator()->ClearGoal();
+
+ // We're outside and have no intent to enter, so we're done
+ Disable();
+
+ // This must be stomped to cause our behavior to relinquish control
+ GetOuter()->ClearSchedule("Passenger enter canceled");
+ }
+
+#ifdef DEBUG
+ if ( GetPassengerState() == PASSENGER_STATE_INSIDE )
+ {
+ Vector vecSeatOrigin;
+ QAngle vecSeatAngles;
+ if ( m_hVehicle && m_hVehicle->GetServerVehicle()->NPC_GetPassengerSeatPositionLocal( GetOuter(), &vecSeatOrigin, &vecSeatAngles ) )
+ {
+ if ( ( GetLocalOrigin() - vecSeatOrigin ).LengthSqr() > Square( 0.1f ) )
+ {
+ Warning( "Passenger has strayed from seat position!\n" );
+ // GetOuter()->SetLocalOrigin( vecSeatOrigin );
+ // GetOuter()->SetLocalAngles( vecSeatAngles );
+ }
+ }
+ else
+ {
+ Warning( "Passenger is in vehicle without a valid seat position! -- EJECTED\n" );
+ GetOuter()->SetParent( NULL );
+ Disable();
+
+ return;
+ }
+ }
+#endif // DEBUG
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gather conditions for our use in making decisions
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::GatherConditions( void )
+{
+ if ( IsEnabled() == false )
+ return BaseClass::GatherConditions();
+
+ // Sense the state of the car
+ GatherVehicleStateConditions();
+
+ BaseClass::GatherConditions();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::ModifyOrAppendCriteria( AI_CriteriaSet& criteriaSet )
+{
+ if ( m_hVehicle == NULL )
+ return;
+
+ // Mark whether we're overturned or not
+ bool bOverturned = m_hVehicle->IsOverturned();
+ criteriaSet.AppendCriteria( "vehicle_overturned", bOverturned ? "1" : "0" );
+
+ // Denote whether we're in the vehicle or not
+ bool bInsideVehicle = ( GetPassengerState() == PASSENGER_STATE_INSIDE );
+ criteriaSet.AppendCriteria( "vehicle_inside", bInsideVehicle ? "1" : "0" );
+
+ // Note what angle we're at (extreme or normal)
+ Vector vecUp( 0.0f, 0.0f, 1.0f );
+ Vector vecVehicleUp;
+ m_hVehicle->GetVectors( NULL, NULL, &vecVehicleUp );
+
+ float flVehicleUp = DotProduct( vecVehicleUp, vecUp );
+ criteriaSet.AppendCriteria( "vehicle_tilt", UTIL_VarArgs( "%.2f", flVehicleUp ) );
+
+ // Set the vehicle's speed (necessary for certain types of movement judgments)
+ float flVehicleSpeed = sqrt( m_vehicleState.m_flLastSpeedSqr );
+ criteriaSet.AppendCriteria( "vehicle_speed", UTIL_VarArgs( "%f", flVehicleSpeed ) );
+
+ // Whether or not the passenger is currently able to enter the vehicle (only accounts for locking really)
+ bool bCanExitVehicle = ( m_hVehicle->NPC_CanExitVehicle( GetOuter(), true ) );
+ criteriaSet.AppendCriteria( "vehicle_can_exit", bCanExitVehicle ? "1" : "0" );
+
+ // Whether or not the passenger is currently able to exit the vehicle (only accounts for locking really)
+ bool bCanEnterVehicle = ( m_hVehicle->NPC_CanEnterVehicle( GetOuter(), true ) );
+ criteriaSet.AppendCriteria( "vehicle_can_enter", bCanEnterVehicle ? "1" : "0" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Cache off our frame numbers from the sequence keyvalue blocks
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::CacheBlendTargets( void )
+{
+ // Get the keyvalues for this sequence
+ KeyValues *seqValues = GetOuter()->GetSequenceKeyValues( m_nTransitionSequence );
+ if ( seqValues == NULL )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ // Get the entry/exit subkeys
+ KeyValues *blendValues = seqValues->FindKey( "entryexit_blend" );
+ if ( blendValues == NULL )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ // Find our frame range on this sequence
+ int nMaxFrames = Studio_MaxFrame( GetOuter()->GetModelPtr(), m_nTransitionSequence, GetOuter()->GetPoseParameterArray() );
+
+ // Find a key by this name
+ KeyValues *subKeys = blendValues->FindKey( ORIGIN_KEYNAME );
+ if ( subKeys )
+ {
+ // Retrieve our frame numbers
+ m_flOriginStartFrame = subKeys->GetFloat( "startframe", 0.0f );
+ m_flOriginEndFrame = subKeys->GetFloat( "endframe", nMaxFrames );
+
+ // Convert to normalized values
+ m_flOriginStartFrame = RemapValClamped( m_flOriginStartFrame, 0, nMaxFrames, 0.0f, 1.0f );
+ m_flOriginEndFrame = RemapValClamped( m_flOriginEndFrame, 0, nMaxFrames, 0.0f, 1.0f );
+ }
+
+ // Find a key by this name
+ subKeys = blendValues->FindKey( ANGLES_KEYNAME );
+ if ( subKeys )
+ {
+ // Retrieve our frame numbers
+ m_flAnglesStartFrame = subKeys->GetFloat( "startframe", 0.0f );
+ m_flAnglesEndFrame = subKeys->GetFloat( "endframe", nMaxFrames );
+
+ // Convert to normalized values
+ m_flAnglesStartFrame = RemapValClamped( m_flAnglesStartFrame, 0, nMaxFrames, 0.0f, 1.0f );
+ m_flAnglesEndFrame = RemapValClamped( m_flAnglesEndFrame, 0, nMaxFrames, 0.0f, 1.0f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::SetTransitionSequence( int nSequence )
+{
+ // We need to use the ACT_SCRIPT_CUSTOM_MOVE scenario for this type of custom anim
+ m_nTransitionSequence = nSequence;
+ GetOuter()->m_iszSceneCustomMoveSeq = AllocPooledString( GetOuter()->GetSequenceName( m_nTransitionSequence ) );
+
+ // Cache off our blending information at this point
+ CacheBlendTargets();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::SpeakIfAllowed( AIConcept_t concept, const char *modifiers /*= NULL*/, bool bRespondingToPlayer /*= false*/, char *pszOutResponseChosen /*= NULL*/, size_t bufsize /*= 0*/ )
+{
+ // FIXME: Store this cast off?
+ CAI_PlayerAlly *pAlly = dynamic_cast<CAI_PlayerAlly *>(GetOuter());
+ if ( pAlly != NULL )
+ return pAlly->SpeakIfAllowed( concept, modifiers, bRespondingToPlayer, pszOutResponseChosen, bufsize );
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forces us to begin a dynamic scripted scene
+// Input : *lpszInteractionName - Name of the sequence we'll play
+// *pOther -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::ForceVehicleInteraction( const char *lpszInteractionName, CBaseCombatCharacter *pOther )
+{
+ // Don't do this unless we're sitting in the cabin of the vehicle!
+ if ( GetPassengerState() != PASSENGER_STATE_INSIDE )
+ return false;
+
+ // Set a sequence and fire it off!
+ GetOuter()->m_iszSceneCustomMoveSeq = AllocPooledString( lpszInteractionName );
+ GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE );
+
+ // Slam our schedule (very unsafe!)
+ GetOuter()->SetSchedule( SCHED_PASSENGER_PLAY_SCRIPTED_ANIM );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fix up teleport event when in the vehicle
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
+{
+ //First, safely remove me from the vehicle
+ if ( GetPassengerState() != PASSENGER_STATE_OUTSIDE )
+ {
+ // Detach from the vehicle
+ DetachFromVehicle();
+ FinishExitVehicle();
+
+ // Turn the behavior off
+ GetOuter()->ClearSchedule( "ai_behavior_passenger: teleport while in vehicle" );
+ Disable();
+ }
+
+ //Then allow the teleportation
+ BaseClass::Teleport( newPosition, newAngles, newVelocity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We override this function because it can completely wreak havoc if
+// we're in the middle of a transition
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::ClearSchedule( const char *szReason )
+{
+ // Cannot do this while we're transitioning, but it's also a bug because the code that called it was probably relying on this to work!
+ if ( GetPassengerState() == PASSENGER_STATE_ENTERING || GetPassengerState() == PASSENGER_STATE_EXITING )
+ {
+ Warning("ClearSchedule rejected due to transitioning passenger: %s\n", szReason );
+ return;
+ }
+
+ // TODO: Even this will probably need more crafting depending on what we're doing in the vehicle
+ // Otherwise allow it
+ GetOuter()->ClearSchedule( szReason );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dictate the terms for being interrupted by scripted schedules or scenes
+//-----------------------------------------------------------------------------
+bool CAI_PassengerBehavior::IsInterruptable( void )
+{
+ // NOTE: We should never be interrupted this way when in a car. This would effectively makes us go comatose if we
+ // start a FACETO, MOVETO, or SEQUENCE command from a VCD.
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PassengerBehavior::CancelEnterVehicle( void )
+{
+ // Stop!
+ if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE )
+ {
+ SetCondition( COND_PASSENGER_CANCEL_ENTER );
+ }
+}
+
+// ----------------------------------------------
+// Custom AI declarations
+// ----------------------------------------------
+
+AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_PassengerBehavior )
+{
+ DECLARE_ACTIVITY( ACT_PASSENGER_IDLE )
+ DECLARE_ACTIVITY( ACT_PASSENGER_RANGE_ATTACK1 )
+
+ DECLARE_CONDITION( COND_PASSENGER_HARD_IMPACT )
+ DECLARE_CONDITION( COND_PASSENGER_ENTERING )
+ DECLARE_CONDITION( COND_PASSENGER_EXITING )
+ DECLARE_CONDITION( COND_PASSENGER_VEHICLE_STARTED )
+ DECLARE_CONDITION( COND_PASSENGER_VEHICLE_STOPPED )
+ DECLARE_CONDITION( COND_PASSENGER_OVERTURNED )
+ DECLARE_CONDITION( COND_PASSENGER_CANCEL_ENTER )
+ DECLARE_CONDITION( COND_PASSENGER_ERRATIC_DRIVING )
+ DECLARE_CONDITION( COND_PASSENGER_PLAYER_ENTERED_VEHICLE )
+ DECLARE_CONDITION( COND_PASSENGER_PLAYER_EXITED_VEHICLE )
+ DECLARE_CONDITION( COND_PASSENGER_JOSTLE_SMALL )
+
+ DECLARE_TASK( TASK_PASSENGER_ENTER_VEHICLE )
+ DECLARE_TASK( TASK_PASSENGER_EXIT_VEHICLE )
+ DECLARE_TASK( TASK_PASSENGER_ATTACH_TO_VEHICLE )
+ DECLARE_TASK( TASK_PASSENGER_DETACH_FROM_VEHICLE )
+ DECLARE_TASK( TASK_PASSENGER_SET_IDEAL_ENTRY_YAW )
+
+ // FIXME: Move to companion
+ DEFINE_SCHEDULE
+ (
+ SCHED_PASSENGER_ENTER_VEHICLE,
+
+ " Tasks"
+ " TASK_PASSENGER_SET_IDEAL_ENTRY_YAW 0"
+ " TASK_FACE_IDEAL 0"
+ " TASK_PASSENGER_ATTACH_TO_VEHICLE 0"
+ " TASK_PASSENGER_ENTER_VEHICLE 0"
+ ""
+ " Interrupts"
+ " COND_NO_CUSTOM_INTERRUPTS"
+ )
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_PASSENGER_EXIT_VEHICLE,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_PASSENGER_IDLE"
+ " TASK_PASSENGER_DETACH_FROM_VEHICLE 0"
+ " TASK_WAIT 0.1" // We must wait one tick for us to start being updated
+ " TASK_PASSENGER_EXIT_VEHICLE 0"
+ ""
+ " Interrupts"
+ " COND_NO_CUSTOM_INTERRUPTS"
+ " COND_TASK_FAILED"
+ )
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_PASSENGER_IDLE,
+
+ " Tasks"
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
+ " TASK_WAIT 2"
+ ""
+ " Interrupts"
+ " COND_PROVOKED"
+ " COND_NEW_ENEMY"
+ " COND_CAN_RANGE_ATTACK1"
+ " COND_CAN_MELEE_ATTACK1"
+ " COND_PASSENGER_EXITING"
+ " COND_HEAR_DANGER"
+ )
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_PASSENGER_PLAY_SCRIPTED_ANIM,
+
+ " Tasks"
+ " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SCRIPT_CUSTOM_MOVE"
+ ""
+ " Interrupts"
+ " COND_PASSENGER_HARD_IMPACT"
+ )
+
+ AI_END_CUSTOM_SCHEDULE_PROVIDER()
+}