mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-14 20:56:56 +00:00
1
This commit is contained in:
@@ -0,0 +1,621 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: FIXME: This will ultimately become a more generic implementation
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "ai_memory.h"
|
||||
#include "ai_speech.h"
|
||||
#include "ai_behavior.h"
|
||||
#include "ai_navigator.h"
|
||||
#include "ai_playerally.h"
|
||||
#include "ai_behavior_follow.h"
|
||||
#include "ai_moveprobe.h"
|
||||
|
||||
#include "ai_behavior_alyx_injured.h"
|
||||
|
||||
ConVar g_debug_injured_follow( "g_debug_injured_follow", "0" );
|
||||
ConVar injured_help_plee_range( "injured_help_plee_range", "256" );
|
||||
|
||||
#define TLK_INJURED_FOLLOW_TOO_FAR "TLK_INJURED_FOLLOW_TOO_FAR"
|
||||
|
||||
BEGIN_DATADESC( CAI_BehaviorAlyxInjured )
|
||||
DEFINE_FIELD( m_flNextWarnTime, FIELD_TIME ),
|
||||
// m_ActivityMap
|
||||
|
||||
END_DATADESC();
|
||||
|
||||
Activity ACT_INJURED_COWER;
|
||||
Activity ACT_GESTURE_INJURED_COWER_FLINCH;
|
||||
|
||||
#define COVER_DISTANCE 128.0f // Distance behind target to find cover
|
||||
#define MIN_ENEMY_MOB 3 // Number of enemies considerd overwhelming
|
||||
#define MAX_DIST_FROM_FOLLOW_TARGET 256 // If the follow target is farther than this, the NPC will run to it
|
||||
|
||||
//=============================================================================
|
||||
|
||||
CAI_BehaviorAlyxInjured::CAI_BehaviorAlyxInjured( void ) : m_flNextWarnTime( 0.0f )
|
||||
{
|
||||
SetDefLessFunc( m_ActivityMap );
|
||||
}
|
||||
|
||||
struct ActivityMap_t
|
||||
{
|
||||
Activity activity;
|
||||
Activity translation;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_BehaviorAlyxInjured::PopulateActivityMap( void )
|
||||
{
|
||||
// Maps one activity to a translated one
|
||||
ActivityMap_t map[] =
|
||||
{
|
||||
// Runs
|
||||
{ ACT_RUN, ACT_RUN_HURT },
|
||||
{ ACT_RUN_AIM, ACT_RUN_AIM }, // FIMXE: No appropriate temp anim right now!
|
||||
{ ACT_RUN_CROUCH, ACT_RUN_HURT },
|
||||
{ ACT_RUN_CROUCH_AIM, ACT_RUN_HURT },
|
||||
{ ACT_RUN_PROTECTED, ACT_RUN_HURT },
|
||||
{ ACT_RUN_RELAXED, ACT_RUN_HURT },
|
||||
{ ACT_RUN_STIMULATED, ACT_RUN_HURT },
|
||||
{ ACT_RUN_AGITATED, ACT_RUN_HURT },
|
||||
{ ACT_RUN_AIM_RELAXED, ACT_RUN_AIM_RELAXED }, // FIMXE: No appropriate temp anim right now!
|
||||
{ ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_STIMULATED }, // FIMXE: No appropriate temp anim right now!
|
||||
{ ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_AGITATED }, // FIMXE: No appropriate temp anim right now!
|
||||
{ ACT_RUN_HURT, ACT_RUN_HURT },
|
||||
|
||||
// Walks
|
||||
{ ACT_WALK, ACT_WALK_HURT },
|
||||
{ ACT_WALK_AIM, ACT_WALK_HURT },
|
||||
{ ACT_WALK_CROUCH, ACT_WALK_HURT },
|
||||
{ ACT_WALK_CROUCH_AIM, ACT_WALK_HURT },
|
||||
{ ACT_WALK_RELAXED, ACT_WALK_HURT },
|
||||
{ ACT_WALK_STIMULATED, ACT_WALK_HURT },
|
||||
{ ACT_WALK_AGITATED, ACT_WALK_HURT },
|
||||
{ ACT_WALK_AIM_RELAXED, ACT_WALK_HURT },
|
||||
{ ACT_WALK_AIM_STIMULATED, ACT_WALK_HURT },
|
||||
{ ACT_WALK_AIM_AGITATED, ACT_WALK_HURT },
|
||||
{ ACT_WALK_HURT, ACT_WALK_HURT },
|
||||
|
||||
{ ACT_IDLE, ACT_IDLE_HURT },
|
||||
{ ACT_COVER_LOW, ACT_INJURED_COWER },
|
||||
{ ACT_COWER, ACT_INJURED_COWER },
|
||||
};
|
||||
|
||||
// Clear the map
|
||||
m_ActivityMap.RemoveAll();
|
||||
|
||||
// Add all translations
|
||||
for ( int i = 0; i < ARRAYSIZE( map ); i++ )
|
||||
{
|
||||
Assert( m_ActivityMap.Find( map[i].activity ) == m_ActivityMap.InvalidIndex() );
|
||||
m_ActivityMap.Insert( map[i].activity, map[i].translation );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Populate the list after save/load
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_BehaviorAlyxInjured::OnRestore( void )
|
||||
{
|
||||
PopulateActivityMap();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Populate the list on spawn
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_BehaviorAlyxInjured::Spawn( void )
|
||||
{
|
||||
PopulateActivityMap();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Get the flinch activity for us to play
|
||||
// Input : bHeavyDamage -
|
||||
// bGesture -
|
||||
// Output : Activity
|
||||
//-----------------------------------------------------------------------------
|
||||
Activity CAI_BehaviorAlyxInjured::GetFlinchActivity( bool bHeavyDamage, bool bGesture )
|
||||
{
|
||||
//
|
||||
if ( ( bGesture == false ) || ( GetOuter()->GetActivity() != ACT_COWER ) )
|
||||
return BaseClass::GetFlinchActivity( bHeavyDamage, bGesture );
|
||||
|
||||
// Translate the flinch if we're cowering
|
||||
return ACT_GESTURE_INJURED_COWER_FLINCH;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : nActivity -
|
||||
//-----------------------------------------------------------------------------
|
||||
Activity CAI_BehaviorAlyxInjured::NPC_TranslateActivity( Activity nActivity )
|
||||
{
|
||||
// Find out what the base class wants to do with the activity
|
||||
Activity nNewActivity = BaseClass::NPC_TranslateActivity( nActivity );
|
||||
|
||||
// Look it up in the translation map
|
||||
int nIndex = m_ActivityMap.Find( nNewActivity );
|
||||
|
||||
if ( m_ActivityMap.IsValidIndex( nIndex ) )
|
||||
return m_ActivityMap[nIndex];
|
||||
|
||||
return nNewActivity;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Determines if Alyx should run away from enemies or stay put
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_BehaviorAlyxInjured::ShouldRunToCover( void )
|
||||
{
|
||||
Vector vecRetreatPos;
|
||||
float flRetreatRadius = 128.0f;
|
||||
|
||||
// See how far off from our cover position we are
|
||||
if ( FindCoverFromEnemyBehindTarget( GetFollowTarget(), flRetreatRadius, &vecRetreatPos ) )
|
||||
{
|
||||
float flDestDistSqr = ( GetOuter()->WorldSpaceCenter() - vecRetreatPos ).LengthSqr();
|
||||
if ( flDestDistSqr > Square( flRetreatRadius ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: See if we need to follow our goal
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_BehaviorAlyxInjured::ShouldRunToFollowGoal( void )
|
||||
{
|
||||
// If we're too far from our follow target, we need to chase after them
|
||||
float flDistToFollowGoalSqr = ( GetOuter()->GetAbsOrigin() - GetFollowTarget()->GetAbsOrigin() ).LengthSqr();
|
||||
if ( flDistToFollowGoalSqr > Square(MAX_DIST_FROM_FOLLOW_TARGET) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Translate base schedules into overridden forms
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_BehaviorAlyxInjured::TranslateSchedule( int scheduleType )
|
||||
{
|
||||
switch( scheduleType )
|
||||
{
|
||||
case SCHED_RUN_FROM_ENEMY:
|
||||
case SCHED_RUN_FROM_ENEMY_MOB:
|
||||
{
|
||||
// Get under cover if we're able to
|
||||
if ( ShouldRunToCover() )
|
||||
return SCHED_INJURED_RUN_FROM_ENEMY;
|
||||
|
||||
// Run to our follow goal if we're too far away from it
|
||||
if ( ShouldRunToFollowGoal() )
|
||||
return SCHED_FOLLOW;
|
||||
|
||||
// Cower if surrounded
|
||||
if ( HasCondition( COND_INJURED_OVERWHELMED ) )
|
||||
return SCHED_INJURED_COWER;
|
||||
|
||||
// Face our enemies
|
||||
return SCHED_INJURED_FEAR_FACE;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCHED_RUN_FROM_ENEMY_FALLBACK:
|
||||
return SCHED_INJURED_COWER;
|
||||
break;
|
||||
}
|
||||
|
||||
return BaseClass::TranslateSchedule( scheduleType );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Pick up failure cases and handle them
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_BehaviorAlyxInjured::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
|
||||
{
|
||||
// Failed schedules
|
||||
switch( failedSchedule )
|
||||
{
|
||||
case SCHED_RUN_FROM_ENEMY:
|
||||
case SCHED_RUN_FROM_ENEMY_MOB:
|
||||
case SCHED_FOLLOW:
|
||||
return SCHED_INJURED_COWER;
|
||||
}
|
||||
|
||||
// Failed tasks
|
||||
switch( failedTask )
|
||||
{
|
||||
case TASK_FIND_COVER_FROM_ENEMY:
|
||||
case TASK_FIND_INJURED_COVER_FROM_ENEMY:
|
||||
|
||||
// Only cower if we're already near enough to our follow target
|
||||
float flDistToFollowTargetSqr = ( GetOuter()->GetAbsOrigin() - GetFollowTarget()->GetAbsOrigin() ).LengthSqr();
|
||||
if ( flDistToFollowTargetSqr > Square( 256 ) )
|
||||
return SCHED_FOLLOW;
|
||||
|
||||
return SCHED_INJURED_COWER;
|
||||
break;
|
||||
}
|
||||
|
||||
return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Find the general direction enemies are coming towards us at
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_BehaviorAlyxInjured::FindThreatDirection2D( const Vector &vecSource, Vector *vecOut )
|
||||
{
|
||||
// Find the general direction our threat is coming from
|
||||
bool bValid = false;
|
||||
Vector vecScratch;
|
||||
AIEnemiesIter_t iter;
|
||||
|
||||
// Iterate through all known enemies
|
||||
for( AI_EnemyInfo_t *pMemory = GetOuter()->GetEnemies()->GetFirst(&iter); pMemory != NULL; pMemory = GetOuter()->GetEnemies()->GetNext(&iter) )
|
||||
{
|
||||
if ( pMemory == NULL || pMemory->hEnemy == NULL )
|
||||
continue;
|
||||
|
||||
vecScratch = ( vecSource - pMemory->hEnemy->WorldSpaceCenter() );
|
||||
VectorNormalize( vecScratch );
|
||||
|
||||
(*vecOut) += vecScratch;
|
||||
bValid = true;
|
||||
}
|
||||
|
||||
// Find the general direction
|
||||
(*vecOut).z = 0.0f;
|
||||
VectorNormalize( (*vecOut) );
|
||||
return bValid;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Find a position that hides us from our threats while interposing the
|
||||
// target entity between us and the threat
|
||||
// Input : pTarget - entity to hide behind
|
||||
// flRadius - Radius around the target to search
|
||||
// *vecOut - position
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_BehaviorAlyxInjured::FindCoverFromEnemyBehindTarget( CBaseEntity *pTarget, float flRadius, Vector *vecOut )
|
||||
{
|
||||
if ( pTarget == NULL )
|
||||
return false;
|
||||
|
||||
Vector vecTargetPos = pTarget->GetAbsOrigin();
|
||||
Vector vecThreatDir = vec3_origin;
|
||||
|
||||
// Find our threat direction and base our cover on that
|
||||
if ( FindThreatDirection2D( vecTargetPos, &vecThreatDir ) )
|
||||
{
|
||||
// Get a general location for taking cover
|
||||
Vector vecTestPos = vecTargetPos + ( vecThreatDir * flRadius );
|
||||
|
||||
if ( g_debug_injured_follow.GetBool() )
|
||||
{
|
||||
NDebugOverlay::HorzArrow( GetOuter()->GetAbsOrigin(), vecTestPos, 8.0f, 255, 255, 0, 32, true, 2.0f );
|
||||
}
|
||||
|
||||
// Make sure we never move towards our threat to get to cover!
|
||||
Vector vecMoveDir = GetOuter()->GetAbsOrigin() - vecTestPos;
|
||||
VectorNormalize( vecMoveDir );
|
||||
float flDotToCover = DotProduct( vecMoveDir, vecThreatDir );
|
||||
if ( flDotToCover > 0.0f )
|
||||
{
|
||||
if ( g_debug_injured_follow.GetBool() )
|
||||
{
|
||||
NDebugOverlay::HorzArrow( GetOuter()->GetAbsOrigin(), vecTestPos, 8.0f, 255, 0, 0, 32, true, 2.0f );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AIMoveTrace_t moveTrace;
|
||||
GetOuter()->GetMoveProbe()->MoveLimit( NAV_GROUND,
|
||||
GetOuter()->GetAbsOrigin(),
|
||||
vecTestPos,
|
||||
MASK_SOLID_BRUSHONLY,
|
||||
NULL,
|
||||
0,
|
||||
&moveTrace );
|
||||
|
||||
bool bWithinRangeToGoal = ( moveTrace.vEndPosition - vecTestPos ).Length2DSqr() < Square( GetOuter()->GetHullWidth() * 3.0f );
|
||||
bool bCanStandAtGoal = GetOuter()->GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, MASK_SOLID_BRUSHONLY );
|
||||
|
||||
if ( bWithinRangeToGoal == false || bCanStandAtGoal == false )
|
||||
{
|
||||
if ( g_debug_injured_follow.GetBool() )
|
||||
{
|
||||
NDebugOverlay::SweptBox( GetOuter()->GetAbsOrigin(), vecTestPos, GetOuter()->GetHullMins(), GetOuter()->GetHullMaxs(), vec3_angle, 255, 0, 0, 0, 2.0f );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accept it
|
||||
*vecOut = moveTrace.vEndPosition;
|
||||
|
||||
if ( g_debug_injured_follow.GetBool() )
|
||||
{
|
||||
NDebugOverlay::SweptBox( GetOuter()->GetAbsOrigin(), (*vecOut), GetOuter()->GetHullMins(), GetOuter()->GetHullMaxs(), vec3_angle, 0, 255, 0, 0, 2.0f );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pTask -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_BehaviorAlyxInjured::StartTask( const Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_FIND_COVER_FROM_ENEMY:
|
||||
{
|
||||
CBaseEntity *pLeader = GetFollowTarget();
|
||||
if ( !pLeader )
|
||||
{
|
||||
BaseClass::StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
|
||||
// Find a position behind our follow target
|
||||
Vector coverPos = vec3_invalid;
|
||||
if ( FindCoverFromEnemyBehindTarget( pLeader, COVER_DISTANCE, &coverPos ) )
|
||||
{
|
||||
AI_NavGoal_t goal( GOALTYPE_LOCATION, coverPos, ACT_RUN, AIN_HULL_TOLERANCE, AIN_DEF_FLAGS );
|
||||
GetOuter()->GetNavigator()->SetGoal( goal );
|
||||
GetOuter()->m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
|
||||
TaskComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
// Couldn't find anything
|
||||
TaskFail( FAIL_NO_COVER );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BaseClass::StartTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Whether or not Alyx is injured
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CAI_BehaviorAlyxInjured::IsInjured( void ) const
|
||||
{
|
||||
return IsAlyxInInjuredMode();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_BehaviorAlyxInjured::GatherConditions( void )
|
||||
{
|
||||
BaseClass::GatherConditions();
|
||||
|
||||
// Always stomp over this
|
||||
ClearCondition( COND_INJURED_TOO_FAR_FROM_PLAYER );
|
||||
ClearCondition( COND_INJURED_OVERWHELMED );
|
||||
|
||||
// See if we're overwhelmed by foes
|
||||
if ( NumKnownEnemiesInRadius( GetOuter()->GetAbsOrigin(), COVER_DISTANCE ) >= MIN_ENEMY_MOB )
|
||||
{
|
||||
SetCondition( COND_INJURED_OVERWHELMED );
|
||||
}
|
||||
|
||||
// Determines whether we consider ourselves in danger
|
||||
bool bInDanger = ( HasCondition( COND_LIGHT_DAMAGE ) ||
|
||||
HasCondition( COND_HEAVY_DAMAGE ) ||
|
||||
HasCondition( COND_INJURED_OVERWHELMED ) );
|
||||
|
||||
// See if we're too far away from the player and in danger
|
||||
if ( AI_IsSinglePlayer() && bInDanger )
|
||||
{
|
||||
bool bWarnPlayer = false;
|
||||
|
||||
// This only works in single-player
|
||||
CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
|
||||
if ( pPlayer != NULL )
|
||||
{
|
||||
// FIXME: This distance may need to be the length of the shortest walked path between the follower and the target
|
||||
|
||||
// Get our approximate distance to the player
|
||||
float flDistToPlayer = UTIL_DistApprox2D( GetOuter()->GetAbsOrigin(), pPlayer->GetAbsOrigin() );
|
||||
if ( flDistToPlayer > injured_help_plee_range.GetFloat() )
|
||||
{
|
||||
bWarnPlayer = true;
|
||||
}
|
||||
else if ( flDistToPlayer > (injured_help_plee_range.GetFloat()*0.5f) && HasCondition( COND_SEE_PLAYER ) == false )
|
||||
{
|
||||
// Cut our distance in half if we can't see the player
|
||||
bWarnPlayer = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Yell for help!
|
||||
if ( bWarnPlayer )
|
||||
{
|
||||
// FIXME: This should be routed through the normal speaking code with a system to emit from the player's suit.
|
||||
|
||||
CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
|
||||
//float flPlayerDistSqr = ( GetOuter()->GetAbsOrigin() - pPlayer->GetAbsOrigin() ).LengthSqr();
|
||||
|
||||
// If the player is too far away or we can't see him
|
||||
//if ( HasCondition( COND_SEE_PLAYER ) == false || flPlayerDistSqr > Square( 128 ) )
|
||||
{
|
||||
if ( m_flNextWarnTime < gpGlobals->curtime )
|
||||
{
|
||||
pPlayer->EmitSound( "npc_alyx.injured_too_far" );
|
||||
m_flNextWarnTime = gpGlobals->curtime + random->RandomFloat( 3.0f, 5.0f );
|
||||
}
|
||||
}
|
||||
/*
|
||||
else
|
||||
{
|
||||
SpeakIfAllowed( TLK_INJURED_FOLLOW_TOO_FAR );
|
||||
m_flNextWarnTime = gpGlobals->curtime + random->RandomFloat( 3.0f, 5.0f );
|
||||
}
|
||||
*/
|
||||
|
||||
SetCondition( COND_INJURED_TOO_FAR_FROM_PLAYER );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Speak a concept if we're able to
|
||||
//-----------------------------------------------------------------------------
|
||||
void CAI_BehaviorAlyxInjured::SpeakIfAllowed( AIConcept_t concept )
|
||||
{
|
||||
CAI_Expresser *pExpresser = GetOuter()->GetExpresser();
|
||||
if ( pExpresser == NULL )
|
||||
return;
|
||||
|
||||
// Must be able to speak the concept
|
||||
if ( pExpresser->CanSpeakConcept( concept ) )
|
||||
{
|
||||
pExpresser->Speak( concept );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Get the number of known enemies within a radius to a point
|
||||
//-----------------------------------------------------------------------------
|
||||
int CAI_BehaviorAlyxInjured::NumKnownEnemiesInRadius( const Vector &vecSource, float flRadius )
|
||||
{
|
||||
int nNumEnemies = 0;
|
||||
float flRadiusSqr = Square( flRadius );
|
||||
|
||||
AIEnemiesIter_t iter;
|
||||
|
||||
// Iterate through all known enemies
|
||||
for( AI_EnemyInfo_t *pMemory = GetEnemies()->GetFirst(&iter); pMemory != NULL; pMemory = GetEnemies()->GetNext(&iter) )
|
||||
{
|
||||
if ( pMemory == NULL || pMemory->hEnemy == NULL )
|
||||
continue;
|
||||
|
||||
// Must hate or fear them
|
||||
if ( GetOuter()->IRelationType( pMemory->hEnemy ) != D_HT && GetOuter()->IRelationType( pMemory->hEnemy ) != D_FR )
|
||||
continue;
|
||||
|
||||
// Count only the enemies I've seen recently
|
||||
if ( gpGlobals->curtime - pMemory->timeLastSeen > 0.5f )
|
||||
continue;
|
||||
|
||||
// Must be within the radius we've specified
|
||||
float flEnemyDistSqr = ( vecSource - pMemory->hEnemy->GetAbsOrigin() ).Length2DSqr();
|
||||
if ( flEnemyDistSqr < flRadiusSqr )
|
||||
{
|
||||
nNumEnemies++;
|
||||
}
|
||||
}
|
||||
|
||||
return nNumEnemies;
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
// Custom AI declarations
|
||||
// ----------------------------------------------
|
||||
|
||||
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_BehaviorAlyxInjured )
|
||||
{
|
||||
DECLARE_ACTIVITY( ACT_GESTURE_INJURED_COWER_FLINCH )
|
||||
DECLARE_ACTIVITY( ACT_INJURED_COWER )
|
||||
|
||||
DECLARE_CONDITION( COND_INJURED_TOO_FAR_FROM_PLAYER )
|
||||
DECLARE_CONDITION( COND_INJURED_OVERWHELMED )
|
||||
|
||||
DECLARE_TASK( TASK_FIND_INJURED_COVER_FROM_ENEMY )
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_INJURED_COWER,
|
||||
|
||||
" Tasks"
|
||||
// TOOD: Announce cower
|
||||
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_COWER"
|
||||
" TASK_WAIT 2"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_GIVE_WAY"
|
||||
" COND_PLAYER_PUSHING"
|
||||
)
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_INJURED_FEAR_FACE,
|
||||
|
||||
" Tasks"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // FIXME: Scared idle?
|
||||
" TASK_FACE_ENEMY 0"
|
||||
""
|
||||
" Interrupts"
|
||||
" COND_GIVE_WAY"
|
||||
" COND_PLAYER_PUSHING"
|
||||
);
|
||||
|
||||
DEFINE_SCHEDULE
|
||||
(
|
||||
SCHED_INJURED_RUN_FROM_ENEMY,
|
||||
|
||||
" Tasks"
|
||||
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_INJURED_COWER"
|
||||
" TASK_STOP_MOVING 0"
|
||||
" TASK_FIND_COVER_FROM_ENEMY 0"
|
||||
" TASK_RUN_PATH 0"
|
||||
" TASK_WAIT_FOR_MOVEMENT 0"
|
||||
""
|
||||
" Interrupts"
|
||||
);
|
||||
|
||||
AI_END_CUSTOM_SCHEDULE_PROVIDER()
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CAI_InjuredFollowGoal
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
BEGIN_DATADESC( CAI_InjuredFollowGoal )
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( ai_goal_injured_follow, CAI_InjuredFollowGoal );
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
void CAI_InjuredFollowGoal::EnableGoal( CAI_BaseNPC *pAI )
|
||||
{
|
||||
CAI_BehaviorAlyxInjured *pBehavior;
|
||||
if ( !pAI->GetBehavior( &pBehavior ) )
|
||||
return;
|
||||
|
||||
if ( GetGoalEntity() == NULL )
|
||||
return;
|
||||
|
||||
pBehavior->SetFollowGoal( this );
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
void CAI_InjuredFollowGoal::DisableGoal( CAI_BaseNPC *pAI )
|
||||
{
|
||||
CAI_BehaviorAlyxInjured *pBehavior;
|
||||
if ( !pAI->GetBehavior( &pBehavior ) )
|
||||
return;
|
||||
|
||||
pBehavior->ClearFollowGoal( this );
|
||||
}
|
||||
Reference in New Issue
Block a user