|
|
|
|
//========= Copyright <EFBFBD> 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
|
|
|
//
|
|
|
|
|
// Purpose:
|
|
|
|
|
//
|
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
|
|
#include "cbase.h"
|
|
|
|
|
#include "ai_motor.h"
|
|
|
|
|
#include "asw_ai_behavior_fear.h"
|
|
|
|
|
#include "ai_hint.h"
|
|
|
|
|
#include "ai_navigator.h"
|
|
|
|
|
#include "ai_memory.h"
|
|
|
|
|
|
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
|
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
|
|
|
|
BEGIN_DATADESC( CAI_ASW_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 ),
|
|
|
|
|
DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
|
|
|
|
|
END_DATADESC();
|
|
|
|
|
|
|
|
|
|
LINK_BEHAVIOR_TO_CLASSNAME( CAI_ASW_FearBehavior );
|
|
|
|
|
|
|
|
|
|
#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 asw_ai_enable_fear_behavior( "asw_ai_enable_fear_behavior", "1", FCVAR_CHEAT );
|
|
|
|
|
ConVar asw_ai_fear_player_dist( "asw_ai_fear_player_dist", "720", FCVAR_CHEAT );
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
CAI_ASW_FearBehavior::CAI_ASW_FearBehavior()
|
|
|
|
|
{
|
|
|
|
|
m_bForceFear = false;
|
|
|
|
|
|
|
|
|
|
ReleaseAllHints();
|
|
|
|
|
m_SafePlaceMoveMonitor.ClearMark();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: function to set up parameters
|
|
|
|
|
// Input : szKeyName - the name of the key
|
|
|
|
|
// szValue - the value to be set
|
|
|
|
|
// Output : returns true of we handled this key
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
bool CAI_ASW_FearBehavior::KeyValue( const char *szKeyName, const char *szValue )
|
|
|
|
|
{
|
|
|
|
|
if ( V_stricmp( szKeyName, "force" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_bForceFear = ( atoi( szValue ) != 0 );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( V_stricmp( szKeyName, "duration" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_flDuration = atof( szValue );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BaseClass::KeyValue( szKeyName, szValue );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_FearBehavior::Precache( void )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// Input : *pTask -
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_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;
|
|
|
|
|
|
|
|
|
|
case TASK_FEAR_FIND_COVER_FROM_ENEMY:
|
|
|
|
|
{
|
|
|
|
|
bool bNodeCover = true;
|
|
|
|
|
float flMinDistance = 1024.0f;
|
|
|
|
|
float flMaxDistance = FLT_MAX;
|
|
|
|
|
|
|
|
|
|
if ( GetOuter()->FindCoverFromEnemy( bNodeCover, flMinDistance, flMaxDistance ) )
|
|
|
|
|
{
|
|
|
|
|
// m_flMoveWaitFinished = gpGlobals->curtime + pTask->flTaskData;
|
|
|
|
|
TaskComplete();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TaskFail( FAIL_NO_COVER );
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
BaseClass::StartTask( pTask );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Purpose:
|
|
|
|
|
// Input : *pTask -
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_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 + 0.5f;// 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Am I in safe place from my enemy?
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
bool CAI_ASW_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_ASW_FearBehavior::SpoilSafePlace()
|
|
|
|
|
{
|
|
|
|
|
m_SafePlaceMoveMonitor.ClearMark();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_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_ASW_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 ( !asw_ai_enable_fear_behavior.GetBool() )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !m_bForceFear && GetOuter()->IRelationType( pEnemy ) != D_FEAR )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BaseClass::CanSelectSchedule();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_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:
|
|
|
|
|
CBaseEntity *pBestObject = NULL;
|
|
|
|
|
bool bFound = false;
|
|
|
|
|
float flLength = 0.0f;
|
|
|
|
|
AIEnemiesIter_t iter;
|
|
|
|
|
Vector vEnemyPos;
|
|
|
|
|
|
|
|
|
|
for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst( &iter ); pEMemory != NULL; pEMemory = GetEnemies()->GetNext( &iter ) )
|
|
|
|
|
{
|
|
|
|
|
CBaseEntity *pEnemy = pEMemory->hEnemy;
|
|
|
|
|
|
|
|
|
|
if ( !pEnemy || !pEnemy->IsAlive() || !GetOuter()->FVisible( pEnemy ) )
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector vDiff = GetAbsOrigin() - pEMemory->vLastKnownLocation;
|
|
|
|
|
float flTestLength = vDiff.LengthSqr();
|
|
|
|
|
|
|
|
|
|
if ( !bFound || flTestLength < flLength )
|
|
|
|
|
{
|
|
|
|
|
bFound = true;
|
|
|
|
|
flLength = flTestLength;
|
|
|
|
|
vEnemyPos = pEMemory->vLastKnownLocation;
|
|
|
|
|
pBestObject = pEnemy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Here's the visibility check. We can't skip this because it's time-sensitive
|
|
|
|
|
if( pBestObject )
|
|
|
|
|
{
|
|
|
|
|
m_flTimePlayerLastVisible = gpGlobals->curtime;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if( gpGlobals->curtime - m_flTimePlayerLastVisible >= 2.0f )
|
|
|
|
|
{
|
|
|
|
|
SetCondition(COND_FEAR_SEPARATED_FROM_PLAYER);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_hTargetEnt = pBestObject;
|
|
|
|
|
|
|
|
|
|
if( HasCondition(COND_FEAR_SEPARATED_FROM_PLAYER) )
|
|
|
|
|
{
|
|
|
|
|
//Msg("I am separated from player\n");
|
|
|
|
|
|
|
|
|
|
if( IsInASafePlace() )
|
|
|
|
|
{
|
|
|
|
|
SpoilSafePlace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_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_ASW_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_ASW_FearBehavior::SelectSchedule()
|
|
|
|
|
{
|
|
|
|
|
bool bInSafePlace = IsInASafePlace();
|
|
|
|
|
|
|
|
|
|
if ( m_bForceFear )
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
return SCHED_FEAR_STAY_IN_SAFE_PLACE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_ASW_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_ASW_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_ASW_FearBehavior::FindFearWithdrawalDest()
|
|
|
|
|
{
|
|
|
|
|
CAI_Hint *pHint;
|
|
|
|
|
CHintCriteria hintCriteria;
|
|
|
|
|
CAI_BaseNPC *pOuter = GetOuter();
|
|
|
|
|
|
|
|
|
|
Assert(pOuter != NULL);
|
|
|
|
|
|
|
|
|
|
if ( m_hTargetEnt == NULL )
|
|
|
|
|
{
|
|
|
|
|
return 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( m_hTargetEnt->GetAbsOrigin(), ( asw_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_ASW_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_TASK( TASK_FEAR_FIND_COVER_FROM_ENEMY )
|
|
|
|
|
|
|
|
|
|
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_FEAR_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_FEAR_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"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
DEFINE_SCHEDULE
|
|
|
|
|
(
|
|
|
|
|
SCHED_FEAR_RUN_FROM_ENEMY,
|
|
|
|
|
|
|
|
|
|
" Tasks"
|
|
|
|
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RUN_FROM_ENEMY_FALLBACK"
|
|
|
|
|
" TASK_STOP_MOVING 0"
|
|
|
|
|
" TASK_FEAR_FIND_COVER_FROM_ENEMY 0"
|
|
|
|
|
" TASK_RUN_PATH 0"
|
|
|
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
|
|
|
""
|
|
|
|
|
" Interrupts"
|
|
|
|
|
" COND_NEW_ENEMY"
|
|
|
|
|
" COND_ENEMY_DEAD"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AI_END_CUSTOM_SCHEDULE_PROVIDER()
|