You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
711 lines
17 KiB
711 lines
17 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
|
||
|
// Author: Michael S. Booth (mike@turtlerockstudios.com), 2003
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "cs_bot.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Begin attacking
|
||
|
*/
|
||
|
void AttackState::OnEnter( CCSBot *me )
|
||
|
{
|
||
|
CBasePlayer *enemy = me->GetBotEnemy();
|
||
|
|
||
|
// store our posture when the attack began
|
||
|
me->PushPostureContext();
|
||
|
|
||
|
me->DestroyPath();
|
||
|
|
||
|
// if we are using a knife, try to sneak up on the enemy
|
||
|
if (enemy && me->IsUsingKnife() && !me->IsPlayerFacingMe( enemy ))
|
||
|
me->Walk();
|
||
|
else
|
||
|
me->Run();
|
||
|
|
||
|
me->GetOffLadder();
|
||
|
me->ResetStuckMonitor();
|
||
|
|
||
|
m_repathTimer.Invalidate();
|
||
|
m_haveSeenEnemy = me->IsEnemyVisible();
|
||
|
m_nextDodgeStateTimestamp = 0.0f;
|
||
|
m_firstDodge = true;
|
||
|
m_isEnemyHidden = false;
|
||
|
m_reacquireTimestamp = 0.0f;
|
||
|
|
||
|
m_pinnedDownTimestamp = gpGlobals->curtime + RandomFloat( 7.0f, 10.0f );
|
||
|
|
||
|
m_shieldToggleTimestamp = gpGlobals->curtime + RandomFloat( 2.0f, 10.0f );
|
||
|
m_shieldForceOpen = false;
|
||
|
|
||
|
// if we encountered someone while escaping, grab our weapon and fight!
|
||
|
if (me->IsEscapingFromBomb())
|
||
|
me->EquipBestWeapon();
|
||
|
|
||
|
if (me->IsUsingKnife())
|
||
|
{
|
||
|
// can't crouch and hold with a knife
|
||
|
m_crouchAndHold = false;
|
||
|
me->StandUp();
|
||
|
}
|
||
|
else if (me->CanSeeSniper() && !me->IsSniper())
|
||
|
{
|
||
|
// don't sit still if we see a sniper!
|
||
|
m_crouchAndHold = false;
|
||
|
me->StandUp();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// decide whether to crouch where we are, or run and gun (if we havent already - see CCSBot::Attack())
|
||
|
if (!m_crouchAndHold)
|
||
|
{
|
||
|
if (enemy)
|
||
|
{
|
||
|
const float crouchFarRange = 750.0f;
|
||
|
float crouchChance;
|
||
|
|
||
|
// more likely to crouch if using sniper rifle or if enemy is far away
|
||
|
if (me->IsUsingSniperRifle())
|
||
|
crouchChance = 50.0f;
|
||
|
else if ((GetCentroid( me ) - GetCentroid( enemy )).IsLengthGreaterThan( crouchFarRange ))
|
||
|
crouchChance = 50.0f;
|
||
|
else
|
||
|
crouchChance = 20.0f * (1.0f - me->GetProfile()->GetAggression());
|
||
|
|
||
|
if (RandomFloat( 0.0f, 100.0f ) < crouchChance)
|
||
|
{
|
||
|
// make sure we can still see if we crouch
|
||
|
trace_t result;
|
||
|
|
||
|
Vector origin = GetCentroid( me );
|
||
|
if (!me->IsCrouching())
|
||
|
{
|
||
|
// we are standing, adjust for lower crouch origin
|
||
|
origin.z -= 20.0f;
|
||
|
}
|
||
|
|
||
|
UTIL_TraceLine( origin, enemy->EyePosition(), MASK_PLAYERSOLID, me, COLLISION_GROUP_NONE, &result );
|
||
|
|
||
|
if (result.fraction == 1.0f)
|
||
|
{
|
||
|
m_crouchAndHold = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (m_crouchAndHold)
|
||
|
{
|
||
|
me->Crouch();
|
||
|
me->PrintIfWatched( "Crouch and hold attack!\n" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_scopeTimestamp = 0;
|
||
|
m_didAmbushCheck = false;
|
||
|
|
||
|
float skill = me->GetProfile()->GetSkill();
|
||
|
|
||
|
// tendency to dodge is proportional to skill
|
||
|
float dodgeChance = 80.0f * skill;
|
||
|
|
||
|
// high skill bots always dodge if outnumbered, or they see a sniper
|
||
|
if (skill > 0.5f && (me->IsOutnumbered() || me->CanSeeSniper()))
|
||
|
{
|
||
|
dodgeChance = 100.0f;
|
||
|
}
|
||
|
|
||
|
m_shouldDodge = (RandomFloat( 0, 100 ) <= dodgeChance);
|
||
|
|
||
|
|
||
|
// decide whether we might bail out of this fight
|
||
|
m_isCoward = (RandomFloat( 0, 100 ) > 100.0f * me->GetProfile()->GetAggression());
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* When we are done attacking, this is invoked
|
||
|
*/
|
||
|
void AttackState::StopAttacking( CCSBot *me )
|
||
|
{
|
||
|
if (me->GetTask() == CCSBot::SNIPING)
|
||
|
{
|
||
|
// stay in our hiding spot
|
||
|
me->Hide( me->GetLastKnownArea(), -1.0f, 50.0f );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
me->StopAttacking();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Do dodge behavior
|
||
|
*/
|
||
|
void AttackState::Dodge( CCSBot *me )
|
||
|
{
|
||
|
//
|
||
|
// Dodge.
|
||
|
// If sniping or crouching, stand still.
|
||
|
//
|
||
|
if (m_shouldDodge && !me->IsUsingSniperRifle() && !m_crouchAndHold)
|
||
|
{
|
||
|
CBasePlayer *enemy = me->GetBotEnemy();
|
||
|
if (enemy == NULL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Vector toEnemy = enemy->GetAbsOrigin() - me->GetAbsOrigin();
|
||
|
float range = toEnemy.Length();
|
||
|
|
||
|
const float hysterisRange = 125.0f; // (+/-) m_combatRange
|
||
|
|
||
|
float minRange = me->GetCombatRange() - hysterisRange;
|
||
|
float maxRange = me->GetCombatRange() + hysterisRange;
|
||
|
|
||
|
if (me->IsUsingKnife())
|
||
|
{
|
||
|
// dodge when far away if armed only with a knife
|
||
|
maxRange = 999999.9f;
|
||
|
}
|
||
|
|
||
|
// move towards (or away from) enemy if we are using a knife, behind a corner, or we aren't very skilled
|
||
|
if (me->GetProfile()->GetSkill() < 0.66f || !me->IsEnemyVisible())
|
||
|
{
|
||
|
if (range > maxRange)
|
||
|
me->MoveForward();
|
||
|
else if (range < minRange)
|
||
|
me->MoveBackward();
|
||
|
}
|
||
|
|
||
|
// don't dodge if enemy is facing away
|
||
|
const float dodgeRange = 2000.0f;
|
||
|
if (!me->CanSeeSniper() && (range > dodgeRange || !me->IsPlayerFacingMe( enemy )))
|
||
|
{
|
||
|
m_dodgeState = STEADY_ON;
|
||
|
m_nextDodgeStateTimestamp = 0.0f;
|
||
|
}
|
||
|
else if (gpGlobals->curtime >= m_nextDodgeStateTimestamp)
|
||
|
{
|
||
|
int next;
|
||
|
|
||
|
// high-skill bots keep moving and don't jump if they see a sniper
|
||
|
if (me->GetProfile()->GetSkill() > 0.5f && me->CanSeeSniper())
|
||
|
{
|
||
|
// juke back and forth
|
||
|
if (m_firstDodge)
|
||
|
{
|
||
|
next = (RandomInt( 0, 100 ) < 50) ? SLIDE_RIGHT : SLIDE_LEFT;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
next = (m_dodgeState == SLIDE_LEFT) ? SLIDE_RIGHT : SLIDE_LEFT;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// select next dodge state that is different that our current one
|
||
|
do
|
||
|
{
|
||
|
// low-skill bots may jump when first engaging the enemy (if they are moving)
|
||
|
const float jumpChance = 33.3f;
|
||
|
if (m_firstDodge && me->GetProfile()->GetSkill() < 0.5f && RandomFloat( 0, 100 ) < jumpChance && !me->IsNotMoving())
|
||
|
next = RandomInt( 0, NUM_ATTACK_STATES-1 );
|
||
|
else
|
||
|
next = RandomInt( 0, NUM_ATTACK_STATES-2 );
|
||
|
}
|
||
|
while( !m_firstDodge && next == m_dodgeState );
|
||
|
}
|
||
|
|
||
|
m_dodgeState = (DodgeStateType)next;
|
||
|
m_nextDodgeStateTimestamp = gpGlobals->curtime + RandomFloat( 0.3f, 1.0f );
|
||
|
m_firstDodge = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
Vector forward, right;
|
||
|
me->EyeVectors( &forward, &right );
|
||
|
|
||
|
const float lookAheadRange = 30.0f;
|
||
|
float ground;
|
||
|
|
||
|
switch( m_dodgeState )
|
||
|
{
|
||
|
case STEADY_ON:
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case SLIDE_LEFT:
|
||
|
{
|
||
|
// don't move left if we will fall
|
||
|
Vector pos = me->GetAbsOrigin() - (lookAheadRange * right);
|
||
|
|
||
|
if (me->GetSimpleGroundHeightWithFloor( pos, &ground ))
|
||
|
{
|
||
|
if (me->GetAbsOrigin().z - ground < StepHeight)
|
||
|
{
|
||
|
me->StrafeLeft();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case SLIDE_RIGHT:
|
||
|
{
|
||
|
// don't move left if we will fall
|
||
|
Vector pos = me->GetAbsOrigin() + (lookAheadRange * right);
|
||
|
|
||
|
if (me->GetSimpleGroundHeightWithFloor( pos, &ground ))
|
||
|
{
|
||
|
if (me->GetAbsOrigin().z - ground < StepHeight)
|
||
|
{
|
||
|
me->StrafeRight();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case JUMP:
|
||
|
{
|
||
|
if (me->m_isEnemyVisible)
|
||
|
{
|
||
|
me->Jump();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Perform attack behavior
|
||
|
*/
|
||
|
void AttackState::OnUpdate( CCSBot *me )
|
||
|
{
|
||
|
// can't be stuck while attacking
|
||
|
me->ResetStuckMonitor();
|
||
|
|
||
|
// if we somehow ended up with the C4 or a grenade in our hands, grab our weapon!
|
||
|
CWeaponCSBase *weapon = me->GetActiveCSWeapon();
|
||
|
if (weapon)
|
||
|
{
|
||
|
if (weapon->GetWeaponID() == WEAPON_C4 ||
|
||
|
weapon->GetWeaponID() == WEAPON_HEGRENADE ||
|
||
|
weapon->GetWeaponID() == WEAPON_FLASHBANG ||
|
||
|
weapon->GetWeaponID() == WEAPON_SMOKEGRENADE)
|
||
|
{
|
||
|
me->EquipBestWeapon();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CBasePlayer *enemy = me->GetBotEnemy();
|
||
|
if (enemy == NULL)
|
||
|
{
|
||
|
StopAttacking( me );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Vector myOrigin = GetCentroid( me );
|
||
|
Vector enemyOrigin = GetCentroid( enemy );
|
||
|
|
||
|
// keep track of whether we have seen our enemy at least once yet
|
||
|
if (!m_haveSeenEnemy)
|
||
|
m_haveSeenEnemy = me->IsEnemyVisible();
|
||
|
|
||
|
|
||
|
//
|
||
|
// Retreat check
|
||
|
// Do not retreat if the enemy is too close
|
||
|
//
|
||
|
if (m_retreatTimer.IsElapsed())
|
||
|
{
|
||
|
// If we've been fighting this battle for awhile, we're "pinned down" and
|
||
|
// need to do something else.
|
||
|
// If we are outnumbered, retreat.
|
||
|
// If we see a sniper and we aren't a sniper, retreat.
|
||
|
|
||
|
bool isPinnedDown = (gpGlobals->curtime > m_pinnedDownTimestamp);
|
||
|
|
||
|
if (isPinnedDown ||
|
||
|
(me->CanSeeSniper() && !me->IsSniper()) ||
|
||
|
(me->IsOutnumbered() && m_isCoward) ||
|
||
|
(me->OutnumberedCount() >= 2 && me->GetProfile()->GetAggression() < 1.0f))
|
||
|
{
|
||
|
// only retreat if at least one of them is aiming at me
|
||
|
if (me->IsAnyVisibleEnemyLookingAtMe( CHECK_FOV ))
|
||
|
{
|
||
|
// tell our teammates our plight
|
||
|
if (isPinnedDown)
|
||
|
me->GetChatter()->PinnedDown();
|
||
|
else if (!me->CanSeeSniper())
|
||
|
me->GetChatter()->Scared();
|
||
|
|
||
|
m_retreatTimer.Start( RandomFloat( 3.0f, 15.0f ) );
|
||
|
|
||
|
// try to retreat
|
||
|
if (me->TryToRetreat())
|
||
|
{
|
||
|
// if we are a sniper, equip our pistol so we can fire while retreating
|
||
|
/*
|
||
|
if (me->IsUsingSniperRifle())
|
||
|
{
|
||
|
// wait a moment to allow one last shot
|
||
|
me->Wait( 0.5f );
|
||
|
//me->EquipPistol();
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
// request backup if outnumbered
|
||
|
if (me->IsOutnumbered())
|
||
|
{
|
||
|
me->GetChatter()->NeedBackup();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
me->PrintIfWatched( "I want to retreat, but no safe spots nearby!\n" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Knife fighting
|
||
|
// We need to pathfind right to the enemy to cut him
|
||
|
//
|
||
|
if (me->IsUsingKnife())
|
||
|
{
|
||
|
// can't crouch and hold with a knife
|
||
|
m_crouchAndHold = false;
|
||
|
me->StandUp();
|
||
|
|
||
|
// if we are using a knife and our prey is looking towards us, run at him
|
||
|
if (me->IsPlayerFacingMe( enemy ))
|
||
|
{
|
||
|
me->ForceRun( 5.0f );
|
||
|
me->Hurry( 10.0f );
|
||
|
}
|
||
|
|
||
|
// slash our victim
|
||
|
me->FireWeaponAtEnemy();
|
||
|
|
||
|
// if toe to toe with our enemy, don't dodge, just slash
|
||
|
const float slashRange = 70.0f;
|
||
|
if ((enemy->GetAbsOrigin() - me->GetAbsOrigin()).IsLengthGreaterThan( slashRange ))
|
||
|
{
|
||
|
const float repathInterval = 0.5f;
|
||
|
|
||
|
// if our victim has moved, repath
|
||
|
bool repath = false;
|
||
|
if (me->HasPath())
|
||
|
{
|
||
|
const float repathRange = 100.0f; // 50
|
||
|
if ((me->GetPathEndpoint() - enemy->GetAbsOrigin()).IsLengthGreaterThan( repathRange ))
|
||
|
{
|
||
|
repath = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
repath = true;
|
||
|
}
|
||
|
|
||
|
if (repath && m_repathTimer.IsElapsed())
|
||
|
{
|
||
|
Vector enemyPos = enemy->GetAbsOrigin() + Vector( 0, 0, HalfHumanHeight );
|
||
|
me->ComputePath( enemyPos, FASTEST_ROUTE );
|
||
|
m_repathTimer.Start( repathInterval );
|
||
|
}
|
||
|
|
||
|
// move towards victim
|
||
|
if (me->UpdatePathMovement( NO_SPEED_CHANGE ) != CCSBot::PROGRESSING)
|
||
|
{
|
||
|
me->DestroyPath();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Simple shield usage
|
||
|
//
|
||
|
if (me->HasShield())
|
||
|
{
|
||
|
if (me->IsEnemyVisible() && !m_shieldForceOpen)
|
||
|
{
|
||
|
if (!me->IsRecognizedEnemyReloading() && !me->IsReloading() && me->IsPlayerLookingAtMe( enemy ))
|
||
|
{
|
||
|
// close up - enemy is pointing his gun at us
|
||
|
if (!me->IsProtectedByShield())
|
||
|
me->SecondaryAttack();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// enemy looking away or reloading his weapon - open up and shoot him
|
||
|
if (me->IsProtectedByShield())
|
||
|
me->SecondaryAttack();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// can't see enemy, open up
|
||
|
if (me->IsProtectedByShield())
|
||
|
me->SecondaryAttack();
|
||
|
}
|
||
|
|
||
|
if (gpGlobals->curtime > m_shieldToggleTimestamp)
|
||
|
{
|
||
|
m_shieldToggleTimestamp = gpGlobals->curtime + RandomFloat( 0.5, 2.0f );
|
||
|
|
||
|
// toggle shield force open
|
||
|
m_shieldForceOpen = !m_shieldForceOpen;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// check if our weapon range is bad and we should switch to pistol
|
||
|
if (me->IsUsingSniperRifle())
|
||
|
{
|
||
|
// if we have a sniper rifle and our enemy is too close, switch to pistol
|
||
|
const float sniperMinRange = 160.0f; // NOTE: Must be larger than NO_ZOOM range in AdjustZoom()
|
||
|
if ((enemyOrigin - myOrigin).IsLengthLessThan( sniperMinRange ))
|
||
|
me->EquipPistol();
|
||
|
}
|
||
|
else if (me->IsUsingShotgun())
|
||
|
{
|
||
|
// if we have a shotgun equipped and enemy is too far away, switch to pistol
|
||
|
const float shotgunMaxRange = 600.0f;
|
||
|
if ((enemyOrigin - myOrigin).IsLengthGreaterThan( shotgunMaxRange ))
|
||
|
me->EquipPistol();
|
||
|
}
|
||
|
|
||
|
// if we're sniping, look through the scope - need to do this here in case a reload resets our scope
|
||
|
if (me->IsUsingSniperRifle())
|
||
|
{
|
||
|
// for Scouts and AWPs, we need to wait for zoom to resume
|
||
|
if (me->m_bResumeZoom)
|
||
|
{
|
||
|
m_scopeTimestamp = gpGlobals->curtime;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Vector toAimSpot3D = me->m_aimSpot - myOrigin;
|
||
|
float targetRange = toAimSpot3D.Length();
|
||
|
|
||
|
// dont adjust zoom level if we're already zoomed in - just fire
|
||
|
if (me->GetZoomLevel() == CCSBot::NO_ZOOM && me->AdjustZoom( targetRange ))
|
||
|
m_scopeTimestamp = gpGlobals->curtime;
|
||
|
|
||
|
const float waitScopeTime = 0.3f + me->GetProfile()->GetReactionTime();
|
||
|
if (gpGlobals->curtime - m_scopeTimestamp < waitScopeTime)
|
||
|
{
|
||
|
// force us to wait until zoomed in before firing
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// see if we "notice" that our prey is dead
|
||
|
if (me->IsAwareOfEnemyDeath())
|
||
|
{
|
||
|
// let team know if we killed the last enemy
|
||
|
if (me->GetLastVictimID() == enemy->entindex() && me->GetNearbyEnemyCount() <= 1)
|
||
|
{
|
||
|
me->GetChatter()->KilledMyEnemy( enemy->entindex() );
|
||
|
|
||
|
// if there are other enemies left, wait a moment - they usually come in groups
|
||
|
if (me->GetEnemiesRemaining())
|
||
|
{
|
||
|
me->Wait( RandomFloat( 1.0f, 3.0f ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
StopAttacking( me );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float notSeenEnemyTime = gpGlobals->curtime - me->GetLastSawEnemyTimestamp();
|
||
|
|
||
|
// if we haven't seen our enemy for a moment, continue on if we dont want to fight, or decide to ambush if we do
|
||
|
if (!me->IsEnemyVisible())
|
||
|
{
|
||
|
// attend to nearby enemy gunfire
|
||
|
if (notSeenEnemyTime > 0.5f && me->CanHearNearbyEnemyGunfire())
|
||
|
{
|
||
|
// give up the attack, since we didn't want it in the first place
|
||
|
StopAttacking( me );
|
||
|
|
||
|
const Vector *pos = me->GetNoisePosition();
|
||
|
if (pos)
|
||
|
{
|
||
|
me->SetLookAt( "Nearby enemy gunfire", *pos, PRIORITY_HIGH, 0.0f );
|
||
|
me->PrintIfWatched( "Checking nearby threatening enemy gunfire!\n" );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if we have lost track of our enemy during combat
|
||
|
if (notSeenEnemyTime > 0.25f)
|
||
|
{
|
||
|
m_isEnemyHidden = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (notSeenEnemyTime > 0.1f)
|
||
|
{
|
||
|
if (me->GetDisposition() == CCSBot::ENGAGE_AND_INVESTIGATE)
|
||
|
{
|
||
|
// decide whether we should hide and "ambush" our enemy
|
||
|
if (m_haveSeenEnemy && !m_didAmbushCheck)
|
||
|
{
|
||
|
float hideChance = 33.3f;
|
||
|
|
||
|
if (RandomFloat( 0.0, 100.0f ) < hideChance)
|
||
|
{
|
||
|
float ambushTime = RandomFloat( 3.0f, 15.0f );
|
||
|
|
||
|
// hide in ambush nearby
|
||
|
/// @todo look towards where we know enemy is
|
||
|
const Vector *spot = FindNearbyRetreatSpot( me, 200.0f );
|
||
|
if (spot)
|
||
|
{
|
||
|
me->IgnoreEnemies( 1.0f );
|
||
|
|
||
|
me->Run();
|
||
|
me->StandUp();
|
||
|
me->Hide( *spot, ambushTime, true );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// don't check again
|
||
|
m_didAmbushCheck = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// give up the attack, since we didn't want it in the first place
|
||
|
StopAttacking( me );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// we can see the enemy again - reset our ambush check
|
||
|
m_didAmbushCheck = false;
|
||
|
|
||
|
// if the enemy is coming out of hiding, we need time to react
|
||
|
if (m_isEnemyHidden)
|
||
|
{
|
||
|
m_reacquireTimestamp = gpGlobals->curtime + me->GetProfile()->GetReactionTime();
|
||
|
m_isEnemyHidden = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// if we haven't seen our enemy for a long time, chase after them
|
||
|
float chaseTime = 2.0f + 2.0f * (1.0f - me->GetProfile()->GetAggression());
|
||
|
|
||
|
// if we are sniping, be very patient
|
||
|
if (me->IsUsingSniperRifle())
|
||
|
chaseTime += 3.0f;
|
||
|
else if (me->IsCrouching()) // if we are crouching, be a little patient
|
||
|
chaseTime += 1.0f;
|
||
|
|
||
|
// if we can't see the enemy, and have either seen him but currently lost sight of him,
|
||
|
// or haven't yet seen him, chase after him (unless we are a sniper)
|
||
|
if (!me->IsEnemyVisible() && (notSeenEnemyTime > chaseTime || !m_haveSeenEnemy))
|
||
|
{
|
||
|
// snipers don't chase their prey - they wait for their prey to come to them
|
||
|
if (me->GetTask() == CCSBot::SNIPING)
|
||
|
{
|
||
|
StopAttacking( me );
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// move to last known position of enemy
|
||
|
me->SetTask( CCSBot::MOVE_TO_LAST_KNOWN_ENEMY_POSITION, enemy );
|
||
|
me->MoveTo( me->GetLastKnownEnemyPosition() );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// if we can't see our enemy at the moment, and were shot by
|
||
|
// a different visible enemy, engage them instead
|
||
|
const float hurtRecentlyTime = 3.0f;
|
||
|
if (!me->IsEnemyVisible() &&
|
||
|
me->GetTimeSinceAttacked() < hurtRecentlyTime &&
|
||
|
me->GetAttacker() &&
|
||
|
me->GetAttacker() != me->GetBotEnemy())
|
||
|
{
|
||
|
// if we can see them, attack, otherwise panic
|
||
|
if (me->IsVisible( me->GetAttacker(), CHECK_FOV ))
|
||
|
{
|
||
|
me->Attack( me->GetAttacker() );
|
||
|
me->PrintIfWatched( "Switching targets to retaliate against new attacker!\n" );
|
||
|
}
|
||
|
/*
|
||
|
* Rethink this
|
||
|
else
|
||
|
{
|
||
|
me->Panic( me->GetAttacker() );
|
||
|
me->PrintIfWatched( "Panicking from crossfire while attacking!\n" );
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (true || gpGlobals->curtime > m_reacquireTimestamp)
|
||
|
me->FireWeaponAtEnemy();
|
||
|
|
||
|
|
||
|
// do dodge behavior
|
||
|
Dodge( me );
|
||
|
}
|
||
|
|
||
|
//--------------------------------------------------------------------------------------------------------------
|
||
|
/**
|
||
|
* Finish attack
|
||
|
*/
|
||
|
void AttackState::OnExit( CCSBot *me )
|
||
|
{
|
||
|
me->PrintIfWatched( "AttackState:OnExit()\n" );
|
||
|
|
||
|
m_crouchAndHold = false;
|
||
|
|
||
|
// clear any noises we heard during battle
|
||
|
me->ForgetNoise();
|
||
|
me->ResetStuckMonitor();
|
||
|
|
||
|
// resume our original posture
|
||
|
me->PopPostureContext();
|
||
|
|
||
|
// put shield away
|
||
|
if (me->IsProtectedByShield())
|
||
|
me->SecondaryAttack();
|
||
|
|
||
|
|
||
|
//me->StopAiming();
|
||
|
}
|
||
|
|