You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
784 lines
21 KiB
784 lines
21 KiB
//========= 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; |
|
m_hPoliceGoal = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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()
|
|
|