summaryrefslogtreecommitdiff
path: root/game/server/cstrike/bot/cs_gamestate.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_gamestate.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_gamestate.cpp')
-rw-r--r--game/server/cstrike/bot/cs_gamestate.cpp767
1 files changed, 767 insertions, 0 deletions
diff --git a/game/server/cstrike/bot/cs_gamestate.cpp b/game/server/cstrike/bot/cs_gamestate.cpp
new file mode 100644
index 0000000..61f537b
--- /dev/null
+++ b/game/server/cstrike/bot/cs_gamestate.cpp
@@ -0,0 +1,767 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Encapsulation of the current scenario/game state. Allows each bot imperfect knowledge.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 2003
+
+#include "cbase.h"
+#include "KeyValues.h"
+
+#include "cs_bot.h"
+#include "cs_gamestate.h"
+#include "cs_simple_hostage.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//--------------------------------------------------------------------------------------------------------------
+CSGameState::CSGameState( CCSBot *owner )
+{
+ m_owner = owner;
+ m_isRoundOver = false;
+
+ m_bombState = MOVING;
+ m_lastSawBomber.Invalidate();
+ m_lastSawLooseBomb.Invalidate();
+ m_isPlantedBombPosKnown = false;
+ m_plantedBombsite = UNKNOWN;
+
+ m_bombsiteCount = 0;
+ m_bombsiteSearchIndex = 0;
+
+ for( int i=0; i<MAX_HOSTAGES; ++i )
+ {
+ m_hostage[i].hostage = NULL;
+ m_hostage[i].isValid = false;
+ m_hostage[i].isAlive = false;
+ m_hostage[i].isFree = true;
+ m_hostage[i].knownPos = Vector( 0, 0, 0 );
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Reset at round start
+ */
+void CSGameState::Reset( void )
+{
+ m_isRoundOver = false;
+
+ // bomb -----------------------------------------------------------------------
+ m_bombState = MOVING;
+ m_lastSawBomber.Invalidate();
+ m_lastSawLooseBomb.Invalidate();
+ m_isPlantedBombPosKnown = false;
+ m_plantedBombsite = UNKNOWN;
+
+ m_bombsiteCount = TheCSBots()->GetZoneCount();
+
+ int i;
+ for( i=0; i<m_bombsiteCount; ++i )
+ {
+ m_isBombsiteClear[i] = false;
+ m_bombsiteSearchOrder[i] = i;
+ }
+
+ // shuffle the bombsite search order
+ // allows T's to plant at random site, and TEAM_CT's to search in a random order
+ // NOTE: VS6 std::random_shuffle() doesn't work well with an array of two elements (most maps)
+ for( i=0; i < m_bombsiteCount; ++i )
+ {
+ int swap = m_bombsiteSearchOrder[i];
+ int rnd = RandomInt( i, m_bombsiteCount-1 );
+ m_bombsiteSearchOrder[i] = m_bombsiteSearchOrder[ rnd ];
+ m_bombsiteSearchOrder[ rnd ] = swap;
+ }
+
+ m_bombsiteSearchIndex = 0;
+
+ // hostage ---------------------------------------------------------------------
+ InitializeHostageInfo();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Update game state based on events we have received
+ */
+void CSGameState::OnHostageRescuedAll( IGameEvent *event )
+{
+ m_allHostagesRescued = true;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Update game state based on events we have received
+ */
+void CSGameState::OnRoundEnd( IGameEvent *event )
+{
+ m_isRoundOver = true;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Update game state based on events we have received
+ */
+void CSGameState::OnRoundStart( IGameEvent *event )
+{
+ Reset();
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Update game state based on events we have received
+ */
+void CSGameState::OnBombPlanted( IGameEvent *event )
+{
+ // change state - the event is announced to everyone
+ SetBombState( PLANTED );
+
+ CBasePlayer *plantingPlayer = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
+
+ // Terrorists always know where the bomb is
+ if (m_owner->GetTeamNumber() == TEAM_TERRORIST && plantingPlayer)
+ {
+ UpdatePlantedBomb( plantingPlayer->GetAbsOrigin() );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Update game state based on events we have received
+ */
+void CSGameState::OnBombDefused( IGameEvent *event )
+{
+ // change state - the event is announced to everyone
+ SetBombState( DEFUSED );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Update game state based on events we have received
+ */
+void CSGameState::OnBombExploded( IGameEvent *event )
+{
+ // change state - the event is announced to everyone
+ SetBombState( EXPLODED );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * True if round has been won or lost (but not yet reset)
+ */
+bool CSGameState::IsRoundOver( void ) const
+{
+ return m_isRoundOver;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+void CSGameState::SetBombState( BombState state )
+{
+ // if state changed, reset "last seen" timestamps
+ if (m_bombState != state)
+ {
+ m_bombState = state;
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+void CSGameState::UpdateLooseBomb( const Vector &pos )
+{
+ m_looseBombPos = pos;
+ m_lastSawLooseBomb.Reset();
+
+ // we saw the loose bomb, update our state
+ SetBombState( LOOSE );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+float CSGameState::TimeSinceLastSawLooseBomb( void ) const
+{
+ return m_lastSawLooseBomb.GetElapsedTime();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+bool CSGameState::IsLooseBombLocationKnown( void ) const
+{
+ if (m_bombState != LOOSE)
+ return false;
+
+ return (m_lastSawLooseBomb.HasStarted()) ? true : false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+void CSGameState::UpdateBomber( const Vector &pos )
+{
+ m_bomberPos = pos;
+ m_lastSawBomber.Reset();
+
+ // we saw the bomber, update our state
+ SetBombState( MOVING );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+float CSGameState::TimeSinceLastSawBomber( void ) const
+{
+ return m_lastSawBomber.GetElapsedTime();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+bool CSGameState::IsPlantedBombLocationKnown( void ) const
+{
+ if (m_bombState != PLANTED)
+ return false;
+
+ return m_isPlantedBombPosKnown;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the zone index of the planted bombsite, or UNKNOWN
+ */
+int CSGameState::GetPlantedBombsite( void ) const
+{
+ if (m_bombState != PLANTED)
+ return UNKNOWN;
+
+ return m_plantedBombsite;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if we are currently in the bombsite where the bomb is planted
+ */
+bool CSGameState::IsAtPlantedBombsite( void ) const
+{
+ if (m_bombState != PLANTED)
+ return false;
+
+ Vector myOrigin = GetCentroid( m_owner );
+ const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone( myOrigin );
+
+ if (zone)
+ {
+ return (m_plantedBombsite == zone->m_index);
+ }
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the zone index of the next bombsite to search
+ */
+int CSGameState::GetNextBombsiteToSearch( void )
+{
+ if (m_bombsiteCount <= 0)
+ return 0;
+
+ int i;
+
+ // return next non-cleared bombsite index
+ for( i=m_bombsiteSearchIndex; i<m_bombsiteCount; ++i )
+ {
+ int z = m_bombsiteSearchOrder[i];
+ if (!m_isBombsiteClear[z])
+ {
+ m_bombsiteSearchIndex = i;
+ return z;
+ }
+ }
+
+ // all the bombsites are clear, someone must have been mistaken - start search over
+ for( i=0; i<m_bombsiteCount; ++i )
+ m_isBombsiteClear[i] = false;
+ m_bombsiteSearchIndex = 0;
+
+ return GetNextBombsiteToSearch();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns position of bomb in its various states (moving, loose, planted),
+ * or NULL if we don't know where the bomb is
+ */
+const Vector *CSGameState::GetBombPosition( void ) const
+{
+ switch( m_bombState )
+ {
+ case MOVING:
+ {
+ if (!m_lastSawBomber.HasStarted())
+ return NULL;
+
+ return &m_bomberPos;
+ }
+
+ case LOOSE:
+ {
+ if (IsLooseBombLocationKnown())
+ return &m_looseBombPos;
+
+ return NULL;
+ }
+
+ case PLANTED:
+ {
+ if (IsPlantedBombLocationKnown())
+ return &m_plantedBombPos;
+
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * We see the planted bomb at 'pos'
+ */
+void CSGameState::UpdatePlantedBomb( const Vector &pos )
+{
+ const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone( pos );
+
+ if (zone == NULL)
+ {
+ CONSOLE_ECHO( "ERROR: Bomb planted outside of a zone!\n" );
+ m_plantedBombsite = UNKNOWN;
+ }
+ else
+ {
+ m_plantedBombsite = zone->m_index;
+ }
+
+ m_plantedBombPos = pos;
+ m_isPlantedBombPosKnown = true;
+ SetBombState( PLANTED );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Someone told us where the bomb is planted
+ */
+void CSGameState::MarkBombsiteAsPlanted( int zoneIndex )
+{
+ m_plantedBombsite = zoneIndex;
+ SetBombState( PLANTED );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Someone told us a bombsite is clear
+ */
+void CSGameState::ClearBombsite( int zoneIndex )
+{
+ if (zoneIndex >= 0 && zoneIndex < m_bombsiteCount)
+ m_isBombsiteClear[ zoneIndex ] = true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+bool CSGameState::IsBombsiteClear( int zoneIndex ) const
+{
+ if (zoneIndex >= 0 && zoneIndex < m_bombsiteCount)
+ return m_isBombsiteClear[ zoneIndex ];
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Initialize our knowledge of the number and location of hostages
+ */
+void CSGameState::InitializeHostageInfo( void )
+{
+ m_hostageCount = 0;
+ m_allHostagesRescued = false;
+ m_haveSomeHostagesBeenTaken = false;
+
+ for( int i=0; i<g_Hostages.Count(); ++i )
+ {
+ m_hostage[ m_hostageCount ].hostage = g_Hostages[i];
+ m_hostage[ m_hostageCount ].knownPos = g_Hostages[i]->GetAbsOrigin();
+ m_hostage[ m_hostageCount ].isValid = true;
+ m_hostage[ m_hostageCount ].isAlive = true;
+ m_hostage[ m_hostageCount ].isFree = true;
+ ++m_hostageCount;
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the closest free and live hostage
+ * If we are a CT this information is perfect.
+ * Otherwise, this is based on our individual memory of the game state.
+ * If NULL is returned, we don't think there are any hostages left, or we dont know where they are.
+ * NOTE: a T can remember a hostage who has died. knowPos will be filled in, but NULL will be
+ * returned, since CHostages get deleted when they die.
+ */
+CHostage *CSGameState::GetNearestFreeHostage( Vector *knowPos ) const
+{
+ if (m_owner == NULL)
+ return NULL;
+
+ CNavArea *startArea = m_owner->GetLastKnownArea();
+ if (startArea == NULL)
+ return NULL;
+
+ CHostage *close = NULL;
+ Vector closePos( 0, 0, 0 );
+ float closeDistance = 9999999999.9f;
+
+ for( int i=0; i<m_hostageCount; ++i )
+ {
+ CHostage *hostage = m_hostage[i].hostage;
+ Vector hostagePos;
+
+ if (m_owner->GetTeamNumber() == TEAM_CT)
+ {
+ // we know exactly where the hostages are, and if they are alive
+ if (!m_hostage[i].hostage || !m_hostage[i].hostage->IsValid())
+ continue;
+
+ if (m_hostage[i].hostage->IsFollowingSomeone())
+ continue;
+
+ hostagePos = m_hostage[i].hostage->GetAbsOrigin();
+ }
+ else
+ {
+ // use our memory of where we think the hostages are
+ if (m_hostage[i].isValid == false)
+ continue;
+
+ hostagePos = m_hostage[i].knownPos;
+ }
+
+ CNavArea *hostageArea = TheNavMesh->GetNearestNavArea( hostagePos );
+ if (hostageArea)
+ {
+ ShortestPathCost cost;
+ float travelDistance = NavAreaTravelDistance( startArea, hostageArea, cost );
+
+ if (travelDistance >= 0.0f && travelDistance < closeDistance)
+ {
+ closeDistance = travelDistance;
+ closePos = hostagePos;
+ close = hostage;
+ }
+ }
+ }
+
+ // return where we think the hostage is
+ if (knowPos && close)
+ *knowPos = closePos;
+
+ return close;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the location of a "free" hostage, or NULL if we dont know of any
+ */
+const Vector *CSGameState::GetRandomFreeHostagePosition( void ) const
+{
+ if (m_owner == NULL)
+ return NULL;
+
+ static Vector freePos[ MAX_HOSTAGES ];
+ int freeCount = 0;
+
+ for( int i=0; i<m_hostageCount; ++i )
+ {
+ const HostageInfo *info = &m_hostage[i];
+
+ if (m_owner->GetTeamNumber() == TEAM_CT)
+ {
+ // we know exactly where the hostages are, and if they are alive
+ if (!info->hostage || !info->hostage->IsAlive())
+ continue;
+
+ // escorted hostages are not "free"
+ if (info->hostage->IsFollowingSomeone())
+ continue;
+
+ freePos[ freeCount++ ] = info->hostage->GetAbsOrigin();
+ }
+ else
+ {
+ // use our memory of where we think the hostages are
+ if (info->isValid == false)
+ continue;
+
+ freePos[ freeCount++ ] = info->knownPos;
+ }
+ }
+
+ if (freeCount)
+ {
+ return &freePos[ RandomInt( 0, freeCount-1 ) ];
+ }
+
+ return NULL;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * If we can see any of the positions where we think a hostage is, validate it
+ * Return status of any changes (a hostage died or was moved)
+ */
+unsigned char CSGameState::ValidateHostagePositions( void )
+{
+ // limit how often we validate
+ if (!m_validateInterval.IsElapsed())
+ return NO_CHANGE;
+
+ const float validateInterval = 0.5f;
+ m_validateInterval.Start( validateInterval );
+
+
+ // check the status of hostages
+ unsigned char status = NO_CHANGE;
+
+ int i;
+ int startValidCount = 0;
+ for( i=0; i<m_hostageCount; ++i )
+ if (m_hostage[i].isValid)
+ ++startValidCount;
+
+ for( i=0; i<m_hostageCount; ++i )
+ {
+ HostageInfo *info = &m_hostage[i];
+
+ if (!info->hostage )
+ continue;
+
+ // if we can see a hostage, update our knowledge of it
+ Vector pos = info->hostage->GetAbsOrigin() + Vector( 0, 0, HalfHumanHeight );
+ if (m_owner->IsVisible( pos, CHECK_FOV ))
+ {
+ if (info->hostage->IsAlive())
+ {
+ // live hostage
+
+ // if hostage is being escorted by a CT, we don't "see" it, we see the CT
+ if (info->hostage->IsFollowingSomeone())
+ {
+ info->isValid = false;
+ }
+ else
+ {
+ info->knownPos = info->hostage->GetAbsOrigin();
+ info->isValid = true;
+ }
+ }
+ else
+ {
+ // dead hostage
+
+ // if we thought it was alive, this is news to us
+ if (info->isAlive)
+ status |= HOSTAGE_DIED;
+
+ info->isAlive = false;
+ info->isValid = false;
+ }
+
+ continue;
+ }
+
+ // if we dont know where this hostage is, nothing to validate
+ if (!info->isValid)
+ continue;
+
+ // can't directly see this hostage
+ // check line of sight to where we think this hostage is, to see if we noticed that is has moved
+ pos = info->knownPos + Vector( 0, 0, HalfHumanHeight );
+ if (m_owner->IsVisible( pos, CHECK_FOV ))
+ {
+ // we can see where we thought the hostage was - verify it is still there and alive
+
+ if (!info->hostage->IsValid())
+ {
+ // since we have line of sight to an invalid hostage, it must be dead
+ // discovered that hostage has been killed
+ status |= HOSTAGE_DIED;
+ info->isAlive = false;
+ info->isValid = false;
+ continue;
+ }
+
+ if (info->hostage->IsFollowingSomeone())
+ {
+ // discovered the hostage has been taken
+ status |= HOSTAGE_GONE;
+ info->isValid = false;
+ continue;
+ }
+
+ const float tolerance = 50.0f;
+ if ((info->hostage->GetAbsOrigin() - info->knownPos).IsLengthGreaterThan( tolerance ))
+ {
+ // discovered that hostage has been moved
+ status |= HOSTAGE_GONE;
+ info->isValid = false;
+ continue;
+ }
+ }
+ }
+
+ int endValidCount = 0;
+ for( i=0; i<m_hostageCount; ++i )
+ if (m_hostage[i].isValid)
+ ++endValidCount;
+
+ if (endValidCount == 0 && startValidCount > 0)
+ {
+ // we discovered all the hostages are gone
+ status &= ~HOSTAGE_GONE;
+ status |= HOSTAGES_ALL_GONE;
+ }
+
+ return status;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the nearest visible free hostage
+ * Since we can actually see any hostage we return, we know its actual position
+ */
+CHostage *CSGameState::GetNearestVisibleFreeHostage( void ) const
+{
+ CHostage *close = NULL;
+ float closeRangeSq = 999999999.9f;
+ float rangeSq;
+
+ Vector pos;
+ Vector myOrigin = GetCentroid( m_owner );
+
+ for( int i=0; i<m_hostageCount; ++i )
+ {
+ const HostageInfo *info = &m_hostage[i];
+
+ if ( !info->hostage )
+ continue;
+
+ // if the hostage is dead or rescued, its not free
+ if (!info->hostage->IsAlive())
+ continue;
+
+ // if this hostage is following someone, its not free
+ if (info->hostage->IsFollowingSomeone())
+ continue;
+
+ /// @todo Use travel distance here
+ pos = info->hostage->GetAbsOrigin();
+ rangeSq = (pos - myOrigin).LengthSqr();
+
+ if (rangeSq < closeRangeSq)
+ {
+ if (!m_owner->IsVisible( pos ))
+ continue;
+
+ close = info->hostage;
+ closeRangeSq = rangeSq;
+ }
+ }
+
+ return close;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if there are no free hostages
+ */
+bool CSGameState::AreAllHostagesBeingRescued( void ) const
+{
+ // if the hostages have all been rescued, they are not being rescued any longer
+ if (m_allHostagesRescued)
+ return false;
+
+ bool isAllDead = true;
+
+ for( int i=0; i<m_hostageCount; ++i )
+ {
+ const HostageInfo *info = &m_hostage[i];
+
+ if (m_owner->GetTeamNumber() == TEAM_CT)
+ {
+ // CT's have perfect knowledge via their radar
+ if (info->hostage && info->hostage->IsValid())
+ {
+ if (!info->hostage->IsFollowingSomeone())
+ return false;
+
+ isAllDead = false;
+ }
+ }
+ else
+ {
+ if (info->isValid && info->isAlive)
+ return false;
+
+ if (info->isAlive)
+ isAllDead = false;
+ }
+ }
+
+ // if all of the remaining hostages are dead, they arent being rescued
+ if (isAllDead)
+ return false;
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * All hostages have been rescued or are dead
+ */
+bool CSGameState::AreAllHostagesGone( void ) const
+{
+ if (m_allHostagesRescued)
+ return true;
+
+ // do we know that all the hostages are dead
+ for( int i=0; i<m_hostageCount; ++i )
+ {
+ const HostageInfo *info = &m_hostage[i];
+
+ if (m_owner->GetTeamNumber() == TEAM_CT)
+ {
+ // CT's have perfect knowledge via their radar
+ if (info->hostage && info->hostage->IsAlive())
+ return false;
+ }
+ else
+ {
+ if (info->isValid && info->isAlive)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Someone told us all the hostages are gone
+ */
+void CSGameState::AllHostagesGone( void )
+{
+ for( int i=0; i<m_hostageCount; ++i )
+ m_hostage[i].isValid = false;
+}
+