mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-16 13:43:00 +00:00
1
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_prepare_stickybomb_trap.cpp
|
||||
// Place stickybombs to create a deadly trap
|
||||
// Michael Booth, July 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_player.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/demoman/tf_bot_prepare_stickybomb_trap.h"
|
||||
#include "tf_weapon_pipebomblauncher.h"
|
||||
|
||||
#define MAX_STICKYBOMB_COUNT 8
|
||||
|
||||
ConVar tf_bot_stickybomb_density( "tf_bot_stickybomb_density", "0.0001", FCVAR_CHEAT, "Number of stickies to place per square inch" );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class PlaceStickyBombReply : public INextBotReply
|
||||
{
|
||||
public:
|
||||
virtual void OnSuccess( INextBot *bot ) // invoked when process completed successfully
|
||||
{
|
||||
CTFBot *me = ToTFBot( bot->GetEntity() );
|
||||
|
||||
CTFWeaponBase *myCurrentWeapon = me->m_Shared.GetActiveTFWeapon();
|
||||
if ( myCurrentWeapon && myCurrentWeapon->GetWeaponID() == TF_WEAPON_PIPEBOMBLAUNCHER )
|
||||
{
|
||||
// launch the sticky
|
||||
me->PressFireButton( 0.1f );
|
||||
|
||||
// increase the bomb count for this target area
|
||||
if ( m_bombTargetArea )
|
||||
{
|
||||
m_bombTargetArea->m_count++;
|
||||
}
|
||||
|
||||
if( m_pLaunchWaitTimer )
|
||||
{
|
||||
// release the latch
|
||||
m_pLaunchWaitTimer->Start( 0.15f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnFail( INextBot *bot, FailureReason reason )// invoked when process failed
|
||||
{
|
||||
// retry aim immediately
|
||||
m_pLaunchWaitTimer->Invalidate();
|
||||
}
|
||||
|
||||
void ClearData()
|
||||
{
|
||||
// Be sure to clear all members here, as we can potentially get an OnSuccess() call
|
||||
// after the ~CTFBotPrepareStickybombTrap.
|
||||
m_bombTargetArea = NULL;
|
||||
m_pLaunchWaitTimer = NULL;
|
||||
}
|
||||
|
||||
CTFBotPrepareStickybombTrap::BombTargetArea *m_bombTargetArea;
|
||||
CountdownTimer *m_pLaunchWaitTimer;
|
||||
};
|
||||
|
||||
|
||||
static PlaceStickyBombReply bombReply;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotPrepareStickybombTrap::CTFBotPrepareStickybombTrap( void )
|
||||
{
|
||||
m_myArea = NULL;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotPrepareStickybombTrap::~CTFBotPrepareStickybombTrap( )
|
||||
{
|
||||
bombReply.ClearData();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Return true if this Action has what it needs to perform right now
|
||||
bool CTFBotPrepareStickybombTrap::IsPossible( CTFBot *me )
|
||||
{
|
||||
// don't lay a trap if we're in the midst of fighting
|
||||
if ( /*me->IsInCombat() || */ me->GetTimeSinceLastInjury() < 1.0f )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !me->IsPlayerClass( TF_CLASS_DEMOMAN ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CTFPipebombLauncher *stickyLauncher = dynamic_cast< CTFPipebombLauncher * >( me->Weapon_GetSlot( TF_WPN_TYPE_SECONDARY ) );
|
||||
if ( stickyLauncher && !me->IsWeaponRestricted( stickyLauncher ) )
|
||||
{
|
||||
if ( stickyLauncher->GetPipeBombCount() >= MAX_STICKYBOMB_COUNT || me->GetAmmoCount( TF_AMMO_SECONDARY ) <= 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotPrepareStickybombTrap::InitBombTargetAreas( CTFBot *me )
|
||||
{
|
||||
const CUtlVector< CTFNavArea * > &invasionAreaVector = m_myArea->GetEnemyInvasionAreaVector( me->GetTeamNumber() );
|
||||
|
||||
// randomly shuffle the target areas
|
||||
CUtlVector< CTFNavArea * > shuffleVector;
|
||||
shuffleVector = invasionAreaVector;
|
||||
int n = shuffleVector.Count();
|
||||
while( n > 1 )
|
||||
{
|
||||
int k = RandomInt( 0, n-1 );
|
||||
n--;
|
||||
|
||||
CTFNavArea *tmp = shuffleVector[n];
|
||||
shuffleVector[n] = shuffleVector[k];
|
||||
shuffleVector[k] = tmp;
|
||||
}
|
||||
|
||||
// initialize each target area to zero sticky bombs
|
||||
m_bombTargetAreaVector.RemoveAll();
|
||||
|
||||
for( int i=0; i<shuffleVector.Count(); ++i )
|
||||
{
|
||||
BombTargetArea target;
|
||||
target.m_area = shuffleVector[i];
|
||||
target.m_count = 0;
|
||||
|
||||
m_bombTargetAreaVector.AddToTail( target );
|
||||
}
|
||||
|
||||
m_launchWaitTimer.Invalidate();
|
||||
|
||||
// Clean up any in-flight AimHeadTowards() replies, since changing m_bombTargetAreaVector
|
||||
// might move memory and invalidate the current reply pointer.
|
||||
me->GetBodyInterface()->ClearPendingAimReply();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPrepareStickybombTrap::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
// detonate old set of stickies
|
||||
// me->PressAltFireButton();
|
||||
|
||||
// reload entire clip before laying sticky trap
|
||||
CTFPipebombLauncher *stickyLauncher = dynamic_cast< CTFPipebombLauncher * >( me->Weapon_GetSlot( TF_WPN_TYPE_SECONDARY ) );
|
||||
if ( stickyLauncher )
|
||||
{
|
||||
m_isFullReloadNeeded = ( me->GetAmmoCount( TF_AMMO_SECONDARY ) >= stickyLauncher->GetMaxClip1() && stickyLauncher->Clip1() < stickyLauncher->GetMaxClip1() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isFullReloadNeeded = false;
|
||||
}
|
||||
|
||||
m_myArea = me->GetLastKnownArea();
|
||||
if ( !m_myArea )
|
||||
{
|
||||
return Done( "No nav mesh" );
|
||||
}
|
||||
|
||||
InitBombTargetAreas( me );
|
||||
|
||||
// own our view updating so we can aim
|
||||
me->StopLookingAroundForEnemies();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPrepareStickybombTrap::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( !TFGameRules()->InSetup() )
|
||||
{
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat )
|
||||
{
|
||||
const float giveUpRange = 500.0f;
|
||||
if ( me->IsDistanceBetweenLessThan( threat->GetLastKnownPosition(), giveUpRange ) )
|
||||
{
|
||||
return Done( "Enemy nearby - giving up" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( me->GetLastKnownArea() && me->GetLastKnownArea() != m_myArea )
|
||||
{
|
||||
// we've moved
|
||||
m_myArea = me->GetLastKnownArea();
|
||||
InitBombTargetAreas( me );
|
||||
}
|
||||
|
||||
CTFWeaponBase *myCurrentWeapon = me->m_Shared.GetActiveTFWeapon();
|
||||
CTFPipebombLauncher *stickyLauncher = dynamic_cast< CTFPipebombLauncher * >( me->Weapon_GetSlot( TF_WPN_TYPE_SECONDARY ) );
|
||||
|
||||
if ( !myCurrentWeapon || !stickyLauncher )
|
||||
{
|
||||
return Done( "Missing weapon" );
|
||||
}
|
||||
|
||||
if ( myCurrentWeapon->GetWeaponID() != TF_WEAPON_PIPEBOMBLAUNCHER )
|
||||
{
|
||||
me->Weapon_Switch( stickyLauncher );
|
||||
}
|
||||
|
||||
// reload fully
|
||||
if ( m_isFullReloadNeeded )
|
||||
{
|
||||
int maxClip = MIN( stickyLauncher->GetMaxClip1(), me->GetAmmoCount( TF_AMMO_SECONDARY ) );
|
||||
|
||||
if ( stickyLauncher->Clip1() >= maxClip )
|
||||
{
|
||||
// fully reloaded
|
||||
m_isFullReloadNeeded = false;
|
||||
}
|
||||
|
||||
me->PressReloadButton();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
if ( stickyLauncher->GetPipeBombCount() >= MAX_STICKYBOMB_COUNT || me->GetAmmoCount( TF_AMMO_SECONDARY ) <= 0 )
|
||||
{
|
||||
return Done( "Max sticky bombs reached" );
|
||||
}
|
||||
|
||||
|
||||
// aim towards areas where enemy will come from
|
||||
if ( m_launchWaitTimer.IsElapsed() )
|
||||
{
|
||||
// find next target that needs bombs
|
||||
int i;
|
||||
for( i=0; i<m_bombTargetAreaVector.Count(); ++i )
|
||||
{
|
||||
CTFNavArea *targetArea = m_bombTargetAreaVector[i].m_area;
|
||||
|
||||
int desiredCount = tf_bot_stickybomb_density.GetFloat() * targetArea->GetSizeX() * targetArea->GetSizeY();
|
||||
if ( desiredCount < 1 )
|
||||
{
|
||||
desiredCount = 1;
|
||||
}
|
||||
|
||||
if ( m_bombTargetAreaVector[i].m_count < desiredCount )
|
||||
{
|
||||
// place a sticky on this area
|
||||
bombReply.m_bombTargetArea = &m_bombTargetAreaVector[i];
|
||||
|
||||
// this timer causes us to wait until the aim finishes and launched before we start another aim
|
||||
m_launchWaitTimer.Start( 2.0f );
|
||||
bombReply.m_pLaunchWaitTimer = &m_launchWaitTimer;
|
||||
|
||||
Vector bombSpot = targetArea->GetRandomPoint();
|
||||
|
||||
me->GetBodyInterface()->AimHeadTowards( bombSpot, IBody::IMPORTANT, 5.0f, &bombReply, "Aiming a sticky bomb" );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i == m_bombTargetAreaVector.Count() )
|
||||
{
|
||||
return Done( "Exhausted bomb target areas" );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotPrepareStickybombTrap::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
// clean up any in-flight AimHeadTowards() replies
|
||||
me->GetBodyInterface()->ClearPendingAimReply();
|
||||
|
||||
me->StartLookingAroundForEnemies();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPrepareStickybombTrap::OnSuspend( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
// this behavior is transitory - if we need to do something else, just give up
|
||||
return Done();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPrepareStickybombTrap::OnInjured( CTFBot *me, const CTakeDamageInfo &info )
|
||||
{
|
||||
return TryDone( RESULT_IMPORTANT, "Ouch!" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotPrepareStickybombTrap::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_prepare_stickybomb_trap.h
|
||||
// Place stickybombs to create a deadly trap
|
||||
// Michael Booth, July 2010
|
||||
|
||||
#ifndef TF_BOT_PREPARE_STICKYBOMB_TRAP_H
|
||||
#define TF_BOT_PREPARE_STICKYBOMB_TRAP_H
|
||||
|
||||
class CTFBotPrepareStickybombTrap : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotPrepareStickybombTrap( void );
|
||||
virtual ~CTFBotPrepareStickybombTrap( );
|
||||
|
||||
static bool IsPossible( CTFBot *me ); // Return true if this Action has what it needs to perform right now
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
|
||||
virtual ActionResult< CTFBot > OnSuspend( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnInjured( CTFBot *me, const CTakeDamageInfo &info );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "PrepareStickybombTrap"; };
|
||||
|
||||
struct BombTargetArea
|
||||
{
|
||||
CTFNavArea *m_area;
|
||||
int m_count;
|
||||
};
|
||||
|
||||
private:
|
||||
bool m_isFullReloadNeeded;
|
||||
|
||||
CTFNavArea *m_myArea;
|
||||
|
||||
CUtlVector< BombTargetArea > m_bombTargetAreaVector;
|
||||
void InitBombTargetAreas( CTFBot *me );
|
||||
CountdownTimer m_launchWaitTimer;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_PREPARE_STICKYBOMB_TRAP_H
|
||||
@@ -0,0 +1,342 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_stickybomb_sentrygun.cpp
|
||||
// Destroy the given sentrygun with stickybombs
|
||||
// Michael Booth, August 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_player.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/demoman/tf_bot_stickybomb_sentrygun.h"
|
||||
#include "tf_weapon_pipebomblauncher.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "NextBotUtil.h"
|
||||
|
||||
ConVar tf_bot_sticky_base_range( "tf_bot_sticky_base_range", "800", FCVAR_CHEAT );
|
||||
ConVar tf_bot_sticky_charge_rate( "tf_bot_sticky_charge_rate", "0.01", FCVAR_CHEAT, "Seconds of charge per unit range beyond base" );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotStickybombSentrygun::CTFBotStickybombSentrygun( CObjectSentrygun *sentrygun )
|
||||
{
|
||||
m_sentrygun = sentrygun;
|
||||
m_hasGivenAim = false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotStickybombSentrygun::CTFBotStickybombSentrygun( CObjectSentrygun *sentrygun, float aimYaw, float aimPitch, float aimCharge )
|
||||
{
|
||||
m_sentrygun = sentrygun;
|
||||
m_hasGivenAim = true;
|
||||
m_givenYaw = aimYaw;
|
||||
m_givenPitch = aimPitch;
|
||||
m_givenCharge = aimCharge;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotStickybombSentrygun::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
// detonate old set of stickies
|
||||
me->PressAltFireButton();
|
||||
|
||||
// own our view updating so we can aim
|
||||
me->StopLookingAroundForEnemies();
|
||||
|
||||
m_isFullReloadNeeded = true;
|
||||
|
||||
// STOP
|
||||
me->SetAbsVelocity( vec3_origin );
|
||||
|
||||
m_searchPitch = 0.0f;
|
||||
m_hasTarget = false;
|
||||
m_searchTimer.Start( 3.0f );
|
||||
|
||||
m_isChargingShot = false;
|
||||
|
||||
if ( m_hasGivenAim )
|
||||
{
|
||||
m_hasTarget = true;
|
||||
|
||||
// remember where we are standing - if we move for any reason, we'll need to re-search
|
||||
m_launchSpot = me->GetAbsOrigin();
|
||||
|
||||
// start charging up the sticky launch
|
||||
m_chargeToLaunch = m_givenCharge;
|
||||
m_isChargingShot = true;
|
||||
|
||||
// aim along given pitch/yaw
|
||||
QAngle angles;
|
||||
angles.x = m_givenPitch;
|
||||
angles.y = m_givenYaw;
|
||||
angles.z = 0.0f;
|
||||
|
||||
Vector aimForward;
|
||||
AngleVectors( angles, &aimForward );
|
||||
|
||||
m_eyeAimTarget = me->EyePosition() + 1500.0f * aimForward;
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool CTFBotStickybombSentrygun::IsAimOnTarget( CTFBot *me, float pitch, float yaw, float charge )
|
||||
{
|
||||
// estimate impact spot
|
||||
Vector impactSpot = me->EstimateStickybombProjectileImpactPosition( pitch, yaw, charge );
|
||||
|
||||
// check if impactSpot landed near sentry
|
||||
const float explosionRadius = 75.0f;
|
||||
if ( ( m_sentrygun->WorldSpaceCenter() - impactSpot ).IsLengthLessThan( explosionRadius ) )
|
||||
{
|
||||
trace_t trace;
|
||||
NextBotTraceFilterIgnoreActors filter( NULL, COLLISION_GROUP_NONE );
|
||||
|
||||
UTIL_TraceLine( m_sentrygun->WorldSpaceCenter(), impactSpot, MASK_SOLID_BRUSHONLY, &filter, &trace );
|
||||
if ( !trace.DidHit() )
|
||||
{
|
||||
// NDebugOverlay::Cross3D( impactSpot, 10.0f, 100, 255, 0, true, 60.0f );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotStickybombSentrygun::Update( CTFBot *me, float interval )
|
||||
{
|
||||
CTFWeaponBase *myCurrentWeapon = me->m_Shared.GetActiveTFWeapon();
|
||||
CTFPipebombLauncher *stickyLauncher = dynamic_cast< CTFPipebombLauncher * >( me->Weapon_GetSlot( TF_WPN_TYPE_SECONDARY ) );
|
||||
|
||||
if ( !myCurrentWeapon || !stickyLauncher )
|
||||
{
|
||||
return Done( "Missing weapon" );
|
||||
}
|
||||
|
||||
if ( myCurrentWeapon->GetWeaponID() != TF_WEAPON_PIPEBOMBLAUNCHER )
|
||||
{
|
||||
me->Weapon_Switch( stickyLauncher );
|
||||
}
|
||||
|
||||
if ( m_sentrygun == NULL || !m_sentrygun->IsAlive() )
|
||||
{
|
||||
return Done( "Sentry destroyed" );
|
||||
}
|
||||
|
||||
if ( !m_hasTarget && m_searchTimer.IsElapsed() )
|
||||
{
|
||||
return Done( "Can't find aim" );
|
||||
}
|
||||
|
||||
// reload fully
|
||||
if ( m_isFullReloadNeeded )
|
||||
{
|
||||
int maxClip = MIN( stickyLauncher->GetMaxClip1(), me->GetAmmoCount( TF_AMMO_SECONDARY ) );
|
||||
|
||||
if ( stickyLauncher->Clip1() >= maxClip )
|
||||
{
|
||||
// fully reloaded
|
||||
m_isFullReloadNeeded = false;
|
||||
}
|
||||
|
||||
me->PressReloadButton();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
int requiredStickyBombs = 3;
|
||||
|
||||
if ( TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
// launch more stickies to make sure we take out beefed-up sentries
|
||||
requiredStickyBombs = 5;
|
||||
}
|
||||
|
||||
if ( stickyLauncher->GetPipeBombCount() >= requiredStickyBombs || me->GetAmmoCount( TF_AMMO_SECONDARY ) <= 0 )
|
||||
{
|
||||
// stickies laid - detonate them once they are on the ground
|
||||
const CUtlVector< CHandle< CTFGrenadePipebombProjectile > > &pipeVector = stickyLauncher->GetPipeBombVector();
|
||||
|
||||
int i;
|
||||
for( i=0; i<pipeVector.Count(); ++i )
|
||||
{
|
||||
if ( pipeVector[i].Get() && !pipeVector[i]->m_bTouched )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i == pipeVector.Count() )
|
||||
{
|
||||
// stickies are on the ground
|
||||
me->PressAltFireButton();
|
||||
|
||||
if ( me->GetAmmoCount( TF_AMMO_SECONDARY ) <= 0 )
|
||||
{
|
||||
return Done( "Out of ammo" );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( m_isChargingShot )
|
||||
{
|
||||
// fudge charge time a bit longer - better to overshoot
|
||||
float stickyChargeTime = 1.1f * m_chargeToLaunch * TF_PIPEBOMB_MAX_CHARGE_TIME;
|
||||
|
||||
me->GetBodyInterface()->AimHeadTowards( m_eyeAimTarget, IBody::CRITICAL, 0.3f, NULL, "Aiming a sticky bomb at a sentrygun" );
|
||||
|
||||
if ( gpGlobals->curtime - stickyLauncher->GetChargeBeginTime() >= stickyChargeTime )
|
||||
{
|
||||
// let go
|
||||
me->ReleaseFireButton();
|
||||
m_isChargingShot = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
me->PressFireButton();
|
||||
}
|
||||
}
|
||||
else if ( stickyLauncher->m_flNextPrimaryAttack < gpGlobals->curtime )
|
||||
{
|
||||
// if we've moved, we need to re-search
|
||||
if ( m_hasTarget )
|
||||
{
|
||||
const float tolerance = 1.0f;
|
||||
if ( me->IsRangeGreaterThan( m_launchSpot, tolerance ) )
|
||||
{
|
||||
m_hasTarget = false;
|
||||
m_searchTimer.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
if ( !m_hasTarget )
|
||||
{
|
||||
// search for angle to land sticky near sentry
|
||||
Vector toSentry = m_sentrygun->WorldSpaceCenter() - me->EyePosition();
|
||||
|
||||
QAngle angles;
|
||||
VectorAngles( toSentry, angles );
|
||||
|
||||
float bestYaw = 0.0f;
|
||||
float bestPitch = 0.0f;
|
||||
float bestCharge = 1.0f;
|
||||
|
||||
const int trials = 100;
|
||||
for( int t=0; t<trials; ++t )
|
||||
{
|
||||
float yaw = angles.y + RandomFloat( -30.0f, 30.0f );
|
||||
// float pitch = ( trials & 0x1 ) ? m_searchPitch : -m_searchPitch;
|
||||
float pitch = RandomFloat( -85.0f, 85.0f );
|
||||
|
||||
float charge = 0.0f;
|
||||
if ( toSentry.IsLengthGreaterThan( tf_bot_sticky_base_range.GetBool() ) )
|
||||
{
|
||||
charge = RandomFloat( 0.1f, 1.0f );
|
||||
|
||||
// skew towards zero - full charge shots are seldom required
|
||||
charge *= charge;
|
||||
}
|
||||
|
||||
if ( IsAimOnTarget( me, pitch, yaw, charge ) )
|
||||
{
|
||||
// found target aim - keep one we find with least required
|
||||
// charge, because we need to be fast in combat
|
||||
if ( charge < bestCharge )
|
||||
{
|
||||
m_hasTarget = true;
|
||||
|
||||
bestCharge = charge;
|
||||
m_chargeToLaunch = bestCharge;
|
||||
|
||||
bestYaw = yaw;
|
||||
bestPitch = pitch;
|
||||
|
||||
if ( bestCharge < 0.01 )
|
||||
{
|
||||
// as quick as possible - no need to search further
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// aim along yaw/pitch to reach impact spot
|
||||
angles.x = bestPitch;
|
||||
angles.y = bestYaw;
|
||||
angles.z = 0.0f;
|
||||
|
||||
Vector aimForward;
|
||||
AngleVectors( angles, &aimForward );
|
||||
|
||||
// always recompute eye aim target so we can update our view
|
||||
m_eyeAimTarget = me->EyePosition() + 500.0f * aimForward;
|
||||
me->GetBodyInterface()->AimHeadTowards( m_eyeAimTarget, IBody::CRITICAL, 0.3f, NULL, "Searching for aim..." );
|
||||
}
|
||||
|
||||
if ( m_hasTarget )
|
||||
{
|
||||
// remember where we are standing - if we move for any reason, we'll need to re-search
|
||||
m_launchSpot = me->GetAbsOrigin();
|
||||
|
||||
// start charging up the sticky launch
|
||||
me->PressFireButton();
|
||||
m_isChargingShot = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotStickybombSentrygun::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
// detonate any stickes left out there
|
||||
me->PressAltFireButton();
|
||||
|
||||
me->StartLookingAroundForEnemies();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotStickybombSentrygun::OnSuspend( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
// detonate any stickes left out there
|
||||
me->PressAltFireButton();
|
||||
|
||||
// this behavior is transitory - if we need to do something else, just give up
|
||||
return Done();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotStickybombSentrygun::OnInjured( CTFBot *me, const CTakeDamageInfo &info )
|
||||
{
|
||||
return TryDone( RESULT_IMPORTANT, "Ouch!" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotStickybombSentrygun::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotStickybombSentrygun::ShouldHurry( const INextBot *me ) const
|
||||
{
|
||||
// while killing a sentry we're "hurrying" so we don't dodge
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotStickybombSentrygun::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
// stay stuck in to try to kill that gun!
|
||||
return ANSWER_NO;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_stickybomb_sentrygun.h
|
||||
// Destroy the given sentrygun with stickybombs
|
||||
// Michael Booth, August 2010
|
||||
|
||||
#ifndef TF_BOT_STICKYBOMB_SENTRY_H
|
||||
#define TF_BOT_STICKYBOMB_SENTRY_H
|
||||
|
||||
class CObjectSentrygun;
|
||||
|
||||
|
||||
class CTFBotStickybombSentrygun : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotStickybombSentrygun( CObjectSentrygun *sentrygun );
|
||||
CTFBotStickybombSentrygun( CObjectSentrygun *sentrygun, float aimYaw, float aimPitch, float aimCharge );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
|
||||
virtual ActionResult< CTFBot > OnSuspend( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnInjured( CTFBot *me, const CTakeDamageInfo &info );
|
||||
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const;
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
|
||||
virtual const char *GetName( void ) const { return "StickybombSentrygun"; };
|
||||
|
||||
private:
|
||||
float m_givenYaw, m_givenPitch, m_givenCharge;
|
||||
bool m_hasGivenAim;
|
||||
|
||||
bool m_isFullReloadNeeded;
|
||||
|
||||
CHandle< CObjectSentrygun > m_sentrygun;
|
||||
|
||||
bool m_isChargingShot;
|
||||
|
||||
CountdownTimer m_searchTimer;
|
||||
bool m_hasTarget;
|
||||
Vector m_eyeAimTarget;
|
||||
Vector m_launchSpot;
|
||||
float m_chargeToLaunch;
|
||||
float m_searchPitch;
|
||||
bool IsAimOnTarget( CTFBot *me, float pitch, float yaw, float charge );
|
||||
};
|
||||
|
||||
#endif // TF_BOT_STICKYBOMB_SENTRY_H
|
||||
Reference in New Issue
Block a user