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.
767 lines
19 KiB
767 lines
19 KiB
//========= 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; 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; |
|
} |
|
|
|
|