diff --git a/src/game/server/CMakeLists.txt b/src/game/server/CMakeLists.txt index 386a7225e7..e251eb5167 100644 --- a/src/game/server/CMakeLists.txt +++ b/src/game/server/CMakeLists.txt @@ -1468,6 +1468,8 @@ target_sources_grouped( neo/bot/behavior/neo_bot_seek_weapon.h neo/bot/behavior/neo_bot_tactical_monitor.cpp neo/bot/behavior/neo_bot_tactical_monitor.h + neo/bot/behavior/neo_bot_throw_weapon_at_player.cpp + neo/bot/behavior/neo_bot_throw_weapon_at_player.h neo/bot/behavior/nav_entities/neo_bot_nav_ent_destroy_entity.cpp neo/bot/behavior/nav_entities/neo_bot_nav_ent_destroy_entity.h neo/bot/behavior/nav_entities/neo_bot_nav_ent_move_to.cpp diff --git a/src/game/server/NextBot/Player/NextBotPlayer.h b/src/game/server/NextBot/Player/NextBotPlayer.h index 2a3c846325..d12df0fe5e 100644 --- a/src/game/server/NextBot/Player/NextBotPlayer.h +++ b/src/game/server/NextBot/Player/NextBotPlayer.h @@ -122,6 +122,9 @@ class INextBotPlayerInput virtual void ReleaseJumpButton( void ) = 0; #ifdef NEO + virtual void PressDropButton( float duration = -1.0f ) = 0; + virtual void ReleaseDropButton( void ) = 0; + virtual void PressThermopticButton( float duration = -1.0f ) = 0; virtual void ReleaseThermopticButton( void ) = 0; #endif // NEO @@ -202,6 +205,9 @@ class NextBotPlayer : public PlayerType, public INextBot, public INextBotPlayerI virtual void ReleaseSpecialFireButton( void ); #ifdef NEO + virtual void PressDropButton( float duration = -1.0f ); + virtual void ReleaseDropButton( void ); + virtual void PressThermopticButton( float duration = -1.0f ); virtual void ReleaseThermopticButton( void ); #endif @@ -286,6 +292,7 @@ class NextBotPlayer : public PlayerType, public INextBot, public INextBotPlayerI CountdownTimer m_walkButtonTimer; CountdownTimer m_buttonScaleTimer; #ifdef NEO + CountdownTimer m_dropButtonTimer; CountdownTimer m_thermopticButtonTimer; CountdownTimer m_leanLeftButtonTimer; CountdownTimer m_leanRightButtonTimer; @@ -450,6 +457,20 @@ inline void NextBotPlayer< PlayerType >::ReleaseJumpButton( void ) } #ifdef NEO +template < typename PlayerType > +inline void NextBotPlayer< PlayerType >::PressDropButton( float duration ) +{ + m_inputButtons |= IN_DROP; + m_dropButtonTimer.Start( duration ); +} + +template < typename PlayerType > +inline void NextBotPlayer< PlayerType >::ReleaseDropButton( void ) +{ + m_inputButtons &= ~IN_DROP; + m_dropButtonTimer.Invalidate(); +} + template < typename PlayerType > inline void NextBotPlayer< PlayerType >::PressThermopticButton( float duration ) { @@ -642,6 +663,7 @@ inline void NextBotPlayer< PlayerType >::Spawn( void ) m_forwardScale = m_rightScale = 0.04; m_burningTimer.Invalidate(); #ifdef NEO + m_dropButtonTimer.Invalidate(); m_thermopticButtonTimer.Invalidate(); m_leanLeftButtonTimer.Invalidate(); m_leanRightButtonTimer.Invalidate(); @@ -780,6 +802,9 @@ inline void NextBotPlayer< PlayerType >::PhysicsSimulate( void ) m_inputButtons |= IN_SPEED; #ifdef NEO + if ( !m_dropButtonTimer.IsElapsed() ) + m_inputButtons |= IN_DROP; + if ( !m_leanLeftButtonTimer.IsElapsed() ) m_inputButtons |= IN_LEAN_LEFT; diff --git a/src/game/server/neo/bot/behavior/neo_bot_command_follow.cpp b/src/game/server/neo/bot/behavior/neo_bot_command_follow.cpp index adaa7545a1..8ac29c37cd 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_command_follow.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_command_follow.cpp @@ -2,6 +2,7 @@ #include "neo_player.h" #include "bot/neo_bot.h" #include "bot/behavior/neo_bot_command_follow.h" +#include "bot/behavior/neo_bot_throw_weapon_at_player.h" #include "nav_mesh.h" // memdbgon must be the last include file in a .cpp file!!! @@ -36,6 +37,8 @@ CNEOBotCommandFollow::CNEOBotCommandFollow() : m_vGoalPos( CNEO_Player::VECTOR_I ActionResult< CNEOBot > CNEOBotCommandFollow::OnStart(CNEOBot *me, Action< CNEOBot > *priorAction) { m_path.SetMinLookAheadDistance(me->GetDesiredPathLookAheadRange()); + m_commanderLookingAtMeTimer.Invalidate(); + m_bWasCommanderLookingAtMe = false; if (!FollowCommandChain(me)) { @@ -53,6 +56,81 @@ ActionResult< CNEOBot > CNEOBotCommandFollow::Update(CNEOBot *me, float interval return Done("Lost commander or released"); } + ActionResult weaponRequestResult = CheckCommanderWeaponRequest(me); + if (weaponRequestResult.IsRequestingChange()) + { + return weaponRequestResult; + } + + return Continue(); +} + + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotCommandFollow::CheckCommanderWeaponRequest(CNEOBot *me) +{ + CNEO_Player* pCommander = me->m_hCommandingPlayer.Get(); + if (pCommander && pCommander->IsAlive()) + { + // Check if commander has dropped primary + if (!pCommander->Weapon_GetSlot(0)) + { + // Check if looking at me + Vector vecToBot = me->EyePosition() - pCommander->EyePosition(); + vecToBot.NormalizeInPlace(); + Vector vecCmdrFacing; + pCommander->EyeVectors(&vecCmdrFacing); + + if (vecCmdrFacing.Dot(vecToBot) > 0.99f) + { + if (!m_bWasCommanderLookingAtMe) + { + // Wait to see if it wasn't just a momentary glance + m_commanderLookingAtMeTimer.Start(); + m_bWasCommanderLookingAtMe = true; + } + + if (m_commanderLookingAtMeTimer.GetElapsedTime() > 0.2f) + { + // Look at commander after 0.5s + me->GetBodyInterface()->AimHeadTowards(pCommander->EyePosition(), IBody::CRITICAL, 0.2f, NULL, "Commander looking at me without a primary"); + } + + if (m_commanderLookingAtMeTimer.GetElapsedTime() > 1.0f) + { + // Sanity check that commander is really looking at me with more expensive traceline + // e.g. for edge case where I am behind the player the commander is looking at + trace_t tr; + const float estimatedReasonableThrowDistance = 300.0f; + Vector traceEnd = pCommander->EyePosition() + vecCmdrFacing * estimatedReasonableThrowDistance; + UTIL_TraceLine(pCommander->EyePosition(), traceEnd, MASK_SHOT_HULL, pCommander, COLLISION_GROUP_NONE, &tr); + + if (tr.DidHit() && tr.m_pEnt == me) + { + m_bWasCommanderLookingAtMe = false; // Reset state + return SuspendFor(new CNEOBotThrowWeaponAtPlayer(pCommander), "Donating weapon to commander"); + } + else + { + // Line of sight blocked + m_commanderLookingAtMeTimer.Invalidate(); + m_bWasCommanderLookingAtMe = false; + } + } + } + else + { + m_commanderLookingAtMeTimer.Invalidate(); + m_bWasCommanderLookingAtMe = false; + } + } + else + { + m_commanderLookingAtMeTimer.Invalidate(); + m_bWasCommanderLookingAtMe = false; + } + } + m_path.Update(me); return Continue(); diff --git a/src/game/server/neo/bot/behavior/neo_bot_command_follow.h b/src/game/server/neo/bot/behavior/neo_bot_command_follow.h index 49035ca12a..3499bad841 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_command_follow.h +++ b/src/game/server/neo/bot/behavior/neo_bot_command_follow.h @@ -21,10 +21,14 @@ class CNEOBotCommandFollow : public Action< CNEOBot > private: bool FollowCommandChain( CNEOBot *me ); bool FanOutAndCover( CNEOBot *me, Vector &movementTarget, bool bMoveToSeparate = true, float flArrivalZoneSizeSq = -1.0f ); + ActionResult< CNEOBot > CheckCommanderWeaponRequest( CNEOBot *me ); PathFollower m_path; CountdownTimer m_repathTimer; + IntervalTimer m_commanderLookingAtMeTimer; + bool m_bWasCommanderLookingAtMe = false; + EHANDLE m_hTargetEntity; bool m_bGoingToTargetEntity = false; Vector m_vGoalPos; diff --git a/src/game/server/neo/bot/behavior/neo_bot_scenario_monitor.cpp b/src/game/server/neo/bot/behavior/neo_bot_scenario_monitor.cpp index 2ef7bf96e7..cb0b348e02 100644 --- a/src/game/server/neo/bot/behavior/neo_bot_scenario_monitor.cpp +++ b/src/game/server/neo/bot/behavior/neo_bot_scenario_monitor.cpp @@ -13,6 +13,8 @@ #include "bot/behavior/neo_bot_retreat_to_cover.h" #include "bot/behavior/neo_bot_get_health.h" #include "bot/behavior/neo_bot_get_ammo.h" +#include "bot/behavior/neo_bot_command_follow.h" +#include "bot/behavior/neo_bot_pause.h" #include "bot/behavior/neo_bot_attack.h" #include "bot/behavior/neo_bot_seek_and_destroy.h" @@ -64,10 +66,26 @@ ActionResult< CNEOBot > CNEOBotScenarioMonitor::OnStart( CNEOBot *me, Action< CN ConVar neo_bot_fetch_lost_flag_time( "neo_bot_fetch_lost_flag_time", "10", FCVAR_CHEAT, "How long busy NEOBots will ignore the dropped flag before they give up what they are doing and go after it" ); ConVar neo_bot_flag_kill_on_touch( "neo_bot_flag_kill_on_touch", "0", FCVAR_CHEAT, "If nonzero, any bot that picks up the flag dies. For testing." ); +extern ConVar sv_neo_bot_cmdr_enable; +extern ConVar sv_neo_bot_cmdr_debug_pause_uncommanded; + //----------------------------------------------------------------------------------------- ActionResult< CNEOBot > CNEOBotScenarioMonitor::Update( CNEOBot *me, float interval ) { + if (sv_neo_bot_cmdr_enable.GetBool()) + { + if (me->m_hLeadingPlayer.Get() || me->m_hCommandingPlayer.Get()) + { + return SuspendFor(new CNEOBotCommandFollow, "Following commander"); + } + + if (sv_neo_bot_cmdr_debug_pause_uncommanded.GetBool()) + { + return SuspendFor(new CNEOBotPause, "Paused by debug convar sv_neo_bot_cmdr_debug_pause_uncommanded"); + } + } + return Continue(); } 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 70dca838ee..4e1049c9f7 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 @@ -10,7 +10,6 @@ #include "bot/behavior/neo_bot_tactical_monitor.h" #include "bot/behavior/neo_bot_scenario_monitor.h" -#include "bot/behavior/neo_bot_command_follow.h" #include "bot/behavior/neo_bot_seek_and_destroy.h" #include "bot/behavior/neo_bot_seek_weapon.h" #include "bot/behavior/neo_bot_retreat_to_cover.h" @@ -26,8 +25,6 @@ ConVar neo_bot_force_jump( "neo_bot_force_jump", "0", FCVAR_CHEAT, "Force bots to continuously jump" ); ConVar neo_bot_grenade_check_radius( "neo_bot_grenade_check_radius", "500", FCVAR_CHEAT ); -extern ConVar sv_neo_bot_cmdr_enable; -extern ConVar sv_neo_bot_cmdr_debug_pause_uncommanded; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -279,19 +276,6 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::Update( CNEOBot *me, float inter return result; } - if (sv_neo_bot_cmdr_enable.GetBool()) - { - if (me->m_hLeadingPlayer.Get() || me->m_hCommandingPlayer.Get()) - { - return SuspendFor(new CNEOBotCommandFollow, "Following commander"); - } - - if (sv_neo_bot_cmdr_debug_pause_uncommanded.GetBool()) - { - return SuspendFor( new CNEOBotPause, "Paused by debug convar sv_neo_bot_cmdr_debug_pause_uncommanded" ); - } - } - const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat(); // check if we need to get to cover @@ -370,7 +354,7 @@ ActionResult< CNEOBot > CNEOBotTacticalMonitor::ScavengeForPrimaryWeapon( CNEOBo { return Continue(); } - m_maintainTimer.Start( RandomFloat( 1.0f, 3.0f ) ); + m_maintainTimer.Start( 1.0f ); // Look for any one valid primary weapon, then dispatch into behavior for more optimal search // true parameter: short-circuit the search if any valid primary weapon is found diff --git a/src/game/server/neo/bot/behavior/neo_bot_throw_weapon_at_player.cpp b/src/game/server/neo/bot/behavior/neo_bot_throw_weapon_at_player.cpp new file mode 100644 index 0000000000..00b4425982 --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_throw_weapon_at_player.cpp @@ -0,0 +1,63 @@ +#include "cbase.h" +#include "bot/neo_bot.h" +#include "neo_player.h" +#include "bot/behavior/neo_bot_throw_weapon_at_player.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//--------------------------------------------------------------------------------------------- +CNEOBotThrowWeaponAtPlayer::CNEOBotThrowWeaponAtPlayer( CNEO_Player *pTargetPlayer ) +{ + m_hTargetPlayer = pTargetPlayer; +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotThrowWeaponAtPlayer::OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) +{ + if ( !m_hTargetPlayer ) + { + return Done( "No target player to throw weapon at" ); + } + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +ActionResult< CNEOBot > CNEOBotThrowWeaponAtPlayer::Update( CNEOBot *me, float interval ) +{ + CNEO_Player *pTarget = m_hTargetPlayer.Get(); + if ( !pTarget || !pTarget->IsAlive() ) + { + return Done( "Target player lost or died" ); + } + + me->GetBodyInterface()->AimHeadTowards( pTarget->EyePosition(), IBody::CRITICAL, 0.2f, NULL, "Aiming at player to throw weapon" ); + + CBaseCombatWeapon *pPrimary = me->Weapon_GetSlot( 0 ); + if ( pPrimary ) + { + if ( me->GetActiveWeapon() != pPrimary ) + { + me->Weapon_Switch( pPrimary ); + return Continue(); // Wait for switch + } + } + else + { + return Done( "No primary weapon to throw" ); + } + + if ( me->GetBodyInterface()->IsHeadAimingOnTarget() ) + { + me->PressDropButton(); + return Done("Weapon dropped"); + } + + return Continue(); +} + +//--------------------------------------------------------------------------------------------- +void CNEOBotThrowWeaponAtPlayer::OnEnd( CNEOBot *me, Action< CNEOBot > *nextAction ) +{ +} diff --git a/src/game/server/neo/bot/behavior/neo_bot_throw_weapon_at_player.h b/src/game/server/neo/bot/behavior/neo_bot_throw_weapon_at_player.h new file mode 100644 index 0000000000..53e23cd93a --- /dev/null +++ b/src/game/server/neo/bot/behavior/neo_bot_throw_weapon_at_player.h @@ -0,0 +1,26 @@ +#ifndef NEO_BOT_THROW_WEAPON_AT_PLAYER_H +#define NEO_BOT_THROW_WEAPON_AT_PLAYER_H +#ifdef _WIN32 +#pragma once +#endif + +#include "NextBotBehavior.h" + +class CNEO_Player; + +class CNEOBotThrowWeaponAtPlayer : public Action< CNEOBot > +{ +public: + CNEOBotThrowWeaponAtPlayer( CNEO_Player *pTargetPlayer ); + + virtual ActionResult< CNEOBot > OnStart( CNEOBot *me, Action< CNEOBot > *priorAction ) override; + virtual ActionResult< CNEOBot > Update( CNEOBot *me, float interval ) override; + virtual void OnEnd( CNEOBot *me, Action< CNEOBot > *nextAction ) override; + + virtual const char *GetName( void ) const override { return "ThrowWeaponAtPlayer"; }; + +private: + CHandle m_hTargetPlayer; +}; + +#endif // NEO_BOT_THROW_WEAPON_AT_PLAYER_H