You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
467 lines
13 KiB
467 lines
13 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "beam_shared.h" |
|
#include "ai_motor.h" |
|
#include "asw_ai_behavior_scuttle.h" |
|
#include "ai_hint.h" |
|
#include "ai_navigator.h" |
|
#include "ai_memory.h" |
|
#include "asw_alien.h" |
|
#include "asw_director.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
BEGIN_DATADESC( CAI_ASW_ScuttleBehavior ) |
|
DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_flDeferUntil, FIELD_TIME ), |
|
DEFINE_FIELD( m_flPackRange, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flPackRangeSquared, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flMinBackOff, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flMaxBackOff, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flMinYawVariation, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flMaxYawVariation, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flMinTimeWait, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_flMaxTimeWait, FIELD_FLOAT ), |
|
END_DATADESC(); |
|
|
|
LINK_BEHAVIOR_TO_CLASSNAME( CAI_ASW_ScuttleBehavior ); |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: constructor |
|
//------------------------------------------------------------------------------ |
|
CAI_ASW_ScuttleBehavior::CAI_ASW_ScuttleBehavior( ) |
|
{ |
|
m_flDeferUntil = gpGlobals->curtime; |
|
m_flPackRange = 800.0f; |
|
m_flPackRangeSquared = m_flPackRange * m_flPackRange; |
|
m_flMinBackOff = 100.0f; |
|
m_flMaxBackOff = 150.0f; |
|
m_flMinYawVariation = 10.0f; |
|
m_flMaxYawVariation = 20.0f; |
|
m_flMinTimeWait = 1.25f; |
|
m_flMaxTimeWait = 2.0f; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// 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_ScuttleBehavior::KeyValue( const char *szKeyName, const char *szValue ) |
|
{ |
|
if ( V_stricmp( szKeyName, "pack_range" ) == 0 ) |
|
{ |
|
m_flPackRange = atof( szValue ); |
|
m_flPackRangeSquared = m_flPackRange * m_flPackRange; |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "min_backoff" ) == 0 ) |
|
{ |
|
m_flMinBackOff = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "max_backoff" ) == 0 ) |
|
{ |
|
m_flMaxBackOff = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "min_yaw" ) == 0 ) |
|
{ |
|
m_flMinYawVariation = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "max_yaw" ) == 0 ) |
|
{ |
|
m_flMaxYawVariation = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "min_wait" ) == 0 ) |
|
{ |
|
m_flMinTimeWait = atof( szValue ); |
|
return true; |
|
} |
|
else if ( V_stricmp( szKeyName, "max_wait" ) == 0 ) |
|
{ |
|
m_flMaxTimeWait = atof( szValue ); |
|
return true; |
|
} |
|
|
|
return BaseClass::KeyValue( szKeyName, szValue ); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: precaches any additional assets this behavior needs |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_ScuttleBehavior::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_ScuttleBehavior::CanSelectSchedule() |
|
{ |
|
if ( !GetOuter()->IsInterruptable() ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( !HasCondition( COND_SCUTTLE_HAS_DESTINATION ) ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( m_flDeferUntil > gpGlobals->curtime ) |
|
{ |
|
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_ScuttleBehavior::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_ScuttleBehavior::GatherConditionsNotActive( ) |
|
{ |
|
BaseClass::GatherConditionsNotActive(); |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: general purpose routine to collect conditions used both during active |
|
// and non-active states of the behavior. |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_ScuttleBehavior::GatherCommonConditions( ) |
|
{ |
|
BaseClass::GatherCommonConditions(); |
|
|
|
ClearCondition( COND_SCUTTLE_HAS_DESTINATION ); |
|
ClearCondition( COND_SCUTTLE_TARGET_CHANGED ); |
|
|
|
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() ) |
|
{ |
|
continue; |
|
} |
|
|
|
Vector vDiff = GetAbsOrigin() - pEMemory->vLastKnownLocation; |
|
float flTestLength = vDiff.LengthSqr(); |
|
|
|
if ( !bFound || flTestLength < flLength ) |
|
{ |
|
bFound = true; |
|
flLength = flTestLength; |
|
vEnemyPos = pEMemory->vLastKnownLocation; |
|
pBestObject = pEnemy; |
|
} |
|
} |
|
|
|
int nCount = 0; |
|
CAI_ASW_ScuttleBehavior *pBehavior; |
|
|
|
int iAliens = IAlienAutoList::AutoList().Count(); |
|
for ( int i = 0; i < iAliens; i++ ) |
|
{ |
|
CASW_Alien *pAlien = static_cast< CASW_Alien* >( IAlienAutoList::AutoList()[ i ] ); |
|
if ( !pAlien || !pAlien->IsAlive() || pAlien->GetBehavior( &pBehavior ) != NULL ) |
|
{ |
|
continue; |
|
} |
|
|
|
if ( GetOuter() != pAlien ) |
|
{ |
|
Vector vDiffEnemy = pAlien->GetAbsOrigin() - vEnemyPos; |
|
float flEnemyLength = vDiffEnemy.LengthSqr(); |
|
Vector vDiffUs = pAlien->GetAbsOrigin() - GetAbsOrigin(); |
|
float flUsLength = vDiffUs.LengthSqr(); |
|
|
|
if ( flEnemyLength < m_flPackRangeSquared && flUsLength < m_flPackRangeSquared ) |
|
{ |
|
nCount++; |
|
} |
|
} |
|
} |
|
|
|
if ( pBestObject && nCount > 0 ) |
|
{ |
|
m_vDestination = pBestObject->GetAbsOrigin(); |
|
SetCondition( COND_SCUTTLE_HAS_DESTINATION ); |
|
|
|
if ( m_hTargetEnt != NULL && m_hTargetEnt != pBestObject ) |
|
{ |
|
SetCondition( COND_SCUTTLE_TARGET_CHANGED ); |
|
} |
|
|
|
m_hTargetEnt = pBestObject; |
|
} |
|
else |
|
{ |
|
if ( m_hTargetEnt != NULL && m_hTargetEnt != pBestObject ) |
|
{ |
|
SetCondition( COND_SCUTTLE_TARGET_CHANGED ); |
|
} |
|
|
|
m_hTargetEnt = NULL; |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: routine called to start when a task initially starts |
|
// Input : pTask - the task structure |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_ScuttleBehavior::StartTask( const Task_t *pTask ) |
|
{ |
|
switch( pTask->iTask ) |
|
{ |
|
case TASK_SCUTTLE_SET_PATH: |
|
{ |
|
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() ) |
|
{ |
|
continue; |
|
} |
|
|
|
Vector vDiff = GetAbsOrigin() - pEMemory->vLastKnownLocation; |
|
float flTestLength = vDiff.LengthSqr(); |
|
|
|
if ( !bFound || flTestLength < flLength ) |
|
{ |
|
bFound = true; |
|
flLength = flTestLength; |
|
vEnemyPos = pEMemory->vLastKnownLocation; |
|
} |
|
} |
|
|
|
if ( bFound == false || flLength > m_flPackRangeSquared ) |
|
{ |
|
TaskFail( FAIL_NO_TARGET ); |
|
break; |
|
} |
|
|
|
Vector vCenter; |
|
int nCount = 0; |
|
CAI_ASW_ScuttleBehavior *pBehavior; |
|
|
|
vCenter.Zero(); |
|
|
|
int iAliens = IAlienAutoList::AutoList().Count(); |
|
for ( int i = 0; i < iAliens; i++ ) |
|
{ |
|
CASW_Alien *pAlien = static_cast< CASW_Alien* >( IAlienAutoList::AutoList()[ i ] ); |
|
if ( !pAlien || !pAlien->IsAlive() || pAlien->GetBehavior( &pBehavior ) != NULL ) |
|
{ |
|
continue; |
|
} |
|
|
|
if ( GetOuter() != pAlien ) |
|
{ |
|
Vector vDiffEnemy = pAlien->GetAbsOrigin() - vEnemyPos; |
|
float flEnemyLength = vDiffEnemy.LengthSqr(); |
|
Vector vDiffUs = pAlien->GetAbsOrigin() - GetAbsOrigin(); |
|
float flUsLength = vDiffUs.LengthSqr(); |
|
|
|
if ( flEnemyLength < m_flPackRangeSquared && flUsLength < m_flPackRangeSquared ) |
|
{ |
|
nCount++; |
|
vCenter += pAlien->GetAbsOrigin(); |
|
} |
|
} |
|
} |
|
|
|
if ( nCount == 0 ) |
|
{ |
|
TaskFail( FAIL_NO_TARGET ); |
|
break; |
|
} |
|
|
|
vCenter /= nCount; |
|
// NDebugOverlay::Cross3D( vCenter, -Vector(32,32,32), Vector(32,32,32), 0, 255, 0, true, 0.1f ); |
|
|
|
Vector vDiff = vCenter - vEnemyPos; |
|
// flLength = vDiff.NormalizeInPlace(); |
|
flLength = RandomFloat( m_flMinBackOff, m_flMaxBackOff ); |
|
|
|
float targetYaw = UTIL_VecToYaw( vDiff ); |
|
|
|
vDiff.z = 0.0f; |
|
|
|
float flTestYaw = targetYaw - RandomFloat( m_flMinYawVariation, m_flMaxYawVariation ); |
|
vDiff.x = cos( DEG2RAD( flTestYaw ) ); |
|
vDiff.y = sin( DEG2RAD( flTestYaw ) ); |
|
Vector vPos1 = vCenter + ( vDiff * flLength ); |
|
|
|
flTestYaw = targetYaw + RandomFloat( m_flMinYawVariation, m_flMaxYawVariation ); |
|
vDiff.x = cos( DEG2RAD( flTestYaw ) ); |
|
vDiff.y = sin( DEG2RAD( flTestYaw ) ); |
|
Vector vPos2 = vCenter + ( vDiff * flLength ); |
|
|
|
Vector vDiff1 = vPos1 - GetAbsOrigin(); |
|
Vector vDiff2 = vPos2 - GetAbsOrigin(); |
|
|
|
Vector vDestination; |
|
|
|
if ( vDiff1.LengthSqr() > vDiff2.LengthSqr() ) |
|
{ |
|
vDestination = vPos1; |
|
// NDebugOverlay::Cross3D( vPos2, -Vector(32,32,32), Vector(32,32,32), 255, 255, 0, true, 0.1f ); |
|
} |
|
else |
|
{ |
|
vDestination = vPos2; |
|
// NDebugOverlay::Cross3D( vPos1, -Vector(32,32,32), Vector(32,32,32), 255, 255, 0, true, 0.1f ); |
|
} |
|
|
|
// NDebugOverlay::Cross3D( vDestination, -Vector(32,32,32), Vector(32,32,32), 255, 255, 255, true, 0.1f ); |
|
|
|
AI_NavGoal_t goal( GOALTYPE_LOCATION, vDestination, ACT_WALK, 50.0f, AIN_YAW_TO_DEST | AIN_LOCAL_SUCCEEED_ON_WITHIN_TOLERANCE, AIN_DEF_TARGET ); |
|
bool bResult = GetNavigator()->SetGoal( goal, AIN_NO_PATH_TASK_FAIL ); |
|
|
|
if ( bResult == false ) |
|
{ |
|
flTestYaw = RandomFloat( 0.0f, 360.0f ); |
|
vDiff.x = cos( DEG2RAD( flTestYaw ) ); |
|
vDiff.y = sin( DEG2RAD( flTestYaw ) ); |
|
vDestination = vCenter + ( vDiff * flLength ); |
|
|
|
AI_NavGoal_t RandomGoal( GOALTYPE_LOCATION, vDestination, ACT_WALK, 50.0f, AIN_YAW_TO_DEST | AIN_LOCAL_SUCCEEED_ON_WITHIN_TOLERANCE, AIN_DEF_TARGET ); |
|
bResult = GetNavigator()->SetGoal( RandomGoal, AIN_NO_PATH_TASK_FAIL ); |
|
} |
|
|
|
GetNavigator()->SetMovementActivity( ACT_WALK ); |
|
GetOuter()->SetWait( RandomFloat( m_flMinTimeWait, m_flMaxTimeWait ) ); |
|
break; |
|
} |
|
|
|
case TASK_SCUTTLE_WAIT: |
|
{ |
|
break; |
|
} |
|
|
|
default: |
|
BaseClass::StartTask( pTask ); |
|
break; |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose: routine called every frame when a task is running |
|
// Input : pTask - the task structure |
|
//------------------------------------------------------------------------------ |
|
void CAI_ASW_ScuttleBehavior::RunTask( const Task_t *pTask ) |
|
{ |
|
switch( pTask->iTask ) |
|
{ |
|
case TASK_SCUTTLE_SET_PATH: |
|
{ |
|
if ( GetNavigator()->GetGoalType() == GOALTYPE_NONE ) |
|
{ |
|
TaskComplete(); |
|
} |
|
break; |
|
} |
|
|
|
case TASK_SCUTTLE_WAIT: |
|
{ |
|
if ( GetOuter()->IsWaitFinished() ) |
|
{ |
|
TaskComplete(); |
|
} |
|
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_ScuttleBehavior::SelectSchedule() |
|
{ |
|
if ( HasCondition( COND_SCUTTLE_HAS_DESTINATION ) ) |
|
{ |
|
return SCHED_SCUTTLE_MOVE_NEAR_TARGET; |
|
} |
|
|
|
return BaseClass::SelectSchedule(); |
|
} |
|
|
|
|
|
AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER( CAI_ASW_ScuttleBehavior ) |
|
|
|
DECLARE_TASK( TASK_SCUTTLE_SET_PATH ) |
|
DECLARE_TASK( TASK_SCUTTLE_WAIT ) |
|
|
|
DECLARE_CONDITION( COND_SCUTTLE_HAS_DESTINATION ) |
|
DECLARE_CONDITION( COND_SCUTTLE_TARGET_CHANGED ) |
|
|
|
//=============================================== |
|
//=============================================== |
|
DEFINE_SCHEDULE |
|
( |
|
SCHED_SCUTTLE_MOVE_NEAR_TARGET, |
|
" Tasks" |
|
" TASK_SCUTTLE_SET_PATH 0" |
|
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" |
|
" TASK_SCUTTLE_WAIT 0" |
|
// " TASK_WALK_PATH_TIMED 1.5" |
|
// " TASK_WAIT_FOR_MOVEMENT 0" |
|
"" |
|
" Interrupts" |
|
"" |
|
// " COND_SCUTTLE_TARGET_CHANGED" |
|
); |
|
|
|
AI_END_CUSTOM_SCHEDULE_PROVIDER() |
|
|
|
#include "tier0/memdbgoff.h"
|
|
|