44#include " bot/behavior/neo_bot_attack.h"
55#include " bot/behavior/neo_bot_ctg_capture.h"
66#include " bot/behavior/neo_bot_ctg_lone_wolf.h"
7+ #include " bot/behavior/neo_bot_ctg_lone_wolf_ambush.h"
78#include " bot/behavior/neo_bot_seek_weapon.h"
89#include " bot/behavior/neo_bot_retreat_to_cover.h"
910#include " bot/neo_bot_path_compute.h"
1011#include " neo_gamerules.h"
1112#include " neo_ghost_cap_point.h"
13+ #include " weapon_detpack.h"
1214#include " weapon_ghost.h"
1315
1416
@@ -17,7 +19,6 @@ CNEOBotCtgLoneWolf::CNEOBotCtgLoneWolf( void )
1719{
1820 m_hGhost = nullptr ;
1921 m_bPursuingDropThreat = false ;
20- m_bHasRetreatedFromGhost = false ;
2122 m_vecDropThreatPos = CNEO_Player::VECTOR_INVALID_WAYPOINT;
2223 m_closestCapturePoint = CNEO_Player::VECTOR_INVALID_WAYPOINT;
2324}
@@ -26,7 +27,7 @@ CNEOBotCtgLoneWolf::CNEOBotCtgLoneWolf( void )
2627ActionResult< CNEOBot > CNEOBotCtgLoneWolf::OnStart ( CNEOBot *me, Action< CNEOBot > *priorAction )
2728{
2829 m_hGhost = nullptr ;
29- m_bHasRetreatedFromGhost = false ;
30+ m_hPursueTarget = nullptr ;
3031 m_bPursuingDropThreat = false ;
3132 m_useAttemptTimer.Invalidate ();
3233 m_lookAroundTimer.Invalidate ();
@@ -35,7 +36,6 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::OnStart( CNEOBot *me, Action< CNEOBo
3536 m_capPointUpdateTimer.Invalidate ();
3637 m_vecDropThreatPos = CNEO_Player::VECTOR_INVALID_WAYPOINT;
3738 m_closestCapturePoint = CNEO_Player::VECTOR_INVALID_WAYPOINT;
38- m_hPursueTarget = nullptr ;
3939
4040 return Continue ();
4141}
@@ -84,42 +84,15 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::Update( CNEOBot *me, float interval
8484 }
8585
8686 // Always need to find the ghost to act on it
87- if (!m_hGhost)
88- {
89- m_hGhost = dynamic_cast <CWeaponGhost*>( gEntList .FindEntityByClassname (nullptr , " weapon_ghost" ) );
90- }
91-
92- if (!m_hGhost)
87+ if ( !UpdateGhostHandle ( me ) )
9388 {
9489 return Done ( " Ghost not found" );
9590 }
9691
9792 // Occasionally reconsider which cap zone is our goal
9893 if ( !m_capPointUpdateTimer.HasStarted () || m_capPointUpdateTimer.IsElapsed () )
9994 {
100- m_closestCapturePoint = CNEO_Player::VECTOR_INVALID_WAYPOINT;
101- float flNearestCapDistSq = FLT_MAX;
102-
103- if ( NEORules ()->m_pGhostCaps .Count () > 0 )
104- {
105- const Vector& vecStart = me->IsCarryingGhost () ? me->GetAbsOrigin () : m_hGhost->GetAbsOrigin ();
106-
107- for ( int i=0 ; i<NEORules ()->m_pGhostCaps .Count (); ++i )
108- {
109- CNEOGhostCapturePoint *pCapPoint = dynamic_cast <CNEOGhostCapturePoint*>( UTIL_EntityByIndex ( NEORules ()->m_pGhostCaps [i] ) );
110- if ( !pCapPoint ) continue ;
111-
112- if ( pCapPoint->owningTeamAlternate () == me->GetTeamNumber () )
113- {
114- float distSq = vecStart.DistToSqr ( pCapPoint->GetAbsOrigin () );
115- if ( distSq < flNearestCapDistSq )
116- {
117- flNearestCapDistSq = distSq;
118- m_closestCapturePoint = pCapPoint->GetAbsOrigin ();
119- }
120- }
121- }
122- }
95+ m_closestCapturePoint = GetNearestCapturePoint ( me, false );
12396 m_capPointUpdateTimer.Start ( RandomFloat ( 0 .5f , 1 .0f ) );
12497 }
12598
@@ -291,11 +264,20 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::Update( CNEOBot *me, float interval
291264 // Ghost is free for taking
292265 if ( bSafeToCap || (bIs1v1 && m_stalemateTimer.HasStarted () && m_stalemateTimer.IsElapsed ()) )
293266 {
294- // Try to cap before enemy can stop us.
295267 float flDistToGhostSq = me->GetAbsOrigin ().DistToSqr (m_hGhost->GetAbsOrigin ());
296- if ( flDistToGhostSq < 100 .0f * 100 .0f )
268+ float flAmbushDistSq = CNEOBotCtgLoneWolf::GetDetpackDeployDistanceSq ( me );
269+
270+ if ( flDistToGhostSq < flAmbushDistSq)
297271 {
298- return SuspendFor (new CNEOBotCtgCapture (m_hGhost.Get ()), " Picking up ghost to make a run for it!" );
272+ if ( !bSafeToCap && me->Weapon_OwnsThisType (" weapon_remotedet" ) )
273+ {
274+ return ChangeTo (new CNEOBotCtgLoneWolfAmbush (), " Setting up detpack ambush at ghost" );
275+ }
276+ else
277+ {
278+ // Try to cap before enemy can stop us.
279+ return SuspendFor (new CNEOBotCtgCapture (m_hGhost.Get ()), " Picking up ghost to make a run for it!" );
280+ }
299281 }
300282
301283 if ( !m_repathTimer.HasStarted () || m_repathTimer.IsElapsed () )
@@ -320,40 +302,27 @@ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::Update( CNEOBot *me, float interval
320302 m_stalemateTimer.Start ( RandomFloat ( 10 .0f , 20 .0f ) );
321303 }
322304
323- if ( m_bHasRetreatedFromGhost )
305+ // Hide out of sight of ghost to ambush anyone that picks up the ghost
306+ float flDistToGhostSq = me->GetAbsOrigin ().DistToSqr (m_hGhost->GetAbsOrigin ());
307+ float flAmbushDistSq = CNEOBotCtgLoneWolf::GetDetpackDeployDistanceSq ( me );
308+ if (flDistToGhostSq < flAmbushDistSq)
324309 {
325- // Waiting in ambush/cover
326- if (threat && me->IsLineOfFireClear ( threat->GetEntity ()->WorldSpaceCenter (), CNEOBot::LINE_OF_FIRE_FLAGS_DEFAULT ))
327- {
328- me->EnableCloak ( 3 .0f );
329- return SuspendFor (new CNEOBotAttack, " Ambushing enemy near ghost!" );
330- }
331- return UpdateLookAround ( me, m_hGhost->GetAbsOrigin () );
310+ return ChangeTo (new CNEOBotCtgLoneWolfAmbush (), " Setting up ambush near the ghost" );
332311 }
333312 else
334313 {
335- // Hide out of sight of ghost to ambush anyone that picks up the ghost
336- float flDistToGhostSq = me->GetAbsOrigin ().DistToSqr (m_hGhost->GetAbsOrigin ());
337- if (flDistToGhostSq < 300 .0f * 300 .0f )
314+ // Get near the ghost first before surveying hiding spots
315+ if ( !m_repathTimer.HasStarted () || m_repathTimer.IsElapsed () )
338316 {
339- m_bHasRetreatedFromGhost = true ;
340- return SuspendFor (new CNEOBotRetreatToCover (), " Finding a hiding spot near the ghost" );
317+ CNEOBotPathCompute (me, m_path, m_hGhost->GetAbsOrigin (), FASTEST_ROUTE);
318+ m_path.Update (me);
319+ m_repathTimer.Start ( RandomFloat ( 0 .5f , 1 .0f ) );
341320 }
342321 else
343322 {
344- // Get near the ghost first before surveying hiding spots
345- if ( !m_repathTimer.HasStarted () || m_repathTimer.IsElapsed () )
346- {
347- CNEOBotPathCompute (me, m_path, m_hGhost->GetAbsOrigin (), FASTEST_ROUTE);
348- m_path.Update (me);
349- m_repathTimer.Start ( RandomFloat ( 0 .5f , 1 .0f ) );
350- }
351- else
352- {
353- m_path.Update (me);
354- }
355- return Continue ();
323+ m_path.Update (me);
356324 }
325+ return Continue ();
357326 }
358327 }
359328 }
@@ -414,7 +383,50 @@ EventDesiredResult< CNEOBot > CNEOBotCtgLoneWolf::OnMoveToFailure( CNEOBot *me,
414383 return TryContinue ();
415384}
416385
386+ // ---------------------------------------------------------------------------------------------
387+ Vector CNEOBotCtgLoneWolf::GetNearestCapturePoint ( CNEOBot *me, bool bEnemyCapPoint )
388+ {
389+ Vector vecBestCapPoint = CNEO_Player::VECTOR_INVALID_WAYPOINT;
390+ float flNearestCapDistSq = FLT_MAX;
391+
392+ if ( NEORules ()->m_pGhostCaps .Count () > 0 )
393+ {
394+ const Vector& vecStart = me->IsCarryingGhost () ? me->GetAbsOrigin () : ( m_hGhost.Get () ? m_hGhost->GetAbsOrigin () : me->GetAbsOrigin () );
395+
396+ for ( int i=0 ; i<NEORules ()->m_pGhostCaps .Count (); ++i )
397+ {
398+ CNEOGhostCapturePoint *pCapPoint = dynamic_cast <CNEOGhostCapturePoint*>( UTIL_EntityByIndex ( NEORules ()->m_pGhostCaps [i] ) );
399+ if ( !pCapPoint ) continue ;
400+
401+ bool bValidTeam = (pCapPoint->owningTeamAlternate () == me->GetTeamNumber ());
402+ if ( bEnemyCapPoint )
403+ {
404+ bValidTeam = (pCapPoint->owningTeamAlternate () != me->GetTeamNumber ());
405+ }
417406
407+ if ( bValidTeam )
408+ {
409+ float distSq = vecStart.DistToSqr ( pCapPoint->GetAbsOrigin () );
410+ if ( distSq < flNearestCapDistSq )
411+ {
412+ flNearestCapDistSq = distSq;
413+ vecBestCapPoint = pCapPoint->GetAbsOrigin ();
414+ }
415+ }
416+ }
417+ }
418+
419+ return vecBestCapPoint;
420+ }
421+
422+ // ---------------------------------------------------------------------------------------------
423+ float CNEOBotCtgLoneWolf::GetDetpackDeployDistanceSq ( CNEOBot *me )
424+ {
425+ float flArmingTime = CWeaponDetpack::GetArmingTime ();
426+ return Square ( MAX ( 100 .0f , flArmingTime * me->GetNormSpeed () ) );
427+ }
428+
429+ // ---------------------------------------------------------------------------------------------
418430// Helper for "UpdateLookAround" - inspired from how CNavArea CollectPotentiallyVisibleAreas works
419431class CCollectPotentiallyVisibleAreas
420432{
@@ -434,7 +446,18 @@ class CCollectPotentiallyVisibleAreas
434446};
435447
436448// ---------------------------------------------------------------------------------------------
437- ActionResult< CNEOBot > CNEOBotCtgLoneWolf::UpdateLookAround ( CNEOBot *me, const Vector &anchorPos )
449+ bool CNEOBotCtgLoneWolf::UpdateGhostHandle ( CNEOBot *me )
450+ {
451+ if ( !m_hGhost )
452+ {
453+ m_hGhost = dynamic_cast <CWeaponGhost*>( gEntList .FindEntityByClassname ( nullptr , " weapon_ghost" ) );
454+ }
455+
456+ return ( m_hGhost != nullptr );
457+ }
458+
459+ // ---------------------------------------------------------------------------------------------
460+ ActionResult< CNEOBot > CNEOBotCtgLoneWolf::UpdateLookAround ( CNEOBot *me )
438461{
439462 if ( !m_lookAroundTimer.HasStarted () || m_lookAroundTimer.IsElapsed () )
440463 {
0 commit comments