diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/hl2/ai_behavior_police.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/hl2/ai_behavior_police.cpp')
| -rw-r--r-- | mp/src/game/server/hl2/ai_behavior_police.cpp | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/mp/src/game/server/hl2/ai_behavior_police.cpp b/mp/src/game/server/hl2/ai_behavior_police.cpp new file mode 100644 index 00000000..e6c6fd64 --- /dev/null +++ b/mp/src/game/server/hl2/ai_behavior_police.cpp @@ -0,0 +1,783 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "ai_behavior_police.h"
+#include "ai_navigator.h"
+#include "ai_memory.h"
+#include "collisionutils.h"
+#include "npc_metropolice.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+BEGIN_DATADESC( CAI_PolicingBehavior )
+
+ DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bStartPolicing, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hPoliceGoal, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_flNextHarassTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flAggressiveTime, FIELD_TIME ),
+ DEFINE_FIELD( m_nNumWarnings, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bTargetIsHostile, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flTargetHostileTime,FIELD_TIME ),
+
+END_DATADESC();
+
+CAI_PolicingBehavior::CAI_PolicingBehavior( void )
+{
+ m_bEnabled = false;
+ m_nNumWarnings = 0;
+ m_bTargetIsHostile = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PolicingBehavior::TargetIsHostile( void )
+{
+ if ( ( m_flTargetHostileTime < gpGlobals->curtime ) && ( !m_bTargetIsHostile ) )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pGoal -
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::Enable( CAI_PoliceGoal *pGoal )
+{
+ m_hPoliceGoal = pGoal;
+ m_bEnabled = true;
+
+ m_bStartPolicing = true;
+
+ // Update ourselves immediately
+ GetOuter()->ClearSchedule( "Enable police behavior" );
+ //NotifyChangeBehaviorStatus( GetOuter()->IsInAScript() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::Disable( void )
+{
+ m_hPoliceGoal = NULL;
+ m_bEnabled = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PolicingBehavior::CanSelectSchedule( void )
+{
+ // Must be activated and valid
+ if ( IsEnabled() == false || !m_hPoliceGoal || !m_hPoliceGoal->GetTarget() )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : false -
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::HostSetBatonState( bool state )
+{
+ // If we're a cop, turn the baton on
+ CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(GetOuter());
+
+ if ( pCop != NULL )
+ {
+ pCop->SetBatonState( state );
+ pCop->SetTarget( m_hPoliceGoal->GetTarget() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : false -
+//-----------------------------------------------------------------------------
+bool CAI_PolicingBehavior::HostBatonIsOn( void )
+{
+ // If we're a cop, turn the baton on
+ CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(GetOuter());
+ if ( pCop )
+ return pCop->BatonActive();
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::HostSpeakSentence( const char *pSentence, SentencePriority_t nSoundPriority, SentenceCriteria_t nCriteria )
+{
+ // If we're a cop, turn the baton on
+ CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(GetOuter());
+
+ if ( pCop != NULL )
+ {
+ CAI_Sentence< CNPC_MetroPolice > *pSentences = pCop->GetSentences();
+
+ pSentences->Speak( pSentence, nSoundPriority, nCriteria );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::BuildScheduleTestBits( void )
+{
+ if ( IsCurSchedule( SCHED_IDLE_STAND ) || IsCurSchedule( SCHED_ALERT_STAND ) )
+ {
+ if ( m_flNextHarassTime < gpGlobals->curtime )
+ {
+ GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_POLICE_TARGET_TOO_CLOSE_HARASS ) );
+ }
+
+ GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::GatherConditions( void )
+{
+ BaseClass::GatherConditions();
+
+ // Mapmaker may have removed our goal while we're running our schedule
+ if ( !m_hPoliceGoal )
+ {
+ Disable();
+ return;
+ }
+
+ ClearCondition( COND_POLICE_TARGET_TOO_CLOSE_HARASS );
+ ClearCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS );
+
+ CBaseEntity *pTarget = m_hPoliceGoal->GetTarget();
+
+ if ( pTarget == NULL )
+ {
+ DevMsg( "ai_goal_police with NULL target entity!\n" );
+ return;
+ }
+
+ // See if we need to knock out our target immediately
+ if ( ShouldKnockOutTarget( pTarget ) )
+ {
+ SetCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS );
+ }
+
+ float flDistSqr = ( m_hPoliceGoal->WorldSpaceCenter() - pTarget->WorldSpaceCenter() ).Length2DSqr();
+ float radius = ( m_hPoliceGoal->GetRadius() * PATROL_RADIUS_RATIO );
+ float zDiff = fabs( m_hPoliceGoal->WorldSpaceCenter().z - pTarget->WorldSpaceCenter().z );
+
+ // If we're too far away, don't bother
+ if ( flDistSqr < (radius*radius) && zDiff < 32.0f )
+ {
+ SetCondition( COND_POLICE_TARGET_TOO_CLOSE_HARASS );
+
+ if ( flDistSqr < (m_hPoliceGoal->GetRadius()*m_hPoliceGoal->GetRadius()) )
+ {
+ SetCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS );
+ }
+ }
+
+ // If we're supposed to stop chasing (aggression over), return
+ if ( m_bTargetIsHostile && m_flAggressiveTime < gpGlobals->curtime && IsCurSchedule(SCHED_CHASE_ENEMY) )
+ {
+ // Force me to re-evaluate my schedule
+ GetOuter()->ClearSchedule( "Stopped chasing, aggression over" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// We're taking cover from danger
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::AnnouncePolicing( void )
+{
+ // We're policing
+ static const char *pWarnings[3] =
+ {
+ "METROPOLICE_MOVE_ALONG_A",
+ "METROPOLICE_MOVE_ALONG_B",
+ "METROPOLICE_MOVE_ALONG_C",
+ };
+
+ if ( m_nNumWarnings <= 3 )
+ {
+ HostSpeakSentence( pWarnings[ m_nNumWarnings - 1 ], SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
+ }
+ else
+ {
+ // We loop at m_nNumWarnings == 4 for players who aren't moving
+ // but still pissing us off, and we're not allowed to do anything about it. (i.e. can't leave post)
+ // First two sentences sound pretty good, so randomly pick one of them.
+ int iSentence = RandomInt( 0, 1 );
+ HostSpeakSentence( pWarnings[ iSentence ], SENTENCE_PRIORITY_MEDIUM, SENTENCE_CRITERIA_NORMAL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : scheduleType -
+// Output : int
+//-----------------------------------------------------------------------------
+int CAI_PolicingBehavior::TranslateSchedule( int scheduleType )
+{
+ if ( scheduleType == SCHED_CHASE_ENEMY )
+ {
+ if ( m_hPoliceGoal->ShouldRemainAtPost() && !MaintainGoalPosition() )
+ return BaseClass::TranslateSchedule( SCHED_COMBAT_FACE );
+ }
+
+ return BaseClass::TranslateSchedule( scheduleType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : newActivity -
+// Output : Activity
+//-----------------------------------------------------------------------------
+Activity CAI_PolicingBehavior::NPC_TranslateActivity( Activity newActivity )
+{
+ // See which harassment to play
+ if ( newActivity == ACT_POLICE_HARASS1 )
+ {
+ switch( m_nNumWarnings )
+ {
+ case 1:
+ return (Activity) ACT_POLICE_HARASS1;
+ break;
+
+ default:
+ case 2:
+ return (Activity) ACT_POLICE_HARASS2;
+ break;
+ }
+ }
+
+ return BaseClass::NPC_TranslateActivity( newActivity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CBaseEntity
+//-----------------------------------------------------------------------------
+CBaseEntity *CAI_PolicingBehavior::GetGoalTarget( void )
+{
+ if ( m_hPoliceGoal == NULL )
+ {
+ //NOTENOTE: This has been called before the behavior is actually active, or the goal has gone invalid
+ Assert(0);
+ return NULL;
+ }
+
+ return m_hPoliceGoal->GetTarget();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : time -
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::SetTargetHostileDuration( float time )
+{
+ m_flTargetHostileTime = gpGlobals->curtime + time;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pTask -
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::StartTask( const Task_t *pTask )
+{
+ switch (pTask->iTask)
+ {
+ case TASK_POLICE_GET_PATH_TO_HARASS_GOAL:
+ {
+ Vector harassDir = ( m_hPoliceGoal->GetTarget()->WorldSpaceCenter() - WorldSpaceCenter() );
+ float flDist = VectorNormalize( harassDir );
+
+ // See if we're already close enough
+ if ( flDist < pTask->flTaskData )
+ {
+ TaskComplete();
+ break;
+ }
+
+ float flInter1, flInter2;
+ Vector harassPos = GetAbsOrigin() + ( harassDir * ( flDist - pTask->flTaskData ) );
+
+ // Find a point on our policing radius to stand on
+ if ( IntersectInfiniteRayWithSphere( GetAbsOrigin(), harassDir, m_hPoliceGoal->GetAbsOrigin(), m_hPoliceGoal->GetRadius(), &flInter1, &flInter2 ) )
+ {
+ Vector vPos = m_hPoliceGoal->GetAbsOrigin() + harassDir * ( MAX( flInter1, flInter2 ) );
+
+ // See how far away the default one is
+ float testDist = UTIL_DistApprox2D( m_hPoliceGoal->GetAbsOrigin(), harassPos );
+
+ // If our other goal is closer, choose it
+ if ( testDist > UTIL_DistApprox2D( m_hPoliceGoal->GetAbsOrigin(), vPos ) )
+ {
+ harassPos = vPos;
+ }
+ }
+
+ if ( GetNavigator()->SetGoal( harassPos, pTask->flTaskData ) )
+ {
+ GetNavigator()->SetMovementActivity( (Activity) ACT_WALK_ANGRY );
+ GetNavigator()->SetArrivalDirection( m_hPoliceGoal->GetTarget() );
+ TaskComplete();
+ }
+ else
+ {
+ TaskFail( FAIL_NO_ROUTE );
+ }
+ }
+ break;
+
+ case TASK_POLICE_GET_PATH_TO_POLICE_GOAL:
+ {
+ if ( GetNavigator()->SetGoal( m_hPoliceGoal->GetAbsOrigin(), pTask->flTaskData ) )
+ {
+ GetNavigator()->SetArrivalDirection( m_hPoliceGoal->GetAbsAngles() );
+ TaskComplete();
+ }
+ else
+ {
+ TaskFail( FAIL_NO_ROUTE );
+ }
+ }
+ break;
+
+ case TASK_POLICE_ANNOUNCE_HARASS:
+ {
+ AnnouncePolicing();
+
+ // Randomly say this again in the future
+ m_flNextHarassTime = gpGlobals->curtime + random->RandomInt( 4, 6 );
+
+ // Scatter rubber-neckers
+ CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin(), 256.0f, 2.0f, GetOuter() );
+ }
+ TaskComplete();
+ break;
+
+ case TASK_POLICE_FACE_ALONG_GOAL:
+ {
+ // We may have lost our police goal in the 2 seconds we wait before this task
+ if ( m_hPoliceGoal )
+ {
+ GetMotor()->SetIdealYaw( m_hPoliceGoal->GetAbsAngles().y );
+ GetOuter()->SetTurnActivity();
+ }
+ }
+ break;
+
+ default:
+ BaseClass::StartTask( pTask );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::RunTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ case TASK_POLICE_FACE_ALONG_GOAL:
+ {
+ GetMotor()->UpdateYaw();
+
+ if ( GetOuter()->FacingIdeal() )
+ {
+ TaskComplete();
+ }
+ break;
+ }
+
+ default:
+ BaseClass::RunTask( pTask);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PolicingBehavior::MaintainGoalPosition( void )
+{
+ Vector vecOrg = GetAbsOrigin();
+ Vector vecTarget = m_hPoliceGoal->GetAbsOrigin();
+
+ // Allow some slop on Z
+ if ( fabs(vecOrg.z - vecTarget.z) > 64 )
+ return true;
+
+ // Need to be very close on X/Y
+ if ( (vecOrg - vecTarget).Length2D() > 16 )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CAI_PolicingBehavior::ShouldKnockOutTarget( CBaseEntity *pTarget )
+{
+ if ( m_hPoliceGoal == NULL )
+ {
+ //NOTENOTE: This has been called before the behavior is actually active, or the goal has gone invalid
+ Assert(0);
+ return false;
+ }
+
+ bool bVisible = GetOuter()->FVisible( pTarget );
+ return m_hPoliceGoal->ShouldKnockOutTarget( pTarget->WorldSpaceCenter(), bVisible );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pTarget -
+//-----------------------------------------------------------------------------
+void CAI_PolicingBehavior::KnockOutTarget( CBaseEntity *pTarget )
+{
+ if ( m_hPoliceGoal == NULL )
+ {
+ //NOTENOTE: This has been called before the behavior is actually active, or the goal has gone invalid
+ Assert(0);
+ return;
+ }
+
+ m_hPoliceGoal->KnockOutTarget( pTarget );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CAI_PolicingBehavior::SelectSuppressSchedule( void )
+{
+ CBaseEntity *pTarget = m_hPoliceGoal->GetTarget();
+
+ m_flAggressiveTime = gpGlobals->curtime + 4.0f;
+
+ if ( m_bTargetIsHostile == false )
+ {
+ // Mark this as a valid target
+ m_bTargetIsHostile = true;
+
+ // Attack the target
+ GetOuter()->SetEnemy( pTarget );
+ GetOuter()->SetState( NPC_STATE_COMBAT );
+ GetOuter()->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin() );
+
+ HostSetBatonState( true );
+
+ // Remember that we're angry with the target
+ m_nNumWarnings = POLICE_MAX_WARNINGS;
+
+ // We need to let the system pickup the new enemy and deal with it on the next frame
+ return SCHED_COMBAT_FACE;
+ }
+
+ // If we're supposed to stand still, then we need to show aggression
+ if ( m_hPoliceGoal->ShouldRemainAtPost() )
+ {
+ // If we're off our mark, fight to it
+ if ( MaintainGoalPosition() )
+ {
+ return SCHED_CHASE_ENEMY;
+ }
+
+ //FIXME: This needs to be a more aggressive warning to the player
+ if ( m_flNextHarassTime < gpGlobals->curtime )
+ {
+ return SCHED_POLICE_WARN_TARGET;
+ }
+ else
+ {
+ return SCHED_COMBAT_FACE;
+ }
+ }
+
+ return SCHED_CHASE_ENEMY;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CAI_PolicingBehavior::SelectHarassSchedule( void )
+{
+ CBaseEntity *pTarget = m_hPoliceGoal->GetTarget();
+
+ m_flAggressiveTime = gpGlobals->curtime + 4.0f;
+
+ // If we just started to police, make sure we're on our mark
+ if ( MaintainGoalPosition() )
+ return SCHED_POLICE_RETURN_FROM_HARASS;
+
+ // Look at the target if they're too close
+ GetOuter()->AddLookTarget( pTarget, 0.5f, 5.0f );
+
+ // Say something if it's been long enough
+ if ( m_flNextHarassTime < gpGlobals->curtime )
+ {
+ // Gesture the player away
+ GetOuter()->SetTarget( pTarget );
+
+ // Send outputs for each level of warning
+ if ( m_nNumWarnings == 0 )
+ {
+ m_hPoliceGoal->FireWarningLevelOutput( 1 );
+ }
+ else if ( m_nNumWarnings == 1 )
+ {
+ m_hPoliceGoal->FireWarningLevelOutput( 2 );
+ }
+
+ if ( m_nNumWarnings < POLICE_MAX_WARNINGS )
+ {
+ m_nNumWarnings++;
+ }
+
+ // If we're over our limit, just suppress the offender
+ if ( m_nNumWarnings >= POLICE_MAX_WARNINGS )
+ {
+ if ( m_bTargetIsHostile == false )
+ {
+ // Mark the target as a valid target
+ m_bTargetIsHostile = true;
+
+ GetOuter()->SetEnemy( pTarget );
+ GetOuter()->SetState( NPC_STATE_COMBAT );
+ GetOuter()->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin() );
+ HostSetBatonState( true );
+
+ m_hPoliceGoal->FireWarningLevelOutput( 4 );
+
+ return SCHED_COMBAT_FACE;
+ }
+
+ if ( m_hPoliceGoal->ShouldRemainAtPost() == false )
+ return SCHED_CHASE_ENEMY;
+ }
+
+ // On our last warning, approach the target
+ if ( m_nNumWarnings == (POLICE_MAX_WARNINGS-1) )
+ {
+ m_hPoliceGoal->FireWarningLevelOutput( 3 );
+
+ GetOuter()->SetTarget( pTarget );
+
+ HostSetBatonState( true );
+
+ if ( m_hPoliceGoal->ShouldRemainAtPost() == false )
+ return SCHED_POLICE_HARASS_TARGET;
+ }
+
+ // Otherwise just verbally warn him
+ return SCHED_POLICE_WARN_TARGET;
+ }
+
+ return SCHED_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CAI_PolicingBehavior::SelectSchedule( void )
+{
+ CBaseEntity *pTarget = m_hPoliceGoal->GetTarget();
+
+ // Validate our target
+ if ( pTarget == NULL )
+ {
+ DevMsg( "ai_goal_police with NULL target entity!\n" );
+
+ // Turn us off
+ Disable();
+ return SCHED_NONE;
+ }
+
+ // Attack if we're supposed to
+ if ( ( m_flAggressiveTime >= gpGlobals->curtime ) && HasCondition( COND_CAN_MELEE_ATTACK1 ) )
+ {
+ return SCHED_MELEE_ATTACK1;
+ }
+
+ // See if we should immediately begin to attack our target
+ if ( HasCondition( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS ) )
+ {
+ return SelectSuppressSchedule();
+ }
+
+ int newSchedule = SCHED_NONE;
+
+ // See if we're harassing
+ if ( HasCondition( COND_POLICE_TARGET_TOO_CLOSE_HARASS ) )
+ {
+ newSchedule = SelectHarassSchedule();
+ }
+
+ // Return that schedule if it was found
+ if ( newSchedule != SCHED_NONE )
+ return newSchedule;
+
+ // If our enemy is set, fogeda'bout it!
+ if ( m_flAggressiveTime < gpGlobals->curtime )
+ {
+ // Return to your initial spot
+ if ( GetEnemy() )
+ {
+ GetOuter()->SetEnemy( NULL );
+ GetOuter()->SetState( NPC_STATE_ALERT );
+ GetOuter()->GetEnemies()->RefreshMemories();
+ }
+
+ HostSetBatonState( false );
+ m_bTargetIsHostile = false;
+ }
+
+ // If we just started to police, make sure we're on our mark
+ if ( MaintainGoalPosition() )
+ return SCHED_POLICE_RETURN_FROM_HARASS;
+
+ // If I've got my baton on, keep looking at the target
+ if ( HostBatonIsOn() )
+ return SCHED_POLICE_TRACK_TARGET;
+
+ // Re-align myself to the goal angles if I've strayed
+ if ( fabs(UTIL_AngleDiff( GetAbsAngles().y, m_hPoliceGoal->GetAbsAngles().y )) > 15 )
+ return SCHED_POLICE_FACE_ALONG_GOAL;
+
+ return SCHED_IDLE_STAND;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CAI_PolicingBehavior::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
+{
+ if ( failedSchedule == SCHED_CHASE_ENEMY )
+ {
+ // We've failed to chase our enemy, return to where we were came from
+ if ( MaintainGoalPosition() )
+ return SCHED_POLICE_RETURN_FROM_HARASS;
+
+ return SCHED_POLICE_WARN_TARGET;
+ }
+
+ return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
+}
+
+//-------------------------------------
+
+AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_PolicingBehavior )
+
+ DECLARE_CONDITION( COND_POLICE_TARGET_TOO_CLOSE_HARASS );
+ DECLARE_CONDITION( COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS );
+
+ DECLARE_TASK( TASK_POLICE_GET_PATH_TO_HARASS_GOAL );
+ DECLARE_TASK( TASK_POLICE_GET_PATH_TO_POLICE_GOAL );
+ DECLARE_TASK( TASK_POLICE_ANNOUNCE_HARASS );
+ DECLARE_TASK( TASK_POLICE_FACE_ALONG_GOAL );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_POLICE_WARN_TARGET,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_FACE_TARGET 0"
+ " TASK_POLICE_ANNOUNCE_HARASS 0"
+ " TASK_PLAY_SEQUENCE ACTIVITY:ACT_POLICE_HARASS1"
+ ""
+ " Interrupts"
+ " COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
+ );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_POLICE_HARASS_TARGET,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_FACE_TARGET 0"
+ " TASK_POLICE_GET_PATH_TO_HARASS_GOAL 64"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " TASK_POLICE_ANNOUNCE_HARASS 0"
+ " TASK_PLAY_SEQUENCE ACTIVITY:ACT_POLICE_HARASS1"
+ ""
+ " Interrupts"
+ " COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
+ );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_POLICE_SUPPRESS_TARGET,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_FACE_TARGET 0"
+ " TASK_POLICE_ANNOUNCE_HARASS 0"
+ " TASK_PLAY_SEQUENCE ACTIVITY:ACT_POLICE_HARASS1"
+ ""
+ " Interrupts"
+ );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_POLICE_RETURN_FROM_HARASS,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_POLICE_GET_PATH_TO_POLICE_GOAL 16"
+ " TASK_WALK_PATH 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " TASK_STOP_MOVING 0"
+ ""
+ " Interrupts"
+ " COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
+ );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_POLICE_TRACK_TARGET,
+
+ " Tasks"
+ " TASK_FACE_TARGET 0"
+ ""
+ " Interrupts"
+ " COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
+ );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_POLICE_FACE_ALONG_GOAL,
+
+ " Tasks"
+ " TASK_WAIT_RANDOM 2"
+ " TASK_POLICE_FACE_ALONG_GOAL 0"
+ ""
+ " Interrupts"
+ " COND_POLICE_TARGET_TOO_CLOSE_SUPPRESS"
+ );
+
+AI_END_CUSTOM_SCHEDULE_PROVIDER()
|