|
|
|
|
//========= Copyright <EFBFBD> 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
|
|
|
//
|
|
|
|
|
// Purpose:
|
|
|
|
|
//
|
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
|
|
#include "cbase.h"
|
|
|
|
|
#include "beam_shared.h"
|
|
|
|
|
#include "ai_motor.h"
|
|
|
|
|
#include "asw_ai_behavior_wander.h"
|
|
|
|
|
#include "ai_hint.h"
|
|
|
|
|
#include "ai_navigator.h"
|
|
|
|
|
#include "ai_memory.h"
|
|
|
|
|
#include "asw_alien.h"
|
|
|
|
|
|
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
|
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BEGIN_DATADESC( CAI_ASW_WanderBehavior )
|
|
|
|
|
END_DATADESC();
|
|
|
|
|
|
|
|
|
|
LINK_BEHAVIOR_TO_CLASSNAME( CAI_ASW_WanderBehavior );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define STRAFE_ANGLE_RANGE 45.0f
|
|
|
|
|
//#define DRAW_DEBUG 1
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: constructor
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
CAI_ASW_WanderBehavior::CAI_ASW_WanderBehavior( )
|
|
|
|
|
{
|
|
|
|
|
m_WanderType = WANDER_TYPE_NORMAL;
|
|
|
|
|
m_flMinDistance = 100.0f;
|
|
|
|
|
m_flMaxDistance = 150.0f;
|
|
|
|
|
m_flStrafeFactor = 1.3f;
|
|
|
|
|
m_flDivisions = 5;
|
|
|
|
|
m_flDistanceTooCloseSq = 200.0f;
|
|
|
|
|
m_flDistanceTooCloseSq *= m_flDistanceTooCloseSq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// 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_WanderBehavior::KeyValue( const char *szKeyName, const char *szValue )
|
|
|
|
|
{
|
|
|
|
|
if ( V_stricmp( szKeyName, "type" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
if ( V_stricmp( szValue, "direction" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_WanderType = WANDER_TYPE_DIRECTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( V_stricmp( szKeyName, "min_distance" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_flMinDistance = atof( szValue );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( V_stricmp( szKeyName, "max_distance" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_flMaxDistance = atof( szValue );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( V_stricmp( szKeyName, "max_distance" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_flMaxDistance = atof( szValue );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( V_stricmp( szKeyName, "strafe_factor" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_flStrafeFactor = atof( szValue );
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( V_stricmp( szKeyName, "tooclose" ) == 0 )
|
|
|
|
|
{
|
|
|
|
|
m_flDistanceTooCloseSq = atof( szValue );
|
|
|
|
|
m_flDistanceTooCloseSq *= m_flDistanceTooCloseSq;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return BaseClass::KeyValue( szKeyName, szValue );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: precaches any additional assets this behavior needs
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_WanderBehavior::Precache( void )
|
|
|
|
|
{
|
|
|
|
|
BaseClass::Precache();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: determines if we can use this behavior currently
|
|
|
|
|
// Output : returns true if this behavior is able to run
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
bool CAI_ASW_WanderBehavior::CanSelectSchedule()
|
|
|
|
|
{
|
|
|
|
|
if ( !GetOuter()->IsInterruptable() )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
if ( GetOuter()->GetState() != NPC_STATE_COMBAT )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if ( m_WanderType == WANDER_TYPE_NORMAL && !HasCondition( COND_LOST_ENEMY ) && !HasCondition( COND_ENEMY_UNREACHABLE ) )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( m_WanderType == WANDER_TYPE_DIRECTION && HasCondition( COND_WANDER_ENEMY_TOO_CLOSE ) )
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BaseClass::CanSelectSchedule();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: sets / clears conditions for when the behavior is active. this is
|
|
|
|
|
// generally a larger set of conditions to interrupt any tasks.
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_WanderBehavior::GatherConditions( )
|
|
|
|
|
{
|
|
|
|
|
BaseClass::GatherConditions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: sets / clears conditions for when the behavior is not active. this is
|
|
|
|
|
// mainly to have a smaller set of conditions to wake up the behavior.
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_WanderBehavior::GatherConditionsNotActive( )
|
|
|
|
|
{
|
|
|
|
|
BaseClass::GatherConditionsNotActive();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: general purpose routine to collect conditions used both during active
|
|
|
|
|
// and non-active states of the behavior.
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_WanderBehavior::GatherCommonConditions( )
|
|
|
|
|
{
|
|
|
|
|
BaseClass::GatherCommonConditions();
|
|
|
|
|
|
|
|
|
|
ClearCondition( COND_WANDER_ENEMY_TOO_CLOSE );
|
|
|
|
|
|
|
|
|
|
if ( GetEnemy() )
|
|
|
|
|
{
|
|
|
|
|
float DistanceSq = ( GetEnemyLKP() - GetAbsOrigin() ).LengthSqr();
|
|
|
|
|
|
|
|
|
|
if ( DistanceSq <= m_flDistanceTooCloseSq )
|
|
|
|
|
{
|
|
|
|
|
SetCondition( COND_WANDER_ENEMY_TOO_CLOSE );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: routine called to start when a task initially starts
|
|
|
|
|
// Input : pTask - the task structure
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_WanderBehavior::StartTask( const Task_t *pTask )
|
|
|
|
|
{
|
|
|
|
|
switch( pTask->iTask )
|
|
|
|
|
{
|
|
|
|
|
case TASK_WANDER_DIRECTION:
|
|
|
|
|
{
|
|
|
|
|
trace_t tr;
|
|
|
|
|
Vector *vGoodResults = ( Vector * )stackalloc( sizeof( Vector ) * ( int )m_flDivisions );
|
|
|
|
|
int nNumResults = 0;
|
|
|
|
|
const float flSpinAmount = 360.0f / m_flDivisions;
|
|
|
|
|
float flCurrentAngle = RandomFloat( 0.0f, flSpinAmount );
|
|
|
|
|
Vector vCurrent = GetAbsOrigin();
|
|
|
|
|
#ifdef DRAW_DEBUG
|
|
|
|
|
int r, g, b;
|
|
|
|
|
#endif // #ifdef DRAW_DEBUG
|
|
|
|
|
|
|
|
|
|
m_bCanStrafeLeft = HaveSequenceForActivity( ACT_STRAFE_LEFT );
|
|
|
|
|
m_bCanStrafeRight = HaveSequenceForActivity( ACT_STRAFE_RIGHT );
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < m_flDivisions; i++ )
|
|
|
|
|
{
|
|
|
|
|
Vector vDest = vCurrent;
|
|
|
|
|
|
|
|
|
|
float flDistance = RandomFloat( m_flMinDistance, m_flMaxDistance );
|
|
|
|
|
float flYawDelta = UTIL_AngleMod( GetLocalAngles().y - flCurrentAngle );
|
|
|
|
|
if ( m_bCanStrafeLeft && fabs( flYawDelta - 270.0f ) <= STRAFE_ANGLE_RANGE )
|
|
|
|
|
{
|
|
|
|
|
flDistance *= m_flStrafeFactor;
|
|
|
|
|
#ifdef DRAW_DEBUG
|
|
|
|
|
r = 255;
|
|
|
|
|
g = 255;
|
|
|
|
|
b = 0;
|
|
|
|
|
#endif // #ifdef DRAW_DEBUG
|
|
|
|
|
}
|
|
|
|
|
else if ( m_bCanStrafeRight && fabs( flYawDelta - 90.0f ) <= STRAFE_ANGLE_RANGE )
|
|
|
|
|
{
|
|
|
|
|
flDistance *= m_flStrafeFactor;
|
|
|
|
|
#ifdef DRAW_DEBUG
|
|
|
|
|
r = 0;
|
|
|
|
|
g = 255;
|
|
|
|
|
b = 255;
|
|
|
|
|
#endif // #ifdef DRAW_DEBUG
|
|
|
|
|
}
|
|
|
|
|
#ifdef DRAW_DEBUG
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
r = 255;
|
|
|
|
|
g = 255;
|
|
|
|
|
b = 255;
|
|
|
|
|
}
|
|
|
|
|
#endif // #ifdef DRAW_DEBUG
|
|
|
|
|
|
|
|
|
|
vDest.x += cos( DEG2RAD( flCurrentAngle ) ) * flDistance;
|
|
|
|
|
vDest.y += sin( DEG2RAD( flCurrentAngle ) ) * flDistance;
|
|
|
|
|
vDest.z += 15.0f;
|
|
|
|
|
|
|
|
|
|
#ifdef DRAW_DEBUG
|
|
|
|
|
NDebugOverlay::Line( vCurrent, vDest, r, g, b, true, 4.0f );
|
|
|
|
|
#endif // #ifdef DRAW_DEBUG
|
|
|
|
|
UTIL_TraceLine( vCurrent, vDest, MASK_SOLID, NULL, ASW_COLLISION_GROUP_IGNORE_NPCS, &tr );
|
|
|
|
|
|
|
|
|
|
if ( tr.fraction > 0.6f )
|
|
|
|
|
{
|
|
|
|
|
vGoodResults[ nNumResults ] = vDest;
|
|
|
|
|
nNumResults++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flCurrentAngle = UTIL_AngleMod( flCurrentAngle + RandomFloat( flSpinAmount / 2.0f, flSpinAmount * 1.5f ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( nNumResults > 0 )
|
|
|
|
|
{
|
|
|
|
|
nNumResults = RandomInt( 0, nNumResults - 1 );
|
|
|
|
|
|
|
|
|
|
float flYaw = UTIL_VecToYaw ( vGoodResults[ nNumResults ] - GetAbsOrigin() );
|
|
|
|
|
float flYawDelta = UTIL_AngleMod( GetLocalAngles().y - flYaw );
|
|
|
|
|
|
|
|
|
|
if ( m_bCanStrafeLeft && fabs( flYawDelta - 270.0f ) <= STRAFE_ANGLE_RANGE )
|
|
|
|
|
{
|
|
|
|
|
AI_NavGoal_t goal( GOALTYPE_LOCATION, vGoodResults[ nNumResults ], ACT_STRAFE_LEFT, 10.0f, AIN_LOCAL_SUCCEEED_ON_WITHIN_TOLERANCE, AIN_DEF_TARGET );
|
|
|
|
|
GetNavigator()->SetGoal( goal );
|
|
|
|
|
}
|
|
|
|
|
else if ( m_bCanStrafeRight && fabs( flYawDelta - 90.0f ) <= STRAFE_ANGLE_RANGE )
|
|
|
|
|
{
|
|
|
|
|
AI_NavGoal_t goal( GOALTYPE_LOCATION, vGoodResults[ nNumResults ], ACT_STRAFE_RIGHT, 10.0f, AIN_LOCAL_SUCCEEED_ON_WITHIN_TOLERANCE, AIN_DEF_TARGET );
|
|
|
|
|
GetNavigator()->SetGoal( goal );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AI_NavGoal_t goal( GOALTYPE_LOCATION, vGoodResults[ nNumResults ], ACT_WALK, 10.0f, AIN_YAW_TO_DEST | AIN_LOCAL_SUCCEEED_ON_WITHIN_TOLERANCE, AIN_DEF_TARGET );
|
|
|
|
|
GetNavigator()->SetGoal( goal );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TaskComplete();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
BaseClass::StartTask( pTask );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: routine called every frame when a task is running
|
|
|
|
|
// Input : pTask - the task structure
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
void CAI_ASW_WanderBehavior::RunTask( const Task_t *pTask )
|
|
|
|
|
{
|
|
|
|
|
switch( pTask->iTask )
|
|
|
|
|
{
|
|
|
|
|
case TASK_WANDER_DIRECTION:
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
BaseClass::RunTask( pTask );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
// Purpose: routine called to select what schedule we want to run
|
|
|
|
|
// Output : returns the schedule id of the schedule we want to run
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
int CAI_ASW_WanderBehavior::SelectSchedule()
|
|
|
|
|
{
|
|
|
|
|
switch( m_WanderType )
|
|
|
|
|
{
|
|
|
|
|
case WANDER_TYPE_NORMAL:
|
|
|
|
|
{
|
|
|
|
|
if ( HasCondition( COND_ENEMY_OCCLUDED ) )
|
|
|
|
|
{
|
|
|
|
|
return SCHED_WANDER_STANDOFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return SCHED_WANDER_MEDIUM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case WANDER_TYPE_DIRECTION:
|
|
|
|
|
{
|
|
|
|
|
return SCHED_WANDER_DIRECTIONAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return SCHED_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_ASW_WanderBehavior )
|
|
|
|
|
|
|
|
|
|
DECLARE_TASK( TASK_WANDER_DIRECTION )
|
|
|
|
|
|
|
|
|
|
DECLARE_CONDITION( COND_WANDER_ENEMY_TOO_CLOSE )
|
|
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
// Wander around for a while so we don't look stupid.
|
|
|
|
|
// this is done if we ever lose track of our enemy.
|
|
|
|
|
//=========================================================
|
|
|
|
|
DEFINE_SCHEDULE
|
|
|
|
|
(
|
|
|
|
|
SCHED_WANDER_MEDIUM,
|
|
|
|
|
|
|
|
|
|
" Tasks"
|
|
|
|
|
" TASK_STOP_MOVING 0"
|
|
|
|
|
" TASK_WANDER 480384" // 4 feet to 32 feet
|
|
|
|
|
" TASK_WALK_PATH 0"
|
|
|
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
|
|
|
" TASK_STOP_MOVING 0"
|
|
|
|
|
" TASK_WAIT_PVS 0" // if the player left my PVS, just wait.
|
|
|
|
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_WANDER_MEDIUM" // keep doing it
|
|
|
|
|
" "
|
|
|
|
|
" Interrupts"
|
|
|
|
|
" COND_NEW_ENEMY"
|
|
|
|
|
" COND_SEE_ENEMY"
|
|
|
|
|
" COND_LIGHT_DAMAGE"
|
|
|
|
|
" COND_HEAVY_DAMAGE"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
DEFINE_SCHEDULE
|
|
|
|
|
(
|
|
|
|
|
SCHED_WANDER_STANDOFF,
|
|
|
|
|
|
|
|
|
|
" Tasks"
|
|
|
|
|
" TASK_STOP_MOVING 0"
|
|
|
|
|
" TASK_WANDER 480384" // 4 feet to 32 feet
|
|
|
|
|
" TASK_WALK_PATH 0"
|
|
|
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
|
|
|
" TASK_STOP_MOVING 0"
|
|
|
|
|
" TASK_WAIT_PVS 0" // if the player left my PVS, just wait.
|
|
|
|
|
" "
|
|
|
|
|
" Interrupts"
|
|
|
|
|
" COND_NEW_ENEMY"
|
|
|
|
|
" COND_LIGHT_DAMAGE"
|
|
|
|
|
" COND_HEAVY_DAMAGE"
|
|
|
|
|
" COND_ENEMY_DEAD"
|
|
|
|
|
" COND_CAN_RANGE_ATTACK1"
|
|
|
|
|
" COND_CAN_MELEE_ATTACK1"
|
|
|
|
|
" COND_CAN_RANGE_ATTACK2"
|
|
|
|
|
" COND_CAN_MELEE_ATTACK2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
DEFINE_SCHEDULE
|
|
|
|
|
(
|
|
|
|
|
SCHED_WANDER_DIRECTIONAL,
|
|
|
|
|
|
|
|
|
|
" Tasks"
|
|
|
|
|
" TASK_STOP_MOVING 0"
|
|
|
|
|
" TASK_WANDER_DIRECTION 0"
|
|
|
|
|
" TASK_WAIT_FOR_MOVEMENT 0"
|
|
|
|
|
" TASK_WAIT_PVS 0" // if the player left my PVS, just wait.
|
|
|
|
|
" "
|
|
|
|
|
" Interrupts"
|
|
|
|
|
" COND_NEW_ENEMY"
|
|
|
|
|
" COND_LIGHT_DAMAGE"
|
|
|
|
|
" COND_HEAVY_DAMAGE"
|
|
|
|
|
" COND_WANDER_ENEMY_TOO_CLOSE"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//=========================================================
|
|
|
|
|
// If you fail to wander, wait just a bit and try again.
|
|
|
|
|
//=========================================================
|
|
|
|
|
DEFINE_SCHEDULE
|
|
|
|
|
(
|
|
|
|
|
SCHED_WANDER_FAIL,
|
|
|
|
|
|
|
|
|
|
" Tasks"
|
|
|
|
|
" TASK_STOP_MOVING 0"
|
|
|
|
|
" TASK_WAIT 1"
|
|
|
|
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_WANDER_MEDIUM"
|
|
|
|
|
" Interrupts"
|
|
|
|
|
" COND_NEW_ENEMY"
|
|
|
|
|
" COND_LIGHT_DAMAGE"
|
|
|
|
|
" COND_HEAVY_DAMAGE"
|
|
|
|
|
" COND_ENEMY_DEAD"
|
|
|
|
|
" COND_CAN_RANGE_ATTACK1"
|
|
|
|
|
" COND_CAN_MELEE_ATTACK1"
|
|
|
|
|
" COND_CAN_RANGE_ATTACK2"
|
|
|
|
|
" COND_CAN_MELEE_ATTACK2"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
AI_END_CUSTOM_SCHEDULE_PROVIDER()
|
|
|
|
|
|
|
|
|
|
#include "tier0/memdbgoff.h"
|