summaryrefslogtreecommitdiff
path: root/game/server/cstrike/bot/cs_bot_statemachine.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/cs_bot_statemachine.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/cs_bot_statemachine.cpp')
-rw-r--r--game/server/cstrike/bot/cs_bot_statemachine.cpp693
1 files changed, 693 insertions, 0 deletions
diff --git a/game/server/cstrike/bot/cs_bot_statemachine.cpp b/game/server/cstrike/bot/cs_bot_statemachine.cpp
new file mode 100644
index 0000000..8cbf9aa
--- /dev/null
+++ b/game/server/cstrike/bot/cs_bot_statemachine.cpp
@@ -0,0 +1,693 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 2003
+
+#include "cbase.h"
+#include "cs_bot.h"
+#include "cs_nav_path.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * This method is the ONLY legal way to change a bot's current state
+ */
+void CCSBot::SetState( BotState *state )
+{
+ PrintIfWatched( "%s: SetState: %s -> %s\n", GetPlayerName(), (m_state) ? m_state->GetName() : "NULL", state->GetName() );
+
+ /*
+ if ( IsDefusingBomb() )
+ {
+ const Vector *bombPos = GetGameState()->GetBombPosition();
+ if ( bombPos != NULL )
+ {
+ if ( TheCSBots()->GetBombDefuser() == this )
+ {
+ if ( TheCSBots()->IsBombPlanted() )
+ {
+ Msg( "Bot %s is switching from defusing the bomb to %s\n",
+ GetPlayerName(), state->GetName() );
+ }
+ }
+ }
+ }
+ */
+
+ // if we changed state from within the special Attack state, we are no longer attacking
+ if (m_isAttacking)
+ StopAttacking();
+
+ if (m_state)
+ m_state->OnExit( this );
+
+ state->OnEnter( this );
+
+ m_state = state;
+ m_stateTimestamp = gpGlobals->curtime;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSBot::Idle( void )
+{
+ SetTask( SEEK_AND_DESTROY );
+ SetState( &m_idleState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSBot::EscapeFromBomb( void )
+{
+ SetTask( ESCAPE_FROM_BOMB );
+ SetState( &m_escapeFromBombState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSBot::Follow( CCSPlayer *player )
+{
+ if (player == NULL)
+ return;
+
+ // note when we began following
+ if (!m_isFollowing || m_leader != player)
+ m_followTimestamp = gpGlobals->curtime;
+
+ m_isFollowing = true;
+ m_leader = player;
+
+ SetTask( FOLLOW );
+ m_followState.SetLeader( player );
+ SetState( &m_followState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Continue following our leader after finishing what we were doing
+ */
+void CCSBot::ContinueFollowing( void )
+{
+ SetTask( FOLLOW );
+
+ m_followState.SetLeader( m_leader );
+
+ SetState( &m_followState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Stop following
+ */
+void CCSBot::StopFollowing( void )
+{
+ m_isFollowing = false;
+ m_leader = NULL;
+ m_allowAutoFollowTime = gpGlobals->curtime + 10.0f;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Begin process of rescuing hostages
+ */
+void CCSBot::RescueHostages( void )
+{
+ SetTask( RESCUE_HOSTAGES );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Use the entity
+ */
+void CCSBot::UseEntity( CBaseEntity *entity )
+{
+ m_useEntityState.SetEntity( entity );
+ SetState( &m_useEntityState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Open the door.
+ * This assumes the bot is directly in front of the door with no obstructions.
+ * NOTE: This state is special, like Attack, in that it suspends the current behavior and returns to it when done.
+ */
+void CCSBot::OpenDoor( CBaseEntity *door )
+{
+ m_openDoorState.SetDoor( door );
+ m_isOpeningDoor = true;
+ m_openDoorState.OnEnter( this );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * DEPRECATED: Use TryToHide() instead.
+ * Move to a hiding place.
+ * If 'searchFromArea' is non-NULL, hiding spots are looked for from that area first.
+ */
+void CCSBot::Hide( CNavArea *searchFromArea, float duration, float hideRange, bool holdPosition )
+{
+ DestroyPath();
+
+ CNavArea *source;
+ Vector sourcePos;
+ if (searchFromArea)
+ {
+ source = searchFromArea;
+ sourcePos = searchFromArea->GetCenter();
+ }
+ else
+ {
+ source = m_lastKnownArea;
+ sourcePos = GetCentroid( this );
+ }
+
+ if (source == NULL)
+ {
+ PrintIfWatched( "Hide from area is NULL.\n" );
+ Idle();
+ return;
+ }
+
+ m_hideState.SetSearchArea( source );
+ m_hideState.SetSearchRange( hideRange );
+ m_hideState.SetDuration( duration );
+ m_hideState.SetHoldPosition( holdPosition );
+
+ // search around source area for a good hiding spot
+ Vector useSpot;
+
+ const Vector *pos = FindNearbyHidingSpot( this, sourcePos, hideRange, IsSniper() );
+ if (pos == NULL)
+ {
+ PrintIfWatched( "No available hiding spots.\n" );
+ // hide at our current position
+ useSpot = GetCentroid( this );
+ }
+ else
+ {
+ useSpot = *pos;
+ }
+
+ m_hideState.SetHidingSpot( useSpot );
+
+ // build a path to our new hiding spot
+ if (ComputePath( useSpot, FASTEST_ROUTE ) == false)
+ {
+ PrintIfWatched( "Can't pathfind to hiding spot\n" );
+ Idle();
+ return;
+ }
+
+ SetState( &m_hideState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Move to the given hiding place
+ */
+void CCSBot::Hide( const Vector &hidingSpot, float duration, bool holdPosition )
+{
+ CNavArea *hideArea = TheNavMesh->GetNearestNavArea( hidingSpot );
+ if (hideArea == NULL)
+ {
+ PrintIfWatched( "Hiding spot off nav mesh\n" );
+ Idle();
+ return;
+ }
+
+ DestroyPath();
+
+ m_hideState.SetSearchArea( hideArea );
+ m_hideState.SetSearchRange( 750.0f );
+ m_hideState.SetDuration( duration );
+ m_hideState.SetHoldPosition( holdPosition );
+ m_hideState.SetHidingSpot( hidingSpot );
+
+ // build a path to our new hiding spot
+ if (ComputePath( hidingSpot, FASTEST_ROUTE ) == false)
+ {
+ PrintIfWatched( "Can't pathfind to hiding spot\n" );
+ Idle();
+ return;
+ }
+
+ SetState( &m_hideState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Try to hide nearby. Return true if hiding, false if can't hide here.
+ * If 'searchFromArea' is non-NULL, hiding spots are looked for from that area first.
+ */
+bool CCSBot::TryToHide( CNavArea *searchFromArea, float duration, float hideRange, bool holdPosition, bool useNearest )
+{
+ CNavArea *source;
+ Vector sourcePos;
+ if (searchFromArea)
+ {
+ source = searchFromArea;
+ sourcePos = searchFromArea->GetCenter();
+ }
+ else
+ {
+ source = m_lastKnownArea;
+ sourcePos = GetCentroid( this );
+ }
+
+ if (source == NULL)
+ {
+ PrintIfWatched( "Hide from area is NULL.\n" );
+ return false;
+ }
+
+ m_hideState.SetSearchArea( source );
+ m_hideState.SetSearchRange( hideRange );
+ m_hideState.SetDuration( duration );
+ m_hideState.SetHoldPosition( holdPosition );
+
+ // search around source area for a good hiding spot
+ const Vector *pos = FindNearbyHidingSpot( this, sourcePos, hideRange, IsSniper(), useNearest );
+ if (pos == NULL)
+ {
+ PrintIfWatched( "No available hiding spots.\n" );
+ return false;
+ }
+
+ m_hideState.SetHidingSpot( *pos );
+
+ // build a path to our new hiding spot
+ if (ComputePath( *pos, FASTEST_ROUTE ) == false)
+ {
+ PrintIfWatched( "Can't pathfind to hiding spot\n" );
+ return false;
+ }
+
+ SetState( &m_hideState );
+ return true;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Retreat to a nearby hiding spot, away from enemies
+ */
+bool CCSBot::TryToRetreat( float maxRange, float duration )
+{
+ const Vector *spot = FindNearbyRetreatSpot( this, maxRange );
+ if (spot)
+ {
+ // ignore enemies for a second to give us time to hide
+ // reaching our hiding spot clears our disposition
+ IgnoreEnemies( 10.0f );
+
+ if (duration < 0.0f)
+ {
+ duration = RandomFloat( 3.0f, 15.0f );
+ }
+
+ StandUp();
+ Run();
+ Hide( *spot, duration );
+
+ PrintIfWatched( "Retreating to a safe spot!\n" );
+
+ return true;
+ }
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSBot::Hunt( void )
+{
+ SetState( &m_huntState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Attack our the given victim
+ * NOTE: Attacking does not change our task.
+ */
+void CCSBot::Attack( CCSPlayer *victim )
+{
+ if (victim == NULL)
+ return;
+
+ // zombies never attack
+ if (cv_bot_zombie.GetBool())
+ return;
+
+ // cannot attack if we are reloading
+ if (IsReloading())
+ return;
+
+ // change enemy
+ SetBotEnemy( victim );
+
+ //
+ // Do not "re-enter" the attack state if we are already attacking
+ //
+ if (IsAttacking())
+ return;
+
+ // if we're holding a grenade, throw it at the victim
+ if (IsUsingGrenade())
+ {
+ // throw towards their feet
+ ThrowGrenade( victim->GetAbsOrigin() );
+ return;
+ }
+
+
+ // if we are currently hiding, increase our chances of crouching and holding position
+ if (IsAtHidingSpot())
+ m_attackState.SetCrouchAndHold( (RandomFloat( 0.0f, 100.0f ) < 60.0f) ? true : false );
+ else
+ m_attackState.SetCrouchAndHold( false );
+
+ //SetState( &m_attackState );
+ //PrintIfWatched( "ATTACK BEGIN (reaction time = %g (+ update time), surprise time = %g, attack delay = %g)\n",
+ // GetProfile()->GetReactionTime(), m_surpriseDelay, GetProfile()->GetAttackDelay() );
+ m_isAttacking = true;
+ m_attackState.OnEnter( this );
+
+
+ Vector victimOrigin = GetCentroid( victim );
+
+ // cheat a bit and give the bot the initial location of its victim
+ m_lastEnemyPosition = victimOrigin;
+ m_lastSawEnemyTimestamp = gpGlobals->curtime;
+ m_aimSpreadTimestamp = gpGlobals->curtime;
+
+ // compute the angle difference between where are looking, and where we need to look
+ Vector toEnemy = victimOrigin - GetCentroid( this );
+
+ QAngle idealAngle;
+ VectorAngles( toEnemy, idealAngle );
+
+ float deltaYaw = (float)fabs(m_lookYaw - idealAngle.y);
+
+ while( deltaYaw > 180.0f )
+ deltaYaw -= 360.0f;
+
+ if (deltaYaw < 0.0f)
+ deltaYaw = -deltaYaw;
+
+ // immediately aim at enemy - accuracy penalty depending on how far we must turn to aim
+ // accuracy is halved if we have to turn 180 degrees
+ float turn = deltaYaw / 180.0f;
+ float accuracy = GetProfile()->GetSkill() / (1.0f + turn);
+
+ SetAimOffset( accuracy );
+
+ // define time when aim offset will automatically be updated
+ // longer time the more we had to turn (surprise)
+ m_aimOffsetTimestamp = gpGlobals->curtime + RandomFloat( 0.25f + turn, 1.5f );
+
+ // forget any look at targets we have
+ ClearLookAt();
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Exit the Attack state
+ */
+void CCSBot::StopAttacking( void )
+{
+ PrintIfWatched( "ATTACK END\n" );
+ m_attackState.OnExit( this );
+ m_isAttacking = false;
+
+ // if we are following someone, go to the Idle state after the attack to decide whether we still want to follow
+ if (IsFollowing())
+ {
+ Idle();
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+bool CCSBot::IsAttacking( void ) const
+{
+ return m_isAttacking;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we are escaping from the bomb
+ */
+bool CCSBot::IsEscapingFromBomb( void ) const
+{
+ if (m_state == static_cast<const BotState *>( &m_escapeFromBombState ))
+ return true;
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we are defusing the bomb
+ */
+bool CCSBot::IsDefusingBomb( void ) const
+{
+ if (m_state == static_cast<const BotState *>( &m_defuseBombState ))
+ return true;
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we are hiding
+ */
+bool CCSBot::IsHiding( void ) const
+{
+ if (m_state == static_cast<const BotState *>( &m_hideState ))
+ return true;
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we are hiding and at our hiding spot
+ */
+bool CCSBot::IsAtHidingSpot( void ) const
+{
+ if (!IsHiding())
+ return false;
+
+ return m_hideState.IsAtSpot();
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return number of seconds we have been at our current hiding spot
+ */
+float CCSBot::GetHidingTime( void ) const
+{
+ if (IsHiding())
+ {
+ return m_hideState.GetHideTime();
+ }
+
+ return 0.0f;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we are huting
+ */
+bool CCSBot::IsHunting( void ) const
+{
+ if (m_state == static_cast<const BotState *>( &m_huntState ))
+ return true;
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we are in the MoveTo state
+ */
+bool CCSBot::IsMovingTo( void ) const
+{
+ if (m_state == static_cast<const BotState *>( &m_moveToState ))
+ return true;
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we are buying
+ */
+bool CCSBot::IsBuying( void ) const
+{
+ if (m_state == static_cast<const BotState *>( &m_buyState ))
+ return true;
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+bool CCSBot::IsInvestigatingNoise( void ) const
+{
+ if (m_state == static_cast<const BotState *>( &m_investigateNoiseState ))
+ return true;
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Move to potentially distant position
+ */
+void CCSBot::MoveTo( const Vector &pos, RouteType route )
+{
+ m_moveToState.SetGoalPosition( pos );
+ m_moveToState.SetRouteType( route );
+ SetState( &m_moveToState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSBot::PlantBomb( void )
+{
+ SetState( &m_plantBombState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Bomb has been dropped - go get it
+ */
+void CCSBot::FetchBomb( void )
+{
+ SetState( &m_fetchBombState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSBot::DefuseBomb( void )
+{
+ SetState( &m_defuseBombState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Investigate recent enemy noise
+ */
+void CCSBot::InvestigateNoise( void )
+{
+ SetState( &m_investigateNoiseState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSBot::Buy( void )
+{
+ SetState( &m_buyState );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Move to a hiding spot and wait for initial encounter with enemy team.
+ * Return false if can't do this behavior (ie: no hiding spots available).
+ */
+bool CCSBot::MoveToInitialEncounter( void )
+{
+ int myTeam = GetTeamNumber();
+ int enemyTeam = OtherTeam( myTeam );
+
+ // build a path to an enemy spawn point
+ CBaseEntity *enemySpawn = TheCSBots()->GetRandomSpawn( enemyTeam );
+
+ if (enemySpawn == NULL)
+ {
+ PrintIfWatched( "MoveToInitialEncounter: No enemy spawn points?\n" );
+ return false;
+ }
+
+ // build a path from us to the enemy spawn
+ CCSNavPath path;
+ PathCost cost( this, FASTEST_ROUTE );
+ path.Compute( WorldSpaceCenter(), enemySpawn->GetAbsOrigin(), cost );
+
+ if (!path.IsValid())
+ {
+ PrintIfWatched( "MoveToInitialEncounter: Pathfind failed.\n" );
+ return false;
+ }
+
+ // find battlefront area where teams will first meet along this path
+ int i;
+ for( i=0; i<path.GetSegmentCount(); ++i )
+ {
+ if (path[i]->area->GetEarliestOccupyTime( myTeam ) > path[i]->area->GetEarliestOccupyTime( enemyTeam ))
+ {
+ break;
+ }
+ }
+
+ if (i == path.GetSegmentCount())
+ {
+ PrintIfWatched( "MoveToInitialEncounter: Can't find battlefront!\n" );
+ return false;
+ }
+
+ /// @todo Remove this evil side-effect
+ SetInitialEncounterArea( path[i]->area );
+
+ // find a hiding spot on our side of the battlefront that has LOS to it
+ const float maxRange = 1500.0f;
+ const HidingSpot *spot = FindInitialEncounterSpot( this, path[i]->area->GetCenter(), path[i]->area->GetEarliestOccupyTime( enemyTeam ), maxRange, IsSniper() );
+
+ if (spot == NULL)
+ {
+ PrintIfWatched( "MoveToInitialEncounter: Can't find a hiding spot\n" );
+ return false;
+ }
+
+ float timeToWait = path[i]->area->GetEarliestOccupyTime( enemyTeam ) - spot->GetArea()->GetEarliestOccupyTime( myTeam );
+ float minWaitTime = 4.0f * GetProfile()->GetAggression() + 3.0f;
+ if (timeToWait < minWaitTime)
+ {
+ timeToWait = minWaitTime;
+ }
+
+ Hide( spot->GetPosition(), timeToWait );
+
+ return true;
+}
+