2020-04-22 18:56:21 +02:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
// Author: Michael S. Booth (mike@turtlerockstudios.com), 2003
# pragma warning( disable : 4530 ) // STL uses exceptions, but we are not compiling with them - ignore warning
# include "cbase.h"
# include "cs_bot.h"
# include "nav_area.h"
# include "cs_gamerules.h"
# include "shared_util.h"
# include "KeyValues.h"
# include "tier0/icommandline.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# ifdef _WIN32
# pragma warning (disable:4701) // disable warning that variable *may* not be initialized
# endif
CBotManager * TheBots = NULL ;
bool CCSBotManager : : m_isMapDataLoaded = false ;
int g_nClientPutInServerOverrides = 0 ;
void DrawOccupyTime ( void ) ;
ConVar bot_show_occupy_time ( " bot_show_occupy_time " , " 0 " , FCVAR_GAMEDLL | FCVAR_CHEAT , " Show when each nav area can first be reached by each team. " ) ;
void DrawBattlefront ( void ) ;
ConVar bot_show_battlefront ( " bot_show_battlefront " , " 0 " , FCVAR_GAMEDLL | FCVAR_CHEAT , " Show areas where rushing players will initially meet. " ) ;
int UTIL_CSSBotsInGame ( void ) ;
ConVar bot_join_delay ( " bot_join_delay " , " 0 " , FCVAR_GAMEDLL , " Prevents bots from joining the server for this many seconds after a map change. " ) ;
/**
* Determine whether bots can be used or not
*/
inline bool AreBotsAllowed ( )
{
// If they pass in -nobots, don't allow bots. This is for people who host servers, to
// allow them to disallow bots to enforce CPU limits.
const char * nobots = CommandLine ( ) - > CheckParm ( " -nobots " ) ;
if ( nobots )
{
return false ;
}
return true ;
}
//--------------------------------------------------------------------------------------------------------------
void InstallBotControl ( void )
{
if ( TheBots ! = NULL )
delete TheBots ;
TheBots = new CCSBotManager ;
}
//--------------------------------------------------------------------------------------------------------------
void RemoveBotControl ( void )
{
if ( TheBots ! = NULL )
delete TheBots ;
TheBots = NULL ;
}
//--------------------------------------------------------------------------------------------------------------
CBasePlayer * ClientPutInServerOverride_Bot ( edict_t * pEdict , const char * playername )
{
CBasePlayer * pPlayer = TheBots - > AllocateAndBindBotEntity ( pEdict ) ;
if ( pPlayer )
{
pPlayer - > SetPlayerName ( playername ) ;
}
+ + g_nClientPutInServerOverrides ;
return pPlayer ;
}
//--------------------------------------------------------------------------------------------------------------
// Constructor
CCSBotManager : : CCSBotManager ( )
{
m_zoneCount = 0 ;
SetLooseBomb ( NULL ) ;
m_serverActive = false ;
m_isBombPlanted = false ;
m_bombDefuser = NULL ;
m_roundStartTimestamp = 0.0f ;
m_eventListenersEnabled = true ;
m_commonEventListeners . AddToTail ( & m_PlayerFootstepEvent ) ;
m_commonEventListeners . AddToTail ( & m_PlayerRadioEvent ) ;
m_commonEventListeners . AddToTail ( & m_PlayerFallDamageEvent ) ;
m_commonEventListeners . AddToTail ( & m_BombBeepEvent ) ;
m_commonEventListeners . AddToTail ( & m_DoorMovingEvent ) ;
m_commonEventListeners . AddToTail ( & m_BreakPropEvent ) ;
m_commonEventListeners . AddToTail ( & m_BreakBreakableEvent ) ;
m_commonEventListeners . AddToTail ( & m_WeaponFireEvent ) ;
m_commonEventListeners . AddToTail ( & m_WeaponFireOnEmptyEvent ) ;
m_commonEventListeners . AddToTail ( & m_WeaponReloadEvent ) ;
m_commonEventListeners . AddToTail ( & m_WeaponZoomEvent ) ;
2024-07-25 18:10:35 +02:00
m_commonEventListeners . AddToTail ( & m_BulletImpactEvent ) ;
m_commonEventListeners . AddToTail ( & m_BulletHitPlayerEvent ) ;
m_commonEventListeners . AddToTail ( & m_BulletPlayerHitboxesEvent ) ;
m_commonEventListeners . AddToTail ( & m_PlayerLagHitboxesEvent ) ;
2020-04-22 18:56:21 +02:00
m_commonEventListeners . AddToTail ( & m_GrenadeBounceEvent ) ;
m_commonEventListeners . AddToTail ( & m_NavBlockedEvent ) ;
TheBotPhrases = new BotPhraseManager ;
TheBotProfiles = new BotProfileManager ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Invoked when a new round begins
*/
void CCSBotManager : : RestartRound ( void )
{
// extend
CBotManager : : RestartRound ( ) ;
SetLooseBomb ( NULL ) ;
m_isBombPlanted = false ;
m_earliestBombPlantTimestamp = gpGlobals - > curtime + RandomFloat ( 10.0f , 30.0f ) ; // 60
m_bombDefuser = NULL ;
ResetRadioMessageTimestamps ( ) ;
m_lastSeenEnemyTimestamp = - 9999.9f ;
m_roundStartTimestamp = gpGlobals - > curtime + mp_freezetime . GetFloat ( ) ;
// randomly decide if defensive team wants to "rush" as a whole
const float defenseRushChance = 33.3f ; // 25.0f;
m_isDefenseRushing = ( RandomFloat ( 0.0f , 100.0f ) < = defenseRushChance ) ? true : false ;
TheBotPhrases - > OnRoundRestart ( ) ;
m_isRoundOver = false ;
}
//--------------------------------------------------------------------------------------------------------------
void UTIL_DrawBox ( Extent * extent , int lifetime , int red , int green , int blue )
{
int darkRed = red / 2 ;
int darkGreen = green / 2 ;
int darkBlue = blue / 2 ;
Vector v [ 8 ] ;
v [ 0 ] . x = extent - > lo . x ; v [ 0 ] . y = extent - > lo . y ; v [ 0 ] . z = extent - > lo . z ;
v [ 1 ] . x = extent - > hi . x ; v [ 1 ] . y = extent - > lo . y ; v [ 1 ] . z = extent - > lo . z ;
v [ 2 ] . x = extent - > hi . x ; v [ 2 ] . y = extent - > hi . y ; v [ 2 ] . z = extent - > lo . z ;
v [ 3 ] . x = extent - > lo . x ; v [ 3 ] . y = extent - > hi . y ; v [ 3 ] . z = extent - > lo . z ;
v [ 4 ] . x = extent - > lo . x ; v [ 4 ] . y = extent - > lo . y ; v [ 4 ] . z = extent - > hi . z ;
v [ 5 ] . x = extent - > hi . x ; v [ 5 ] . y = extent - > lo . y ; v [ 5 ] . z = extent - > hi . z ;
v [ 6 ] . x = extent - > hi . x ; v [ 6 ] . y = extent - > hi . y ; v [ 6 ] . z = extent - > hi . z ;
v [ 7 ] . x = extent - > lo . x ; v [ 7 ] . y = extent - > hi . y ; v [ 7 ] . z = extent - > hi . z ;
static int edge [ ] =
{
1 , 2 , 3 , 4 , - 1 ,
5 , 6 , 7 , 8 , - 5 ,
1 , - 5 ,
2 , - 6 ,
3 , - 7 ,
4 , - 8 ,
0
} ;
Vector from , to ;
bool restart = true ;
for ( int i = 0 ; edge [ i ] ! = 0 ; + + i )
{
if ( restart )
{
to = v [ edge [ i ] - 1 ] ;
restart = false ;
continue ;
}
from = to ;
int index = edge [ i ] ;
if ( index < 0 )
{
restart = true ;
index = - index ;
}
to = v [ index - 1 ] ;
NDebugOverlay : : Line ( from , to , darkRed , darkGreen , darkBlue , true , 0.1f ) ;
NDebugOverlay : : Line ( from , to , red , green , blue , false , 0.15f ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : EnableEventListeners ( bool enable )
{
if ( m_eventListenersEnabled = = enable )
{
return ;
}
m_eventListenersEnabled = enable ;
// enable/disable the most frequent event listeners, to improve performance when no bots are present.
for ( int i = 0 ; i < m_commonEventListeners . Count ( ) ; + + i )
{
if ( enable )
{
gameeventmanager - > AddListener ( m_commonEventListeners [ i ] , m_commonEventListeners [ i ] - > GetEventName ( ) , true ) ;
}
else
{
gameeventmanager - > RemoveListener ( m_commonEventListeners [ i ] ) ;
}
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Called each frame
*/
void CCSBotManager : : StartFrame ( void )
{
if ( ! AreBotsAllowed ( ) )
{
EnableEventListeners ( false ) ;
return ;
}
// EXTEND
CBotManager : : StartFrame ( ) ;
MaintainBotQuota ( ) ;
EnableEventListeners ( UTIL_CSSBotsInGame ( ) > 0 ) ;
// debug zone extent visualization
if ( cv_bot_debug . GetInt ( ) = = 5 )
{
for ( int z = 0 ; z < m_zoneCount ; + + z )
{
Zone * zone = & m_zone [ z ] ;
if ( zone - > m_isBlocked )
{
UTIL_DrawBox ( & zone - > m_extent , 1 , 255 , 0 , 200 ) ;
}
else
{
UTIL_DrawBox ( & zone - > m_extent , 1 , 255 , 100 , 0 ) ;
}
}
}
if ( bot_show_occupy_time . GetBool ( ) )
{
DrawOccupyTime ( ) ;
}
if ( bot_show_battlefront . GetBool ( ) )
{
DrawBattlefront ( ) ;
}
if ( m_checkTransientAreasTimer . IsElapsed ( ) & & ! nav_edit . GetBool ( ) )
{
CUtlVector < CNavArea * > & transientAreas = TheNavMesh - > GetTransientAreas ( ) ;
for ( int i = 0 ; i < transientAreas . Count ( ) ; + + i )
{
CNavArea * area = transientAreas [ i ] ;
if ( area - > GetAttributes ( ) & NAV_MESH_TRANSIENT )
{
area - > UpdateBlocked ( ) ;
}
}
m_checkTransientAreasTimer . Start ( 2.0f ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return true if the bot can use this weapon
*/
bool CCSBotManager : : IsWeaponUseable ( const CWeaponCSBase * weapon ) const
{
if ( weapon = = NULL )
return false ;
if ( weapon - > IsA ( WEAPON_C4 ) )
return true ;
if ( ( ! AllowShotguns ( ) & & weapon - > IsKindOf ( WEAPONTYPE_SHOTGUN ) ) | |
( ! AllowMachineGuns ( ) & & weapon - > IsKindOf ( WEAPONTYPE_MACHINEGUN ) ) | |
( ! AllowRifles ( ) & & weapon - > IsKindOf ( WEAPONTYPE_RIFLE ) ) | |
( ! AllowShotguns ( ) & & weapon - > IsKindOf ( WEAPONTYPE_SHOTGUN ) ) | |
( ! AllowSnipers ( ) & & weapon - > IsKindOf ( WEAPONTYPE_SNIPER_RIFLE ) ) | |
( ! AllowSubMachineGuns ( ) & & weapon - > IsKindOf ( WEAPONTYPE_SUBMACHINEGUN ) ) | |
( ! AllowPistols ( ) & & weapon - > IsKindOf ( WEAPONTYPE_PISTOL ) ) | |
( ! AllowGrenades ( ) & & weapon - > IsKindOf ( WEAPONTYPE_GRENADE ) ) )
{
return false ;
}
return true ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return true if this player is on " defense "
*/
bool CCSBotManager : : IsOnDefense ( const CCSPlayer * player ) const
{
switch ( GetScenario ( ) )
{
case SCENARIO_DEFUSE_BOMB :
return ( player - > GetTeamNumber ( ) = = TEAM_CT ) ;
case SCENARIO_RESCUE_HOSTAGES :
return ( player - > GetTeamNumber ( ) = = TEAM_TERRORIST ) ;
case SCENARIO_ESCORT_VIP :
return ( player - > GetTeamNumber ( ) = = TEAM_TERRORIST ) ;
}
return false ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return true if this player is on " offense "
*/
bool CCSBotManager : : IsOnOffense ( const CCSPlayer * player ) const
{
return ! IsOnDefense ( player ) ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Invoked when a map has just been loaded
*/
void CCSBotManager : : ServerActivate ( void )
{
m_isMapDataLoaded = false ;
// load the database of bot radio chatter
TheBotPhrases - > Reset ( ) ;
TheBotPhrases - > Initialize ( " BotChatter.db " , 0 ) ;
TheBotProfiles - > Reset ( ) ;
TheBotProfiles - > FindVoiceBankIndex ( " BotChatter.db " ) ; // make sure default voice bank is first
const char * filename ;
if ( false ) // g_engfuncs.pfnIsCareerMatch() )
{
filename = " MissionPacks/BotPackList.db " ;
}
else
{
filename = " BotPackList.db " ;
}
// read in the list of bot profile DBs
FileHandle_t file = filesystem - > Open ( filename , " r " ) ;
if ( ! file )
{
TheBotProfiles - > Init ( " BotProfile.db " ) ;
}
else
{
int dataLength = filesystem - > Size ( filename ) ;
char * dataPointer = new char [ dataLength ] ;
filesystem - > Read ( dataPointer , dataLength , file ) ;
filesystem - > Close ( file ) ;
const char * dataFile = SharedParse ( dataPointer ) ;
const char * token ;
while ( dataFile )
{
token = SharedGetToken ( ) ;
char * clone = CloneString ( token ) ;
TheBotProfiles - > Init ( clone ) ;
delete [ ] clone ;
dataFile = SharedParse ( dataFile ) ;
}
delete [ ] dataPointer ;
}
// Now that we've parsed all the profiles, we have a list of the voice banks they're using.
// Go back and parse the custom voice speakables.
const BotProfileManager : : VoiceBankList * voiceBanks = TheBotProfiles - > GetVoiceBanks ( ) ;
for ( int i = 1 ; i < voiceBanks - > Count ( ) ; + + i )
{
TheBotPhrases - > Initialize ( ( * voiceBanks ) [ i ] , i ) ;
}
// tell the Navigation Mesh system what CS spawn points are named
TheNavMesh - > SetPlayerSpawnName ( " info_player_terrorist " ) ;
ExtractScenarioData ( ) ;
RestartRound ( ) ;
TheBotPhrases - > OnMapChange ( ) ;
m_serverActive = true ;
}
void CCSBotManager : : ServerDeactivate ( void )
{
m_serverActive = false ;
}
void CCSBotManager : : ClientDisconnect ( CBaseEntity * entity )
{
/*
if ( FBitSet ( entity - > GetFlags ( ) , FL_FAKECLIENT ) )
{
FREE_PRIVATE ( entity ) ;
}
*/
/*
// make sure voice feedback is turned off
CBasePlayer * pPlayer = ( CBasePlayer * ) CBaseEntity : : Instance ( pEntity ) ;
if ( pPlayer & & pPlayer - > IsBot ( ) )
{
CCSBot * pBot = static_cast < CCSBot * > ( pPlayer ) ;
if ( pBot )
{
pBot - > EndVoiceFeedback ( true ) ;
}
}
*/
}
//--------------------------------------------------------------------------------------------------------------
/**
* Parses out bot name / template / etc params from the current ConCommand
*/
void BotArgumentsFromArgv ( const CCommand & args , const char * * name , CSWeaponType * weaponType , BotDifficultyType * difficulty , int * team = NULL , bool * all = NULL )
{
static char s_name [ MAX_PLAYER_NAME_LENGTH ] ;
s_name [ 0 ] = 0 ;
* name = s_name ;
* difficulty = NUM_DIFFICULTY_LEVELS ;
if ( team )
{
* team = TEAM_UNASSIGNED ;
}
if ( all )
{
* all = false ;
}
* weaponType = WEAPONTYPE_UNKNOWN ;
for ( int arg = 1 ; arg < args . ArgC ( ) ; + + arg )
{
bool found = false ;
const char * token = args [ arg ] ;
if ( all & & FStrEq ( token , " all " ) )
{
* all = true ;
found = true ;
}
else if ( team & & FStrEq ( token , " t " ) )
{
* team = TEAM_TERRORIST ;
found = true ;
}
else if ( team & & FStrEq ( token , " ct " ) )
{
* team = TEAM_CT ;
found = true ;
}
for ( int i = 0 ; i < NUM_DIFFICULTY_LEVELS & & ! found ; + + i )
{
if ( ! stricmp ( BotDifficultyName [ i ] , token ) )
{
* difficulty = ( BotDifficultyType ) i ;
found = true ;
}
}
if ( ! found )
{
* weaponType = WeaponClassFromString ( token ) ;
if ( * weaponType ! = WEAPONTYPE_UNKNOWN )
{
found = true ;
}
}
if ( ! found )
{
Q_strncpy ( s_name , token , sizeof ( s_name ) ) ;
}
}
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_add , " bot_add <t|ct> <type> <difficulty> <name> - Adds a bot matching the given criteria. " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
const char * name ;
BotDifficultyType difficulty ;
CSWeaponType weaponType ;
int team ;
BotArgumentsFromArgv ( args , & name , & weaponType , & difficulty , & team ) ;
TheCSBots ( ) - > BotAddCommand ( team , FROM_CONSOLE , name , weaponType , difficulty ) ;
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_add_t , " bot_add_t <type> <difficulty> <name> - Adds a terrorist bot matching the given criteria. " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
const char * name ;
BotDifficultyType difficulty ;
CSWeaponType weaponType ;
BotArgumentsFromArgv ( args , & name , & weaponType , & difficulty ) ;
TheCSBots ( ) - > BotAddCommand ( TEAM_TERRORIST , FROM_CONSOLE , name , weaponType , difficulty ) ;
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_add_ct , " bot_add_ct <type> <difficulty> <name> - Adds a Counter-Terrorist bot matching the given criteria. " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
const char * name ;
BotDifficultyType difficulty ;
CSWeaponType weaponType ;
BotArgumentsFromArgv ( args , & name , & weaponType , & difficulty ) ;
TheCSBots ( ) - > BotAddCommand ( TEAM_CT , FROM_CONSOLE , name , weaponType , difficulty ) ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Collects all bots matching the given criteria ( player name , profile template name , difficulty , and team )
*/
class CollectBots
{
public :
CollectBots ( const char * name , CSWeaponType weaponType , BotDifficultyType difficulty , int team )
{
m_name = name ;
m_difficulty = difficulty ;
m_team = team ;
m_weaponType = weaponType ;
}
bool operator ( ) ( CBasePlayer * player )
{
if ( ! player - > IsBot ( ) )
{
return true ;
}
CCSBot * bot = dynamic_cast < CCSBot * > ( player ) ;
if ( ! bot | | ! bot - > GetProfile ( ) )
{
return true ;
}
if ( m_name & & * m_name )
{
// accept based on name
if ( FStrEq ( m_name , bot - > GetProfile ( ) - > GetName ( ) ) )
{
m_bots . RemoveAll ( ) ;
m_bots . AddToTail ( bot ) ;
return false ;
}
// Reject based on profile template name
if ( ! bot - > GetProfile ( ) - > InheritsFrom ( m_name ) )
{
return true ;
}
}
// reject based on difficulty
if ( m_difficulty ! = NUM_DIFFICULTY_LEVELS )
{
if ( ! bot - > GetProfile ( ) - > IsDifficulty ( m_difficulty ) )
{
return true ;
}
}
// reject based on team
if ( m_team = = TEAM_CT | | m_team = = TEAM_TERRORIST )
{
if ( bot - > GetTeamNumber ( ) ! = m_team )
{
return true ;
}
}
// reject based on weapon preference
if ( m_weaponType ! = WEAPONTYPE_UNKNOWN )
{
if ( ! bot - > GetProfile ( ) - > GetWeaponPreferenceCount ( ) )
{
return true ;
}
if ( m_weaponType ! = WeaponClassFromWeaponID ( ( CSWeaponID ) bot - > GetProfile ( ) - > GetWeaponPreference ( 0 ) ) )
{
return true ;
}
}
// A match!
m_bots . AddToTail ( bot ) ;
return true ;
}
CUtlVector < CCSBot * > m_bots ;
private :
const char * m_name ;
CSWeaponType m_weaponType ;
BotDifficultyType m_difficulty ;
int m_team ;
} ;
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_kill , " bot_kill <all> <t|ct> <type> <difficulty> <name> - Kills a specific bot, or all bots, matching the given criteria. " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
const char * name ;
BotDifficultyType difficulty ;
CSWeaponType weaponType ;
int team ;
bool all ;
BotArgumentsFromArgv ( args , & name , & weaponType , & difficulty , & team , & all ) ;
if ( ( ! name | | ! * name ) & & team = = TEAM_UNASSIGNED & & difficulty = = NUM_DIFFICULTY_LEVELS )
{
all = true ;
}
CollectBots collector ( name , weaponType , difficulty , team ) ;
ForEachPlayer ( collector ) ;
for ( int i = 0 ; i < collector . m_bots . Count ( ) ; + + i )
{
CCSBot * bot = collector . m_bots [ i ] ;
if ( ! bot - > IsAlive ( ) )
continue ;
bot - > CommitSuicide ( ) ;
if ( ! all )
{
return ;
}
}
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_kick , " bot_kick <all> <t|ct> <type> <difficulty> <name> - Kicks a specific bot, or all bots, matching the given criteria. " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
const char * name ;
BotDifficultyType difficulty ;
CSWeaponType weaponType ;
int team ;
bool all ;
BotArgumentsFromArgv ( args , & name , & weaponType , & difficulty , & team , & all ) ;
if ( ( ! name | | ! * name ) & & team = = TEAM_UNASSIGNED & & difficulty = = NUM_DIFFICULTY_LEVELS )
{
all = true ;
}
CollectBots collector ( name , weaponType , difficulty , team ) ;
ForEachPlayer ( collector ) ;
for ( int i = 0 ; i < collector . m_bots . Count ( ) ; + + i )
{
CCSBot * bot = collector . m_bots [ i ] ;
engine - > ServerCommand ( UTIL_VarArgs ( " kick \" %s \" \n " , bot - > GetPlayerName ( ) ) ) ;
if ( ! all )
{
// adjust bot quota so kicked bot is not immediately added back in
int newQuota = cv_bot_quota . GetInt ( ) - 1 ;
cv_bot_quota . SetValue ( clamp ( newQuota , 0 , cv_bot_quota . GetInt ( ) ) ) ;
return ;
}
}
// adjust bot quota so kicked bot is not immediately added back in
if ( all & & ( ! name | | ! * name ) & & team = = TEAM_UNASSIGNED & & difficulty = = NUM_DIFFICULTY_LEVELS )
{
cv_bot_quota . SetValue ( 0 ) ;
}
else
{
int newQuota = cv_bot_quota . GetInt ( ) - collector . m_bots . Count ( ) ;
cv_bot_quota . SetValue ( clamp ( newQuota , 0 , cv_bot_quota . GetInt ( ) ) ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_knives_only , " Restricts the bots to only using knives " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
cv_bot_allow_pistols . SetValue ( 0 ) ;
cv_bot_allow_shotguns . SetValue ( 0 ) ;
cv_bot_allow_sub_machine_guns . SetValue ( 0 ) ;
cv_bot_allow_rifles . SetValue ( 0 ) ;
cv_bot_allow_machine_guns . SetValue ( 0 ) ;
cv_bot_allow_grenades . SetValue ( 0 ) ;
cv_bot_allow_snipers . SetValue ( 0 ) ;
# ifdef CS_SHIELD_ENABLED
cv_bot_allow_shield . SetValue ( 0 ) ;
# endif // CS_SHIELD_ENABLED
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_pistols_only , " Restricts the bots to only using pistols " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
cv_bot_allow_pistols . SetValue ( 1 ) ;
cv_bot_allow_shotguns . SetValue ( 0 ) ;
cv_bot_allow_sub_machine_guns . SetValue ( 0 ) ;
cv_bot_allow_rifles . SetValue ( 0 ) ;
cv_bot_allow_machine_guns . SetValue ( 0 ) ;
cv_bot_allow_grenades . SetValue ( 0 ) ;
cv_bot_allow_snipers . SetValue ( 0 ) ;
# ifdef CS_SHIELD_ENABLED
cv_bot_allow_shield . SetValue ( 0 ) ;
# endif // CS_SHIELD_ENABLED
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_snipers_only , " Restricts the bots to only using sniper rifles " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
cv_bot_allow_pistols . SetValue ( 0 ) ;
cv_bot_allow_shotguns . SetValue ( 0 ) ;
cv_bot_allow_sub_machine_guns . SetValue ( 0 ) ;
cv_bot_allow_rifles . SetValue ( 0 ) ;
cv_bot_allow_machine_guns . SetValue ( 0 ) ;
cv_bot_allow_grenades . SetValue ( 0 ) ;
cv_bot_allow_snipers . SetValue ( 1 ) ;
# ifdef CS_SHIELD_ENABLED
cv_bot_allow_shield . SetValue ( 0 ) ;
# endif // CS_SHIELD_ENABLED
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_all_weapons , " Allows the bots to use all weapons " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
cv_bot_allow_pistols . SetValue ( 1 ) ;
cv_bot_allow_shotguns . SetValue ( 1 ) ;
cv_bot_allow_sub_machine_guns . SetValue ( 1 ) ;
cv_bot_allow_rifles . SetValue ( 1 ) ;
cv_bot_allow_machine_guns . SetValue ( 1 ) ;
cv_bot_allow_grenades . SetValue ( 1 ) ;
cv_bot_allow_snipers . SetValue ( 1 ) ;
# ifdef CS_SHIELD_ENABLED
cv_bot_allow_shield . SetValue ( 1 ) ;
# endif // CS_SHIELD_ENABLED
}
//--------------------------------------------------------------------------------------------------------------
CON_COMMAND_F ( bot_goto_mark , " Sends a bot to the selected nav area (useful for testing navigation meshes) " , FCVAR_GAMEDLL | FCVAR_CHEAT )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
// tell the first bot we find to go to our marked area
CNavArea * area = TheNavMesh - > GetMarkedArea ( ) ;
if ( area )
{
for ( int i = 1 ; i < = gpGlobals - > maxClients ; + + i )
{
CBasePlayer * player = static_cast < CBasePlayer * > ( UTIL_PlayerByIndex ( i ) ) ;
if ( player = = NULL )
continue ;
if ( player - > IsBot ( ) )
{
CCSBot * bot = dynamic_cast < CCSBot * > ( player ) ;
if ( bot )
{
bot - > MoveTo ( area - > GetCenter ( ) , FASTEST_ROUTE ) ;
}
break ;
}
}
}
}
//--------------------------------------------------------------------------------------------------------------
#if 0
CON_COMMAND_F ( bot_memory_usage , " Reports on the bots' memory usage " , FCVAR_GAMEDLL )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
Msg ( " Memory usage: \n " ) ;
Msg ( " %d bytes per bot \n " , sizeof ( CCSBot ) ) ;
Msg ( " %d Navigation Areas @ %d bytes each = %d bytes \n " ,
TheNavMesh - > GetNavAreaCount ( ) ,
sizeof ( CNavArea ) ,
TheNavMesh - > GetNavAreaCount ( ) * sizeof ( CNavArea ) ) ;
Msg ( " %d Hiding Spots @ %d bytes each = %d bytes \n " ,
TheHidingSpotList . Count ( ) ,
sizeof ( HidingSpot ) ,
TheHidingSpotList . Count ( ) * sizeof ( HidingSpot ) ) ;
/*
unsigned int encounterMem = 0 ;
FOR_EACH_LL ( TheNavAreaList , it )
{
CNavArea * area = TheNavAreaList [ it ] ;
FOR_EACH_LL ( area - > m_spotEncounterList , it )
{
SpotEncounter * se = area - > m_spotEncounterList [ it ] ;
encounterMem + = sizeof ( SpotEncounter ) ;
encounterMem + = se - > spotList . Count ( ) * sizeof ( SpotOrder ) ;
}
}
Msg ( " Encounter Spot data = %d bytes \n " , encounterMem ) ;
*/
}
# endif
bool CCSBotManager : : ServerCommand ( const char * cmd )
{
return false ;
}
bool CCSBotManager : : ClientCommand ( CBasePlayer * player , const CCommand & args )
{
return false ;
}
/**
* Process the " bot_add " console command
*/
bool CCSBotManager : : BotAddCommand ( int team , bool isFromConsole , const char * profileName , CSWeaponType weaponType , BotDifficultyType difficulty )
{
if ( ! TheNavMesh - > IsLoaded ( ) )
{
// If there isn't a Navigation Mesh in memory, create one
if ( ! TheNavMesh - > IsGenerating ( ) )
{
if ( ! m_isMapDataLoaded )
{
TheNavMesh - > BeginGeneration ( ) ;
m_isMapDataLoaded = true ;
}
return false ;
}
}
// dont allow bots to join if the Navigation Mesh is being generated
if ( TheNavMesh - > IsGenerating ( ) )
return false ;
const BotProfile * profile = NULL ;
if ( ! isFromConsole )
{
profileName = NULL ;
difficulty = GetDifficultyLevel ( ) ;
}
else
{
if ( difficulty = = NUM_DIFFICULTY_LEVELS )
{
difficulty = GetDifficultyLevel ( ) ;
}
// if team not specified, check bot_join_team cvar for preference
if ( team = = TEAM_UNASSIGNED )
{
if ( ! stricmp ( cv_bot_join_team . GetString ( ) , " T " ) )
team = TEAM_TERRORIST ;
else if ( ! stricmp ( cv_bot_join_team . GetString ( ) , " CT " ) )
team = TEAM_CT ;
else
team = CSGameRules ( ) - > SelectDefaultTeam ( ) ;
}
}
if ( profileName & & * profileName )
{
// in career, ignore humans, since we want to add anyway
bool ignoreHumans = CSGameRules ( ) - > IsCareer ( ) ;
if ( UTIL_IsNameTaken ( profileName , ignoreHumans ) )
{
if ( isFromConsole )
{
Msg ( " Error - %s is already in the game. \n " , profileName ) ;
}
return true ;
}
// try to add a bot by name
profile = TheBotProfiles - > GetProfile ( profileName , team ) ;
if ( ! profile )
{
// try to add a bot by template
profile = TheBotProfiles - > GetProfileMatchingTemplate ( profileName , team , difficulty ) ;
if ( ! profile )
{
if ( isFromConsole )
{
Msg ( " Error - no profile for '%s' exists. \n " , profileName ) ;
}
return true ;
}
}
}
else
{
// if team not specified, check bot_join_team cvar for preference
if ( team = = TEAM_UNASSIGNED )
{
if ( ! stricmp ( cv_bot_join_team . GetString ( ) , " T " ) )
team = TEAM_TERRORIST ;
else if ( ! stricmp ( cv_bot_join_team . GetString ( ) , " CT " ) )
team = TEAM_CT ;
else
team = CSGameRules ( ) - > SelectDefaultTeam ( ) ;
}
profile = TheBotProfiles - > GetRandomProfile ( difficulty , team , weaponType ) ;
if ( profile = = NULL )
{
if ( isFromConsole )
{
Msg ( " All bot profiles at this difficulty level are in use. \n " ) ;
}
return true ;
}
}
if ( team = = TEAM_UNASSIGNED | | team = = TEAM_SPECTATOR )
{
if ( isFromConsole )
{
Msg ( " Could not add bot to the game: The game is full \n " ) ;
}
return false ;
}
if ( CSGameRules ( ) - > TeamFull ( team ) )
{
if ( isFromConsole )
{
Msg ( " Could not add bot to the game: Team is full \n " ) ;
}
return false ;
}
if ( CSGameRules ( ) - > TeamStacked ( team , TEAM_UNASSIGNED ) )
{
if ( isFromConsole )
{
Msg ( " Could not add bot to the game: Team is stacked (to disable this check, set mp_autoteambalance to zero, increase mp_limitteams, and restart the round). \n " ) ;
}
return false ;
}
// create the actual bot
CCSBot * bot = CreateBot < CCSBot > ( profile , team ) ;
if ( bot = = NULL )
{
if ( isFromConsole )
{
Msg ( " Error: CreateBot() failed. \n " ) ;
}
return false ;
}
if ( isFromConsole )
{
// increase the bot quota to account for manually added bot
cv_bot_quota . SetValue ( cv_bot_quota . GetInt ( ) + 1 ) ;
}
return true ;
}
int UTIL_CSSBotsInGame ( )
{
int count = 0 ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; + + i )
{
CCSBot * player = dynamic_cast < CCSBot * > ( UTIL_PlayerByIndex ( i ) ) ;
if ( player = = NULL )
continue ;
count + + ;
}
return count ;
}
bool UTIL_CSSKickBotFromTeam ( int kickTeam )
{
int i ;
// try to kick a dead bot first
for ( i = 1 ; i < = gpGlobals - > maxClients ; + + i )
{
CCSBot * player = dynamic_cast < CCSBot * > ( UTIL_PlayerByIndex ( i ) ) ;
if ( player = = NULL )
continue ;
if ( ! player - > IsAlive ( ) & & player - > GetTeamNumber ( ) = = kickTeam )
{
// its a bot on the right team - kick it
engine - > ServerCommand ( UTIL_VarArgs ( " kick \" %s \" \n " , player - > GetPlayerName ( ) ) ) ;
return true ;
}
}
// no dead bots, kick any bot on the given team
for ( i = 1 ; i < = gpGlobals - > maxClients ; + + i )
{
CCSBot * player = dynamic_cast < CCSBot * > ( UTIL_PlayerByIndex ( i ) ) ;
if ( player = = NULL )
continue ;
if ( player - > GetTeamNumber ( ) = = kickTeam )
{
// its a bot on the right team - kick it
engine - > ServerCommand ( UTIL_VarArgs ( " kick \" %s \" \n " , player - > GetPlayerName ( ) ) ) ;
return true ;
}
}
return false ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Keep a minimum quota of bots in the game
*/
void CCSBotManager : : MaintainBotQuota ( void )
{
if ( ! AreBotsAllowed ( ) )
return ;
if ( TheNavMesh - > IsGenerating ( ) )
return ;
int totalHumansInGame = UTIL_HumansInGame ( ) ;
int humanPlayersInGame = UTIL_HumansInGame ( IGNORE_SPECTATORS ) ;
// don't add bots until local player has been registered, to make sure he's player ID #1
if ( ! engine - > IsDedicatedServer ( ) & & totalHumansInGame = = 0 )
return ;
// new players can't spawn immediately after the round has been going for some time
if ( ! CSGameRules ( ) | | ! TheCSBots ( ) )
{
return ;
}
int desiredBotCount = cv_bot_quota . GetInt ( ) ;
int botsInGame = UTIL_CSSBotsInGame ( ) ;
/// isRoundInProgress is true if the round has progressed far enough that new players will join as dead.
bool isRoundInProgress = CSGameRules ( ) - > m_bFirstConnected & &
! TheCSBots ( ) - > IsRoundOver ( ) & &
( CSGameRules ( ) - > GetRoundElapsedTime ( ) > = 20.0f ) ;
if ( FStrEq ( cv_bot_quota_mode . GetString ( ) , " fill " ) )
{
// If bot_quota_mode is 'fill', we want the number of bots and humans together to equal bot_quota
// unless the round is already in progress, in which case we play with what we've been dealt
if ( ! isRoundInProgress )
{
desiredBotCount = MAX ( 0 , desiredBotCount - humanPlayersInGame ) ;
}
else
{
desiredBotCount = botsInGame ;
}
}
else if ( FStrEq ( cv_bot_quota_mode . GetString ( ) , " match " ) )
{
// If bot_quota_mode is 'match', we want the number of bots to be bot_quota * total humans
// unless the round is already in progress, in which case we play with what we've been dealt
if ( ! isRoundInProgress )
{
desiredBotCount = ( int ) MAX ( 0 , cv_bot_quota . GetFloat ( ) * humanPlayersInGame ) ;
}
else
{
desiredBotCount = botsInGame ;
}
}
// wait for a player to join, if necessary
if ( cv_bot_join_after_player . GetBool ( ) )
{
if ( humanPlayersInGame = = 0 )
desiredBotCount = 0 ;
}
// wait until the map has been loaded for a bit, to allow players to transition across
// the transition without missing the pistol round
if ( bot_join_delay . GetInt ( ) > CSGameRules ( ) - > GetMapElapsedTime ( ) )
{
desiredBotCount = 0 ;
}
// if bots will auto-vacate, we need to keep one slot open to allow players to join
if ( cv_bot_auto_vacate . GetBool ( ) )
desiredBotCount = MIN ( desiredBotCount , gpGlobals - > maxClients - ( humanPlayersInGame + 1 ) ) ;
else
desiredBotCount = MIN ( desiredBotCount , gpGlobals - > maxClients - humanPlayersInGame ) ;
// Try to balance teams, if we are in the first 20 seconds of a round and bots can join either team.
if ( botsInGame > 0 & & desiredBotCount = = botsInGame & & CSGameRules ( ) - > m_bFirstConnected )
{
if ( CSGameRules ( ) - > GetRoundElapsedTime ( ) < 20.0f ) // new bots can still spawn during this time
{
if ( mp_autoteambalance . GetBool ( ) )
{
int numAliveTerrorist ;
int numAliveCT ;
int numDeadTerrorist ;
int numDeadCT ;
CSGameRules ( ) - > InitializePlayerCounts ( numAliveTerrorist , numAliveCT , numDeadTerrorist , numDeadCT ) ;
if ( ! FStrEq ( cv_bot_join_team . GetString ( ) , " T " ) & &
! FStrEq ( cv_bot_join_team . GetString ( ) , " CT " ) )
{
if ( numAliveTerrorist > CSGameRules ( ) - > m_iNumCT + 1 )
{
if ( UTIL_KickBotFromTeam ( TEAM_TERRORIST ) )
return ;
}
else if ( numAliveCT > CSGameRules ( ) - > m_iNumTerrorist + 1 )
{
if ( UTIL_KickBotFromTeam ( TEAM_CT ) )
return ;
}
}
}
}
}
// add bots if necessary
if ( desiredBotCount > botsInGame )
{
// don't try to add a bot if all teams are full
if ( ! CSGameRules ( ) - > TeamFull ( TEAM_TERRORIST ) | | ! CSGameRules ( ) - > TeamFull ( TEAM_CT ) )
TheCSBots ( ) - > BotAddCommand ( TEAM_UNASSIGNED ) ;
}
else if ( desiredBotCount < botsInGame )
{
// kick a bot to maintain quota
// first remove any unassigned bots
if ( UTIL_CSSKickBotFromTeam ( TEAM_UNASSIGNED ) )
return ;
int kickTeam ;
// remove from the team that has more players
if ( CSGameRules ( ) - > m_iNumTerrorist > CSGameRules ( ) - > m_iNumCT )
{
kickTeam = TEAM_TERRORIST ;
}
else if ( CSGameRules ( ) - > m_iNumTerrorist < CSGameRules ( ) - > m_iNumCT )
{
kickTeam = TEAM_CT ;
}
// remove from the team that's winning
else if ( CSGameRules ( ) - > m_iNumTerroristWins > CSGameRules ( ) - > m_iNumCTWins )
{
kickTeam = TEAM_TERRORIST ;
}
else if ( CSGameRules ( ) - > m_iNumCTWins > CSGameRules ( ) - > m_iNumTerroristWins )
{
kickTeam = TEAM_CT ;
}
else
{
// teams and scores are equal, pick a team at random
kickTeam = ( RandomInt ( 0 , 1 ) = = 0 ) ? TEAM_CT : TEAM_TERRORIST ;
}
// attempt to kick a bot from the given team
if ( UTIL_CSSKickBotFromTeam ( kickTeam ) )
return ;
// if there were no bots on the team, kick a bot from the other team
if ( kickTeam = = TEAM_TERRORIST )
UTIL_CSSKickBotFromTeam ( TEAM_CT ) ;
else
UTIL_CSSKickBotFromTeam ( TEAM_TERRORIST ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Collect all nav areas that overlap the given zone
*/
class CollectOverlappingAreas
{
public :
CollectOverlappingAreas ( CCSBotManager : : Zone * zone )
{
m_zone = zone ;
zone - > m_areaCount = 0 ;
}
bool operator ( ) ( CNavArea * area )
{
Extent areaExtent ;
area - > GetExtent ( & areaExtent ) ;
if ( areaExtent . hi . x > = m_zone - > m_extent . lo . x & & areaExtent . lo . x < = m_zone - > m_extent . hi . x & &
areaExtent . hi . y > = m_zone - > m_extent . lo . y & & areaExtent . lo . y < = m_zone - > m_extent . hi . y & &
areaExtent . hi . z > = m_zone - > m_extent . lo . z & & areaExtent . lo . z < = m_zone - > m_extent . hi . z )
{
// area overlaps m_zone
m_zone - > m_area [ m_zone - > m_areaCount + + ] = area ;
if ( m_zone - > m_areaCount = = CCSBotManager : : MAX_ZONE_NAV_AREAS )
{
return false ;
}
}
return true ;
}
private :
CCSBotManager : : Zone * m_zone ;
} ;
//--------------------------------------------------------------------------------------------------------------
/**
* Search the map entities to determine the game scenario and define important zones .
*/
void CCSBotManager : : ExtractScenarioData ( void )
{
if ( ! TheNavMesh - > IsLoaded ( ) )
return ;
m_zoneCount = 0 ;
m_gameScenario = SCENARIO_DEATHMATCH ;
//
// Search all entities in the map and set the game type and
// store all zones (bomb target, etc).
//
CBaseEntity * entity ;
int i ;
for ( i = 1 ; i < gpGlobals - > maxEntities ; + + i )
{
entity = CBaseEntity : : Instance ( engine - > PEntityOfEntIndex ( i ) ) ;
if ( entity = = NULL )
continue ;
bool found = false ;
bool isLegacy = false ;
if ( FClassnameIs ( entity , " func_bomb_target " ) )
{
m_gameScenario = SCENARIO_DEFUSE_BOMB ;
found = true ;
isLegacy = false ;
}
else if ( FClassnameIs ( entity , " info_bomb_target " ) )
{
m_gameScenario = SCENARIO_DEFUSE_BOMB ;
found = true ;
isLegacy = true ;
}
else if ( FClassnameIs ( entity , " func_hostage_rescue " ) )
{
m_gameScenario = SCENARIO_RESCUE_HOSTAGES ;
found = true ;
isLegacy = false ;
}
else if ( FClassnameIs ( entity , " info_hostage_rescue " ) )
{
m_gameScenario = SCENARIO_RESCUE_HOSTAGES ;
found = true ;
isLegacy = true ;
}
else if ( FClassnameIs ( entity , " hostage_entity " ) )
{
// some very old maps (ie: cs_assault) use info_player_start
// as rescue zones, so set the scenario if there are hostages
// in the map
m_gameScenario = SCENARIO_RESCUE_HOSTAGES ;
}
else if ( FClassnameIs ( entity , " func_vip_safetyzone " ) )
{
m_gameScenario = SCENARIO_ESCORT_VIP ;
found = true ;
isLegacy = false ;
}
if ( found )
{
if ( m_zoneCount < MAX_ZONES )
{
Vector absmin , absmax ;
entity - > CollisionProp ( ) - > WorldSpaceAABB ( & absmin , & absmax ) ;
m_zone [ m_zoneCount ] . m_isBlocked = false ;
m_zone [ m_zoneCount ] . m_center = ( isLegacy ) ? entity - > GetAbsOrigin ( ) : ( absmin + absmax ) / 2.0f ;
m_zone [ m_zoneCount ] . m_isLegacy = isLegacy ;
m_zone [ m_zoneCount ] . m_index = m_zoneCount ;
m_zone [ m_zoneCount + + ] . m_entity = entity ;
}
else
Msg ( " Warning: Too many zones, some will be ignored. \n " ) ;
}
}
//
// If there are no zones and the scenario is hostage rescue,
// use the info_player_start entities as rescue zones.
//
if ( m_zoneCount = = 0 & & m_gameScenario = = SCENARIO_RESCUE_HOSTAGES )
{
for ( entity = gEntList . FindEntityByClassname ( NULL , " info_player_start " ) ;
entity & & ! FNullEnt ( entity - > edict ( ) ) ;
entity = gEntList . FindEntityByClassname ( entity , " info_player_start " ) )
{
if ( m_zoneCount < MAX_ZONES )
{
m_zone [ m_zoneCount ] . m_isBlocked = false ;
m_zone [ m_zoneCount ] . m_center = entity - > GetAbsOrigin ( ) ;
m_zone [ m_zoneCount ] . m_isLegacy = true ;
m_zone [ m_zoneCount ] . m_index = m_zoneCount ;
m_zone [ m_zoneCount + + ] . m_entity = entity ;
}
else
{
Msg ( " Warning: Too many zones, some will be ignored. \n " ) ;
}
}
}
//
// Collect nav areas that overlap each zone
//
for ( i = 0 ; i < m_zoneCount ; + + i )
{
Zone * zone = & m_zone [ i ] ;
if ( zone - > m_isLegacy )
{
const float legacyRange = 256.0f ;
zone - > m_extent . lo . x = zone - > m_center . x - legacyRange ;
zone - > m_extent . lo . y = zone - > m_center . y - legacyRange ;
zone - > m_extent . lo . z = zone - > m_center . z - legacyRange ;
zone - > m_extent . hi . x = zone - > m_center . x + legacyRange ;
zone - > m_extent . hi . y = zone - > m_center . y + legacyRange ;
zone - > m_extent . hi . z = zone - > m_center . z + legacyRange ;
}
else
{
Vector absmin , absmax ;
zone - > m_entity - > CollisionProp ( ) - > WorldSpaceAABB ( & absmin , & absmax ) ;
zone - > m_extent . lo = absmin ;
zone - > m_extent . hi = absmax ;
}
// ensure Z overlap
const float zFudge = 50.0f ;
zone - > m_extent . lo . z - = zFudge ;
zone - > m_extent . hi . z + = zFudge ;
// build a list of nav areas that overlap this zone
CollectOverlappingAreas collector ( zone ) ;
TheNavMesh - > ForAllAreas ( collector ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return the zone that contains the given position
*/
const CCSBotManager : : Zone * CCSBotManager : : GetZone ( const Vector & pos ) const
{
for ( int z = 0 ; z < m_zoneCount ; + + z )
{
if ( m_zone [ z ] . m_extent . Contains ( pos ) )
{
return & m_zone [ z ] ;
}
}
return NULL ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return the closest zone to the given position
*/
const CCSBotManager : : Zone * CCSBotManager : : GetClosestZone ( const Vector & pos ) const
{
const Zone * close = NULL ;
float closeRangeSq = 999999999.9f ;
for ( int z = 0 ; z < m_zoneCount ; + + z )
{
if ( m_zone [ z ] . m_isBlocked )
continue ;
float rangeSq = ( m_zone [ z ] . m_center - pos ) . LengthSqr ( ) ;
if ( rangeSq < closeRangeSq )
{
closeRangeSq = rangeSq ;
close = & m_zone [ z ] ;
}
}
return close ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return a random position inside the given zone
*/
const Vector * CCSBotManager : : GetRandomPositionInZone ( const Zone * zone ) const
{
static Vector pos ;
if ( zone = = NULL )
return NULL ;
if ( zone - > m_areaCount = = 0 )
return NULL ;
// pick a random overlapping area
CNavArea * area = GetRandomAreaInZone ( zone ) ;
// pick a location inside both the nav area and the zone
/// @todo Randomize this
if ( zone - > m_isLegacy )
{
/// @todo It is possible that the radius might not overlap this area at all...
area - > GetClosestPointOnArea ( zone - > m_center , & pos ) ;
}
else
{
Extent areaExtent ;
area - > GetExtent ( & areaExtent ) ;
Extent overlap ;
overlap . lo . x = MAX ( areaExtent . lo . x , zone - > m_extent . lo . x ) ;
overlap . lo . y = MAX ( areaExtent . lo . y , zone - > m_extent . lo . y ) ;
overlap . hi . x = MIN ( areaExtent . hi . x , zone - > m_extent . hi . x ) ;
overlap . hi . y = MIN ( areaExtent . hi . y , zone - > m_extent . hi . y ) ;
pos . x = ( overlap . lo . x + overlap . hi . x ) / 2.0f ;
pos . y = ( overlap . lo . y + overlap . hi . y ) / 2.0f ;
pos . z = area - > GetZ ( pos ) ;
}
return & pos ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return a random area inside the given zone
*/
CNavArea * CCSBotManager : : GetRandomAreaInZone ( const Zone * zone ) const
{
int areaCount = zone - > m_areaCount ;
if ( areaCount = = 0 )
{
assert ( false & & " CCSBotManager::GetRandomAreaInZone: No areas for this zone " ) ;
return NULL ;
}
// Random, but weighted. Jump areas score zero, since you aren't ever meant to stop on one of those.
// Avoid areas score 1 to a normal area's 20 because pathfinding treats Avoid as a 20x penalty.
int totalWeight = 0 ;
for ( int areaIndex = 0 ; areaIndex < areaCount ; areaIndex + + )
{
CNavArea * currentArea = zone - > m_area [ areaIndex ] ;
if ( currentArea - > GetAttributes ( ) & NAV_MESH_JUMP )
totalWeight + = 0 ;
else if ( currentArea - > GetAttributes ( ) & NAV_MESH_AVOID )
totalWeight + = 1 ;
else
totalWeight + = 20 ;
}
if ( totalWeight = = 0 )
{
assert ( false & & " CCSBotManager::GetRandomAreaInZone: No real areas for this zone " ) ;
return NULL ;
}
int randomPick = RandomInt ( 1 , totalWeight ) ;
for ( int areaIndex = 0 ; areaIndex < areaCount ; areaIndex + + )
{
CNavArea * currentArea = zone - > m_area [ areaIndex ] ;
if ( currentArea - > GetAttributes ( ) & NAV_MESH_JUMP )
randomPick - = 0 ;
else if ( currentArea - > GetAttributes ( ) & NAV_MESH_AVOID )
randomPick - = 1 ;
else
randomPick - = 20 ;
if ( randomPick < = 0 )
return currentArea ;
}
// Won't ever get here, but the compiler will cry without it.
return zone - > m_area [ 0 ] ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnServerShutdown ( IGameEvent * event )
{
if ( ! engine - > IsDedicatedServer ( ) )
{
// Since we're a listenserver, save some config info for the next time we start up
static const char * botVars [ ] =
{
" bot_quota " ,
" bot_difficulty " ,
" bot_chatter " ,
" bot_prefix " ,
" bot_join_team " ,
" bot_defer_to_human " ,
# ifdef CS_SHIELD_ENABLED
" bot_allow_shield " ,
# endif // CS_SHIELD_ENABLED
" bot_join_after_player " ,
" bot_allow_rogues " ,
" bot_allow_pistols " ,
" bot_allow_shotguns " ,
" bot_allow_sub_machine_guns " ,
" bot_allow_machine_guns " ,
" bot_allow_rifles " ,
" bot_allow_snipers " ,
" bot_allow_grenades "
} ;
KeyValues * data = new KeyValues ( " ServerConfig " ) ;
// load the config data
if ( data )
{
data - > LoadFromFile ( filesystem , " ServerConfig.vdf " , " GAME " ) ;
for ( int i = 0 ; i < sizeof ( botVars ) / sizeof ( botVars [ 0 ] ) ; + + i )
{
const char * varName = botVars [ i ] ;
if ( varName )
{
ConVar * var = cvar - > FindVar ( varName ) ;
if ( var )
{
data - > SetString ( varName , var - > GetString ( ) ) ;
}
}
}
data - > SaveToFile ( filesystem , " ServerConfig.vdf " , " GAME " ) ;
data - > deleteThis ( ) ;
}
return ;
}
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnPlayerFootstep ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnPlayerFootstep , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnPlayerRadio ( IGameEvent * event )
{
// if it's an Enemy Spotted radio, update our enemy spotted timestamp
if ( event - > GetInt ( " slot " ) = = RADIO_ENEMY_SPOTTED )
{
// to have some idea of when a human Player has seen an enemy
SetLastSeenEnemyTimestamp ( ) ;
}
CCSBOTMANAGER_ITERATE_BOTS ( OnPlayerRadio , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnPlayerDeath ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnPlayerDeath , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnPlayerFallDamage ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnPlayerFallDamage , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBombPickedUp ( IGameEvent * event )
{
// bomb no longer loose
SetLooseBomb ( NULL ) ;
CCSBOTMANAGER_ITERATE_BOTS ( OnBombPickedUp , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBombPlanted ( IGameEvent * event )
{
m_isBombPlanted = true ;
m_bombPlantTimestamp = gpGlobals - > curtime ;
CCSBOTMANAGER_ITERATE_BOTS ( OnBombPlanted , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBombBeep ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnBombBeep , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBombDefuseBegin ( IGameEvent * event )
{
m_bombDefuser = static_cast < CCSPlayer * > ( UTIL_PlayerByUserId ( event - > GetInt ( " userid " ) ) ) ;
CCSBOTMANAGER_ITERATE_BOTS ( OnBombDefuseBegin , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBombDefused ( IGameEvent * event )
{
m_isBombPlanted = false ;
m_bombDefuser = NULL ;
CCSBOTMANAGER_ITERATE_BOTS ( OnBombDefused , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBombDefuseAbort ( IGameEvent * event )
{
m_bombDefuser = NULL ;
CCSBOTMANAGER_ITERATE_BOTS ( OnBombDefuseAbort , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBombExploded ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnBombExploded , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnRoundEnd ( IGameEvent * event )
{
m_isRoundOver = true ;
CCSBOTMANAGER_ITERATE_BOTS ( OnRoundEnd , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnRoundStart ( IGameEvent * event )
{
RestartRound ( ) ;
CCSBOTMANAGER_ITERATE_BOTS ( OnRoundStart , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
static CBaseEntity * SelectSpawnSpot ( const char * pEntClassName )
{
CBaseEntity * pSpot = NULL ;
// Find the next spawn spot.
pSpot = gEntList . FindEntityByClassname ( pSpot , pEntClassName ) ;
if ( pSpot = = NULL ) // skip over the null point
pSpot = gEntList . FindEntityByClassname ( pSpot , pEntClassName ) ;
CBaseEntity * pFirstSpot = pSpot ;
do
{
if ( pSpot )
{
// check if pSpot is valid
if ( pSpot - > GetAbsOrigin ( ) = = Vector ( 0 , 0 , 0 ) )
{
pSpot = gEntList . FindEntityByClassname ( pSpot , pEntClassName ) ;
continue ;
}
// if so, go to pSpot
return pSpot ;
}
// increment pSpot
pSpot = gEntList . FindEntityByClassname ( pSpot , pEntClassName ) ;
} while ( pSpot ! = pFirstSpot ) ; // loop if we're not back to the start
return NULL ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Pathfind from each zone to a spawn point to ensure it is valid . Assumes that every spawn can pathfind to
* every other spawn .
*/
void CCSBotManager : : CheckForBlockedZones ( void )
{
CBaseEntity * pSpot = SelectSpawnSpot ( " info_player_counterterrorist " ) ;
if ( ! pSpot )
pSpot = SelectSpawnSpot ( " info_player_terrorist " ) ;
if ( ! pSpot )
return ;
Vector spawnPos = pSpot - > GetAbsOrigin ( ) ;
CNavArea * spawnArea = TheNavMesh - > GetNearestNavArea ( spawnPos ) ;
if ( ! spawnArea )
return ;
ShortestPathCost costFunc ;
for ( int i = 0 ; i < m_zoneCount ; + + i )
{
if ( m_zone [ i ] . m_areaCount = = 0 )
continue ;
// just use the first overlapping nav area as a reasonable approximation
float dist = NavAreaTravelDistance ( spawnArea , m_zone [ i ] . m_area [ 0 ] , costFunc ) ;
m_zone [ i ] . m_isBlocked = ( dist < 0.0f ) ;
if ( cv_bot_debug . GetInt ( ) = = 5 )
{
if ( m_zone [ i ] . m_isBlocked )
DevMsg ( " %.1f: Zone %d, area %d (%.0f %.0f %.0f) is blocked from spawn area %d (%.0f %.0f %.0f) \n " ,
gpGlobals - > curtime , i , m_zone [ i ] . m_area [ 0 ] - > GetID ( ) ,
m_zone [ i ] . m_area [ 0 ] - > GetCenter ( ) . x , m_zone [ i ] . m_area [ 0 ] - > GetCenter ( ) . y , m_zone [ i ] . m_area [ 0 ] - > GetCenter ( ) . z ,
spawnArea - > GetID ( ) ,
spawnPos . x , spawnPos . y , spawnPos . z ) ;
}
}
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnRoundFreezeEnd ( IGameEvent * event )
{
bool reenableEvents = m_NavBlockedEvent . IsEnabled ( ) ;
m_NavBlockedEvent . Enable ( false ) ; // don't listen to nav_blocked events - there could be several, and we don't have bots pathing
CUtlVector < CNavArea * > & transientAreas = TheNavMesh - > GetTransientAreas ( ) ;
for ( int i = 0 ; i < transientAreas . Count ( ) ; + + i )
{
CNavArea * area = transientAreas [ i ] ;
if ( area - > GetAttributes ( ) & NAV_MESH_TRANSIENT )
{
area - > UpdateBlocked ( ) ;
}
}
if ( reenableEvents )
{
m_NavBlockedEvent . Enable ( true ) ;
}
CheckForBlockedZones ( ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnNavBlocked ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnNavBlocked , event ) ;
CheckForBlockedZones ( ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnDoorMoving ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnDoorMoving , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Check all nav areas inside the breakable ' s extent to see if players would now fall through
*/
class CheckAreasOverlappingBreakable
{
public :
CheckAreasOverlappingBreakable ( CBaseEntity * breakable )
{
m_breakable = breakable ;
ICollideable * collideable = breakable - > GetCollideable ( ) ;
collideable - > WorldSpaceSurroundingBounds ( & m_breakableExtent . lo , & m_breakableExtent . hi ) ;
const float expand = 10.0f ;
m_breakableExtent . lo + = Vector ( - expand , - expand , - expand ) ;
m_breakableExtent . hi + = Vector ( expand , expand , expand ) ;
}
bool operator ( ) ( CNavArea * area )
{
Extent areaExtent ;
area - > GetExtent ( & areaExtent ) ;
if ( areaExtent . hi . x > = m_breakableExtent . lo . x & & areaExtent . lo . x < = m_breakableExtent . hi . x & &
areaExtent . hi . y > = m_breakableExtent . lo . y & & areaExtent . lo . y < = m_breakableExtent . hi . y & &
areaExtent . hi . z > = m_breakableExtent . lo . z & & areaExtent . lo . z < = m_breakableExtent . hi . z )
{
// area overlaps the breakable
area - > CheckFloor ( m_breakable ) ;
}
return true ;
}
private :
Extent m_breakableExtent ;
CBaseEntity * m_breakable ;
} ;
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBreakBreakable ( IGameEvent * event )
{
CheckAreasOverlappingBreakable collector ( UTIL_EntityByIndex ( event - > GetInt ( " entindex " ) ) ) ;
TheNavMesh - > ForAllAreas ( collector ) ;
CCSBOTMANAGER_ITERATE_BOTS ( OnBreakBreakable , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBreakProp ( IGameEvent * event )
{
CheckAreasOverlappingBreakable collector ( UTIL_EntityByIndex ( event - > GetInt ( " entindex " ) ) ) ;
TheNavMesh - > ForAllAreas ( collector ) ;
CCSBOTMANAGER_ITERATE_BOTS ( OnBreakProp , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnHostageFollows ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnHostageFollows , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnHostageRescuedAll ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnHostageRescuedAll , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnWeaponFire ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnWeaponFire , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnWeaponFireOnEmpty ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnWeaponFireOnEmpty , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnWeaponReload ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnWeaponReload , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnWeaponZoom ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnWeaponZoom , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnBulletImpact ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnBulletImpact , event ) ;
}
2024-07-25 18:10:35 +02:00
void CCSBotManager : : OnBulletHitPlayer ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnBulletHitPlayer , event ) ;
}
void CCSBotManager : : OnBulletPlayerHitboxes ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnBulletPlayerHitboxes , event ) ;
}
void CCSBotManager : : OnPlayerLagHitboxes ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnPlayerLagHitboxes , event ) ;
}
2020-04-22 18:56:21 +02:00
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnHEGrenadeDetonate ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnHEGrenadeDetonate , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnFlashbangDetonate ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnFlashbangDetonate , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnSmokeGrenadeDetonate ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnSmokeGrenadeDetonate , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : OnGrenadeBounce ( IGameEvent * event )
{
CCSBOTMANAGER_ITERATE_BOTS ( OnGrenadeBounce , event ) ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Get the time remaining before the planted bomb explodes
*/
float CCSBotManager : : GetBombTimeLeft ( void ) const
{
return ( mp_c4timer . GetFloat ( ) - ( gpGlobals - > curtime - m_bombPlantTimestamp ) ) ;
}
//--------------------------------------------------------------------------------------------------------------
void CCSBotManager : : SetLooseBomb ( CBaseEntity * bomb )
{
m_looseBomb = bomb ;
if ( bomb )
{
m_looseBombArea = TheNavMesh - > GetNearestNavArea ( bomb - > GetAbsOrigin ( ) ) ;
}
else
{
m_looseBombArea = NULL ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return true if player is important to scenario ( VIP , bomb carrier , etc )
*/
bool CCSBotManager : : IsImportantPlayer ( CCSPlayer * player ) const
{
switch ( GetScenario ( ) )
{
case SCENARIO_DEFUSE_BOMB :
{
if ( player - > GetTeamNumber ( ) = = TEAM_TERRORIST & & player - > HasC4 ( ) )
return true ;
/// @todo TEAM_CT's defusing the bomb are important
return false ;
}
case SCENARIO_ESCORT_VIP :
{
if ( player - > GetTeamNumber ( ) = = TEAM_CT & & player - > IsVIP ( ) )
return true ;
return false ;
}
case SCENARIO_RESCUE_HOSTAGES :
{
/// @todo TEAM_CT's escorting hostages are important
return false ;
}
}
// everyone is equally important in a deathmatch
return false ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return priority of player ( 0 = max pri )
*/
unsigned int CCSBotManager : : GetPlayerPriority ( CBasePlayer * player ) const
{
const unsigned int lowestPriority = 0xFFFFFFFF ;
if ( ! player - > IsPlayer ( ) )
return lowestPriority ;
// human players have highest priority
if ( ! player - > IsBot ( ) )
return 0 ;
CCSBot * bot = dynamic_cast < CCSBot * > ( player ) ;
if ( ! bot )
return 0 ;
// bots doing something important for the current scenario have high priority
switch ( GetScenario ( ) )
{
case SCENARIO_DEFUSE_BOMB :
{
// the bomb carrier has high priority
if ( bot - > GetTeamNumber ( ) = = TEAM_TERRORIST & & bot - > HasC4 ( ) )
return 1 ;
break ;
}
case SCENARIO_ESCORT_VIP :
{
// the VIP has high priority
if ( bot - > GetTeamNumber ( ) = = TEAM_CT & & bot - > m_bIsVIP )
return 1 ;
break ;
}
case SCENARIO_RESCUE_HOSTAGES :
{
// TEAM_CT's rescuing hostages have high priority
if ( bot - > GetTeamNumber ( ) = = TEAM_CT & & bot - > GetHostageEscortCount ( ) )
return 1 ;
break ;
}
}
// everyone else is ranked by their unique ID (which cannot be zero)
return 1 + bot - > GetID ( ) ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Returns a random spawn point for the given team ( no arg means use both team spawnpoints )
*/
CBaseEntity * CCSBotManager : : GetRandomSpawn ( int team ) const
{
CUtlVector < CBaseEntity * > spawnSet ;
CBaseEntity * spot ;
if ( team = = TEAM_TERRORIST | | team = = TEAM_MAXCOUNT )
{
// collect T spawns
for ( spot = gEntList . FindEntityByClassname ( NULL , " info_player_terrorist " ) ;
spot ;
spot = gEntList . FindEntityByClassname ( spot , " info_player_terrorist " ) )
{
spawnSet . AddToTail ( spot ) ;
}
}
if ( team = = TEAM_CT | | team = = TEAM_MAXCOUNT )
{
// collect CT spawns
for ( spot = gEntList . FindEntityByClassname ( NULL , " info_player_counterterrorist " ) ;
spot ;
spot = gEntList . FindEntityByClassname ( spot , " info_player_counterterrorist " ) )
{
spawnSet . AddToTail ( spot ) ;
}
}
if ( spawnSet . Count ( ) = = 0 )
{
return NULL ;
}
// select one at random
int which = RandomInt ( 0 , spawnSet . Count ( ) - 1 ) ;
return spawnSet [ which ] ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return the last time the given radio message was sent for given team
* ' teamID ' can be TEAM_CT or TEAM_TERRORIST
*/
float CCSBotManager : : GetRadioMessageTimestamp ( RadioType event , int teamID ) const
{
int i = ( teamID = = TEAM_TERRORIST ) ? 0 : 1 ;
if ( event > RADIO_START_1 & & event < RADIO_END )
return m_radioMsgTimestamp [ event - RADIO_START_1 ] [ i ] ;
return 0.0f ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return the interval since the last time this message was sent
*/
float CCSBotManager : : GetRadioMessageInterval ( RadioType event , int teamID ) const
{
int i = ( teamID = = TEAM_TERRORIST ) ? 0 : 1 ;
if ( event > RADIO_START_1 & & event < RADIO_END )
return gpGlobals - > curtime - m_radioMsgTimestamp [ event - RADIO_START_1 ] [ i ] ;
return 99999999.9f ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Set the given radio message timestamp .
* ' teamID ' can be TEAM_CT or TEAM_TERRORIST
*/
void CCSBotManager : : SetRadioMessageTimestamp ( RadioType event , int teamID )
{
int i = ( teamID = = TEAM_TERRORIST ) ? 0 : 1 ;
if ( event > RADIO_START_1 & & event < RADIO_END )
m_radioMsgTimestamp [ event - RADIO_START_1 ] [ i ] = gpGlobals - > curtime ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Reset all radio message timestamps
*/
void CCSBotManager : : ResetRadioMessageTimestamps ( void )
{
for ( int t = 0 ; t < 2 ; + + t )
{
for ( int m = 0 ; m < ( RADIO_END - RADIO_START_1 ) ; + + m )
m_radioMsgTimestamp [ m ] [ t ] = 0.0f ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Display nav areas as they become reachable by each team
*/
void DrawOccupyTime ( void )
{
FOR_EACH_VEC ( TheNavAreas , it )
{
CNavArea * area = TheNavAreas [ it ] ;
int r , g , b ;
if ( TheCSBots ( ) - > GetElapsedRoundTime ( ) > area - > GetEarliestOccupyTime ( TEAM_TERRORIST ) )
{
if ( TheCSBots ( ) - > GetElapsedRoundTime ( ) > area - > GetEarliestOccupyTime ( TEAM_CT ) )
{
r = 255 ; g = 0 ; b = 255 ;
}
else
{
r = 255 ; g = 0 ; b = 0 ;
}
}
else if ( TheCSBots ( ) - > GetElapsedRoundTime ( ) > area - > GetEarliestOccupyTime ( TEAM_CT ) )
{
r = 0 ; g = 0 ; b = 255 ;
}
else
{
continue ;
}
const Vector & nw = area - > GetCorner ( NORTH_WEST ) ;
const Vector & ne = area - > GetCorner ( NORTH_EAST ) ;
const Vector & sw = area - > GetCorner ( SOUTH_WEST ) ;
const Vector & se = area - > GetCorner ( SOUTH_EAST ) ;
NDebugOverlay : : Line ( nw , ne , r , g , b , true , 0.1f ) ;
NDebugOverlay : : Line ( nw , sw , r , g , b , true , 0.1f ) ;
NDebugOverlay : : Line ( se , sw , r , g , b , true , 0.1f ) ;
NDebugOverlay : : Line ( se , ne , r , g , b , true , 0.1f ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Display areas where players will likely have initial battles
*/
void DrawBattlefront ( void )
{
const float epsilon = 1.0f ;
int r = 255 , g = 50 , b = 0 ;
FOR_EACH_VEC ( TheNavAreas , it )
{
CNavArea * area = TheNavAreas [ it ] ;
if ( fabs ( area - > GetEarliestOccupyTime ( TEAM_TERRORIST ) - area - > GetEarliestOccupyTime ( TEAM_CT ) ) > epsilon )
{
continue ;
}
const Vector & nw = area - > GetCorner ( NORTH_WEST ) ;
const Vector & ne = area - > GetCorner ( NORTH_EAST ) ;
const Vector & sw = area - > GetCorner ( SOUTH_WEST ) ;
const Vector & se = area - > GetCorner ( SOUTH_EAST ) ;
NDebugOverlay : : Line ( nw , ne , r , g , b , true , 0.1f ) ;
NDebugOverlay : : Line ( nw , sw , r , g , b , true , 0.1f ) ;
NDebugOverlay : : Line ( se , sw , r , g , b , true , 0.1f ) ;
NDebugOverlay : : Line ( se , ne , r , g , b , true , 0.1f ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
static bool CheckAreaAgainstAllZoneAreas ( CNavArea * queryArea )
{
// A marked area means they just want to double check this one spot
int goalZoneCount = TheCSBots ( ) - > GetZoneCount ( ) ;
for ( int zoneIndex = 0 ; zoneIndex < goalZoneCount ; zoneIndex + + )
{
const CCSBotManager : : Zone * currentZone = TheCSBots ( ) - > GetZone ( zoneIndex ) ;
int zoneAreaCount = currentZone - > m_areaCount ;
for ( int areaIndex = 0 ; areaIndex < zoneAreaCount ; areaIndex + + )
{
CNavArea * zoneArea = currentZone - > m_area [ areaIndex ] ;
// We need to be connected to every area in the zone, since we don't know what other code might pick for an area
ShortestPathCost cost ;
if ( NavAreaTravelDistance ( queryArea , zoneArea , cost ) = = - 1.0f )
{
Msg ( " Area #%d is disconnected from goal area #%d. \n " ,
queryArea - > GetID ( ) ,
zoneArea - > GetID ( )
) ;
return false ;
}
}
}
return true ;
}
CON_COMMAND_F ( nav_check_connectivity , " Checks to be sure every (or just the marked) nav area can get to every goal area for the map (hostages or bomb site). " , FCVAR_CHEAT )
{
//Nav command in here since very CS specific.
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
if ( TheNavMesh - > GetMarkedArea ( ) )
{
CNavArea * markedArea = TheNavMesh - > GetMarkedArea ( ) ;
bool fine = CheckAreaAgainstAllZoneAreas ( markedArea ) ;
if ( fine )
{
Msg ( " Area #%d is connected to all goal areas. \n " , markedArea - > GetID ( ) ) ;
}
}
else
{
// Otherwise, loop through every area, and make sure they can all get to the goal.
float start = engine - > Time ( ) ;
FOR_EACH_VEC ( TheNavAreas , nit )
{
CheckAreaAgainstAllZoneAreas ( TheNavAreas [ nit ] ) ;
}
float end = engine - > Time ( ) ;
float time = ( end - start ) * 1000.0f ;
Msg ( " nav_check_connectivity took %2.2f ms \n " , time ) ;
}
}