2008-09-15 01:00:17 -05:00
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
# include "cbase.h"
# include "ai_network.h"
# include "ai_default.h"
# include "ai_schedule.h"
# include "ai_hull.h"
# include "ai_node.h"
# include "ai_task.h"
# include "ai_senses.h"
# include "ai_navigator.h"
# include "ai_route.h"
# include "entitylist.h"
# include "soundenvelope.h"
# include "gamerules.h"
# include "ndebugoverlay.h"
# include "soundflags.h"
# include "trains.h"
# include "globalstate.h"
# include "vehicle_base.h"
# include "npc_vehicledriver.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# define DRIVER_DEBUG_PATH 1
# define DRIVER_DEBUG_PATH_SPLINE 2
//------------------------------------
//
//------------------------------------
ConVar g_debug_vehicledriver ( " g_debug_vehicledriver " , " 0 " , FCVAR_CHEAT ) ;
BEGIN_DATADESC ( CNPC_VehicleDriver )
DEFINE_KEYFIELD ( m_iszVehicleName , FIELD_STRING , " vehicle " ) ,
// DEFINE_FIELD( m_hVehicle, FIELD_EHANDLE ),
// DEFINE_FIELD( m_pVehicleInterface, FIELD_POINTER ),
DEFINE_FIELD ( m_hVehicleEntity , FIELD_EHANDLE ) ,
// DEFINE_FIELD( m_Waypoints, FIELD_???? ),
// DEFINE_FIELD( m_pCurrentWaypoint, FIELD_POINTER ),
// DEFINE_FIELD( m_pNextWaypoint, FIELD_POINTER ),
DEFINE_FIELD ( m_vecDesiredVelocity , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_vecDesiredPosition , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_vecPrevPoint , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_vecPrevPrevPoint , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_vecPostPoint , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_vecPostPostPoint , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_flDistanceAlongSpline , FIELD_FLOAT ) ,
DEFINE_KEYFIELD ( m_flDriversMaxSpeed , FIELD_FLOAT , " drivermaxspeed " ) ,
DEFINE_KEYFIELD ( m_flDriversMinSpeed , FIELD_FLOAT , " driverminspeed " ) ,
DEFINE_FIELD ( m_flMaxSpeed , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_flGoalSpeed , FIELD_FLOAT ) ,
//DEFINE_KEYFIELD( m_flInitialSpeed, FIELD_FLOAT ),
DEFINE_FIELD ( m_flSteering , FIELD_FLOAT ) ,
// Inputs
DEFINE_INPUTFUNC ( FIELD_FLOAT , " SetDriversMaxSpeed " , InputSetDriversMaxSpeed ) ,
DEFINE_INPUTFUNC ( FIELD_FLOAT , " SetDriversMinSpeed " , InputSetDriversMinSpeed ) ,
DEFINE_INPUTFUNC ( FIELD_VOID , " StartForward " , InputStartForward ) ,
DEFINE_INPUTFUNC ( FIELD_VOID , " Stop " , InputStop ) ,
DEFINE_INPUTFUNC ( FIELD_VOID , " StartFiring " , InputStartFiring ) ,
DEFINE_INPUTFUNC ( FIELD_VOID , " StopFiring " , InputStopFiring ) ,
DEFINE_INPUTFUNC ( FIELD_STRING , " GotoPathCorner " , InputGotoPathCorner ) ,
END_DATADESC ( )
LINK_ENTITY_TO_CLASS ( npc_vehicledriver , CNPC_VehicleDriver ) ;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CNPC_VehicleDriver : : CNPC_VehicleDriver ( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CNPC_VehicleDriver : : ~ CNPC_VehicleDriver ( void )
{
ClearWaypoints ( ) ;
}
//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CNPC_VehicleDriver : : Spawn ( void )
{
Precache ( ) ;
BaseClass : : Spawn ( ) ;
CapabilitiesClear ( ) ;
CapabilitiesAdd ( bits_CAP_MOVE_GROUND ) ;
CapabilitiesAdd ( bits_CAP_MOVE_SHOOT ) ;
SetModel ( " models/roller_vehicledriver.mdl " ) ;
SetHullType ( HULL_LARGE ) ;
SetHullSizeNormal ( ) ;
m_iMaxHealth = m_iHealth = 1 ;
m_flFieldOfView = VIEW_FIELD_FULL ;
SetSolid ( SOLID_BBOX ) ;
AddSolidFlags ( FSOLID_NOT_SOLID ) ;
SetMoveType ( MOVETYPE_NONE ) ;
AddEffects ( EF_NODRAW ) ;
m_lifeState = LIFE_ALIVE ;
SetCycle ( 0 ) ;
ResetSequenceInfo ( ) ;
AddFlag ( FL_NPC ) ;
m_flMaxSpeed = 0 ;
m_flGoalSpeed = m_flInitialSpeed ;
m_vecDesiredVelocity = vec3_origin ;
m_vecPrevPoint = vec3_origin ;
m_vecPrevPrevPoint = vec3_origin ;
m_vecPostPoint = vec3_origin ;
m_vecPostPostPoint = vec3_origin ;
m_vecDesiredPosition = vec3_origin ;
m_flSteering = 45 ;
m_flDistanceAlongSpline = 0.2 ;
m_pCurrentWaypoint = m_pNextWaypoint = NULL ;
GetNavigator ( ) - > SetPathcornerPathfinding ( false ) ;
NPCInit ( ) ;
m_takedamage = DAMAGE_NO ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : Precache ( void )
{
PrecacheModel ( " models/roller_vehicledriver.mdl " ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : Activate ( void )
{
BaseClass : : Activate ( ) ;
// Restore doesn't need to do this
if ( m_hVehicleEntity )
return ;
// Make sure we've got a vehicle
if ( m_iszVehicleName = = NULL_STRING )
{
Warning ( " npc_vehicledriver %s has no vehicle to drive. \n " , STRING ( GetEntityName ( ) ) ) ;
UTIL_Remove ( this ) ;
return ;
}
m_hVehicleEntity = ( gEntList . FindEntityByName ( NULL , STRING ( m_iszVehicleName ) ) ) ;
if ( ! m_hVehicleEntity )
{
Warning ( " npc_vehicledriver %s couldn't find his vehicle named %s. \n " , STRING ( GetEntityName ( ) ) , STRING ( m_iszVehicleName ) ) ;
UTIL_Remove ( this ) ;
return ;
}
m_pVehicleInterface = m_hVehicleEntity - > GetServerVehicle ( ) ;
Assert ( m_pVehicleInterface ) ;
if ( ! m_pVehicleInterface - > NPC_CanDrive ( ) )
{
Warning ( " npc_vehicledriver %s doesn't know how to drive vehicle %s. \n " , STRING ( GetEntityName ( ) ) , STRING ( m_hVehicleEntity - > GetEntityName ( ) ) ) ;
UTIL_Remove ( this ) ;
return ;
}
// We've found our vehicle. Move to it and start following it.
SetAbsOrigin ( m_hVehicleEntity - > WorldSpaceCenter ( ) ) ;
m_pVehicleInterface - > NPC_SetDriver ( this ) ;
RecalculateSpeeds ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : OnRestore ( void )
{
BaseClass : : OnRestore ( ) ;
if ( m_hVehicleEntity )
{
m_pVehicleInterface = m_hVehicleEntity - > GetServerVehicle ( ) ;
Assert ( m_pVehicleInterface ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : UpdateOnRemove ( void )
{
// Leave our vehicle
if ( m_pVehicleInterface )
{
m_pVehicleInterface - > NPC_SetDriver ( NULL ) ;
}
BaseClass : : UpdateOnRemove ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : PrescheduleThink ( void )
{
if ( ! m_hVehicleEntity )
{
m_pVehicleInterface = NULL ;
UTIL_Remove ( this ) ;
return ;
}
// Keep up with my vehicle
SetAbsOrigin ( m_hVehicleEntity - > WorldSpaceCenter ( ) ) ;
SetAbsAngles ( m_hVehicleEntity - > GetAbsAngles ( ) ) ;
BaseClass : : PrescheduleThink ( ) ;
if ( m_NPCState = = NPC_STATE_IDLE )
{
m_pVehicleInterface - > NPC_Brake ( ) ;
return ;
}
// If we've been picked up by something (dropship probably), abort.
if ( m_hVehicleEntity - > GetParent ( ) )
{
SetState ( NPC_STATE_IDLE ) ;
ClearWaypoints ( ) ;
SetGoalEnt ( NULL ) ;
return ;
}
DriveVehicle ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CNPC_VehicleDriver : : SelectSchedule ( void )
{
// Vehicle driver hangs in the air inside the vehicle, so we never need to fall to ground
ClearCondition ( COND_FLOATING_OFF_GROUND ) ;
if ( HasSpawnFlags ( SF_VEHICLEDRIVER_INACTIVE ) )
{
SetState ( NPC_STATE_IDLE ) ;
return SCHED_VEHICLEDRIVER_INACTIVE ;
}
if ( GetGoalEnt ( ) )
return SCHED_VEHICLEDRIVER_DRIVE_PATH ;
switch ( m_NPCState )
{
case NPC_STATE_IDLE :
break ;
case NPC_STATE_ALERT :
break ;
case NPC_STATE_COMBAT :
{
if ( HasCondition ( COND_NEW_ENEMY ) | | HasCondition ( COND_ENEMY_DEAD ) )
return BaseClass : : SelectSchedule ( ) ;
if ( HasCondition ( COND_SEE_ENEMY ) )
{
// we can see the enemy
if ( HasCondition ( COND_CAN_RANGE_ATTACK2 ) )
return SCHED_RANGE_ATTACK2 ;
if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
return SCHED_RANGE_ATTACK1 ;
// What to do here? Not necessarily easy to face enemy.
//if ( HasCondition(COND_NOT_FACING_ATTACK) )
//return SCHED_COMBAT_FACE;
}
// We can see him, but can't shoot him. Just wait and hope he comes closer.
return SCHED_VEHICLEDRIVER_COMBAT_WAIT ;
}
break ;
2008-09-15 01:33:59 -05:00
default :
break ;
2008-09-15 01:00:17 -05:00
}
return BaseClass : : SelectSchedule ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CNPC_VehicleDriver : : RangeAttack1Conditions ( float flDot , float flDist )
{
// Vehicle not ready to fire again yet?
if ( m_pVehicleInterface - > Weapon_PrimaryCanFireAt ( ) > gpGlobals - > curtime )
return 0 ;
// Check weapon range
float flMinRange , flMaxRange ;
m_pVehicleInterface - > Weapon_PrimaryRanges ( & flMinRange , & flMaxRange ) ;
if ( flDist < flMinRange )
return COND_TOO_CLOSE_TO_ATTACK ;
if ( flDist > flMaxRange )
return COND_TOO_FAR_TO_ATTACK ;
// Don't shoot backwards
Vector vecForward ;
Vector vecToTarget = ( GetEnemy ( ) - > GetAbsOrigin ( ) - GetAbsOrigin ( ) ) ;
VectorNormalize ( vecToTarget ) ;
m_hVehicleEntity - > GetVectors ( & vecForward , NULL , NULL ) ;
float flForwardDot = DotProduct ( vecForward , vecToTarget ) ;
if ( flForwardDot < 0 & & fabs ( flDot ) < 0.5 )
return COND_NOT_FACING_ATTACK ;
return COND_CAN_RANGE_ATTACK1 ;
}
//=========================================================
// RangeAttack2Conditions
//=========================================================
int CNPC_VehicleDriver : : RangeAttack2Conditions ( float flDot , float flDist )
{
// Vehicle not ready to fire again yet?
if ( m_pVehicleInterface - > Weapon_SecondaryCanFireAt ( ) > gpGlobals - > curtime )
return 0 ;
// Check weapon range
float flMinRange , flMaxRange ;
m_pVehicleInterface - > Weapon_SecondaryRanges ( & flMinRange , & flMaxRange ) ;
if ( flDist < flMinRange )
return COND_TOO_CLOSE_TO_ATTACK ;
if ( flDist > flMaxRange )
return COND_TOO_FAR_TO_ATTACK ;
return COND_CAN_RANGE_ATTACK2 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CNPC_VehicleDriver : : TranslateSchedule ( int scheduleType )
{
switch ( scheduleType )
{
case SCHED_COMBAT_FACE :
{
// Vehicles can't rotate, so don't try and face
return TranslateSchedule ( SCHED_CHASE_ENEMY ) ;
}
break ;
case SCHED_ALERT_FACE :
{
// Vehicles can't rotate, so don't try and face
return SCHED_ALERT_STAND ;
}
break ;
case SCHED_CHASE_ENEMY_FAILED :
case SCHED_FAIL :
{
return SCHED_FAIL ;
}
break ;
}
return BaseClass : : TranslateSchedule ( scheduleType ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTask -
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : StartTask ( const Task_t * pTask )
{
switch ( pTask - > iTask )
{
case TASK_RUN_PATH :
case TASK_WALK_PATH :
TaskComplete ( ) ;
break ;
case TASK_FACE_IDEAL :
case TASK_FACE_ENEMY :
{
// Vehicle ignores face commands, since it can't rotate on the spot.
TaskComplete ( ) ;
}
break ;
case TASK_VEHICLEDRIVER_GET_PATH :
{
if ( ! GetGoalEnt ( ) )
{
TaskFail ( FAIL_NO_TARGET ) ;
return ;
}
CheckForTeleport ( ) ;
if ( g_debug_vehicledriver . GetInt ( ) & DRIVER_DEBUG_PATH )
{
NDebugOverlay : : Box ( GetGoalEnt ( ) - > GetAbsOrigin ( ) , - Vector ( 50 , 50 , 50 ) , Vector ( 50 , 50 , 50 ) , 255 , 255 , 255 , true , 5 ) ;
}
AI_NavGoal_t goal ( GOALTYPE_PATHCORNER , GetGoalEnt ( ) - > GetLocalOrigin ( ) , ACT_WALK , AIN_DEF_TOLERANCE , AIN_YAW_TO_DEST ) ;
if ( ! GetNavigator ( ) - > SetGoal ( goal ) )
{
TaskFail ( FAIL_NO_ROUTE ) ;
return ;
}
TaskComplete ( ) ;
}
break ;
case TASK_WAIT_FOR_MOVEMENT :
{
if ( GetNavigator ( ) - > GetGoalType ( ) = = GOALTYPE_NONE )
{
TaskComplete ( ) ;
GetNavigator ( ) - > StopMoving ( ) ; // Stop moving
}
else if ( ! GetNavigator ( ) - > IsGoalActive ( ) )
{
SetIdealActivity ( GetStoppedActivity ( ) ) ;
}
else
{
// Check validity of goal type
ValidateNavGoal ( ) ;
}
}
break ;
default :
BaseClass : : StartTask ( pTask ) ;
break ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : RunTask ( const Task_t * pTask )
{
switch ( pTask - > iTask )
{
case TASK_RANGE_ATTACK1 :
{
// Vehicle driver has no animations, so fire a burst at the target
CBaseEntity * pEnemy = GetEnemy ( ) ;
if ( pEnemy )
{
// TODO: Get a bodytarget from the firing point of the gun in the vehicle
Vector vecTarget = GetEnemy ( ) - > BodyTarget ( GetAbsOrigin ( ) , false ) ;
m_pVehicleInterface - > NPC_AimPrimaryWeapon ( vecTarget ) ;
m_pVehicleInterface - > NPC_PrimaryFire ( ) ;
TaskComplete ( ) ;
}
else
{
TaskFail ( FAIL_NO_ENEMY ) ;
return ;
}
}
break ;
case TASK_RANGE_ATTACK2 :
{
// Vehicle driver has no animations, so fire a burst at the target
CBaseEntity * pEnemy = GetEnemy ( ) ;
if ( pEnemy )
{
// TODO: Get a bodytarget from the firing point of the gun in the vehicle
Vector vecTarget = GetEnemy ( ) - > BodyTarget ( GetAbsOrigin ( ) , false ) ;
m_pVehicleInterface - > NPC_AimSecondaryWeapon ( vecTarget ) ;
m_pVehicleInterface - > NPC_SecondaryFire ( ) ;
TaskComplete ( ) ;
}
else
{
TaskFail ( FAIL_NO_ENEMY ) ;
return ;
}
}
break ;
case TASK_WAIT_FOR_MOVEMENT :
{
BaseClass : : RunTask ( pTask ) ;
if ( HasCondition ( COND_SEE_ENEMY ) )
{
// we can see the enemy
if ( HasCondition ( COND_CAN_RANGE_ATTACK2 ) )
{
ChainRunTask ( TASK_RANGE_ATTACK2 , pTask - > flTaskData ) ;
}
if ( HasCondition ( COND_CAN_RANGE_ATTACK1 ) )
{
ChainRunTask ( TASK_RANGE_ATTACK1 , pTask - > flTaskData ) ;
}
}
}
break ;
default :
BaseClass : : RunTask ( pTask ) ;
break ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : GatherEnemyConditions ( CBaseEntity * pEnemy )
{
BaseClass : : GatherEnemyConditions ( pEnemy ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Overridden because if the player is a criminal, we hate them.
//-----------------------------------------------------------------------------
Disposition_t CNPC_VehicleDriver : : IRelationType ( CBaseEntity * pTarget )
{
// If it's the player and they are a criminal, we hate them.
if ( pTarget - > Classify ( ) = = CLASS_PLAYER )
{
if ( GlobalEntity_GetState ( " gordon_precriminal " ) = = GLOBAL_ON )
{
return ( D_NU ) ;
}
}
return ( BaseClass : : IRelationType ( pTarget ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CNPC_VehicleDriver : : OverrideMove ( float flInterval )
{
if ( ! m_hVehicleEntity )
return true ;
// If we don't have a maxspeed, we've been stopped, so abort early
// Or we've been picked up by something (dropship probably).
if ( ! m_flMaxSpeed | | m_hVehicleEntity - > GetParent ( ) )
{
m_pVehicleInterface - > NPC_Brake ( ) ;
return true ;
}
// -----------------------------------------------------------------
// If I have a route, keep it updated and move toward target
// ------------------------------------------------------------------
if ( GetNavigator ( ) - > IsGoalActive ( ) )
{
if ( OverridePathMove ( flInterval ) )
return true ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : CalculatePostPoints ( void )
{
m_vecPostPoint = m_vecDesiredPosition ;
m_vecPostPostPoint = m_vecDesiredPosition ;
// If we have a waypoint beyond our current, use it instead.
if ( ! GetNavigator ( ) - > CurWaypointIsGoal ( ) )
{
AI_Waypoint_t * pCurWaypoint = GetNavigator ( ) - > GetPath ( ) - > GetCurWaypoint ( ) ;
m_vecPostPoint = pCurWaypoint - > GetNext ( ) - > GetPos ( ) ;
if ( pCurWaypoint - > GetNext ( ) - > GetNext ( ) )
{
m_vecPostPostPoint = pCurWaypoint - > GetNext ( ) - > GetNext ( ) - > GetPos ( ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Destroy our current waypoints
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : ClearWaypoints ( void )
{
m_vecDesiredPosition = vec3_origin ;
if ( m_pCurrentWaypoint )
{
delete m_pCurrentWaypoint ;
m_pCurrentWaypoint = NULL ;
}
if ( m_pNextWaypoint )
{
delete m_pNextWaypoint ;
m_pNextWaypoint = NULL ;
}
}
//-----------------------------------------------------------------------------
// Purpose: We've hit a waypoint. Handle it, and return true if this is the
// end of the path.
//-----------------------------------------------------------------------------
bool CNPC_VehicleDriver : : WaypointReached ( void )
{
// We reached our current waypoint.
m_vecPrevPrevPoint = m_vecPrevPoint ;
m_vecPrevPoint = GetAbsOrigin ( ) ;
// If we've got to our goal, we're done here.
if ( GetNavigator ( ) - > CurWaypointIsGoal ( ) )
{
// Necessary for InPass outputs to be fired, is a no-op otherwise
GetNavigator ( ) - > AdvancePath ( ) ;
// Stop pathing
ClearWaypoints ( ) ;
TaskComplete ( ) ;
SetGoalEnt ( NULL ) ;
return true ;
}
AI_Waypoint_t * pCurWaypoint = GetNavigator ( ) - > GetPath ( ) - > GetCurWaypoint ( ) ;
if ( ! pCurWaypoint )
return false ;
// Check to see if the waypoint wants us to change speed
if ( pCurWaypoint - > Flags ( ) & bits_WP_TO_PATHCORNER )
{
CBaseEntity * pEntity = pCurWaypoint - > hPathCorner ;
if ( pEntity )
{
if ( pEntity - > m_flSpeed > 0 )
{
if ( pEntity - > m_flSpeed < = 1.0 )
{
m_flDriversMaxSpeed = pEntity - > m_flSpeed ;
RecalculateSpeeds ( ) ;
}
else
{
Warning ( " path_track %s tried to tell the npc_vehicledriver to set speed to %.3f. npc_vehicledriver only accepts values between 0 and 1. \n " , STRING ( pEntity - > GetEntityName ( ) ) , pEntity - > m_flSpeed ) ;
}
}
}
}
// Get the waypoints for the next part of the path
GetNavigator ( ) - > AdvancePath ( ) ;
if ( ! GetNavigator ( ) - > GetPath ( ) - > GetCurWaypoint ( ) )
{
ClearWaypoints ( ) ;
TaskComplete ( ) ;
SetGoalEnt ( NULL ) ;
return true ;
}
m_vecDesiredPosition = GetNavigator ( ) - > GetCurWaypointPos ( ) ;
CalculatePostPoints ( ) ;
// Move to the next waypoint
delete m_pCurrentWaypoint ;
m_pCurrentWaypoint = m_pNextWaypoint ;
m_Waypoints [ 1 ] = new CVehicleWaypoint ( m_vecPrevPoint , m_vecDesiredPosition , m_vecPostPoint , m_vecPostPostPoint ) ;
m_pNextWaypoint = m_Waypoints [ 1 ] ;
// Drop the spline marker back
2011-04-28 01:29:22 -05:00
m_flDistanceAlongSpline = MAX ( 0 , m_flDistanceAlongSpline - 1.0 ) ;
2008-09-15 01:00:17 -05:00
CheckForTeleport ( ) ;
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CNPC_VehicleDriver : : OverridePathMove ( float flInterval )
{
// Setup our initial path data if we've just started running a path
if ( ! m_pCurrentWaypoint )
{
m_vecPrevPoint = GetAbsOrigin ( ) ;
m_vecPrevPrevPoint = GetAbsOrigin ( ) ;
m_vecDesiredPosition = GetNavigator ( ) - > GetCurWaypointPos ( ) ;
CalculatePostPoints ( ) ;
// Init our two waypoints
m_Waypoints [ 0 ] = new CVehicleWaypoint ( m_vecPrevPrevPoint , m_vecPrevPoint , m_vecDesiredPosition , m_vecPostPoint ) ;
m_Waypoints [ 1 ] = new CVehicleWaypoint ( m_vecPrevPoint , m_vecDesiredPosition , m_vecPostPoint , m_vecPostPostPoint ) ;
m_pCurrentWaypoint = m_Waypoints [ 0 ] ;
m_pNextWaypoint = m_Waypoints [ 1 ] ;
m_flDistanceAlongSpline = 0.2 ;
}
// Have we reached our target? See if we've passed the current waypoint's plane.
Vector vecAbsMins , vecAbsMaxs ;
CollisionProp ( ) - > WorldSpaceAABB ( & vecAbsMins , & vecAbsMaxs ) ;
if ( BoxOnPlaneSide ( vecAbsMins , vecAbsMaxs , & m_pCurrentWaypoint - > planeWaypoint ) = = 3 )
{
if ( WaypointReached ( ) )
return true ;
}
// Did we bypass it and reach the next one already?
if ( m_pNextWaypoint & & BoxOnPlaneSide ( vecAbsMins , vecAbsMaxs , & m_pNextWaypoint - > planeWaypoint ) = = 3 )
{
if ( WaypointReached ( ) )
return true ;
}
// We may have just teleported, so check to make sure we have a waypoint
if ( ! m_pCurrentWaypoint | | ! m_pNextWaypoint )
return false ;
// Figure out which spline we're trucking along
CVehicleWaypoint * pCurrentSplineBeingTraversed = m_pCurrentWaypoint ;
if ( m_flDistanceAlongSpline > 1 )
{
pCurrentSplineBeingTraversed = m_pNextWaypoint ;
}
// Get our current speed, and check it against the length of the spline to know how far to advance our marker
AngularImpulse angVel ;
Vector vecVelocity ;
IPhysicsObject * pVehiclePhysics = m_hVehicleEntity - > VPhysicsGetObject ( ) ;
if ( ! pVehiclePhysics )
{
// I think my vehicle has been destroyed.
return false ;
}
pVehiclePhysics - > GetVelocity ( & vecVelocity , & angVel ) ;
float flSpeed = vecVelocity . Length ( ) ;
float flIncTime = gpGlobals - > curtime - GetLastThink ( ) ;
float flIncrement = flIncTime * ( flSpeed / pCurrentSplineBeingTraversed - > GetLength ( ) ) ;
// Now advance our point along the spline
m_flDistanceAlongSpline = clamp ( m_flDistanceAlongSpline + flIncrement , 0 , 2 ) ;
if ( m_flDistanceAlongSpline > 1 )
{
// We crossed the spline boundary
pCurrentSplineBeingTraversed = m_pNextWaypoint ;
}
Vector vSplinePoint = pCurrentSplineBeingTraversed - > GetPointAt ( m_flDistanceAlongSpline > 1 ? m_flDistanceAlongSpline - 1 : m_flDistanceAlongSpline ) ;
Vector vSplineTangent = pCurrentSplineBeingTraversed - > GetTangentAt ( m_flDistanceAlongSpline > 1 ? m_flDistanceAlongSpline - 1 : m_flDistanceAlongSpline ) ;
// Now that we've got the target spline point & tangent, use it to decide what our desired velocity is.
// If we're close to the tangent, just use the tangent. Otherwise, Lerp towards it.
Vector vecToDesired = ( vSplinePoint - GetAbsOrigin ( ) ) ;
float flDistToDesired = VectorNormalize ( vecToDesired ) ;
float flTangentLength = VectorNormalize ( vSplineTangent ) ;
if ( flDistToDesired > ( flTangentLength * 0.75 ) )
{
m_vecDesiredVelocity = vecToDesired * flTangentLength ;
}
else
{
VectorLerp ( vSplineTangent , vecToDesired * flTangentLength , ( flDistToDesired / ( flTangentLength * 0.5 ) ) , m_vecDesiredVelocity ) ;
}
// Decrease speed according to the turn we're trying to make
Vector vecRight ;
m_hVehicleEntity - > GetVectors ( NULL , & vecRight , NULL ) ;
Vector vecNormVel = m_vecDesiredVelocity ;
VectorNormalize ( vecNormVel ) ;
float flDotRight = DotProduct ( vecRight , vecNormVel ) ;
flSpeed = ( 1.0 - fabs ( flDotRight ) ) ;
// Don't go slower than we've been told to go
if ( flSpeed < m_flDriversMinSpeed )
{
flSpeed = m_flDriversMinSpeed ;
}
m_vecDesiredVelocity = vecNormVel * ( flSpeed * m_flMaxSpeed ) ;
// Bunch o'debug
if ( g_debug_vehicledriver . GetInt ( ) & DRIVER_DEBUG_PATH )
{
NDebugOverlay : : Box ( m_vecPrevPrevPoint , - Vector ( 15 , 15 , 15 ) , Vector ( 15 , 15 , 15 ) , 192 , 0 , 0 , true , 0.1 ) ;
NDebugOverlay : : Box ( m_vecPrevPoint , - Vector ( 20 , 20 , 20 ) , Vector ( 20 , 20 , 20 ) , 255 , 0 , 0 , true , 0.1 ) ;
NDebugOverlay : : Box ( m_vecPostPoint , - Vector ( 20 , 20 , 20 ) , Vector ( 20 , 20 , 20 ) , 0 , 192 , 0 , true , 0.1 ) ;
NDebugOverlay : : Box ( m_vecPostPostPoint , - Vector ( 20 , 20 , 20 ) , Vector ( 20 , 20 , 20 ) , 0 , 128 , 0 , true , 0.1 ) ;
NDebugOverlay : : Box ( vSplinePoint , - Vector ( 10 , 10 , 10 ) , Vector ( 10 , 10 , 10 ) , 0 , 0 , 255 , true , 0.1 ) ;
NDebugOverlay : : Line ( vSplinePoint , vSplinePoint + ( vSplineTangent * 40 ) , 0 , 0 , 255 , true , 0.1 ) ;
// Draw the plane we're checking against for waypoint passing
Vector vecPlaneRight ;
CrossProduct ( m_pCurrentWaypoint - > planeWaypoint . normal , Vector ( 0 , 0 , 1 ) , vecPlaneRight ) ;
Vector vecPlane = m_pCurrentWaypoint - > splinePoints [ 2 ] ;
NDebugOverlay : : Line ( vecPlane + ( vecPlaneRight * - 100 ) , vecPlane + ( vecPlaneRight * 100 ) , 255 , 0 , 0 , true , 0.1 ) ;
// Draw the next plane too
CrossProduct ( m_pNextWaypoint - > planeWaypoint . normal , Vector ( 0 , 0 , 1 ) , vecPlaneRight ) ;
vecPlane = m_pNextWaypoint - > splinePoints [ 2 ] ;
NDebugOverlay : : Line ( vecPlane + ( vecPlaneRight * - 100 ) , vecPlane + ( vecPlaneRight * 100 ) , 192 , 0 , 0 , true , 0.1 ) ;
}
if ( g_debug_vehicledriver . GetInt ( ) & DRIVER_DEBUG_PATH_SPLINE )
{
for ( int i = 0 ; i < 10 ; i + + )
{
Vector vecTarget = m_pCurrentWaypoint - > GetPointAt ( 0.1 * i ) ;
Vector vecTangent = m_pCurrentWaypoint - > GetTangentAt ( 0.1 * i ) ;
VectorNormalize ( vecTangent ) ;
NDebugOverlay : : Box ( vecTarget , - Vector ( 10 , 10 , 10 ) , Vector ( 10 , 10 , 10 ) , 255 , 0 , 0 , true , 0.1 ) ;
NDebugOverlay : : Line ( vecTarget , vecTarget + ( vecTangent * 10 ) , 255 , 255 , 0 , true , 0.1 ) ;
}
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: This takes the current place the NPC's trying to get to, figures out
// what keys to press to get the vehicle to go there, and then sends
// them to the vehicle.
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : DriveVehicle ( void )
{
AngularImpulse angVel ;
Vector vecVelocity ;
IPhysicsObject * pVehiclePhysics = m_hVehicleEntity - > VPhysicsGetObject ( ) ;
if ( ! pVehiclePhysics )
return ;
pVehiclePhysics - > GetVelocity ( & vecVelocity , & angVel ) ;
float flSpeed = VectorNormalize ( vecVelocity ) ;
// If we have no target position to drive to, brake to a halt
if ( ! m_flMaxSpeed | | m_vecDesiredPosition = = vec3_origin )
{
if ( flSpeed > 1 )
{
m_pVehicleInterface - > NPC_Brake ( ) ;
}
return ;
}
if ( g_debug_vehicledriver . GetInt ( ) & DRIVER_DEBUG_PATH )
{
NDebugOverlay : : Box ( m_vecDesiredPosition , - Vector ( 20 , 20 , 20 ) , Vector ( 20 , 20 , 20 ) , 0 , 255 , 0 , true , 0.1 ) ;
NDebugOverlay : : Line ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + m_vecDesiredVelocity , 0 , 255 , 0 , true , 0.1 ) ;
}
m_flGoalSpeed = VectorNormalize ( m_vecDesiredVelocity ) ;
// Is our target in front or behind us?
Vector vecForward , vecRight ;
m_hVehicleEntity - > GetVectors ( & vecForward , & vecRight , NULL ) ;
float flDot = DotProduct ( vecForward , m_vecDesiredVelocity ) ;
bool bBehind = ( flDot < 0 ) ;
float flVelDot = DotProduct ( vecVelocity , m_vecDesiredVelocity ) ;
bool bGoingWrongWay = ( flVelDot < 0 ) ;
// Figure out whether we should accelerate / decelerate
if ( bGoingWrongWay | | ( flSpeed < m_flGoalSpeed ) )
{
// If it's behind us, go backwards not forwards
if ( bBehind )
{
m_pVehicleInterface - > NPC_ThrottleReverse ( ) ;
}
else
{
m_pVehicleInterface - > NPC_ThrottleForward ( ) ;
}
}
else
{
// Brake if we're go significantly too fast
if ( ( flSpeed - 200 ) > m_flGoalSpeed )
{
m_pVehicleInterface - > NPC_Brake ( ) ;
}
else
{
m_pVehicleInterface - > NPC_ThrottleCenter ( ) ;
}
}
// Do we need to turn?
float flDotRight = DotProduct ( vecRight , m_vecDesiredVelocity ) ;
if ( bBehind )
{
// If we're driving backwards, flip our turning
flDotRight * = - 1 ;
}
// Map it to the vehicle's steering
flDotRight * = ( m_flSteering / 90 ) ;
if ( flDotRight < 0 )
{
// Turn left
m_pVehicleInterface - > NPC_TurnLeft ( - flDotRight ) ;
}
else if ( flDotRight > 0 )
{
// Turn right
m_pVehicleInterface - > NPC_TurnRight ( flDotRight ) ;
}
else
{
m_pVehicleInterface - > NPC_TurnCenter ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Check to see if we should teleport to the current path corner
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : CheckForTeleport ( void )
{
if ( ! GetGoalEnt ( ) )
return ;
CPathTrack * pTrack = dynamic_cast < CPathTrack * > ( GetGoalEnt ( ) ) ;
if ( ! pTrack )
return ;
// Does it have the teleport flag set?
if ( pTrack - > HasSpawnFlags ( SF_PATH_TELEPORT ) )
{
AddEffects ( EF_NOINTERP ) ;
// Teleport the vehicle to the pathcorner
Vector vecMins , vecMaxs ;
vecMins = m_hVehicleEntity - > CollisionProp ( ) - > OBBMins ( ) ;
vecMaxs = m_hVehicleEntity - > CollisionProp ( ) - > OBBMaxs ( ) ;
Vector vecTarget = pTrack - > GetAbsOrigin ( ) - ( vecMins + vecMaxs ) * 0.5 ;
vecTarget . z + = ( ( vecMaxs . z - vecMins . z ) * 0.5 ) + 8 ; // Safety buffer
// Orient it to face the next point
QAngle vecAngles = pTrack - > GetAbsAngles ( ) ;
Vector vecToTarget = vec3_origin ;
if ( pTrack - > GetNext ( ) )
{
vecToTarget = ( pTrack - > GetNext ( ) - > GetAbsOrigin ( ) - pTrack - > GetAbsOrigin ( ) ) ;
VectorNormalize ( vecToTarget ) ;
// Vehicles are rotated 90 degrees
VectorAngles ( vecToTarget , vecAngles ) ;
vecAngles [ YAW ] - = 90 ;
}
m_hVehicleEntity - > Teleport ( & vecTarget , & vecAngles , & vec3_origin ) ;
// Teleport the driver
SetAbsOrigin ( m_hVehicleEntity - > WorldSpaceCenter ( ) ) ;
SetAbsAngles ( m_hVehicleEntity - > GetAbsAngles ( ) ) ;
m_vecPrevPoint = pTrack - > GetAbsOrigin ( ) ;
// Move to the next waypoint, we've reached this one
if ( GetNavigator ( ) - > GetPath ( ) )
{
WaypointReached ( ) ;
}
// Clear our waypoints, because the next waypoint is certainly invalid now.
ClearWaypoints ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CNPC_VehicleDriver : : GetDefaultNavGoalTolerance ( )
{
return 48 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : RecalculateSpeeds ( void )
{
// Get data from the vehicle
const vehicleparams_t * pParams = m_pVehicleInterface - > GetVehicleParams ( ) ;
if ( pParams )
{
m_flMaxSpeed = pParams - > engine . maxSpeed * m_flDriversMaxSpeed ;
m_flSteering = pParams - > steering . degrees ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : InputSetDriversMaxSpeed ( inputdata_t & inputdata )
{
m_flDriversMaxSpeed = inputdata . value . Float ( ) ;
RecalculateSpeeds ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : InputSetDriversMinSpeed ( inputdata_t & inputdata )
{
m_flDriversMinSpeed = inputdata . value . Float ( ) ;
RecalculateSpeeds ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : InputStartForward ( inputdata_t & inputdata )
{
CLEARBITS ( m_spawnflags , SF_VEHICLEDRIVER_INACTIVE ) ;
if ( m_NPCState = = NPC_STATE_IDLE )
{
SetState ( NPC_STATE_ALERT ) ;
}
SetCondition ( COND_PROVOKED ) ;
RecalculateSpeeds ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Tell the driver to stop moving
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : InputStop ( inputdata_t & inputdata )
{
m_flMaxSpeed = 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: Tell the driver to start firing at targets
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : InputStartFiring ( inputdata_t & inputdata )
{
CLEARBITS ( m_spawnflags , SF_VEHICLEDRIVER_INACTIVE ) ;
SetCondition ( COND_PROVOKED ) ;
float flMinRange , flMaxRange ;
// If the vehicle has a weapon, set our capability
if ( m_pVehicleInterface - > NPC_HasPrimaryWeapon ( ) )
{
CapabilitiesAdd ( bits_CAP_INNATE_RANGE_ATTACK1 ) ;
m_pVehicleInterface - > Weapon_PrimaryRanges ( & flMinRange , & flMaxRange ) ;
// Ensure the look distances is long enough
if ( m_flDistTooFar < flMaxRange | | GetSenses ( ) - > GetDistLook ( ) < flMaxRange )
{
m_flDistTooFar = flMaxRange ;
SetDistLook ( flMaxRange ) ;
}
}
if ( m_pVehicleInterface - > NPC_HasSecondaryWeapon ( ) )
{
CapabilitiesAdd ( bits_CAP_INNATE_RANGE_ATTACK2 ) ;
m_pVehicleInterface - > Weapon_SecondaryRanges ( & flMinRange , & flMaxRange ) ;
// Ensure the look distances is long enough
if ( m_flDistTooFar < flMaxRange | | GetSenses ( ) - > GetDistLook ( ) < flMaxRange )
{
m_flDistTooFar = flMaxRange ;
SetDistLook ( flMaxRange ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Tell the driver to stop firing at targets
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : InputStopFiring ( inputdata_t & inputdata )
{
// If the vehicle has a weapon, set our capability
CapabilitiesRemove ( bits_CAP_INNATE_RANGE_ATTACK1 ) ;
CapabilitiesRemove ( bits_CAP_INNATE_RANGE_ATTACK2 ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_VehicleDriver : : InputGotoPathCorner ( inputdata_t & inputdata )
{
string_t iszPathName = inputdata . value . StringID ( ) ;
if ( iszPathName ! = NULL_STRING )
{
CBaseEntity * pEntity = gEntList . FindEntityByName ( NULL , iszPathName ) ;
if ( ! pEntity )
{
Warning ( " npc_vehicledriver %s couldn't find entity named %s \n " , STRING ( GetEntityName ( ) ) , STRING ( iszPathName ) ) ;
return ;
}
ClearWaypoints ( ) ;
// Drive to the point
SetGoalEnt ( pEntity ) ;
if ( m_NPCState = = NPC_STATE_IDLE )
{
SetState ( NPC_STATE_ALERT ) ;
}
SetCondition ( COND_PROVOKED ) ;
// Force him to start forward
InputStartForward ( inputdata ) ;
}
}
//-----------------------------------------------------------------------------
//
// Schedules
//
//-----------------------------------------------------------------------------
AI_BEGIN_CUSTOM_NPC ( npc_vehicledriver , CNPC_VehicleDriver )
//Tasks
DECLARE_TASK ( TASK_VEHICLEDRIVER_GET_PATH )
// Schedules
DEFINE_SCHEDULE
(
SCHED_VEHICLEDRIVER_INACTIVE ,
" Tasks "
" TASK_WAIT_INDEFINITE 0 "
" "
" Interrupts "
" COND_PROVOKED "
)
DEFINE_SCHEDULE
(
SCHED_VEHICLEDRIVER_COMBAT_WAIT ,
" Tasks "
" TASK_WAIT 5 "
" "
" Interrupts "
" COND_NEW_ENEMY "
" COND_LIGHT_DAMAGE "
" COND_HEAVY_DAMAGE "
" COND_PROVOKED "
" COND_CAN_RANGE_ATTACK1 "
" COND_CAN_RANGE_ATTACK2 "
)
DEFINE_SCHEDULE
(
SCHED_VEHICLEDRIVER_DRIVE_PATH ,
" Tasks "
" TASK_VEHICLEDRIVER_GET_PATH 0 "
" TASK_WALK_PATH 9999 "
" TASK_WAIT_FOR_MOVEMENT 0 "
" TASK_WAIT_PVS 0 "
" "
" Interrupts "
" COND_NEW_ENEMY "
" COND_PROVOKED "
)
AI_END_CUSTOM_NPC ( )