//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Encapsulation of the current scenario/game state. Allows each bot imperfect knowledge. // // $NoKeywords: $ //=============================================================================// // Author: Michael S. Booth (mike@turtlerockstudios.com), 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; iGetZoneCount(); int i; for( i=0; iGetInt( "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; iGetClosestZone( 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; iGetAbsOrigin(); 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; iGetTeamNumber() == 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; iGetTeamNumber() == 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; ihostage ) 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 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; ihostage ) 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; iGetTeamNumber() == 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; iGetTeamNumber() == 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