diff --git a/src/game/server/neo/bot/behavior/neo_bot_attack.cpp b/src/game/server/neo/bot/behavior/neo_bot_attack.cpp index 4ea2cfc26..aa721fb6b 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_attack.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_attack.cpp @@ -355,10 +355,10 @@ ActionResult< CNEOBot > CNEOBotAttack::Update( CNEOBot *me, float interval ) // Consider throwing a grenade if ( !m_grenadeThrowCooldownTimer.HasStarted() || m_grenadeThrowCooldownTimer.IsElapsed() ) { + m_grenadeThrowCooldownTimer.Start( sv_neo_bot_grenade_throw_cooldown.GetFloat() ); Action *pGrenadeBehavior = CNEOBotGrenadeDispatch::ChooseGrenadeThrowBehavior( me, threat ); if ( pGrenadeBehavior ) { - m_grenadeThrowCooldownTimer.Start( sv_neo_bot_grenade_throw_cooldown.GetFloat() ); return SuspendFor( pGrenadeBehavior, "Throwing grenade before chasing threat!" ); } } diff --git a/src/game/server/neo/bot/behavior/neo_bot_grenade_dispatch.cpp b/src/game/server/neo/bot/behavior/neo_bot_grenade_dispatch.cpp index fc2b2e356..359dd7752 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_grenade_dispatch.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_grenade_dispatch.cpp @@ -124,6 +124,12 @@ Action< CNEOBot > *CNEOBotGrenadeDispatch::ChooseGrenadeThrowBehavior( const CNE } } + // Don't commit to a throw when there's nowhere to throw from yet + if ( me->FindVisibleThrowPointNear( threat->GetLastKnownPosition() ) == vec3_invalid ) + { + return nullptr; + } + return new CNEOBotGrenadeThrowFrag( pFragGrenade, threat ); } diff --git a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw.cpp b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw.cpp index 780665191..968426b1a 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw.cpp @@ -7,7 +7,6 @@ #include "weapon_grenade.h" #include "weapon_smokegrenade.h" #include "nav_mesh.h" -#include "nav_pathfind.h" #include "bot/neo_bot_path_compute.h" extern ConVar sv_neo_bot_grenade_frag_safety_range_multiplier; @@ -44,68 +43,6 @@ CNEOBotGrenadeThrow::CNEOBotGrenadeThrow( CNEOBaseCombatWeapon *pWeapon, const C } } -//--------------------------------------------------------------------------------------------- -// Used to anticipate the emergence point from cover ahead of a path -// Is calculated in reverse of path to find the farthest point from bot -// (assuming "familiar" position is closer to the bot than the "obscured" position) -const Vector& CNEOBotGrenadeThrow::FindEmergencePointAlongPath( const CNEOBot *me, const Vector &familiarPos, const Vector &obscuredPos ) -{ - CNavArea *familiarArea = TheNavMesh->GetNavArea( familiarPos ); - if ( !familiarArea ) - { - return vec3_invalid; - } - - CNavArea *obscuredArea = TheNavMesh->GetNavArea( obscuredPos ); - if ( !obscuredArea ) - { - return vec3_invalid; - } - - ShortestPathCost cost; - const Vector& vecGoal = obscuredPos; - if ( NavAreaBuildPath( familiarArea, obscuredArea, &vecGoal, cost ) ) - { - // search backwards from obscured position to find the first point visible to me - for ( CNavArea *area = obscuredArea; area; area = area->GetParent() ) - { - // DEBUG: Draw emergence path - // Color: Yellow (255, 255, 0) to distinguish path analysis - if ( sv_neo_bot_grenade_debug_behavior.GetBool() ) - { - if ( area->GetParent() ) - { - NDebugOverlay::HorzArrow( area->GetCenter(), area->GetParent()->GetCenter(), 2.0f, 255, 255, 0, 255, true, 2.0f ); - } - else - { - NDebugOverlay::Cross3D( area->GetCenter(), 16.0f, 255, 255, 0, true, 2.0f ); - } - } - - const Vector& vecTest = area->GetCenter(); - - if ( me->IsLineOfFireClear( vecTest, CNEOBot::LINE_OF_FIRE_FLAGS_SHOTGUN ) ) - { - // DEBUG: Draw emergence point - if ( sv_neo_bot_grenade_debug_behavior.GetBool() ) - { - NDebugOverlay::Box( vecTest, Vector(-16,-16,-16), Vector(16,16,16), 255, 255, 0, 50, 2.0f ); - } - return vecTest; - } - - if ( area == familiarArea ) - { - return vec3_invalid; - } - } - } - - return vec3_invalid; -} - - //--------------------------------------------------------------------------------------------- class CFindVantagePointTargetPos : public ISearchSurroundingAreasFunctor { @@ -524,6 +461,8 @@ ActionResult< CNEOBot > CNEOBotGrenadeThrow::Update( CNEOBot *me, float interval // and want to bypass the weapon's internal scheduled throw to ensure it happens NOW. if ( bAimOnTarget ) { + OnThrowReleased( me ); + // Play first person throw animation pWep->SendWeaponAnim( ACT_VM_THROW ); diff --git a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw.h b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw.h index 54a55e8c9..67bba14a6 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw.h +++ b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw.h @@ -52,9 +52,8 @@ class CNEOBotGrenadeThrow : public Action< CNEOBot > CNavArea *FindVantageArea( CNEOBot *me ); - static const Vector& FindEmergencePointAlongPath( const CNEOBot *me, const Vector &familiarPos, const Vector &obscuredPos ); - virtual ThrowTargetResult UpdateGrenadeTargeting( CNEOBot *me, CNEOBaseCombatWeapon *pWeapon ) = 0; + virtual void OnThrowReleased( CNEOBot *me ) { } }; diff --git a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_frag.cpp b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_frag.cpp index 893d5b7e0..f28686ea1 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_frag.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_frag.cpp @@ -1,6 +1,5 @@ #include "cbase.h" #include "bot/neo_bot.h" -#include "bot/neo_bot_path_compute.h" #include "bot/neo_bot_path_reservation.h" #include "bot/behavior/neo_bot_grenade_throw_frag.h" #include "neo_gamerules.h" @@ -8,7 +7,6 @@ #include "weapon_neobasecombatweapon.h" #include "nav_mesh.h" -#include "nav_pathfind.h" extern ConVar sv_neo_grenade_blast_radius; extern ConVar sv_neo_grenade_fuse_timer; @@ -69,11 +67,9 @@ CNEOBotGrenadeThrow::ThrowTargetResult CNEOBotGrenadeThrowFrag::UpdateGrenadeTar { // Last known location in view, but don't see threat // Infer where the threat could have gone - // CHEAT: calculate a path from the last known position to the actual threat position - // and throw at the furthest visible point along that path - if ( me->IsLineOfSightClear( m_vecThreatLastKnownPos ) && m_scanTimer.IsElapsed() ) + if ( m_scanTimer.IsElapsed() ) { - const Vector& vecThrowTarget = FindEmergencePointAlongPath( me, m_vecThreatLastKnownPos, m_hThreatGrenadeTarget->GetAbsOrigin() ); + const Vector vecThrowTarget = me->FindVisibleThrowPointNear( m_vecThreatLastKnownPos ); if ( vecThrowTarget != vec3_invalid ) { @@ -108,6 +104,11 @@ CNEOBotGrenadeThrow::ThrowTargetResult CNEOBotGrenadeThrowFrag::UpdateGrenadeTar return THROW_TARGET_CANCEL; // risk of friendly fire } + return THROW_TARGET_READY; +} + +void CNEOBotGrenadeThrowFrag::OnThrowReleased( CNEOBot *me ) +{ // Register explosive hazard at the target position // so the throwing bot's team can avoid the trajectory and landing areas CNavArea *area = TheNavMesh->GetNearestNavArea(m_vecTarget); @@ -116,8 +117,6 @@ CNEOBotGrenadeThrow::ThrowTargetResult CNEOBotGrenadeThrowFrag::UpdateGrenadeTar int team = me->GetTeamNumber(); CNEOBotPathReservations()->AddFragHazard(area->GetID(), gpGlobals->curtime + sv_neo_grenade_fuse_timer.GetFloat(), team); } - - return THROW_TARGET_READY; } float CNEOBotGrenadeThrowFrag::GetFragSafetyRadius() diff --git a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_frag.h b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_frag.h index d4e6666ec..e5f1fa1c2 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_frag.h +++ b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_frag.h @@ -21,6 +21,7 @@ class CNEOBotGrenadeThrowFrag : public CNEOBotGrenadeThrow protected: virtual ThrowTargetResult UpdateGrenadeTargeting( CNEOBot *me, CNEOBaseCombatWeapon *pWeapon ) override; + virtual void OnThrowReleased( CNEOBot *me ) override; }; #endif // NEO_BOT_GRENADE_THROW_FRAG_H diff --git a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_smoke.cpp b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_smoke.cpp index 93feea29b..48e445aa3 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_smoke.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_grenade_throw_smoke.cpp @@ -2,11 +2,8 @@ #include "neo_player.h" #include "bot/neo_bot.h" #include "bot/behavior/neo_bot_grenade_throw_smoke.h" -#include "bot/neo_bot_path_compute.h" #include "weapon_neobasecombatweapon.h" -#include "nav_pathfind.h" - CNEOBotGrenadeThrowSmoke::CNEOBotGrenadeThrowSmoke( CNEOBaseCombatWeapon *pWeapon, const CKnownEntity *threat ) : CNEOBotGrenadeThrow( pWeapon, threat ) { @@ -29,7 +26,7 @@ CNEOBotGrenadeThrow::ThrowTargetResult CNEOBotGrenadeThrowSmoke::UpdateGrenadeTa if (m_vecTarget == vec3_invalid) { - const Vector& vecThrowTarget = FindEmergencePointAlongPath(me, me->GetAbsOrigin(), m_vecThreatLastKnownPos); + const Vector vecThrowTarget = me->FindVisibleThrowPointNear( m_vecThreatLastKnownPos ); if (vecThrowTarget != vec3_invalid) { diff --git a/src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp b/src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp index ff0dc03fb..b53734489 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_retreat_to_cover.cpp @@ -274,10 +274,10 @@ ActionResult< CNEOBot > CNEOBotRetreatToCover::Update( CNEOBot *me, float interv if ( ( !m_grenadeThrowCooldownTimer.HasStarted() || m_grenadeThrowCooldownTimer.IsElapsed() ) && threat && threat->GetEntity() && !me->IsLineOfFireClear( threat->GetEntity()->EyePosition(), CNEOBot::LINE_OF_FIRE_FLAGS_DEFAULT ) ) { + m_grenadeThrowCooldownTimer.Start( sv_neo_bot_grenade_throw_cooldown.GetFloat() ); Action *pGrenadeBehavior = CNEOBotGrenadeDispatch::ChooseGrenadeThrowBehavior( me, threat ); if ( pGrenadeBehavior ) { - m_grenadeThrowCooldownTimer.Start( sv_neo_bot_grenade_throw_cooldown.GetFloat() ); return SuspendFor( pGrenadeBehavior, "Throwing grenade while taking cover!" ); } } diff --git a/src/game/server/neo/bot/neo_bot.cpp b/src/game/server/neo/bot/neo_bot.cpp index 0d0ec60a5..675822d30 100644 --- a/src/game/server/neo/bot/neo_bot.cpp +++ b/src/game/server/neo/bot/neo_bot.cpp @@ -1148,10 +1148,10 @@ void CNEOBot::OnWeaponFired(CBaseCombatCharacter* whoFired, CBaseCombatWeapon* w //----------------------------------------------------------------------------------------------------- -class CFindClosestPotentiallyVisibleAreaToPos +class CNEOFindClosestPotentiallyVisibleAreaToPos { public: - CFindClosestPotentiallyVisibleAreaToPos(const Vector& pos) + CNEOFindClosestPotentiallyVisibleAreaToPos(const Vector& pos) { m_pos = pos; m_closeArea = NULL; @@ -1181,6 +1181,34 @@ class CFindClosestPotentiallyVisibleAreaToPos }; +//----------------------------------------------------------------------------------------------------- +Vector CNEOBot::FindVisibleThrowPointNear( const Vector &vecPos ) const +{ + CNavArea *myArea = GetLastKnownArea(); + if ( !myArea ) + { + return vec3_invalid; + } + + CNEOFindClosestPotentiallyVisibleAreaToPos find( vecPos ); + myArea->ForAllPotentiallyVisibleAreas( find ); + if ( !find.m_closeArea ) + { + return vec3_invalid; + } + + Vector vecPoint; + find.m_closeArea->GetClosestPointOnArea( vecPos, &vecPoint ); + if ( IsThrowLineClear( vecPoint ) ) + { + return vecPoint; + } + + const Vector &vecCenter = find.m_closeArea->GetCenter(); + return IsThrowLineClear( vecCenter ) ? vecCenter : vec3_invalid; +} + + //----------------------------------------------------------------------------------------------------- // Update our view to watch where members of the given team will be coming from void CNEOBot::UpdateLookingAroundForIncomingPlayers(bool lookForEnemies) @@ -1255,7 +1283,7 @@ void CNEOBot::UpdateLookingAroundForEnemies(void) if (myArea) { const CNavArea* closeArea = NULL; - CFindClosestPotentiallyVisibleAreaToPos find(known->GetLastKnownPosition()); + CNEOFindClosestPotentiallyVisibleAreaToPos find(known->GetLastKnownPosition()); myArea->ForAllPotentiallyVisibleAreas(find); closeArea = find.m_closeArea; @@ -2035,6 +2063,24 @@ bool CNEOBot::IsLineOfFireClear(const Vector& where, const LineOfFireFlags flags } +//----------------------------------------------------------------------------------------------------- +// Return true if a thrown object (like a grenade) has a completely unobstructed physical line to the target. +// Unlike IsLineOfFireClear, this requires !trace.DidHit() and will NOT treat breakable entities (like glass) as clear. +bool CNEOBot::IsThrowLineClear(const Vector& from, const Vector& to) const +{ + trace_t trace; + NextBotTraceFilterIgnoreActors filter(NULL, COLLISION_GROUP_NONE); + UTIL_TraceLine(from, to, MASK_SHOT, &filter, &trace); + return !trace.DidHit(); +} + +//----------------------------------------------------------------------------------------------------- +bool CNEOBot::IsThrowLineClear(const Vector& where) const +{ + return IsThrowLineClear(const_cast(this)->EyePosition(), where); +} + + //----------------------------------------------------------------------------------------------------- // Return whether there is a friendly player blocking the line of fire bool CNEOBot::IsLineOfFireClearOfFriendlies(const Vector& from, CBaseEntity* who) const diff --git a/src/game/server/neo/bot/neo_bot.h b/src/game/server/neo/bot/neo_bot.h index 5f388f5d3..cfd8a422a 100644 --- a/src/game/server/neo/bot/neo_bot.h +++ b/src/game/server/neo/bot/neo_bot.h @@ -230,6 +230,10 @@ class CNEOBot : public NextBotPlayer< CNEO_Player >, public CGameEventListener bool IsLineOfFireClearOfFriendlies(const Vector& from, const Vector& to) const; void RepathIfFriendlyBlockingLineOfFire(); + Vector FindVisibleThrowPointNear( const Vector &vecPos ) const; + bool IsThrowLineClear(const Vector& where) const; + bool IsThrowLineClear(const Vector& from, const Vector& to) const; + bool IsEntityBetweenTargetAndSelf(CBaseEntity* other, CBaseEntity* target); // return true if "other" is positioned inbetween us and "target" CNEO_Player* GetClosestHumanLookingAtMe(int team = TEAM_ANY); // return the nearest human player on the given team who is looking directly at me