diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl2/ai_behavior_police.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/hl2/ai_behavior_police.cpp')
| -rw-r--r-- | game/server/hl2/ai_behavior_police.cpp | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/game/server/hl2/ai_behavior_police.cpp b/game/server/hl2/ai_behavior_police.cpp new file mode 100644 index 0000000..eb1b45b --- /dev/null +++ b/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() |