diff options
Diffstat (limited to 'game/server/episodic/ai_behavior_passenger_zombie.cpp')
| -rw-r--r-- | game/server/episodic/ai_behavior_passenger_zombie.cpp | 878 |
1 files changed, 878 insertions, 0 deletions
diff --git a/game/server/episodic/ai_behavior_passenger_zombie.cpp b/game/server/episodic/ai_behavior_passenger_zombie.cpp new file mode 100644 index 0000000..0667530 --- /dev/null +++ b/game/server/episodic/ai_behavior_passenger_zombie.cpp @@ -0,0 +1,878 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Zombies on cars! +// +//============================================================================= + +#include "cbase.h" +#include "npcevent.h" +#include "ai_motor.h" +#include "ai_senses.h" +#include "vehicle_jeep_episodic.h" +#include "npc_alyx_episodic.h" +#include "ai_behavior_passenger_zombie.h" + +#define JUMP_ATTACH_DIST_THRESHOLD 1000 +#define JUMP_ATTACH_FACING_THRESHOLD DOT_45DEGREE + +#define ATTACH_PREDICTION_INTERVAL 0.2f +#define ATTACH_PREDICTION_FACING_THRESHOLD 0.75f +#define ATTACH_PREDICTION_DIST_THRESHOLD 128 + +int ACT_PASSENGER_MELEE_ATTACK1; +int ACT_PASSENGER_THREATEN; +int ACT_PASSENGER_FLINCH; +int ACT_PASSENGER_ZOMBIE_LEAP_LOOP; + +BEGIN_DATADESC( CAI_PassengerBehaviorZombie ) + + DEFINE_FIELD( m_flLastVerticalLean, FIELD_FLOAT ), + DEFINE_FIELD( m_flLastLateralLean, FIELD_FLOAT ), + DEFINE_FIELD( m_flNextLeapTime, FIELD_TIME ), + +END_DATADESC(); + +extern int AE_PASSENGER_PHYSICS_PUSH; + +//============================================================================================== +// Passenger damage table +//============================================================================================== +static impactentry_t zombieLinearTable[] = +{ + { 200*200, 100 }, +}; + +static impactentry_t zombieAngularTable[] = +{ + { 100*100, 100 }, +}; + +impactdamagetable_t gZombiePassengerImpactDamageTable = +{ + zombieLinearTable, + zombieAngularTable, + + ARRAYSIZE(zombieLinearTable), + ARRAYSIZE(zombieAngularTable), + + 24*24, // minimum linear speed squared + 360*360, // minimum angular speed squared (360 deg/s to cause spin/slice damage) + 2, // can't take damage from anything under 2kg + + 5, // anything less than 5kg is "small" + 5, // never take more than 5 pts of damage from anything under 5kg + 36*36, // <5kg objects must go faster than 36 in/s to do damage + + VPHYSICS_LARGE_OBJECT_MASS, // large mass in kg + 4, // large mass scale (anything over 500kg does 4X as much energy to read from damage table) + 5, // large mass falling scale (emphasize falling/crushing damage over sideways impacts since the stress will kill you anyway) + 0.0f, // min vel +}; + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAI_PassengerBehaviorZombie::CAI_PassengerBehaviorZombie( void ) : +m_flLastVerticalLean( 0.0f ), +m_flLastLateralLean( 0.0f ), +m_flNextLeapTime( 0.0f ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_PassengerBehaviorZombie::CanEnterVehicle( void ) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Translate into vehicle passengers +//----------------------------------------------------------------------------- +int CAI_PassengerBehaviorZombie::TranslateSchedule( int scheduleType ) +{ + // We do different animations when inside the vehicle + if ( GetPassengerState() == PASSENGER_STATE_INSIDE ) + { + if ( scheduleType == SCHED_MELEE_ATTACK1 ) + return SCHED_PASSENGER_ZOMBIE_MELEE_ATTACK1; + + if ( scheduleType == SCHED_RANGE_ATTACK1 ) + return SCHED_PASSENGER_ZOMBIE_RANGE_ATTACK1; + } + + return BaseClass::TranslateSchedule( scheduleType ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : activity - +// Output : Activity +//----------------------------------------------------------------------------- +Activity CAI_PassengerBehaviorZombie::NPC_TranslateActivity( Activity activity ) +{ + Activity nNewActivity = BaseClass::NPC_TranslateActivity( activity ); + if ( activity == ACT_IDLE ) + return (Activity) ACT_PASSENGER_IDLE; + + return nNewActivity; +} + +//----------------------------------------------------------------------------- +// Purpose: Suppress melee attacks against enemies for the given duration +// Input : flDuration - Amount of time to suppress the attacks +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::SuppressAttack( float flDuration ) +{ + GetOuter()->SetNextAttack( gpGlobals->curtime + flDuration ); +} + +//----------------------------------------------------------------------------- +// Purpose: Determines if an enemy is inside a vehicle or not +// Output : Returns true if the enemy is outside the vehicle. +//----------------------------------------------------------------------------- +bool CAI_PassengerBehaviorZombie::EnemyInVehicle( void ) +{ + // Obviously they're not... + if ( GetOuter()->GetEnemy() == NULL ) + return false; + + // See if they're in a vehicle, currently + CBaseCombatCharacter *pCCEnemy = GetOuter()->GetEnemy()->MyCombatCharacterPointer(); + if ( pCCEnemy && pCCEnemy->IsInAVehicle() ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Select a schedule when we're outside of the vehicle +//----------------------------------------------------------------------------- +int CAI_PassengerBehaviorZombie::SelectOutsideSchedule( void ) +{ + // Attaching to target + if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) + return SCHED_PASSENGER_ZOMBIE_RANGE_ATTACK1; + + // Attack the player if we're able + if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) ) + return SCHED_MELEE_ATTACK1; + + // Attach to the vehicle + if ( HasCondition( COND_PASSENGER_ZOMBIE_CAN_ATTACH_TO_VEHICLE ) ) + return SCHED_PASSENGER_ZOMBIE_ATTACH; + + // Otherwise chase after him + return SCHED_PASSENGER_ZOMBIE_RUN_TO_VEHICLE; +} + +//----------------------------------------------------------------------------- +// Purpose: Pick a schedule for being "inside" the vehicle +//----------------------------------------------------------------------------- +int CAI_PassengerBehaviorZombie::SelectInsideSchedule( void ) +{ + // Attacking target + if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) ) + return SCHED_PASSENGER_ZOMBIE_MELEE_ATTACK1; + + return SCHED_IDLE_STAND; +} + +//----------------------------------------------------------------------------- +// Purpose: Move the zombie to the vehicle +//----------------------------------------------------------------------------- +int CAI_PassengerBehaviorZombie::SelectSchedule( void ) +{ + // See if our enemy got out + if ( GetOuter()->GetEnemy() != NULL && EnemyInVehicle() == false ) + { + if ( GetPassengerState() == PASSENGER_STATE_INSIDE ) + { + // Exit the vehicle + SetCondition( COND_PASSENGER_EXITING ); + } + else if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE ) + { + // Our target has left the vehicle and we're outside as well, so give up + Disable(); + return BaseClass::SelectSchedule(); + } + } + + // Entering schedule + if ( HasCondition( COND_PASSENGER_ENTERING ) ) + { + ClearCondition( COND_PASSENGER_ENTERING ); + return SCHED_PASSENGER_ZOMBIE_ENTER_VEHICLE; + } + + // Exiting schedule + if ( HasCondition( COND_PASSENGER_EXITING ) ) + { + ClearCondition( COND_PASSENGER_EXITING ); + return SCHED_PASSENGER_ZOMBIE_EXIT_VEHICLE; + } + + // Select different schedules based on our state + PassengerState_e nState = GetPassengerState(); + int nNewSchedule = SCHED_NONE; + + if ( nState == PASSENGER_STATE_INSIDE ) + { + nNewSchedule = SelectInsideSchedule(); + if ( nNewSchedule != SCHED_NONE ) + return nNewSchedule; + } + else if ( nState == PASSENGER_STATE_OUTSIDE ) + { + nNewSchedule = SelectOutsideSchedule(); + if ( nNewSchedule != SCHED_NONE ) + return nNewSchedule; + } + + // Worst case he just stands here + Assert(0); + return SCHED_IDLE_STAND; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_PassengerBehaviorZombie::CanJumpToAttachToVehicle( void ) +{ + // FIXME: Probably move this up one level and out of this function + if ( m_flNextLeapTime > gpGlobals->curtime ) + return false; + + // Predict an attachment jump + CBaseEntity *pEnemy = GetOuter()->GetEnemy(); + + Vector vecPredictedPosition; + UTIL_PredictedPosition( pEnemy, 1.0f, &vecPredictedPosition ); + + float flDist = UTIL_DistApprox( vecPredictedPosition, GetOuter()->GetAbsOrigin() ); + + // If we're facing them enough, allow the jump + if ( ( flDist < JUMP_ATTACH_DIST_THRESHOLD ) && UTIL_IsFacingWithinTolerance( GetOuter(), pEnemy, JUMP_ATTACH_FACING_THRESHOLD ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Determine if we can jump to be on the enemy's vehicle +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +inline bool CAI_PassengerBehaviorZombie::CanBeOnEnemyVehicle( void ) +{ + CBaseCombatCharacter *pEnemy = ToBaseCombatCharacter( GetOuter()->GetEnemy() ); + if ( pEnemy != NULL ) + { + IServerVehicle *pVehicle = pEnemy->GetVehicle(); + if ( pVehicle && pVehicle->NPC_HasAvailableSeat( GetRoleName() ) ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::GatherConditions( void ) +{ + BaseClass::GatherConditions(); + + // Always clear the base conditions + ClearCondition( COND_CAN_MELEE_ATTACK1 ); + + // Behavior when outside the vehicle + if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE ) + { + if ( CanBeOnEnemyVehicle() && CanJumpToAttachToVehicle() ) + { + SetCondition( COND_CAN_RANGE_ATTACK1 ); + } + + // Determine if we can latch on to the vehicle (out of sight) + ClearCondition( COND_PASSENGER_ZOMBIE_CAN_ATTACH_TO_VEHICLE ); + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + + if ( pPlayer != NULL && + GetOuter()->GetEnemy() == pPlayer && + pPlayer->GetVehicleEntity() == m_hVehicle ) + { + // Can't be visible to the player and must be close enough + bool bNotVisibleToPlayer = ( pPlayer->FInViewCone( GetOuter() ) == false ); + float flDistSqr = ( pPlayer->GetAbsOrigin() - GetOuter()->GetAbsOrigin() ).LengthSqr(); + bool bInRange = ( flDistSqr < Square(250.0f) ); + if ( bNotVisibleToPlayer && bInRange ) + { + // We can latch on and "enter" the vehicle + SetCondition( COND_PASSENGER_ZOMBIE_CAN_ATTACH_TO_VEHICLE ); + } + else if ( bNotVisibleToPlayer == false && flDistSqr < Square(128.0f) ) + { + // Otherwise just hit the vehicle in anger + SetCondition( COND_CAN_MELEE_ATTACK1 ); + } + } + } + + // Behavior when on the car + if ( GetPassengerState() == PASSENGER_STATE_INSIDE ) + { + // Check for melee attack + if ( GetOuter()->GetNextAttack() < gpGlobals->curtime ) + { + SetCondition( COND_CAN_MELEE_ATTACK1 ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle death case +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::Event_Killed( const CTakeDamageInfo &info ) +{ + if ( m_hVehicle ) + { + // Stop taking messages from the vehicle + m_hVehicle->RemovePhysicsChild( GetOuter() ); + m_hVehicle->NPC_RemovePassenger( GetOuter() ); + m_hVehicle->NPC_FinishedExitVehicle( GetOuter(), false ); + } + + BaseClass::Event_Killed( info ); +} + +//----------------------------------------------------------------------------- +// Purpose: Build our custom interrupt cases for the behavior +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::BuildScheduleTestBits( void ) +{ + // Always interrupt when we need to get in or out + if ( GetPassengerState() == PASSENGER_STATE_OUTSIDE ) + { + GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_CAN_RANGE_ATTACK1 ) ); + GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_PASSENGER_ENTERING ) ); + } + + BaseClass::BuildScheduleTestBits(); +} + +//----------------------------------------------------------------------------- +// Purpose: Get the absolute position of the desired attachment point +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::GetAttachmentPoint( Vector *vecPoint ) +{ + Vector vecEntryOffset, vecFinalOffset; + GetEntryTarget( &vecEntryOffset, NULL ); + VectorRotate( vecEntryOffset, m_hVehicle->GetAbsAngles(), vecFinalOffset ); + *vecPoint = ( m_hVehicle->GetAbsOrigin() + vecFinalOffset ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +int CAI_PassengerBehaviorZombie::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; + + // 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() ) ); + Assert( nSequence != -1 ); + if ( nSequence == -1 ) + continue; + + return nSequence; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::StartDismount( void ) +{ + // Leap off the vehicle + int nSequence = FindExitSequence(); + Assert( nSequence != -1 ); + + SetTransitionSequence( nSequence ); + GetOuter()->SetIdealActivity( ACT_SCRIPT_CUSTOM_MOVE ); + + // This removes the NPC from the vehicle's handling and fires all necessary outputs + m_hVehicle->RemovePhysicsChild( GetOuter() ); + m_hVehicle->NPC_RemovePassenger( GetOuter() ); + m_hVehicle->NPC_FinishedExitVehicle( GetOuter(), (IsPassengerHostile()==false) ); + + // Detach from the parent + GetOuter()->SetParent( NULL ); + GetOuter()->SetMoveType( MOVETYPE_STEP ); + GetMotor()->SetYawLocked( false ); + + QAngle vecAngles = GetAbsAngles(); + vecAngles.z = 0.0f; + GetOuter()->SetAbsAngles( vecAngles ); + + // HACK: Will this work? + IPhysicsObject *pPhysObj = GetOuter()->VPhysicsGetObject(); + if ( pPhysObj != NULL ) + { + pPhysObj->EnableCollisions( true ); + } + + // Clear this + m_PassengerIntent = PASSENGER_INTENT_NONE; + SetPassengerState( PASSENGER_STATE_EXITING ); + + // Get the velocity + Vector vecUp, vecJumpDir; + GetOuter()->GetVectors( &vecJumpDir, NULL, &vecUp ); + + // Move back and up + vecJumpDir *= random->RandomFloat( -400.0f, -500.0f ); + vecJumpDir += vecUp * 150.0f; + GetOuter()->SetAbsVelocity( vecJumpDir ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::FinishDismount( void ) +{ + SetPassengerState( PASSENGER_STATE_OUTSIDE ); + Disable(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::StartTask( const Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_FACE_HINTNODE: + case TASK_FACE_LASTPOSITION: + case TASK_FACE_SAVEPOSITION: + case TASK_FACE_TARGET: + case TASK_FACE_IDEAL: + case TASK_FACE_SCRIPT: + case TASK_FACE_PATH: + TaskComplete(); + break; + + case TASK_PASSENGER_ZOMBIE_RANGE_ATTACK1: + break; + + case TASK_MELEE_ATTACK1: + { + // Only override this if we're "in" the vehicle + if ( GetPassengerState() != PASSENGER_STATE_INSIDE ) + { + BaseClass::StartTask( pTask ); + break; + } + + // Swipe + GetOuter()->SetIdealActivity( (Activity) ACT_PASSENGER_MELEE_ATTACK1 ); + + // Randomly attack again in the future + float flWait = random->RandomFloat( 0.0f, 1.0f ); + SuppressAttack( flWait ); + } + break; + + case TASK_PASSENGER_ZOMBIE_DISMOUNT: + { + // Start the process of dismounting from the vehicle + StartDismount(); + } + break; + + case TASK_PASSENGER_ZOMBIE_ATTACH: + { + if ( AttachToVehicle() ) + { + TaskComplete(); + return; + } + + TaskFail( "Unable to attach to vehicle!" ); + } + break; + + default: + BaseClass::StartTask( pTask ); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Handle task running +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::RunTask( const Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_PASSENGER_ZOMBIE_RANGE_ATTACK1: + { + // Face the entry point + Vector vecAttachPoint; + GetAttachmentPoint( &vecAttachPoint ); + GetOuter()->GetMotor()->SetIdealYawToTarget( vecAttachPoint ); + + // All done when you touch the ground + if ( GetOuter()->GetFlags() & FL_ONGROUND ) + { + m_flNextLeapTime = gpGlobals->curtime + 2.0f; + TaskComplete(); + return; + } + } + break; + + case TASK_MELEE_ATTACK1: + + if ( GetOuter()->IsSequenceFinished() ) + { + TaskComplete(); + } + + break; + + case TASK_PASSENGER_ZOMBIE_DISMOUNT: + { + if ( GetOuter()->IsSequenceFinished() ) + { + // Completely separate from the vehicle + FinishDismount(); + TaskComplete(); + } + + break; + } + + default: + BaseClass::RunTask( pTask ); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Find the relative cost of an entry point based on facing +// Input : &vecEntryPos - Position we're evaluating +// Output : Returns the cost as a modified distance value +//----------------------------------------------------------------------------- +float CAI_PassengerBehaviorZombie::GetEntryPointCost( const Vector &vecEntryPos ) +{ + // FIXME: We don't care about cost any longer! + return 1.0f; + + // Find the direction from us to the entry point + Vector vecEntryDir = ( vecEntryPos - GetAbsOrigin() ); + float flCost = VectorNormalize( vecEntryDir ); + + // Get our current facing + Vector vecDir; + GetOuter()->GetVectors( &vecDir, NULL, NULL ); + + // Scale our cost by how closely it matches our facing + float flDot = DotProduct( vecEntryDir, vecDir ); + if ( flDot < 0.0f ) + return FLT_MAX; + + flCost *= RemapValClamped( flDot, 1.0f, 0.0f, 1.0f, 2.0f ); + + return flCost; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bNearest - +// Output : int +//----------------------------------------------------------------------------- +int CAI_PassengerBehaviorZombie::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; + + Vector vecStartPos; + const CPassengerSeatTransition *pTransition; + float flBestCost = FLT_MAX; + float flCost; + int nBestSequence = -1; + int nSequence = -1; + + // 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() ) ); + + Assert( nSequence != -1 ); + if ( nSequence == -1 ) + continue; + + // Test this entry for validity + GetEntryPoint( nSequence, &vecStartPos ); + + // Evaluate the cost + flCost = GetEntryPointCost( vecStartPos ); + if ( flCost < flBestCost ) + { + nBestSequence = nSequence; + flBestCost = flCost; + continue; + } + } + + return nBestSequence; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::ExitVehicle( void ) +{ + BaseClass::ExitVehicle(); + + // Remove us as a passenger + m_hVehicle->NPC_RemovePassenger( GetOuter() ); + m_hVehicle->NPC_FinishedExitVehicle( GetOuter(), false ); + +} + +//----------------------------------------------------------------------------- +// Purpose: Calculate our body lean based on our delta velocity +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::CalculateBodyLean( void ) +{ + // Calculate our lateral displacement from a perfectly centered start + float flLateralDisp = SimpleSplineRemapVal( m_vehicleState.m_vecLastAngles.z, 100.0f, -100.0f, -1.0f, 1.0f ); + flLateralDisp = clamp( flLateralDisp, -1.0f, 1.0f ); + + // FIXME: Framerate dependant! + m_flLastLateralLean = ( m_flLastLateralLean * 0.2f ) + ( flLateralDisp * 0.8f ); + + // Factor in a "stun" if the zombie was moved too far off course + if ( fabs( m_flLastLateralLean ) > 0.75f ) + { + SuppressAttack( 0.5f ); + } + + // Calc our vertical displacement + float flVerticalDisp = SimpleSplineRemapVal( m_vehicleState.m_vecDeltaVelocity.z, -50.0f, 50.0f, -1.0f, 1.0f ); + flVerticalDisp = clamp( flVerticalDisp, -1.0f, 1.0f ); + + // FIXME: Framerate dependant! + m_flLastVerticalLean = ( m_flLastVerticalLean * 0.75f ) + ( flVerticalDisp * 0.25f ); + + // Set these parameters + GetOuter()->SetPoseParameter( "lean_lateral", m_flLastLateralLean ); + GetOuter()->SetPoseParameter( "lean_vertical", m_flLastVerticalLean ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::GatherVehicleStateConditions( void ) +{ + // Call to the base + BaseClass::GatherVehicleStateConditions(); + + // Only do this if we're on the vehicle + if ( GetPassengerState() != PASSENGER_STATE_INSIDE ) + return; + + // Calculate how our body is leaning + CalculateBodyLean(); + + // The forward delta of the vehicle + float flLateralDelta = ( m_vehicleState.m_vecDeltaVelocity.x + m_vehicleState.m_vecDeltaVelocity.y ); + + // Detect a sudden stop + if ( flLateralDelta < -350.0f ) + { + if ( m_hVehicle ) + { + Vector vecDamageForce; + m_hVehicle->GetVelocity( &vecDamageForce, NULL ); + VectorNormalize( vecDamageForce ); + vecDamageForce *= random->RandomFloat( 50000.0f, 60000.0f ); + + //NDebugOverlay::HorzArrow( GetAbsOrigin(), GetAbsOrigin() + ( vecDamageForce * 256.0f ), 16.0f, 255, 0, 0, 16, true, 2.0f ); + + // Fake it! + CTakeDamageInfo info( m_hVehicle, m_hVehicle, vecDamageForce, GetOuter()->WorldSpaceCenter(), 200, (DMG_CRUSH|DMG_VEHICLE) ); + GetOuter()->TakeDamage( info ); + } + } + else if ( flLateralDelta < -150.0f ) + { + // FIXME: Realistically this should interrupt and play a schedule to do it + GetOuter()->SetIdealActivity( (Activity) ACT_PASSENGER_FLINCH ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEvent - +//----------------------------------------------------------------------------- +void CAI_PassengerBehaviorZombie::HandleAnimEvent( animevent_t *pEvent ) +{ + if ( pEvent->event == AE_PASSENGER_PHYSICS_PUSH ) + { + // Add a push into the vehicle + float flForce = (float) atof( pEvent->options ); + AddPhysicsPush( flForce * 0.75f ); + return; + } + + BaseClass::HandleAnimEvent( pEvent ); +} + +//----------------------------------------------------------------------------- +// Purpose: Attach to the vehicle if we're able +//----------------------------------------------------------------------------- +bool CAI_PassengerBehaviorZombie::AttachToVehicle( void ) +{ + // Must be able to enter the vehicle + if ( m_hVehicle->NPC_CanEnterVehicle( GetOuter(), false ) == false ) + return false; + + // Reserve the seat + if ( ReserveEntryPoint( VEHICLE_SEAT_ANY ) == false ) + return false; + + // Use the best one we've found + int nSequence = FindEntrySequence(); + if ( nSequence == -1 ) + return false; + + // Take the transition sequence + SetTransitionSequence( nSequence ); + + // Get in the vehicle + EnterVehicle(); + + // Start our scripted sequence with any other passengers + // Find Alyx + // TODO: Iterate through the list of passengers in the vehicle and find one we can interact with + CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx(); + if ( pAlyx ) + { + // Tell Alyx to play along! + pAlyx->ForceVehicleInteraction( GetOuter()->GetSequenceName( nSequence ), GetOuter() ); + } + + return true; +} + +AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_PassengerBehaviorZombie ) +{ + DECLARE_ACTIVITY( ACT_PASSENGER_MELEE_ATTACK1 ) + DECLARE_ACTIVITY( ACT_PASSENGER_THREATEN ) + DECLARE_ACTIVITY( ACT_PASSENGER_FLINCH ) + DECLARE_ACTIVITY( ACT_PASSENGER_ZOMBIE_LEAP_LOOP ) + + DECLARE_TASK( TASK_PASSENGER_ZOMBIE_RANGE_ATTACK1 ) + DECLARE_TASK( TASK_PASSENGER_ZOMBIE_DISMOUNT ) + DECLARE_TASK( TASK_PASSENGER_ZOMBIE_ATTACH ) + + DECLARE_CONDITION( COND_PASSENGER_ZOMBIE_CAN_ATTACH_TO_VEHICLE ) + + DEFINE_SCHEDULE + ( + SCHED_PASSENGER_ZOMBIE_ENTER_VEHICLE, + + " Tasks" + " TASK_PASSENGER_ATTACH_TO_VEHICLE 0" + " TASK_PASSENGER_ENTER_VEHICLE 0" + "" + " Interrupts" + ) + + DEFINE_SCHEDULE + ( + SCHED_PASSENGER_ZOMBIE_EXIT_VEHICLE, + + " Tasks" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_PASSENGER_IDLE" + " TASK_STOP_MOVING 0" + " TASK_PASSENGER_ZOMBIE_DISMOUNT 0" + "" + " Interrupts" + " COND_TASK_FAILED" + ) + + DEFINE_SCHEDULE + ( + SCHED_PASSENGER_ZOMBIE_MELEE_ATTACK1, + + " Tasks" + " TASK_ANNOUNCE_ATTACK 1" + " TASK_MELEE_ATTACK1 0" + "" + " Interrupts" + ) + + DEFINE_SCHEDULE + ( + SCHED_PASSENGER_ZOMBIE_RANGE_ATTACK1, + + " Tasks" + " TASK_PLAY_SEQUENCE ACTIVITY:ACT_PASSENGER_RANGE_ATTACK1" + " TASK_SET_ACTIVITY ACTIVITY:ACT_PASSENGER_ZOMBIE_LEAP_LOOP" + " TASK_PASSENGER_ZOMBIE_RANGE_ATTACK1 0" + " " + " Interrupts" + ) + + DEFINE_SCHEDULE + ( + SCHED_PASSENGER_ZOMBIE_RUN_TO_VEHICLE, + + " Tasks" + " TASK_STOP_MOVING 0" + " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED" + " TASK_GET_CHASE_PATH_TO_ENEMY 2400" + " TASK_RUN_PATH 0" + " TASK_WAIT_FOR_MOVEMENT 0" + "" + " Interrupts" + " COND_NEW_ENEMY" + " COND_ENEMY_DEAD" + " COND_ENEMY_UNREACHABLE" + " COND_TASK_FAILED" + " COND_LOST_ENEMY" + " COND_PASSENGER_ZOMBIE_CAN_ATTACH_TO_VEHICLE" + ) + + DEFINE_SCHEDULE + ( + SCHED_PASSENGER_ZOMBIE_ATTACH, + + " Tasks" + " TASK_PASSENGER_ZOMBIE_ATTACH 0" + "" + " Interrupts" + ) + + AI_END_CUSTOM_SCHEDULE_PROVIDER() +} |