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/behavior/tf_bot_get_health.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/behavior/tf_bot_get_health.cpp')
| -rw-r--r-- | game/server/tf/bot/behavior/tf_bot_get_health.cpp | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/game/server/tf/bot/behavior/tf_bot_get_health.cpp b/game/server/tf/bot/behavior/tf_bot_get_health.cpp new file mode 100644 index 0000000..91e51ef --- /dev/null +++ b/game/server/tf/bot/behavior/tf_bot_get_health.cpp @@ -0,0 +1,324 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// tf_bot_get_health.h +// Pick up any nearby health kit +// Michael Booth, May 2009 + +#include "cbase.h" +#include "tf_gamerules.h" +#include "tf_obj.h" +#include "bot/tf_bot.h" +#include "bot/behavior/tf_bot_get_health.h" + +extern ConVar tf_bot_path_lookahead_range; + +ConVar tf_bot_health_critical_ratio( "tf_bot_health_critical_ratio", "0.3", FCVAR_CHEAT ); +ConVar tf_bot_health_ok_ratio( "tf_bot_health_ok_ratio", "0.8", FCVAR_CHEAT ); +ConVar tf_bot_health_search_near_range( "tf_bot_health_search_near_range", "1000", FCVAR_CHEAT ); +ConVar tf_bot_health_search_far_range( "tf_bot_health_search_far_range", "2000", FCVAR_CHEAT ); + + +//--------------------------------------------------------------------------------------------- +class CHealthFilter : public INextBotFilter +{ +public: + CHealthFilter( CTFBot *me ) + { + m_me = me; + } + + bool IsSelected( const CBaseEntity *constCandidate ) const + { + if ( !constCandidate ) + return false; + + CBaseEntity *candidate = const_cast< CBaseEntity * >( constCandidate ); + + CTFNavArea *area = (CTFNavArea *)TheNavMesh->GetNearestNavArea( candidate->WorldSpaceCenter() ); + if ( !area ) + return false; + + CClosestTFPlayer close( candidate ); + ForEachPlayer( close ); + + // if the closest player to this candidate object is an enemy, don't use it + if ( close.m_closePlayer && !m_me->InSameTeam( close.m_closePlayer ) ) + return false; + + // resupply cabinets (not assigned a team) + if ( candidate->ClassMatches( "func_regenerate" ) ) + { + if ( !area->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE | TF_NAV_SPAWN_ROOM_RED ) ) + { + // Assume any resupply cabinets not in a teamed spawn room are inaccessible. + // Ex: pl_upward has forward spawn rooms that neither team can use until + // certain checkpoints are reached. + return false; + } + + if ( ( m_me->GetTeamNumber() == TF_TEAM_RED && area->HasAttributeTF( TF_NAV_SPAWN_ROOM_RED ) ) || + ( m_me->GetTeamNumber() == TF_TEAM_BLUE && area->HasAttributeTF( TF_NAV_SPAWN_ROOM_BLUE ) ) ) + { + // the supply cabinet is in my spawn room + return true; + } + + return false; + } + + // ignore non-existent ammo to ensure we collect nearby existing ammo + if ( candidate->IsEffectActive( EF_NODRAW ) ) + return false; + + if ( candidate->ClassMatches( "item_healthkit*" ) ) + return true; + + if ( m_me->InSameTeam( candidate ) ) + { + // friendly engineer's dispenser + if ( candidate->ClassMatches( "obj_dispenser*" ) ) + { + CBaseObject *dispenser = (CBaseObject *)candidate; + if ( !dispenser->IsBuilding() && !dispenser->IsPlacing() && !dispenser->IsDisabled() ) + { + return true; + } + } + } + + return false; + } + + CTFBot *m_me; +}; + + +//--------------------------------------------------------------------------------------------- +static CTFBot *s_possibleBot = NULL; +static CHandle< CBaseEntity > s_possibleHealth = NULL; +static int s_possibleFrame = 0; + + +//--------------------------------------------------------------------------------------------- +/** + * Return true if this Action has what it needs to perform right now + */ +bool CTFBotGetHealth::IsPossible( CTFBot *me ) +{ + VPROF_BUDGET( "CTFBotGetHealth::IsPossible", "NextBot" ); + + // don't move to fetch health if we have a healer + if ( me->m_Shared.GetNumHealers() > 0 ) + return false; + +#ifdef TF_RAID_MODE + // mobs don't heal + if ( TFGameRules()->IsRaidMode() && me->HasAttribute( CTFBot::AGGRESSIVE ) ) + { + return false; + } +#endif // TF_RAID_MODE + + if ( TFGameRules()->IsMannVsMachineMode() ) + { + return false; + } + + float healthRatio = (float)me->GetHealth() / (float)me->GetMaxHealth(); + + float t = ( healthRatio - tf_bot_health_critical_ratio.GetFloat() ) / ( tf_bot_health_ok_ratio.GetFloat() - tf_bot_health_critical_ratio.GetFloat() ); + t = clamp( t, 0.0f, 1.0f ); + + if ( me->m_Shared.InCond( TF_COND_BURNING ) ) + { + // on fire - get health now + t = 0.0f; + } + + // the more we are hurt, the farther we'll travel to get health + float searchRange = tf_bot_health_search_far_range.GetFloat() + t * ( tf_bot_health_search_near_range.GetFloat() - tf_bot_health_search_far_range.GetFloat() ); + + CUtlVector< CHandle< CBaseEntity > > healthVector; + CHealthFilter healthFilter( me ); + + me->SelectReachableObjects( TFGameRules()->GetHealthEntityVector(), &healthVector, healthFilter, me->GetLastKnownArea(), searchRange ); + + if ( healthVector.Count() == 0 ) + { + if ( me->IsDebugging( NEXTBOT_BEHAVIOR ) ) + { + Warning( "%3.2f: No health nearby\n", gpGlobals->curtime ); + } + return false; + } + + // use the first item in the list, since it will be the closest to us (or nearly so) + CBaseEntity *health = healthVector[0]; + for( int i=0; i<healthVector.Count(); ++i ) + { + if ( healthVector[i]->GetTeamNumber() != GetEnemyTeam( me->GetTeamNumber() ) ) + { + health = healthVector[i]; + break; + } + } + + if ( health == NULL ) + { + if ( me->IsDebugging( NEXTBOT_BEHAVIOR ) ) + { + Warning( "%3.2f: No health available to my team nearby\n", gpGlobals->curtime ); + } + return false; + } + + CTFBotPathCost cost( me, FASTEST_ROUTE ); + PathFollower path; + if ( !path.Compute( me, health->WorldSpaceCenter(), cost ) ) + { + if ( me->IsDebugging( NEXTBOT_BEHAVIOR ) ) + { + Warning( "%3.2f: No path to health!\n", gpGlobals->curtime ); + } + return false; + } + + s_possibleBot = me; + s_possibleHealth = health; + s_possibleFrame = gpGlobals->framecount; + + return true; +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotGetHealth::OnStart( CTFBot *me, Action< CTFBot > *priorAction ) +{ + VPROF_BUDGET( "CTFBotGetHealth::OnStart", "NextBot" ); + + m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() ); + + // if IsPossible() has already been called, use its cached data + if ( s_possibleFrame != gpGlobals->framecount || s_possibleBot != me ) + { + if ( !IsPossible( me ) || s_possibleHealth == NULL ) + { + return Done( "Can't get health" ); + } + } + + m_healthKit = s_possibleHealth; + m_isGoalDispenser = m_healthKit->ClassMatches( "obj_dispenser*" ); + + CTFBotPathCost cost( me, SAFEST_ROUTE ); + if ( !m_path.Compute( me, m_healthKit->WorldSpaceCenter(), cost ) ) + { + return Done( "No path to health!" ); + } + + // if I'm a spy, cloak and disguise + if ( me->IsPlayerClass( TF_CLASS_SPY ) ) + { + if ( !me->m_Shared.IsStealthed() ) + { + me->PressAltFireButton(); + } + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CTFBot > CTFBotGetHealth::Update( CTFBot *me, float interval ) +{ + if ( m_healthKit == NULL || ( m_healthKit->IsEffectActive( EF_NODRAW ) && !FClassnameIs( m_healthKit, "func_regenerate" ) ) ) + { + return Done( "Health kit I was going for has been taken" ); + } + + // if a medic is healing us, give up on getting a kit + int i; + for( i=0; i<me->m_Shared.GetNumHealers(); ++i ) + { + if ( !me->m_Shared.HealerIsDispenser( i ) ) + break; + } + + if ( i < me->m_Shared.GetNumHealers() ) + { + return Done( "A Medic is healing me" ); + } + + if ( me->m_Shared.GetNumHealers() ) + { + // a dispenser is healing me, don't wait if I'm in combat + const CKnownEntity *known = me->GetVisionInterface()->GetPrimaryKnownThreat(); + if ( known && known->IsVisibleInFOVNow() ) + { + return Done( "No time to wait for health, I must fight" ); + } + } + + if ( me->GetHealth() >= me->GetMaxHealth() ) + { + return Done( "I've been healed" ); + } + + // if the closest player to the item we're after is an enemy, give up + CClosestTFPlayer close( m_healthKit ); + ForEachPlayer( close ); + if ( close.m_closePlayer && !me->InSameTeam( close.m_closePlayer ) ) + return Done( "An enemy is closer to it" ); + + // un-zoom + CTFWeaponBase *myWeapon = me->m_Shared.GetActiveTFWeapon(); + if ( myWeapon && myWeapon->IsWeapon( TF_WEAPON_SNIPERRIFLE ) && me->m_Shared.InCond( TF_COND_ZOOMED ) ) + me->PressAltFireButton(); + + if ( !m_path.IsValid() ) + { + // this can occur if we overshoot the health kit's location + // because it is momentarily gone + CTFBotPathCost cost( me, SAFEST_ROUTE ); + if ( !m_path.Compute( me, m_healthKit->WorldSpaceCenter(), cost ) ) + { + return Done( "No path to health!" ); + } + } + + m_path.Update( me ); + + // may need to switch weapons (ie: engineer holding toolbox now needs to heal and defend himself) + const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); + me->EquipBestWeaponForThreat( threat ); + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGetHealth::OnStuck( CTFBot *me ) +{ + return TryDone( RESULT_CRITICAL, "Stuck trying to reach health kit" ); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGetHealth::OnMoveToSuccess( CTFBot *me, const Path *path ) +{ + return TryContinue(); +} + + +//--------------------------------------------------------------------------------------------- +EventDesiredResult< CTFBot > CTFBotGetHealth::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason ) +{ + return TryDone( RESULT_CRITICAL, "Failed to reach health kit" ); +} + + +//--------------------------------------------------------------------------------------------- +// We are always hurrying if we need to collect health +QueryResultType CTFBotGetHealth::ShouldHurry( const INextBot *me ) const +{ + return ANSWER_YES; +} |