diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/tf/bot/tf_bot_vision.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/tf/bot/tf_bot_vision.cpp')
| -rw-r--r-- | game/server/tf/bot/tf_bot_vision.cpp | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/game/server/tf/bot/tf_bot_vision.cpp b/game/server/tf/bot/tf_bot_vision.cpp new file mode 100644 index 0000000..6fae0ff --- /dev/null +++ b/game/server/tf/bot/tf_bot_vision.cpp @@ -0,0 +1,482 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_vision.cpp +// Team Fortress NextBot vision interface +// Michael Booth, May 2009 + +#include "cbase.h" +#include "vprof.h" + +#include "tf_bot.h" +#include "tf_bot_vision.h" +#include "tf_player.h" +#include "tf_gamerules.h" +#include "tf_obj_sentrygun.h" + +ConVar tf_bot_choose_target_interval( "tf_bot_choose_target_interval", "0.3f", FCVAR_CHEAT, "How often, in seconds, a TFBot can reselect his target" ); +ConVar tf_bot_sniper_choose_target_interval( "tf_bot_sniper_choose_target_interval", "3.0f", FCVAR_CHEAT, "How often, in seconds, a zoomed-in Sniper can reselect his target" ); + + +//------------------------------------------------------------------------------------------ +// Update internal state +void CTFBotVision::Update( void ) +{ + if ( TFGameRules()->IsMannVsMachineMode() ) + { + // Throttle vision update rate of robots in MvM for perf at the expense of reaction times + if ( !m_scanTimer.IsElapsed() ) + { + return; + } + + m_scanTimer.Start( RandomFloat( 0.9f, 1.1f ) ); + } + + IVision::Update(); + + CTFBot *me = (CTFBot *)GetBot()->GetEntity(); + if ( !me ) + return; + + // forget spies we have lost sight of + CUtlVector< CTFPlayer * > playerVector; + CollectPlayers( &playerVector, GetEnemyTeam( me->GetTeamNumber() ), COLLECT_ONLY_LIVING_PLAYERS ); + + for( int i=0; i<playerVector.Count(); ++i ) + { + if ( !playerVector[i]->IsPlayerClass( TF_CLASS_SPY ) ) + continue; + + const CKnownEntity *known = GetKnown( playerVector[i] ); + + if ( !known || !known->IsVisibleRecently() ) + { + // if a hidden spy changes disguises, we no longer recognize him + if ( playerVector[i]->m_Shared.InCond( TF_COND_DISGUISING ) ) + { + me->ForgetSpy( playerVector[i] ); + } + } + } +} + + +//------------------------------------------------------------------------------------------ +void CTFBotVision::CollectPotentiallyVisibleEntities( CUtlVector< CBaseEntity * > *potentiallyVisible ) +{ + VPROF_BUDGET( "CTFBotVision::CollectPotentiallyVisibleEntities", "NextBot" ); + + potentiallyVisible->RemoveAll(); + + // include all players + for( int i=1; i<=gpGlobals->maxClients; ++i ) + { + CBasePlayer *player = UTIL_PlayerByIndex( i ); + + if ( player == NULL ) + continue; + + if ( FNullEnt( player->edict() ) ) + continue; + + if ( !player->IsPlayer() ) + continue; + + if ( !player->IsConnected() ) + continue; + + if ( !player->IsAlive() ) + continue; + + potentiallyVisible->AddToTail( player ); + } + + // include sentry guns + UpdatePotentiallyVisibleNPCVector(); + + FOR_EACH_VEC( m_potentiallyVisibleNPCVector, it ) + { + potentiallyVisible->AddToTail( m_potentiallyVisibleNPCVector[ it ] ); + } +} + + +//------------------------------------------------------------------------------------------ +void CTFBotVision::UpdatePotentiallyVisibleNPCVector( void ) +{ + if ( m_potentiallyVisibleUpdateTimer.IsElapsed() ) + { + m_potentiallyVisibleUpdateTimer.Start( RandomFloat( 3.0f, 4.0f ) ); + + // collect list of active buildings + m_potentiallyVisibleNPCVector.RemoveAll(); + + bool bShouldSeeTeleporter = !TFGameRules()->IsMannVsMachineMode() || GetBot()->GetEntity()->GetTeamNumber() != TF_TEAM_PVE_INVADERS; + for ( int i=0; i<IBaseObjectAutoList::AutoList().Count(); ++i ) + { + CBaseObject* pObj = static_cast< CBaseObject* >( IBaseObjectAutoList::AutoList()[i] ); + if ( pObj->ObjectType() == OBJ_SENTRYGUN ) + { + m_potentiallyVisibleNPCVector.AddToTail( pObj ); + } + else if ( pObj->ObjectType() == OBJ_DISPENSER && pObj->ClassMatches( "obj_dispenser" ) ) + { + m_potentiallyVisibleNPCVector.AddToTail( pObj ); + } + else if ( bShouldSeeTeleporter && pObj->ObjectType() == OBJ_TELEPORTER ) + { + m_potentiallyVisibleNPCVector.AddToTail( pObj ); + } + } + + CUtlVector< INextBot * > botVector; + TheNextBots().CollectAllBots( &botVector ); + for( int i=0; i<botVector.Count(); ++i ) + { + CBaseCombatCharacter *botEntity = botVector[i]->GetEntity(); + if ( botEntity && !botEntity->IsPlayer() ) + { + // NPC + m_potentiallyVisibleNPCVector.AddToTail( botEntity ); + } + } + } +} + + +//------------------------------------------------------------------------------------------ +/** + * Return true to completely ignore this entity. + * This is mostly for enemy spies. If we don't ignore them, we will look at them. + */ +bool CTFBotVision::IsIgnored( CBaseEntity *subject ) const +{ + CTFBot *me = (CTFBot *)GetBot()->GetEntity(); + +#ifdef TF_RAID_MODE + if ( TFGameRules()->IsRaidMode() ) + { + if ( me->IsPlayerClass( TF_CLASS_SCOUT ) ) + { + // Scouts are wandering defenders, and aggro purely on proximity or damage, not vision + return true; + } + } +#endif // TF_RAID_MODE + + if ( me->IsAttentionFocused() ) + { + // our attention is restricted to certain subjects + if ( !me->IsAttentionFocusedOn( subject ) ) + { + return false; + } + } + + if ( !me->IsEnemy( subject ) ) + { + // don't ignore friends + return false; + } + + if ( subject->IsEffectActive( EF_NODRAW ) ) + { + return true; + } + + if ( subject->IsPlayer() ) + { + CTFPlayer *enemy = static_cast< CTFPlayer * >( subject ); + + // test for designer-defined ignorance + switch( enemy->GetPlayerClass()->GetClassIndex() ) + { + case TF_CLASS_MEDIC: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_MEDICS ) ) + { + return true; + } + break; + + case TF_CLASS_ENGINEER: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_ENGINEERS ) ) + { + return true; + } + break; + + case TF_CLASS_SNIPER: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_SNIPERS ) ) + { + return true; + } + break; + + case TF_CLASS_SCOUT: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_SCOUTS ) ) + { + return true; + } + break; + + case TF_CLASS_SPY: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_SPIES ) ) + { + return true; + } + break; + + case TF_CLASS_DEMOMAN: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_DEMOMEN ) ) + { + return true; + } + break; + + case TF_CLASS_SOLDIER: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_SOLDIERS ) ) + { + return true; + } + break; + + case TF_CLASS_HEAVYWEAPONS: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_HEAVIES ) ) + { + return true; + } + break; + + case TF_CLASS_PYRO: + if ( me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_PYROS ) ) + { + return true; + } + break; + } + +#ifdef STAGING_ONLY + if ( enemy->m_Shared.InCond( TF_COND_REPROGRAMMED ) ) + { + return true; + } +#endif // STAGING_ONLY + + if ( me->IsKnownSpy( enemy ) ) + { + // don't ignore revealed spies + return false; + } + + if ( enemy->m_Shared.InCond( TF_COND_BURNING ) || + enemy->m_Shared.InCond( TF_COND_URINE ) || + enemy->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) || + enemy->m_Shared.InCond( TF_COND_BLEEDING ) ) + { + // always notice players with these conditions + return false; + } + + // An upgrade in MvM grants AE stealth where the player can fire + // while in stealth, and for a short period after it drops + if ( enemy->m_Shared.InCond( TF_COND_STEALTHED_USER_BUFF_FADING ) ) + { + return true; + } + + if ( enemy->m_Shared.IsStealthed() ) + { + if ( enemy->m_Shared.GetPercentInvisible() < 0.75f ) + { + // spy is partially cloaked, and therefore attracts our attention + return false; + } + + // invisible! + return true; + } + + if ( enemy->IsPlacingSapper() ) + { + return false; + } + + if ( enemy->m_Shared.InCond( TF_COND_DISGUISING ) ) + { + return false; + } + + if ( enemy->m_Shared.InCond( TF_COND_DISGUISED ) && enemy->m_Shared.GetDisguiseTeam() == me->GetTeamNumber() ) + { + // spy is disguised as a member of my team + return true; + } + } + else if ( subject->IsBaseObject() ) // not a player + { + CBaseObject *object = assert_cast< CBaseObject * >( subject ); + if ( object ) + { + // ignore sapped enemy objects + if ( object->HasSapper() ) + { + // unless we're in MvM where buildings can have really large health pools, + // so an engineer can die and run back in time to repair their stuff + if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) + { + return false; + } + + return true; + } + + // ignore carried objects + if ( object->IsPlacing() || object->IsCarried() ) + { + return true; + } + + if ( object->GetType() == OBJ_SENTRYGUN && me->IsBehaviorFlagSet( TFBOT_IGNORE_ENEMY_SENTRY_GUNS ) ) + { + return true; + } + } + } + + return false; +} + + +//------------------------------------------------------------------------------------------ +// Return true if we 'notice' the subject, even though we have LOS to it +bool CTFBotVision::IsVisibleEntityNoticed( CBaseEntity *subject ) const +{ + CTFBot *me = (CTFBot *)GetBot()->GetEntity(); + + if ( subject->IsPlayer() && me->IsEnemy( subject ) ) + { + CTFPlayer *player = static_cast< CTFPlayer * >( subject ); + + if ( player->m_Shared.InCond( TF_COND_BURNING ) || + player->m_Shared.InCond( TF_COND_URINE ) || + player->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) || + player->m_Shared.InCond( TF_COND_BLEEDING ) ) + { + // always notice players with these conditions + if ( player->m_Shared.InCond( TF_COND_STEALTHED ) ) + { + me->RealizeSpy( player ); + } + return true; + } + +#ifdef STAGING_ONLY + // Bots can be hacked/reprogrammed by spies. Ignore. + if ( player->m_Shared.InCond( TF_COND_REPROGRAMMED ) ) + { + return false; + } +#endif // STAGING_ONLY + + // An upgrade in MvM grants AE stealth where the player can fire + // while in stealth, and for a short period after it drops + if ( player->m_Shared.InCond( TF_COND_STEALTHED_USER_BUFF_FADING ) ) + { + me->ForgetSpy( player ); + return false; + } + + if ( player->m_Shared.IsStealthed() ) + { + if ( player->m_Shared.GetPercentInvisible() < 0.75f ) + { + // spy is partially cloaked, and therefore attracts our attention + me->RealizeSpy( player ); + return true; + } + + // invisible! + me->ForgetSpy( player ); + return false; + } + + if ( TFGameRules()->IsMannVsMachineMode() ) // in MvM mode, forget spies as soon as they are fully disguised + { + CTFBot::SuspectedSpyInfo_t* pSuspectInfo = me->IsSuspectedSpy( player ); + // But only if we aren't suspecting them currently. This happens when we bump into them. + if( !pSuspectInfo || !pSuspectInfo->IsCurrentlySuspected() ) + { + if ( player->m_Shared.InCond( TF_COND_DISGUISED ) && player->m_Shared.GetDisguiseTeam() == me->GetTeamNumber() ) + { + me->ForgetSpy( player ); + return false; + } + } + } + + if ( me->IsKnownSpy( player ) ) + { + // always notice non-invisible revealed spies + return true; + } + + if ( !TFGameRules()->IsMannVsMachineMode() ) // ignore in MvM mode + { + if ( player->IsPlacingSapper() ) + { + // spotted a spy! + me->RealizeSpy( player ); + return true; + } + } + + if ( player->m_Shared.InCond( TF_COND_DISGUISING ) ) + { + // spotted a spy! + me->RealizeSpy( player ); + return true; + } + + if ( player->m_Shared.InCond( TF_COND_DISGUISED ) && player->m_Shared.GetDisguiseTeam() == me->GetTeamNumber() ) + { + // spy is disguised as a member of my team, don't notice him + return false; + } + } + + return true; +} + + +//------------------------------------------------------------------------------------------ +// Return VISUAL reaction time +float CTFBotVision::GetMinRecognizeTime( void ) const +{ + CTFBot *me = (CTFBot *)GetBot(); + + switch ( me->GetDifficulty() ) + { + case CTFBot::EASY: return 1.0f; + case CTFBot::NORMAL: return 0.5f; + case CTFBot::HARD: return 0.3f; + case CTFBot::EXPERT: return 0.2f; + } + + return 1.0f; +} + + + +//------------------------------------------------------------------------------------------ +float CTFBotVision::GetMaxVisionRange( void ) const +{ + CTFBot *me = (CTFBot *)GetBot(); + + if ( me->GetMaxVisionRangeOverride() > 0.0f ) + { + // designer specified vision range + return me->GetMaxVisionRangeOverride(); + } + + // long range, particularly for snipers + return 6000.0f; +} |