summaryrefslogtreecommitdiff
path: root/game/server/cstrike/bot/states/cs_bot_attack.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/cstrike/bot/states/cs_bot_attack.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/cstrike/bot/states/cs_bot_attack.cpp')
-rw-r--r--game/server/cstrike/bot/states/cs_bot_attack.cpp710
1 files changed, 710 insertions, 0 deletions
diff --git a/game/server/cstrike/bot/states/cs_bot_attack.cpp b/game/server/cstrike/bot/states/cs_bot_attack.cpp
new file mode 100644
index 0000000..1f44b6c
--- /dev/null
+++ b/game/server/cstrike/bot/states/cs_bot_attack.cpp
@@ -0,0 +1,710 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 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();
+}
+