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/ai_behavior_fear.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/ai_behavior_fear.cpp')
| -rw-r--r-- | mp/src/game/server/ai_behavior_fear.cpp | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/mp/src/game/server/ai_behavior_fear.cpp b/mp/src/game/server/ai_behavior_fear.cpp new file mode 100644 index 00000000..57f1bd7d --- /dev/null +++ b/mp/src/game/server/ai_behavior_fear.cpp @@ -0,0 +1,552 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "ai_motor.h"
+#include "ai_behavior_fear.h"
+#include "ai_hint.h"
+#include "ai_navigator.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+BEGIN_DATADESC( CAI_FearBehavior )
+ DEFINE_FIELD( m_flTimeToSafety, FIELD_TIME ),
+ DEFINE_FIELD( m_flTimePlayerLastVisible, FIELD_TIME ),
+ DEFINE_FIELD( m_hSafePlaceHint, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hMovingToHint, FIELD_EHANDLE ),
+ DEFINE_EMBEDDED( m_SafePlaceMoveMonitor ),
+ DEFINE_FIELD( m_flDeferUntil, FIELD_TIME ),
+END_DATADESC();
+
+#define BEHAVIOR_FEAR_SAFETY_TIME 5
+#define FEAR_SAFE_PLACE_TOLERANCE 36.0f
+#define FEAR_ENEMY_TOLERANCE_CLOSE_DIST_SQR Square(300.0f) // (25 feet)
+#define FEAR_ENEMY_TOLERANCE_TOO_CLOSE_DIST_SQR Square( 60.0f ) // (5 Feet)
+
+ConVar ai_enable_fear_behavior( "ai_enable_fear_behavior", "1" );
+
+ConVar ai_fear_player_dist("ai_fear_player_dist", "720" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CAI_FearBehavior::CAI_FearBehavior()
+{
+ ReleaseAllHints();
+ m_SafePlaceMoveMonitor.ClearMark();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::Precache( void )
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pTask -
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::StartTask( const Task_t *pTask )
+{
+ switch( pTask->iTask )
+ {
+ case TASK_FEAR_IN_SAFE_PLACE:
+ // We've arrived! Lock the hint and set the marker. we're safe for now.
+ m_hSafePlaceHint = m_hMovingToHint;
+ m_hSafePlaceHint->Lock( GetOuter() );
+ m_SafePlaceMoveMonitor.SetMark( GetOuter(), FEAR_SAFE_PLACE_TOLERANCE );
+ TaskComplete();
+ break;
+
+ case TASK_FEAR_GET_PATH_TO_SAFETY_HINT:
+ // Using TaskInterrupt() optimizations. See RunTask().
+ break;
+
+ case TASK_FEAR_WAIT_FOR_SAFETY:
+ m_flTimeToSafety = gpGlobals->curtime + BEHAVIOR_FEAR_SAFETY_TIME;
+ break;
+
+ default:
+ BaseClass::StartTask( pTask );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pTask -
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::RunTask( const Task_t *pTask )
+{
+ switch( pTask->iTask )
+ {
+ case TASK_FEAR_WAIT_FOR_SAFETY:
+ if( HasCondition(COND_SEE_ENEMY) )
+ {
+ m_flTimeToSafety = gpGlobals->curtime + BEHAVIOR_FEAR_SAFETY_TIME;
+ }
+ else
+ {
+ if( gpGlobals->curtime > m_flTimeToSafety )
+ {
+ TaskComplete();
+ }
+ }
+ break;
+
+ case TASK_FEAR_GET_PATH_TO_SAFETY_HINT:
+ {
+ switch( GetOuter()->GetTaskInterrupt() )
+ {
+ case 0:// Find the hint node
+ {
+ ReleaseAllHints();
+ CAI_Hint *pHint = FindFearWithdrawalDest();
+
+ if( pHint == NULL )
+ {
+ TaskFail("Fear: Couldn't find hint node\n");
+ m_flDeferUntil = gpGlobals->curtime + 3.0f;// Don't bang the hell out of this behavior. If we don't find a node, take a short break and run regular AI.
+ }
+ else
+ {
+ m_hMovingToHint.Set( pHint );
+ GetOuter()->TaskInterrupt();
+ }
+ }
+ break;
+
+ case 1:// Do the pathfinding.
+ {
+ Assert( m_hMovingToHint != NULL );
+
+ AI_NavGoal_t goal(m_hMovingToHint->GetAbsOrigin());
+ goal.pTarget = NULL;
+ if( GetNavigator()->SetGoal( goal ) == false )
+ {
+ m_hMovingToHint.Set( NULL );
+ // Do whatever we'd want to do if we can't find a path
+ /*
+ Msg("Can't path to the Fear Hint!\n");
+
+ AI_NavGoal_t nearGoal( GOALTYPE_LOCATION_NEAREST_NODE, m_hRallyPoint->GetAbsOrigin(), AIN_DEF_ACTIVITY, 256 );
+ if ( GetNavigator()->SetGoal( nearGoal, AIN_CLEAR_PREVIOUS_STATE ) )
+ {
+ //FIXME: HACK! The internal pathfinding is setting this without our consent, so override it!
+ ClearCondition( COND_TASK_FAILED );
+ GetNavigator()->SetArrivalDirection( m_hRallyPoint->GetAbsAngles() );
+ TaskComplete();
+ return;
+ }
+ */
+ }
+ else
+ {
+ GetNavigator()->SetArrivalDirection( m_hMovingToHint->GetAbsAngles() );
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ default:
+ BaseClass::RunTask( pTask );
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : TRUE if I have an enemy and that enemy would attack me if it could
+// Notes : Returns FALSE if the enemy is neutral or likes me.
+//-----------------------------------------------------------------------------
+bool CAI_FearBehavior::EnemyDislikesMe()
+{
+ CBaseEntity *pEnemy = GetEnemy();
+
+ if( pEnemy == NULL )
+ return false;
+
+ if( pEnemy->MyNPCPointer() == NULL )
+ return false;
+
+ Disposition_t disposition = pEnemy->MyNPCPointer()->IRelationType(GetOuter());
+
+ Assert(disposition != D_ER);
+
+ if( disposition >= D_LI )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// This place is definitely no longer safe. Stop picking it for a while.
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::MarkAsUnsafe()
+{
+ Assert( m_hSafePlaceHint );
+
+ // Disable the node to stop anyone from picking it for a while.
+ m_hSafePlaceHint->DisableForSeconds( 5.0f );
+}
+
+//-----------------------------------------------------------------------------
+// Am I in safe place from my enemy?
+//-----------------------------------------------------------------------------
+bool CAI_FearBehavior::IsInASafePlace()
+{
+ // No safe place in mind.
+ if( !m_SafePlaceMoveMonitor.IsMarkSet() )
+ return false;
+
+ // I have a safe place, but I'm not there.
+ if( m_SafePlaceMoveMonitor.TargetMoved(GetOuter()) )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::SpoilSafePlace()
+{
+ m_SafePlaceMoveMonitor.ClearMark();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::ReleaseAllHints()
+{
+ if( m_hSafePlaceHint )
+ {
+ // If I have a safe place, unlock it for others.
+ m_hSafePlaceHint->Unlock();
+
+ // Don't make it available right away. I probably left for a good reason.
+ // We also don't want to oscillate
+ m_hSafePlaceHint->DisableForSeconds( 4.0f );
+ m_hSafePlaceHint = NULL;
+ }
+
+ if( m_hMovingToHint )
+ {
+ m_hMovingToHint->Unlock();
+ m_hMovingToHint = NULL;
+ }
+
+ m_SafePlaceMoveMonitor.ClearMark();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+// Notes : This behavior runs when I have an enemy that I fear, but who
+// does NOT hate or fear me (meaning they aren't going to fight me)
+//-----------------------------------------------------------------------------
+bool CAI_FearBehavior::CanSelectSchedule()
+{
+ if( !GetOuter()->IsInterruptable() )
+ return false;
+
+ if( m_flDeferUntil > gpGlobals->curtime )
+ return false;
+
+ CBaseEntity *pEnemy = GetEnemy();
+
+ if( pEnemy == NULL )
+ return false;
+
+ //if( !HasCondition(COND_SEE_PLAYER) )
+ // return false;
+
+ if( !ai_enable_fear_behavior.GetBool() )
+ return false;
+
+ if( GetOuter()->IRelationType(pEnemy) != D_FR )
+ return false;
+
+ if( !pEnemy->ClassMatches("npc_hunter") )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::GatherConditions()
+{
+ BaseClass::GatherConditions();
+
+ ClearCondition( COND_FEAR_ENEMY_CLOSE );
+ ClearCondition( COND_FEAR_ENEMY_TOO_CLOSE );
+ if( GetEnemy() )
+ {
+ float flEnemyDistSqr = GetAbsOrigin().DistToSqr(GetEnemy()->GetAbsOrigin());
+
+ if( flEnemyDistSqr < FEAR_ENEMY_TOLERANCE_TOO_CLOSE_DIST_SQR )
+ {
+ SetCondition( COND_FEAR_ENEMY_TOO_CLOSE );
+ if( IsInASafePlace() )
+ {
+ SpoilSafePlace();
+ }
+ }
+ else if( flEnemyDistSqr < FEAR_ENEMY_TOLERANCE_CLOSE_DIST_SQR && GetEnemy()->GetEnemy() == GetOuter() )
+ {
+ // Only become scared of an enemy at this range if they're my enemy, too
+ SetCondition( COND_FEAR_ENEMY_CLOSE );
+ if( IsInASafePlace() )
+ {
+ SpoilSafePlace();
+ }
+ }
+ }
+
+ ClearCondition(COND_FEAR_SEPARATED_FROM_PLAYER);
+
+ // Check for separation from the player
+ // -The player is farther away than 60 feet
+ // -I haven't seen the player in 2 seconds
+ //
+ // Here's the distance check:
+ CBasePlayer *pPlayer = AI_GetSinglePlayer();
+ if( pPlayer != NULL && GetAbsOrigin().DistToSqr(pPlayer->GetAbsOrigin()) >= Square( ai_fear_player_dist.GetFloat() * 1.5f ) )
+ {
+ SetCondition(COND_FEAR_SEPARATED_FROM_PLAYER);
+ }
+
+ // Here's the visibility check. We can't skip this because it's time-sensitive
+ if( GetOuter()->FVisible(pPlayer) )
+ {
+ m_flTimePlayerLastVisible = gpGlobals->curtime;
+ }
+ else
+ {
+ if( gpGlobals->curtime - m_flTimePlayerLastVisible >= 2.0f )
+ {
+ SetCondition(COND_FEAR_SEPARATED_FROM_PLAYER);
+ }
+ }
+
+ if( HasCondition(COND_FEAR_SEPARATED_FROM_PLAYER) )
+ {
+ //Msg("I am separated from player\n");
+
+ if( IsInASafePlace() )
+ {
+ SpoilSafePlace();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::BeginScheduleSelection()
+{
+ if( m_hSafePlaceHint )
+ {
+ // We think we're safe. Is it true?
+ if( !IsInASafePlace() )
+ {
+ // no! So mark it so.
+ ReleaseAllHints();
+ }
+ }
+
+ m_flTimePlayerLastVisible = gpGlobals->curtime;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::EndScheduleSelection()
+{
+ // We don't have to release our hints or markers or anything here.
+ // Just because we ran other AI for a while doesn't mean we aren't still in a safe place.
+ //ReleaseAllHints();
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+// Notes : If fear behavior is running at all, we know we're afraid of our enemy
+//-----------------------------------------------------------------------------
+int CAI_FearBehavior::SelectSchedule()
+{
+ bool bInSafePlace = IsInASafePlace();
+
+ if( !HasCondition(COND_HEAR_DANGER) )
+ {
+ if( !bInSafePlace )
+ {
+ // Always move to a safe place if we're not running from a danger sound
+ return SCHED_FEAR_MOVE_TO_SAFE_PLACE;
+ }
+ else
+ {
+ // We ARE in a safe place
+ if( HasCondition(COND_CAN_RANGE_ATTACK1) )
+ return SCHED_RANGE_ATTACK1;
+
+ return SCHED_FEAR_STAY_IN_SAFE_PLACE;
+ }
+ }
+
+ return BaseClass::SelectSchedule();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CAI_FearBehavior::BuildScheduleTestBits()
+{
+ BaseClass::BuildScheduleTestBits();
+
+ if( GetOuter()->GetState() != NPC_STATE_SCRIPT )
+ {
+ // Stop doing ANYTHING if we get scared.
+ //GetOuter()->SetCustomInterruptCondition( COND_HEAR_DANGER );
+
+ if( !IsCurSchedule(SCHED_FEAR_MOVE_TO_SAFE_PLACE_RETRY, false) && !IsCurSchedule(SCHED_FEAR_MOVE_TO_SAFE_PLACE, false) )
+ {
+ GetOuter()->SetCustomInterruptCondition( GetClassScheduleIdSpace()->ConditionLocalToGlobal(COND_FEAR_SEPARATED_FROM_PLAYER) );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int CAI_FearBehavior::TranslateSchedule( int scheduleType )
+{
+ switch( scheduleType )
+ {
+ case SCHED_FEAR_MOVE_TO_SAFE_PLACE:
+ if( HasCondition(COND_FEAR_ENEMY_TOO_CLOSE) )
+ {
+ // If I'm moving to a safe place AND have an enemy too close to me,
+ // make the move to safety while ignoring the condition.
+ // this stops an oscillation
+ // IS THIS CODE EVER EVEN BEING CALLED? (sjb)
+ return SCHED_FEAR_MOVE_TO_SAFE_PLACE_RETRY;
+ }
+ break;
+ }
+
+ return BaseClass::TranslateSchedule( scheduleType );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CAI_Hint *CAI_FearBehavior::FindFearWithdrawalDest()
+{
+ CAI_Hint *pHint;
+ CHintCriteria hintCriteria;
+ CAI_BaseNPC *pOuter = GetOuter();
+
+ Assert(pOuter != NULL);
+
+ hintCriteria.AddHintType( HINT_PLAYER_ALLY_FEAR_DEST );
+ hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE_TO_PLAYER | bits_HINT_NOT_CLOSE_TO_ENEMY /*| bits_HINT_NODE_IN_VIEWCONE | bits_HINT_NPC_IN_NODE_FOV*/ );
+ hintCriteria.AddIncludePosition( AI_GetSinglePlayer()->GetAbsOrigin(), ( ai_fear_player_dist.GetFloat() ) );
+
+ pHint = CAI_HintManager::FindHint( pOuter, hintCriteria );
+
+ if( pHint )
+ {
+ // Reserve this node while I try to get to it. When I get there I will lock it.
+ // Otherwise, if I fail to get there, the node will come available again soon.
+ pHint->DisableForSeconds( 4.0f );
+ }
+#if 0
+ else
+ {
+ Msg("DID NOT FIND HINT\n");
+ NDebugOverlay::Cross3D( GetOuter()->WorldSpaceCenter(), 32, 255, 255, 0, false, 10.0f );
+ }
+#endif
+
+ return pHint;
+}
+
+AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_FearBehavior )
+
+ DECLARE_TASK( TASK_FEAR_GET_PATH_TO_SAFETY_HINT )
+ DECLARE_TASK( TASK_FEAR_WAIT_FOR_SAFETY )
+ DECLARE_TASK( TASK_FEAR_IN_SAFE_PLACE )
+
+ DECLARE_CONDITION( COND_FEAR_ENEMY_CLOSE )
+ DECLARE_CONDITION( COND_FEAR_ENEMY_TOO_CLOSE )
+ DECLARE_CONDITION( COND_FEAR_SEPARATED_FROM_PLAYER )
+
+ //===============================================
+ //===============================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_FEAR_MOVE_TO_SAFE_PLACE,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RUN_FROM_ENEMY"
+ " TASK_FEAR_GET_PATH_TO_SAFETY_HINT 0"
+ " TASK_RUN_PATH 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
+ " TASK_FEAR_IN_SAFE_PLACE 0"
+ " TASK_SET_SCHEDULE SCHEDULE:SCHED_FEAR_STAY_IN_SAFE_PLACE"
+ ""
+ " Interrupts"
+ ""
+ " COND_HEAR_DANGER"
+ " COND_NEW_ENEMY"
+ " COND_FEAR_ENEMY_TOO_CLOSE"
+ );
+
+ DEFINE_SCHEDULE
+ (
+ SCHED_FEAR_MOVE_TO_SAFE_PLACE_RETRY,
+
+ " Tasks"
+ " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RUN_FROM_ENEMY"
+ " TASK_FEAR_GET_PATH_TO_SAFETY_HINT 0"
+ " TASK_RUN_PATH 0"
+ " TASK_WAIT_FOR_MOVEMENT 0"
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
+ " TASK_FEAR_IN_SAFE_PLACE 0"
+ " TASK_SET_SCHEDULE SCHEDULE:SCHED_FEAR_STAY_IN_SAFE_PLACE"
+ ""
+ " Interrupts"
+ ""
+ " COND_HEAR_DANGER"
+ " COND_NEW_ENEMY"
+ );
+
+ //===============================================
+ //===============================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_FEAR_STAY_IN_SAFE_PLACE,
+
+ " Tasks"
+ " TASK_FEAR_WAIT_FOR_SAFETY 0"
+ ""
+ " Interrupts"
+ ""
+ " COND_NEW_ENEMY"
+ " COND_HEAR_DANGER"
+ " COND_FEAR_ENEMY_CLOSE"
+ " COND_FEAR_ENEMY_TOO_CLOSE"
+ " COND_CAN_RANGE_ATTACK1"
+ " COND_FEAR_SEPARATED_FROM_PLAYER"
+ );
+
+
+AI_END_CUSTOM_SCHEDULE_PROVIDER()
|