2013-12-03 07:31:46 +04:00
//========= 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 ( )
}