diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.cpp index 9add7c666..c8c0ee6a6 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.cpp @@ -6,7 +6,6 @@ #include "bot/neo_bot_path_compute.h" #include "nav_mesh.h" #include "neo_gamerules.h" -#include "neo_ghost_cap_point.h" #include "debugoverlay_shared.h" #include "weapon_ghost.h" @@ -361,7 +360,7 @@ ActionResult< CNEOBot > CNEOBotCtgCarrier::OnStart( CNEOBot *me, Action< CNEOBot m_teammates.RemoveAll(); CollectPlayers( me, &m_teammates ); - m_closestCapturePoint = GetNearestCapPoint( me ); + m_closestCapturePoint = NEORules()->GetNearestGhostCapPoint( me->GetTeamNumber(), me->GetAbsOrigin() ); UpdateFollowPath( me, m_teammates ); return Continue(); @@ -419,44 +418,6 @@ ActionResult< CNEOBot > CNEOBotCtgCarrier::Update( CNEOBot *me, float interval ) } -//--------------------------------------------------------------------------------------------- -Vector CNEOBotCtgCarrier::GetNearestCapPoint( const CNEOBot *me ) const -{ - if ( !me ) - return CNEO_Player::VECTOR_INVALID_WAYPOINT; - - const int iMyTeam = me->GetTeamNumber(); - - if ( NEORules()->m_pGhostCaps.Count() > 0 ) - { - Vector bestPos = CNEO_Player::VECTOR_INVALID_WAYPOINT; - float flNearestCapDistSq = FLT_MAX; - for( int i=0; im_pGhostCaps.Count(); ++i ) - { - CNEOGhostCapturePoint *pCapPoint = dynamic_cast( UTIL_EntityByIndex( NEORules()->m_pGhostCaps[i] ) ); - if (!pCapPoint || !pCapPoint->GetActive()) - { - continue; - } - - int iCapTeam = pCapPoint->owningTeamAlternate(); - if ( iCapTeam == iMyTeam || iCapTeam == TEAM_ANY ) - { - float distanceToCap = me->GetAbsOrigin().DistToSqr( pCapPoint->GetAbsOrigin() ); - if ( distanceToCap < flNearestCapDistSq ) - { - flNearestCapDistSq = distanceToCap; - bestPos = pCapPoint->GetAbsOrigin(); - } - } - } - return bestPos; - } - - return CNEO_Player::VECTOR_INVALID_WAYPOINT; -} - - //--------------------------------------------------------------------------------------------- void CNEOBotCtgCarrier::UpdateFollowPath( CNEOBot *me, const CUtlVector &teammates ) { @@ -649,6 +610,84 @@ void CNEOBotCtgCarrier::UpdateFollowPath( CNEOBot *me, const CUtlVectorIsCarryingGhost() ) + { + return false; + } + + if ( m_closestCapturePoint == CNEO_Player::VECTOR_INVALID_WAYPOINT ) + { + return false; + } + + const float flMyDistToGoalSq = me->GetAbsOrigin().DistToSqr( m_closestCapturePoint ); + + CUtlVector< CKnownEntity > knownVector; + me->GetVisionInterface()->CollectKnownEntities( &knownVector ); + + for ( int i = 0; i < knownVector.Count(); ++i ) + { + CBaseEntity *pKnown = knownVector[i].GetEntity(); + if ( !pKnown || !pKnown->IsPlayer() || !pKnown->IsAlive() ) + { + continue; + } + + if ( pKnown->GetTeamNumber() == me->GetTeamNumber() ) + { + continue; + } + + if ( pKnown->GetAbsOrigin().DistToSqr( m_closestCapturePoint ) <= flMyDistToGoalSq ) + { + // A known enemy is at least as close to the cap as we are. + return false; + } + } + + return true; +} + +//--------------------------------------------------------------------------------------------- +bool CNEOBotCtgCarrier::IsCapPotentiallyVisible( const CNEOBot *me ) const +{ + if ( !me->IsCarryingGhost() ) + { + return false; + } + + if ( m_closestCapturePoint == CNEO_Player::VECTOR_INVALID_WAYPOINT ) + { + return false; + } + + const CNavArea *capArea = TheNavMesh->GetNearestNavArea( m_closestCapturePoint ); + const CNavArea *myArea = me->GetLastKnownArea(); + if ( !capArea || !myArea ) + { + return false; + } + + return capArea->IsPotentiallyVisible( myArea ); +} + +//--------------------------------------------------------------------------------------------- +QueryResultType CNEOBotCtgCarrier::ShouldHurry( const INextBot *me ) const +{ + const CNEOBot *meBot = static_cast( me ); + + // Within sight of the cap zone, run for it regardless of who is closing in + if ( IsCapPotentiallyVisible( meBot ) || HasCleanRunToCap( meBot ) ) + { + return ANSWER_YES; + } + + return ANSWER_UNDEFINED; +} + //--------------------------------------------------------------------------------------------- ActionResult< CNEOBot > CNEOBotCtgCarrier::OnResume( CNEOBot *me, Action< CNEOBot > *interruptingAction ) { @@ -656,7 +695,7 @@ ActionResult< CNEOBot > CNEOBotCtgCarrier::OnResume( CNEOBot *me, Action< CNEOBo CollectPlayers( me, &m_teammates ); // Re-evaluate nearest cap point on resume (in case we moved significantly while interrupted) - m_closestCapturePoint = GetNearestCapPoint( me ); + m_closestCapturePoint = NEORules()->GetNearestGhostCapPoint( me->GetTeamNumber(), me->GetAbsOrigin() ); UpdateFollowPath( me, m_teammates ); return Continue(); diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.h index 4b70b47ca..a2a48dc96 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_carrier.h @@ -39,6 +39,8 @@ class CNEOBotCtgCarrier : public Action< CNEOBot > virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ) override; virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) override; + virtual QueryResultType ShouldHurry( const INextBot *me ) const override; + virtual const char *GetName( void ) const override { return "ctgGhostCarrier"; } private: @@ -51,7 +53,8 @@ class CNEOBotCtgCarrier : public Action< CNEOBot > Vector m_closestCapturePoint; CUtlVector m_teammates; - Vector GetNearestCapPoint( const CNEOBot *me ) const; + bool HasCleanRunToCap( const CNEOBot *me ) const; + bool IsCapPotentiallyVisible( const CNEOBot *me ) const; void UpdateFollowPath( CNEOBot *me, const CUtlVector &teammates ); }; diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_enemy.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_enemy.cpp index 9ad40bf0a..de66157b4 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_enemy.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_enemy.cpp @@ -76,3 +76,26 @@ EventDesiredResult< CNEOBot > CNEOBotCtgEnemy::OnMoveToFailure( CNEOBot *me, con { return TryContinue(); } + +//--------------------------------------------------------------------------------------------- +QueryResultType CNEOBotCtgEnemy::ShouldHurry( const INextBot *me ) const +{ + const CNEOBot *meBot = static_cast( me ); + + CNEO_Player *pCarrier = ToNEOPlayer( UTIL_PlayerByIndex( NEORules()->GetGhosterPlayer() ) ); + if ( !pCarrier || !pCarrier->IsAlive() || pCarrier->GetTeamNumber() == meBot->GetTeamNumber() ) + { + return ANSWER_UNDEFINED; + } + + const Vector vecCarrierGoal = NEORules()->GetNearestGhostCapPoint( pCarrier->GetTeamNumber(), pCarrier->GetAbsOrigin() ); + if ( vecCarrierGoal == CNEO_Player::VECTOR_INVALID_WAYPOINT ) + { + return ANSWER_UNDEFINED; + } + + const float flCarrierToGoalSq = pCarrier->GetAbsOrigin().DistToSqr( vecCarrierGoal ); + const float flMeToGoalSq = meBot->GetAbsOrigin().DistToSqr( vecCarrierGoal ); + + return ( flMeToGoalSq > flCarrierToGoalSq ) ? ANSWER_YES : ANSWER_UNDEFINED; +} diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_enemy.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_enemy.h index 8ae07df41..ff616a177 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_enemy.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_enemy.h @@ -18,6 +18,8 @@ class CNEOBotCtgEnemy : public Action< CNEOBot > virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ) override; virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) override; + virtual QueryResultType ShouldHurry( const INextBot *me ) const override; + virtual const char *GetName( void ) const override { return "ctgEnemy"; } private: diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_escort.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_escort.cpp index 923a0e5c9..36ba3cb2d 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_escort.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_escort.cpp @@ -5,7 +5,6 @@ #include "bot/behavior/neo_bot_attack.h" #include "bot/neo_bot_path_compute.h" #include "neo_gamerules.h" -#include "neo_ghost_cap_point.h" #include "weapons/weapon_ghost.h" extern ConVar sv_neo_grenade_blast_radius; @@ -241,6 +240,46 @@ EventDesiredResult< CNEOBot > CNEOBotCtgEscort::OnMoveToFailure( CNEOBot *me, co return TryContinue(); } +//--------------------------------------------------------------------------------------------- +// Hurry back to friendly ghost carrier if there are enemies closer to them +QueryResultType CNEOBotCtgEscort::ShouldHurry( const INextBot *me ) const +{ + const CNEOBot *meBot = static_cast( me ); + + CNEO_Player *pCarrier = ToNEOPlayer( UTIL_PlayerByIndex( NEORules()->GetGhosterPlayer() ) ); + if ( !pCarrier || !pCarrier->IsAlive() || pCarrier->GetTeamNumber() != meBot->GetTeamNumber() || pCarrier == meBot ) + { + return ANSWER_UNDEFINED; + } + + const Vector &vecCarrierPos = pCarrier->GetAbsOrigin(); + const float flMyDistToCarrierSq = meBot->GetAbsOrigin().DistToSqr( vecCarrierPos ); + + CUtlVector< CKnownEntity > knownVector; + meBot->GetVisionInterface()->CollectKnownEntities( &knownVector ); + + for ( int i = 0; i < knownVector.Count(); ++i ) + { + CBaseEntity *pKnown = knownVector[i].GetEntity(); + if ( !pKnown || !pKnown->IsPlayer() || !pKnown->IsAlive() ) + { + continue; + } + + if ( pKnown->GetTeamNumber() == meBot->GetTeamNumber() ) + { + continue; + } + + if ( pKnown->GetAbsOrigin().DistToSqr( vecCarrierPos ) < flMyDistToCarrierSq ) + { + return ANSWER_YES; + } + } + + return ANSWER_UNDEFINED; +} + //--------------------------------------------------------------------------------------------- CNEOBotCtgEscort::EscortRole CNEOBotCtgEscort::UpdateRoleAssignment( CNEOBot *me, CNEO_Player *pGhostCarrier, const Vector &vecGoalPos ) { @@ -324,29 +363,6 @@ CNEOBotCtgEscort::EscortRole CNEOBotCtgEscort::UpdateRoleAssignment( CNEOBot *me //--------------------------------------------------------------------------------------------- void CNEOBotCtgEscort::UpdateGoalPosition( CNEOBot *me, CNEO_Player *pGhostCarrier ) { - m_vecGoalPos = CNEO_Player::VECTOR_INVALID_WAYPOINT; - m_bHasGoal = false; - - float flNearestCapDistSq = FLT_MAX; - const int iMyTeam = me->GetTeamNumber(); - - for( int i=0; im_pGhostCaps.Count(); ++i ) - { - CNEOGhostCapturePoint *pCapPoint = dynamic_cast( UTIL_EntityByIndex( NEORules()->m_pGhostCaps[i] ) ); - if ( !pCapPoint || !pCapPoint->GetActive() ) - { - continue; - } - const int iCapTeam = pCapPoint->owningTeamAlternate(); - if ( iCapTeam == iMyTeam || iCapTeam == TEAM_ANY ) - { - float d = pGhostCarrier->GetAbsOrigin().DistToSqr( pCapPoint->GetAbsOrigin() ); - if ( d < flNearestCapDistSq ) - { - flNearestCapDistSq = d; - m_vecGoalPos = pCapPoint->GetAbsOrigin(); - m_bHasGoal = true; - } - } - } + m_vecGoalPos = NEORules()->GetNearestGhostCapPoint( me->GetTeamNumber(), pGhostCarrier->GetAbsOrigin() ); + m_bHasGoal = ( m_vecGoalPos != CNEO_Player::VECTOR_INVALID_WAYPOINT ); } diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_escort.h b/src/game/server/neo/bot/behavior/neo_bot_ctg_escort.h index d8e932c5c..22f96c67f 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_escort.h +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_escort.h @@ -18,6 +18,8 @@ class CNEOBotCtgEscort : public Action< CNEOBot > virtual EventDesiredResult< CNEOBot > OnMoveToSuccess( CNEOBot *me, const Path *path ) override; virtual EventDesiredResult< CNEOBot > OnMoveToFailure( CNEOBot *me, const Path *path, MoveToFailureType reason ) override; + virtual QueryResultType ShouldHurry( const INextBot *me ) const override; + virtual const char *GetName( void ) const override { return "ctgEscort"; } private: diff --git a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp index de23f4355..091f75eeb 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_ctg_lone_wolf.cpp @@ -10,7 +10,6 @@ #include "bot/behavior/neo_bot_detpack_deploy.h" #include "bot/neo_bot_path_compute.h" #include "neo_gamerules.h" -#include "neo_ghost_cap_point.h" #include "weapon_detpack.h" #include "weapon_ghost.h" @@ -229,33 +228,6 @@ Vector CNEOBotCtgLoneWolf::GetNearestEnemyCapPoint( CNEOBot *me ) const } const int iEnemyTeam = NEORules()->GetOpposingTeam( me->GetTeamNumber() ); - - if ( NEORules()->m_pGhostCaps.Count() > 0 ) - { - const Vector* pBestPos = nullptr; - float flNearestSq = FLT_MAX; - for ( int i = 0; i < NEORules()->m_pGhostCaps.Count(); ++i ) - { - CNEOGhostCapturePoint *pCapPoint = dynamic_cast( UTIL_EntityByIndex( NEORules()->m_pGhostCaps[i] ) ); - if ( !pCapPoint || !pCapPoint->GetActive() ) - { - continue; - } - - int iCapTeam = pCapPoint->owningTeamAlternate(); - if ( iCapTeam == iEnemyTeam || iCapTeam == TEAM_ANY ) - { - float distSq = me->GetAbsOrigin().DistToSqr( pCapPoint->GetAbsOrigin() ); - if ( distSq < flNearestSq ) - { - flNearestSq = distSq; - pBestPos = &pCapPoint->GetAbsOrigin(); - } - } - } - return pBestPos ? *pBestPos : CNEO_Player::VECTOR_INVALID_WAYPOINT; - } - - return CNEO_Player::VECTOR_INVALID_WAYPOINT; + return NEORules()->GetNearestGhostCapPoint( iEnemyTeam, me->GetAbsOrigin() ); } diff --git a/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp b/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp index c62193966..a0474435c 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_seek_and_destroy.cpp @@ -1,7 +1,6 @@ #include "cbase.h" #include "neo_player.h" #include "neo_gamerules.h" -#include "neo_ghost_cap_point.h" #include "team_control_point_master.h" #include "bot/neo_bot.h" #include "bot/behavior/neo_bot_attack.h" @@ -161,26 +160,8 @@ ActionResult< CNEOBot > CNEOBotSeekAndDestroy::Update( CNEOBot *me, float interv if (NEORules()->GetGameType() == NEO_GAME_TYPE_CTG) { // Only switch to CTG behavior if there are available capture zones this round - bool bHasAvailableCapZone = false; - const int iMyTeam = me->GetTeamNumber(); - - for( int i=0; im_pGhostCaps.Count(); ++i ) - { - CNEOGhostCapturePoint *pCapPoint = dynamic_cast( UTIL_EntityByIndex( NEORules()->m_pGhostCaps[i] ) ); - if ( !pCapPoint || !pCapPoint->GetActive() ) - { - continue; - } - - const int iCapTeam = pCapPoint->owningTeamAlternate(); - if ( iCapTeam == iMyTeam || iCapTeam == TEAM_ANY ) - { - bHasAvailableCapZone = true; - break; - } - } - - if ( bHasAvailableCapZone ) + const Vector vecCapPoint = NEORules()->GetNearestGhostCapPoint( me->GetTeamNumber(), me->GetAbsOrigin() ); + if ( vecCapPoint != CNEO_Player::VECTOR_INVALID_WAYPOINT ) { return SuspendFor( new CNEOBotCtgSeek, "Switching to Ghost-related Seek and Destroy" ); } diff --git a/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp b/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp index e0f1cfffc..359ff102a 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_tactical_monitor.cpp @@ -327,53 +327,58 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter return result; } - // check if we need to get to cover - QueryResultType shouldRetreat = me->GetIntentionInterface()->ShouldRetreat( me ); - - if ( shouldRetreat == ANSWER_YES ) + if ( CBaseEntity *breakable = CNEOBotPathClearBreakable::GetBreakableInPath( me ) ) { - return SuspendFor( new CNEOBotRetreatToCover, "Backing off" ); + return SuspendFor( new CNEOBotPathClearBreakable( breakable ), "Clearing breakable in path" ); } - else if ( shouldRetreat != ANSWER_NO ) + + const bool bInAHurry = ( me->GetIntentionInterface()->ShouldHurry( me ) == ANSWER_YES ); + + if ( !bInAHurry ) { - // retreat if we need to do a full reload - if ( me->IsDifficulty( CNEOBot::HARD ) || me->IsDifficulty( CNEOBot::EXPERT ) ) + // check if we need to get to cover + QueryResultType shouldRetreat = me->GetIntentionInterface()->ShouldRetreat( me ); + + if ( shouldRetreat == ANSWER_YES ) { - CNEOBaseCombatWeapon *weapon = (CNEOBaseCombatWeapon*) me->GetActiveWeapon(); - if ( weapon && weapon->GetPrimaryAmmoCount() > 0 && me->IsBarrageAndReloadWeapon(weapon)) + return SuspendFor( new CNEOBotRetreatToCover, "Backing off" ); + } + else if ( shouldRetreat != ANSWER_NO ) + { + // retreat if we need to do a full reload + if ( me->IsDifficulty( CNEOBot::HARD ) || me->IsDifficulty( CNEOBot::EXPERT ) ) { - if ( weapon->Clip1() <= 1 ) + CNEOBaseCombatWeapon *weapon = (CNEOBaseCombatWeapon*) me->GetActiveWeapon(); + if ( weapon && weapon->GetPrimaryAmmoCount() > 0 && me->IsBarrageAndReloadWeapon(weapon)) { - return SuspendFor( new CNEOBotRetreatToCover, "Moving to cover to reload" ); + if ( weapon->Clip1() <= 1 ) + { + return SuspendFor( new CNEOBotRetreatToCover, "Moving to cover to reload" ); + } } } } - } - if ( CBaseEntity *breakable = CNEOBotPathClearBreakable::GetBreakableInPath( me ) ) - { - return SuspendFor( new CNEOBotPathClearBreakable( breakable ), "Clearing breakable in path" ); - } - - // Don't want to interfere with human squad leader's control just to ignore hazards - // Might be annoying if bots ignored following or waypoints (e.g. smoke sightlines avoidance) - if ( !me->m_hCommandingPlayer.Get() && m_hazardCheckTimer.IsElapsed() ) - { - m_hazardCheckTimer.Start( 0.5f ); - CNavArea *myArea = me->GetLastKnownArea(); - if ( myArea ) + // Don't want to interfere with human squad leader's control just to ignore hazards + // Might be annoying if bots ignored following or waypoints (e.g. smoke sightlines avoidance) + if ( !me->m_hCommandingPlayer.Get() && m_hazardCheckTimer.IsElapsed() ) { - if ( CNEOBotPathReservations()->IsAreaHazardous( myArea->GetID(), me ) ) + m_hazardCheckTimer.Start( 0.5f ); + CNavArea *myArea = me->GetLastKnownArea(); + if ( myArea ) { - return SuspendFor( new CNEOBotRetreatFromHazardArea( ), "Avoiding hazard area" ); + if ( CNEOBotPathReservations()->IsAreaHazardous( myArea->GetID(), me ) ) + { + return SuspendFor( new CNEOBotRetreatFromHazardArea( ), "Avoiding hazard area" ); + } } } - } - ActionResult< CNEOBot > scavengeResult = ScavengeForPrimaryWeapon( me ); - if ( scavengeResult.IsRequestingChange() ) - { - return scavengeResult; + ActionResult< CNEOBot > scavengeResult = ScavengeForPrimaryWeapon( me ); + if ( scavengeResult.IsRequestingChange() ) + { + return scavengeResult; + } } #if 0 // NEO TODO (Adam) search for dropped weapons to resupply ammunition diff --git a/src/game/shared/neo/neo_gamerules.cpp b/src/game/shared/neo/neo_gamerules.cpp index 979d70fed..58a28f695 100644 --- a/src/game/shared/neo/neo_gamerules.cpp +++ b/src/game/shared/neo/neo_gamerules.cpp @@ -3263,6 +3263,37 @@ void CNEORules::ResetGhostCapPoints() } } +Vector CNEORules::GetNearestGhostCapPoint(const int iTeam, const Vector &vecFrom) const +{ + Vector vecBest = CNEO_Player::VECTOR_INVALID_WAYPOINT; + float flNearestDistSq = FLT_MAX; + + for (int i = 0; i < m_pGhostCaps.Count(); i++) + { + auto pGhostCap = dynamic_cast(UTIL_EntityByIndex(m_pGhostCaps[i])); + if (!pGhostCap || !pGhostCap->GetActive()) + { + continue; + } + + // A neutral zone accepts the ghost from either team + const int iCapTeam = pGhostCap->owningTeamAlternate(); + if (iCapTeam != iTeam && iCapTeam != TEAM_ANY) + { + continue; + } + + const float flDistSq = vecFrom.DistToSqr(pGhostCap->GetAbsOrigin()); + if (flDistSq < flNearestDistSq) + { + flNearestDistSq = flDistSq; + vecBest = pGhostCap->GetAbsOrigin(); + } + } + + return vecBest; +} + void CNEORules::SetGameRelatedVars() { ResetTDM(); diff --git a/src/game/shared/neo/neo_gamerules.h b/src/game/shared/neo/neo_gamerules.h index 48f008ee8..a33ea6ab0 100644 --- a/src/game/shared/neo/neo_gamerules.h +++ b/src/game/shared/neo/neo_gamerules.h @@ -87,7 +87,6 @@ class CWeaponGhost; class CNEOBotCtgLoneWolf; class CNEOBotCtgLoneWolfAmbush; class CNEOBotCtgLoneWolfSeek; -class CNEOBotSeekAndDestroy; extern ConVar sv_neo_mirror_teamdamage_multiplier; extern ConVar sv_neo_mirror_teamdamage_duration; @@ -284,6 +283,11 @@ class CNEORules : public CHL2MPRules, public CGameEventListener void ResetGhostCapPoints(); +#ifdef GAME_DLL + // Nearest active capture zone that iTeam can deliver the ghost to, or VECTOR_INVALID_WAYPOINT if none + Vector GetNearestGhostCapPoint(const int iTeam, const Vector &vecFrom) const; +#endif + void SetGameRelatedVars(); void ResetTDM(); void ResetGhost(); @@ -500,15 +504,12 @@ class CNEORules : public CHL2MPRules, public CGameEventListener float m_flJuggernautDeathTime = 0.0f; int m_iLastJuggernautTeam = TEAM_INVALID; - // For looking up capture zone locations - friend class CNEOBotCtgCarrier; - friend class CNEOBotCtgEscort; + // For looking up the ghost friend class CNEOBotCtgLoneWolf; friend class CNEOBotCtgLoneWolfAmbush; friend class CNEOBotCtgLoneWolfSeek; friend class CNEOBotTacticalMonitor; - friend class CNEOBotSeekAndDestroy; CUtlVector m_pGhostCaps; CWeaponGhost *m_pGhost = nullptr; CNEO_Player *m_pVIP = nullptr;