2016-03-05 21:04:46 +03:00
//
2014-09-09 18:30:40 +04:00
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
2016-09-11 21:01:06 +03:00
// https://yapb.jeefo.net/license
2014-09-09 18:30:40 +04:00
//
# include <core.h>
2016-09-13 19:09:20 +03:00
ConVar yb_autovacate ( " yb_autovacate " , " 1 " ) ;
2016-10-29 21:01:41 +03:00
ConVar yb_autovacate_smart_kick ( " yb_autovacate_smart_kick " , " 1 " ) ;
2014-09-09 18:30:40 +04:00
2016-01-12 23:57:02 +03:00
ConVar yb_quota ( " yb_quota " , " 0 " , VT_NORMAL ) ;
ConVar yb_quota_mode ( " yb_quota_mode " , " normal " ) ;
2014-09-09 18:30:40 +04:00
2016-01-04 15:28:38 +03:00
ConVar yb_join_after_player ( " yb_join_after_player " , " 0 " ) ;
2014-09-09 18:30:40 +04:00
ConVar yb_join_team ( " yb_join_team " , " any " ) ;
2016-01-04 15:28:38 +03:00
ConVar yb_name_prefix ( " yb_name_prefix " , " " ) ;
2015-06-04 11:52:48 +03:00
ConVar yb_difficulty ( " yb_difficulty " , " 4 " ) ;
2014-09-09 18:30:40 +04:00
2016-01-04 15:28:38 +03:00
ConVar yb_latency_display ( " yb_latency_display " , " 2 " ) ;
ConVar yb_avatar_display ( " yb_avatar_display " , " 1 " ) ;
2014-09-09 18:30:40 +04:00
2016-10-18 20:34:02 +03:00
ConVar mp_limitteams ( " mp_limitteams " , nullptr , VT_NOREGISTER ) ;
2014-09-09 18:30:40 +04:00
BotManager : : BotManager ( void )
{
// this is a bot manager class constructor
m_lastWinner = - 1 ;
2015-06-24 15:38:48 +03:00
m_deathMsgSent = false ;
2014-09-09 18:30:40 +04:00
2016-09-13 22:40:06 +03:00
for ( int i = 0 ; i < SPECTATOR ; i + + )
{
m_leaderChoosen [ i ] = false ;
m_economicsGood [ i ] = true ;
}
2014-09-09 18:30:40 +04:00
memset ( m_bots , 0 , sizeof ( m_bots ) ) ;
2015-06-29 20:51:25 +03:00
m_maintainTime = 0.0f ;
2016-01-16 21:08:22 +03:00
m_quotaMaintainTime = 0.0f ;
2016-03-25 14:56:40 +03:00
m_grenadeUpdateTime = 0.0f ;
2016-01-16 21:08:22 +03:00
2015-06-29 20:51:25 +03:00
m_creationTab . RemoveAll ( ) ;
2016-09-11 21:01:06 +03:00
m_killerEntity = nullptr ;
2016-03-09 22:34:24 +03:00
m_balanceCount = 0 ;
2014-09-09 18:30:40 +04:00
}
BotManager : : ~ BotManager ( void )
{
2016-03-01 13:37:10 +03:00
// this is a bot manager class destructor, do not use engine.MaxClients () here !!
2014-09-09 18:30:40 +04:00
Free ( ) ;
}
2015-07-26 21:47:29 +03:00
void BotManager : : CreateKillerEntity ( void )
{
// this function creates single trigger_hurt for using in Bot::Kill, to reduce lags, when killing all the bots
m_killerEntity = g_engfuncs . pfnCreateNamedEntity ( MAKE_STRING ( " trigger_hurt " ) ) ;
m_killerEntity - > v . dmg = 9999.0f ;
m_killerEntity - > v . dmg_take = 1.0f ;
m_killerEntity - > v . dmgtime = 2.0f ;
m_killerEntity - > v . effects | = EF_NODRAW ;
g_engfuncs . pfnSetOrigin ( m_killerEntity , Vector ( - 99999.0f , - 99999.0f , - 99999.0f ) ) ;
MDLL_Spawn ( m_killerEntity ) ;
}
void BotManager : : DestroyKillerEntity ( void )
{
2016-03-05 23:08:07 +03:00
if ( ! engine . IsNullEntity ( m_killerEntity ) )
2015-07-26 21:47:29 +03:00
g_engfuncs . pfnRemoveEntity ( m_killerEntity ) ;
}
void BotManager : : TouchWithKillerEntity ( Bot * bot )
{
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( m_killerEntity ) )
2015-07-26 21:47:29 +03:00
{
MDLL_ClientKill ( bot - > GetEntity ( ) ) ;
return ;
}
2016-01-03 22:03:02 +03:00
2015-07-26 21:47:29 +03:00
m_killerEntity - > v . classname = MAKE_STRING ( g_weaponDefs [ bot - > m_currentWeapon ] . className ) ;
m_killerEntity - > v . dmg_inflictor = bot - > GetEntity ( ) ;
KeyValueData kv ;
kv . szClassName = const_cast < char * > ( g_weaponDefs [ bot - > m_currentWeapon ] . className ) ;
kv . szKeyName = " damagetype " ;
kv . szValue = const_cast < char * > ( FormatBuffer ( " %d " , ( 1 < < 4 ) ) ) ;
2016-09-11 21:13:57 +03:00
kv . fHandled = FALSE ;
2015-07-26 21:47:29 +03:00
MDLL_KeyValue ( m_killerEntity , & kv ) ;
MDLL_Touch ( m_killerEntity , bot - > GetEntity ( ) ) ;
}
2016-01-27 21:40:47 +03:00
// it's already defined in interface.cpp
extern " C " void player ( entvars_t * pev ) ;
2014-09-09 18:30:40 +04:00
void BotManager : : CallGameEntity ( entvars_t * vars )
{
// this function calls gamedll player() function, in case to create player entity in game
2016-09-10 19:31:38 +03:00
if ( g_gameFlags & GAME_METAMOD )
2014-09-09 18:30:40 +04:00
{
CALL_GAME_ENTITY ( PLID , " player " , vars ) ;
return ;
}
2016-01-27 21:40:47 +03:00
player ( vars ) ;
2014-09-09 18:30:40 +04:00
}
2016-09-10 19:31:38 +03:00
BotCreationResult BotManager : : CreateBot ( const String & name , int difficulty , int personality , int team , int member , bool isConsoleCmd )
2014-09-09 18:30:40 +04:00
{
2015-06-04 11:52:48 +03:00
// this function completely prepares bot entity (edict) for creation, creates team, difficulty, sets name etc, and
2014-09-09 18:30:40 +04:00
// then sends result to bot constructor
2016-09-11 21:01:06 +03:00
edict_t * bot = nullptr ;
2014-09-09 18:30:40 +04:00
char outputName [ 33 ] ;
if ( g_numWaypoints < 1 ) // don't allow creating bots with no waypoints loaded
{
2016-03-01 13:37:10 +03:00
engine . CenterPrintf ( " Map is not waypointed. Cannot create bot " ) ;
2016-09-10 19:31:38 +03:00
return BOT_RESULT_NAV_ERROR ;
2014-09-09 18:30:40 +04:00
}
2016-09-10 19:31:38 +03:00
else if ( waypoints . HasChanged ( ) ) // don't allow creating bots with changed waypoints (distance tables are messed up)
2014-09-09 18:30:40 +04:00
{
2016-03-01 13:37:10 +03:00
engine . CenterPrintf ( " Waypoints have been changed. Load waypoints again... " ) ;
2016-09-10 19:31:38 +03:00
return BOT_RESULT_NAV_ERROR ;
2014-09-09 18:30:40 +04:00
}
2016-10-18 20:34:02 +03:00
else if ( team ! = - 1 & & IsTeamStacked ( team - 1 ) )
{
engine . CenterPrintf ( " Desired team is stacked. Unable to proceed with bot creation " ) ;
return BOT_RESULT_TEAM_STACKED ;
}
2014-09-09 18:30:40 +04:00
2015-06-04 11:52:48 +03:00
if ( difficulty < 0 | | difficulty > 4 )
2016-10-18 20:34:02 +03:00
{
2015-06-04 11:52:48 +03:00
difficulty = yb_difficulty . GetInt ( ) ;
2014-09-09 18:30:40 +04:00
2016-10-18 20:34:02 +03:00
if ( difficulty < 0 | | difficulty > 4 )
{
difficulty = Random . Int ( 3 , 4 ) ;
yb_difficulty . SetInt ( difficulty ) ;
}
2015-06-04 11:52:48 +03:00
}
2014-09-09 18:30:40 +04:00
2016-10-18 20:34:02 +03:00
if ( personality < PERSONALITY_NORMAL | | personality > PERSONALITY_CAREFUL )
2014-09-09 18:30:40 +04:00
{
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 0 , 100 ) < 50 )
2014-09-09 18:30:40 +04:00
personality = PERSONALITY_NORMAL ;
else
{
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 0 , 100 ) < 65 )
2014-09-09 18:30:40 +04:00
personality = PERSONALITY_RUSHER ;
else
personality = PERSONALITY_CAREFUL ;
}
}
2015-06-20 13:38:13 +03:00
String steamId = " " ;
2016-09-15 13:26:13 +03:00
BotName * botName = nullptr ;
2015-06-20 13:38:13 +03:00
2014-09-09 18:30:40 +04:00
// setup name
if ( name . IsEmpty ( ) )
{
if ( ! g_botNames . IsEmpty ( ) )
{
bool nameFound = false ;
2016-09-15 13:26:13 +03:00
FOR_EACH_AE ( g_botNames , i )
2014-09-09 18:30:40 +04:00
{
if ( nameFound )
break ;
2016-09-15 13:26:13 +03:00
botName = & g_botNames . GetRandomElement ( ) ;
2015-06-04 11:52:48 +03:00
2016-09-15 13:26:13 +03:00
if ( botName = = nullptr )
2015-06-04 11:52:48 +03:00
continue ;
2014-09-09 18:30:40 +04:00
2016-09-16 16:10:22 +03:00
if ( botName - > name . GetLength ( ) < 3 | | botName - > usedBy ! = 0 )
2014-09-09 18:30:40 +04:00
continue ;
2016-09-15 13:26:13 +03:00
nameFound = true ;
2015-06-20 13:38:13 +03:00
2016-09-16 16:10:22 +03:00
strncpy ( outputName , botName - > name . GetBuffer ( ) , SIZEOF_CHAR ( outputName ) ) ;
2016-09-15 13:26:13 +03:00
steamId = botName - > steamId ;
2014-09-09 18:30:40 +04:00
}
}
else
2016-09-11 21:01:06 +03:00
sprintf ( outputName , " bot%i " , Random . Int ( 0 , 100 ) ) ; // just pick ugly random name
2014-09-09 18:30:40 +04:00
}
else
strncpy ( outputName , name , 21 ) ;
2015-06-04 11:52:48 +03:00
if ( ! IsNullString ( yb_name_prefix . GetString ( ) ) )
2014-09-09 18:30:40 +04:00
{
char prefixedName [ 33 ] ; // temp buffer for storing modified name
if ( ! IsNullString ( yb_name_prefix . GetString ( ) ) )
2016-09-16 16:10:22 +03:00
snprintf ( prefixedName , SIZEOF_CHAR ( prefixedName ) , " %s %s " , yb_name_prefix . GetString ( ) , outputName ) ;
2014-09-09 18:30:40 +04:00
// buffer has been modified, copy to real name
if ( ! IsNullString ( prefixedName ) )
2016-09-16 16:10:22 +03:00
strncpy ( outputName , prefixedName , SIZEOF_CHAR ( outputName ) ) ;
2014-09-09 18:30:40 +04:00
}
2016-06-23 15:30:19 +03:00
bot = g_engfuncs . pfnCreateFakeClient ( outputName ) ;
2014-09-09 18:30:40 +04:00
2016-06-23 15:30:19 +03:00
if ( engine . IsNullEntity ( bot ) )
2014-09-09 18:30:40 +04:00
{
2016-03-01 13:37:10 +03:00
engine . CenterPrintf ( " Maximum players reached (%d/%d). Unable to create Bot. " , engine . MaxClients ( ) , engine . MaxClients ( ) ) ;
2016-09-10 19:31:38 +03:00
return BOT_RESULT_MAX_PLAYERS_REACHED ;
2014-09-09 18:30:40 +04:00
}
2016-03-05 23:08:07 +03:00
int index = engine . IndexOfEntity ( bot ) - 1 ;
2014-09-09 18:30:40 +04:00
2016-09-15 13:26:13 +03:00
// ensure it free
Free ( index ) ;
2016-03-10 00:37:33 +03:00
InternalAssert ( index > = 0 & & index < = MAX_ENGINE_PLAYERS ) ; // check index
2016-09-11 21:01:06 +03:00
InternalAssert ( m_bots [ index ] = = nullptr ) ; // check bot slot
2014-09-09 18:30:40 +04:00
2015-06-20 13:38:13 +03:00
m_bots [ index ] = new Bot ( bot , difficulty , personality , team , member , steamId ) ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
if ( m_bots [ index ] = = nullptr )
2014-09-09 18:30:40 +04:00
TerminateOnMalloc ( ) ;
2016-09-15 13:26:13 +03:00
// assign owner of bot name
if ( botName ! = nullptr )
botName - > usedBy = m_bots [ index ] - > GetIndex ( ) ;
2016-03-01 13:37:10 +03:00
engine . Printf ( " Connecting Bot... " ) ;
2014-09-09 18:30:40 +04:00
2016-06-23 15:30:19 +03:00
if ( isConsoleCmd )
yb_quota . SetInt ( yb_quota . GetInt ( ) + 1 ) ;
2016-09-10 19:31:38 +03:00
return BOT_RESULT_CREATED ;
2014-09-09 18:30:40 +04:00
}
int BotManager : : GetIndex ( edict_t * ent )
{
// this function returns index of bot (using own bot array)
2016-03-05 23:08:07 +03:00
if ( engine . IsNullEntity ( ent ) )
2014-09-09 18:30:40 +04:00
return - 1 ;
2016-03-05 23:08:07 +03:00
int index = engine . IndexOfEntity ( ent ) - 1 ;
2014-09-09 18:30:40 +04:00
2016-03-10 00:37:33 +03:00
if ( index < 0 | | index > = MAX_ENGINE_PLAYERS )
2014-09-09 18:30:40 +04:00
return - 1 ;
2016-09-11 21:01:06 +03:00
if ( m_bots [ index ] ! = nullptr )
2014-09-09 18:30:40 +04:00
return index ;
return - 1 ; // if no edict, return -1;
}
Bot * BotManager : : GetBot ( int index )
{
// this function finds a bot specified by index, and then returns pointer to it (using own bot array)
2016-03-10 00:37:33 +03:00
if ( index < 0 | | index > = MAX_ENGINE_PLAYERS )
2016-09-11 21:01:06 +03:00
return nullptr ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
if ( m_bots [ index ] ! = nullptr )
2014-09-09 18:30:40 +04:00
return m_bots [ index ] ;
2016-09-11 21:01:06 +03:00
return nullptr ; // no bot
2014-09-09 18:30:40 +04:00
}
Bot * BotManager : : GetBot ( edict_t * ent )
{
// same as above, but using bot entity
return GetBot ( GetIndex ( ent ) ) ;
}
2016-11-01 23:57:51 +03:00
Bot * BotManager : : GetAliveBot ( void )
2014-09-09 18:30:40 +04:00
{
// this function finds one bot, alive bot :)
2016-09-10 19:31:38 +03:00
Array < int > result ;
2014-09-09 18:30:40 +04:00
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-10 19:31:38 +03:00
if ( result . GetSize ( ) > 4 )
2015-07-26 13:02:05 +03:00
break ;
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr & & IsAlive ( m_bots [ i ] - > GetEntity ( ) ) )
2016-09-10 19:31:38 +03:00
result . Push ( i ) ;
2014-09-09 18:30:40 +04:00
}
2016-09-10 19:31:38 +03:00
if ( ! result . IsEmpty ( ) )
return m_bots [ result . GetRandomElement ( ) ] ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
return nullptr ;
2014-09-09 18:30:40 +04:00
}
void BotManager : : Think ( void )
{
2015-07-05 18:53:58 +03:00
// this function calls think () function for all available at call moment bots
2014-09-09 18:30:40 +04:00
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr )
2015-07-26 21:47:29 +03:00
m_bots [ i ] - > Think ( ) ;
2014-09-09 18:30:40 +04:00
}
}
2015-07-12 17:18:20 +03:00
void BotManager : : PeriodicThink ( void )
{
// this function calls periodic SecondThink () function for all available at call moment bots
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2015-07-12 17:18:20 +03:00
{
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr )
2015-07-12 17:18:20 +03:00
m_bots [ i ] - > PeriodicThink ( ) ;
}
}
2016-06-23 15:30:19 +03:00
void BotManager : : AddBot ( const String & name , int difficulty , int personality , int team , int member , bool isConsoleCmd )
2014-09-09 18:30:40 +04:00
{
// this function putting bot creation process to queue to prevent engine crashes
CreateQueue bot ;
// fill the holder
bot . name = name ;
2015-06-04 11:52:48 +03:00
bot . difficulty = difficulty ;
2014-09-09 18:30:40 +04:00
bot . personality = personality ;
bot . team = team ;
bot . member = member ;
2016-06-23 15:30:19 +03:00
bot . console = isConsoleCmd ;
2014-09-09 18:30:40 +04:00
// put to queue
m_creationTab . Push ( bot ) ;
}
2016-06-23 15:30:19 +03:00
void BotManager : : AddBot ( const String & name , const String & difficulty , const String & personality , const String & team , const String & member , bool isConsoleCmd )
2014-09-09 18:30:40 +04:00
{
// this function is same as the function above, but accept as parameters string instead of integers
CreateQueue bot ;
const String & any = " * " ;
2015-06-04 11:52:48 +03:00
bot . name = ( name . IsEmpty ( ) | | name = = any ) ? String ( " \0 " ) : name ;
bot . difficulty = ( difficulty . IsEmpty ( ) | | difficulty = = any ) ? - 1 : difficulty . ToInt ( ) ;
bot . team = ( team . IsEmpty ( ) | | team = = any ) ? - 1 : team . ToInt ( ) ;
bot . member = ( member . IsEmpty ( ) | | member = = any ) ? - 1 : member . ToInt ( ) ;
bot . personality = ( personality . IsEmpty ( ) | | personality = = any ) ? - 1 : personality . ToInt ( ) ;
2016-07-04 13:19:04 +03:00
bot . console = isConsoleCmd ;
2014-09-09 18:30:40 +04:00
m_creationTab . Push ( bot ) ;
2016-01-12 23:57:02 +03:00
}
2016-11-01 23:57:51 +03:00
void BotManager : : AdjustQuota ( bool isPlayerConnecting , edict_t * ent )
2016-01-12 23:57:02 +03:00
{
2016-03-05 21:04:46 +03:00
// this function increases or decreases bot quota amount depending on auto vacate variables
2016-01-12 23:57:02 +03:00
2016-11-01 23:57:51 +03:00
if ( ! engine . IsDedicatedServer ( ) | | ! yb_autovacate . GetBool ( ) | | IsValidBot ( ent ) )
2016-01-12 23:57:02 +03:00
return ;
2016-11-01 23:57:51 +03:00
if ( isPlayerConnecting )
2016-01-16 21:08:22 +03:00
{
if ( yb_autovacate_smart_kick . GetBool ( ) )
AddPlayerToCheckTeamQueue ( ent ) ;
else
2016-01-30 14:02:28 +03:00
{
2016-01-16 21:08:22 +03:00
RemoveRandom ( ) ;
2016-01-30 14:02:28 +03:00
m_balanceCount - - ;
}
2016-01-16 21:08:22 +03:00
}
2016-11-01 23:57:51 +03:00
else if ( m_balanceCount < 0 )
2016-01-30 13:15:50 +03:00
{
2016-11-01 23:57:51 +03:00
AddRandom ( false ) ;
2016-01-30 13:15:50 +03:00
m_balanceCount + + ;
}
2016-01-16 21:08:22 +03:00
}
void BotManager : : AddPlayerToCheckTeamQueue ( edict_t * ent )
{
2016-11-01 23:57:51 +03:00
if ( ! engine . IsDedicatedServer ( ) | | ! yb_autovacate . GetBool ( ) | | IsValidBot ( ent ) )
return ;
2016-01-16 21:08:22 +03:00
// entity must be unique
bool hasFound = false ;
FOR_EACH_AE ( m_trackedPlayers , it )
{
if ( m_trackedPlayers [ it ] = = ent )
{
hasFound = true ;
break ;
}
}
if ( ! hasFound )
m_trackedPlayers . Push ( ent ) ;
}
void BotManager : : VerifyPlayersHasJoinedTeam ( int & desiredCount )
{
2016-11-01 23:57:51 +03:00
if ( ! engine . IsDedicatedServer ( ) | | ! yb_autovacate . GetBool ( ) | | m_trackedPlayers . IsEmpty ( ) )
return ;
2016-01-16 21:08:22 +03:00
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2016-01-16 21:08:22 +03:00
{
2016-09-10 19:31:38 +03:00
const Client & client = g_clients [ i ] ;
2016-01-16 21:08:22 +03:00
2016-09-10 19:31:38 +03:00
if ( ! ( client . flags & CF_USED ) | | client . team = = SPECTATOR | | IsValidBot ( client . ent ) )
2016-06-23 15:30:19 +03:00
continue ;
FOR_EACH_AE ( m_trackedPlayers , it )
2016-01-16 21:08:22 +03:00
{
2016-09-10 19:31:38 +03:00
if ( client . ent ! = m_trackedPlayers [ it ] )
2016-06-23 15:30:19 +03:00
continue ;
2016-01-16 21:08:22 +03:00
2016-06-23 15:30:19 +03:00
m_balanceCount - - ;
desiredCount - - ;
2016-01-30 14:02:28 +03:00
2016-06-23 15:30:19 +03:00
m_trackedPlayers . RemoveAt ( it ) ;
2016-01-16 21:08:22 +03:00
2016-06-23 15:30:19 +03:00
break ;
2016-01-16 21:08:22 +03:00
}
}
2014-09-09 18:30:40 +04:00
}
void BotManager : : MaintainBotQuota ( void )
{
// this function keeps number of bots up to date, and don't allow to maintain bot creation
// while creation process in process.
2016-09-10 19:31:38 +03:00
if ( g_numWaypoints < 1 | | waypoints . HasChanged ( ) )
2015-12-26 01:31:46 +03:00
return ;
2016-01-12 23:57:02 +03:00
// bot's creation update
2016-03-01 13:37:10 +03:00
if ( ! m_creationTab . IsEmpty ( ) & & m_maintainTime < engine . Time ( ) )
2014-09-09 18:30:40 +04:00
{
2016-09-10 19:31:38 +03:00
const CreateQueue & last = m_creationTab . Pop ( ) ;
const BotCreationResult callResult = CreateBot ( last . name , last . difficulty , last . personality , last . team , last . member , last . console ) ;
2014-09-09 18:30:40 +04:00
// check the result of creation
2016-09-10 19:31:38 +03:00
if ( callResult = = BOT_RESULT_NAV_ERROR )
2014-09-09 18:30:40 +04:00
{
2015-06-04 11:52:48 +03:00
m_creationTab . RemoveAll ( ) ; // something wrong with waypoints, reset tab of creation
2014-09-09 18:30:40 +04:00
yb_quota . SetInt ( 0 ) ; // reset quota
}
2016-09-10 19:31:38 +03:00
else if ( callResult = = BOT_RESULT_MAX_PLAYERS_REACHED )
2014-09-09 18:30:40 +04:00
{
m_creationTab . RemoveAll ( ) ; // maximum players reached, so set quota to maximum players
yb_quota . SetInt ( GetBotsNum ( ) ) ;
}
2016-10-18 20:34:02 +03:00
else if ( callResult = = BOT_RESULT_TEAM_STACKED )
{
engine . Printf ( " Could not add bot to the game: Team is stacked (to disable this check, set mp_limitteams and mp_autoteambalance to zero and restart the round) " ) ;
2016-10-23 01:49:05 +03:00
m_creationTab . RemoveAll ( ) ;
yb_quota . SetInt ( GetBotsNum ( ) ) ;
2016-10-18 20:34:02 +03:00
}
2016-03-01 22:52:17 +03:00
m_maintainTime = engine . Time ( ) + 0.20f ;
2014-09-09 18:30:40 +04:00
}
// now keep bot number up to date
2016-03-01 13:37:10 +03:00
if ( m_quotaMaintainTime < engine . Time ( ) )
2014-09-09 18:30:40 +04:00
{
2016-06-23 15:30:19 +03:00
// keep the quota number in valid ranges
{
if ( yb_quota . GetInt ( ) < 0 )
yb_quota . SetInt ( 0 ) ;
2016-09-18 21:57:12 +03:00
int maxClients = engine . MaxClients ( ) ;
2016-09-13 19:09:20 +03:00
if ( yb_quota . GetInt ( ) > maxClients )
yb_quota . SetInt ( maxClients ) ;
2016-06-23 15:30:19 +03:00
}
2016-01-12 23:57:02 +03:00
int numBots = GetBotsNum ( ) ;
2016-10-31 19:27:58 +03:00
int numHumans = yb_autovacate_smart_kick . GetBool ( ) ? GetHumansJoinedTeam ( ) : GetHumansNum ( ) ;
2016-01-12 23:57:02 +03:00
int desiredCount = yb_quota . GetInt ( ) ;
2016-06-23 15:30:19 +03:00
if ( yb_join_after_player . GetBool ( ) & & ! numHumans )
2016-01-12 23:57:02 +03:00
desiredCount = 0 ;
// quota mode
char mode = yb_quota_mode . GetString ( ) [ 0 ] ;
2016-10-23 01:49:05 +03:00
if ( mode = = ' f ' | | mode = = ' F ' ) // fill
2016-09-11 21:01:06 +03:00
desiredCount = A_max ( 0 , desiredCount - numHumans ) ;
2016-10-23 01:49:05 +03:00
else if ( mode = = ' m ' | | mode = = ' M ' ) // match
2016-09-11 21:01:06 +03:00
desiredCount = A_max ( 0 , yb_quota . GetInt ( ) * numHumans ) ;
2016-01-12 23:57:02 +03:00
2016-09-11 21:01:06 +03:00
desiredCount = A_min ( desiredCount , engine . MaxClients ( ) - ( numHumans + ( yb_autovacate . GetBool ( ) ? 1 : 0 ) ) ) ;
2016-06-23 15:30:19 +03:00
2016-01-16 21:08:22 +03:00
if ( yb_autovacate_smart_kick . GetBool ( ) & & numBots > 1 & & desiredCount > 1 )
VerifyPlayersHasJoinedTeam ( desiredCount ) ;
2016-01-12 23:57:02 +03:00
if ( desiredCount > numBots )
2016-06-23 15:30:19 +03:00
AddRandom ( false ) ;
2016-01-12 23:57:02 +03:00
else if ( desiredCount < numBots )
2016-06-23 15:30:19 +03:00
RemoveRandom ( true ) ;
2016-01-12 23:57:02 +03:00
2016-06-23 15:30:19 +03:00
m_quotaMaintainTime = engine . Time ( ) + 0.40f ;
2014-09-09 18:30:40 +04:00
}
}
void BotManager : : InitQuota ( void )
{
2016-01-30 13:15:50 +03:00
m_balanceCount = 0 ;
2016-03-01 13:37:10 +03:00
m_maintainTime = engine . Time ( ) + 3.0f ;
m_quotaMaintainTime = engine . Time ( ) + 3.0f ;
2016-01-16 21:08:22 +03:00
m_trackedPlayers . RemoveAll ( ) ;
2014-09-09 18:30:40 +04:00
m_creationTab . RemoveAll ( ) ;
}
2015-06-04 11:52:48 +03:00
void BotManager : : FillServer ( int selection , int personality , int difficulty , int numToAdd )
2014-09-09 18:30:40 +04:00
{
// this function fill server with bots, with specified team & personality
2016-09-13 19:09:20 +03:00
// always keep one slot
2016-09-13 20:03:50 +03:00
int maxClients = yb_autovacate . GetBool ( ) ? engine . MaxClients ( ) - 1 - ( engine . IsDedicatedServer ( ) ? 0 : GetHumansNum ( ) ) : engine . MaxClients ( ) ;
2016-09-13 19:09:20 +03:00
if ( GetBotsNum ( ) > = maxClients - GetHumansNum ( ) )
2014-09-09 18:30:40 +04:00
return ;
if ( selection = = 1 | | selection = = 2 )
{
CVAR_SET_STRING ( " mp_limitteams " , " 0 " ) ;
CVAR_SET_STRING ( " mp_autoteambalance " , " 0 " ) ;
}
else
selection = 5 ;
2016-09-13 19:09:20 +03:00
char teams [ 6 ] [ 12 ] =
2014-09-09 18:30:40 +04:00
{
" " ,
{ " Terrorists " } ,
{ " CTs " } ,
" " ,
" " ,
{ " Random " } ,
} ;
2016-09-13 19:09:20 +03:00
int toAdd = numToAdd = = - 1 ? maxClients - ( GetHumansNum ( ) + GetBotsNum ( ) ) : numToAdd ;
2014-09-09 18:30:40 +04:00
for ( int i = 0 ; i < = toAdd ; i + + )
2015-06-04 11:52:48 +03:00
AddBot ( " " , difficulty , personality , selection , - 1 ) ;
2014-09-09 18:30:40 +04:00
2016-09-13 19:09:20 +03:00
engine . CenterPrintf ( " Fill Server with %s bots... " , & teams [ selection ] [ 0 ] ) ;
2014-09-09 18:30:40 +04:00
}
2016-06-23 15:30:19 +03:00
void BotManager : : RemoveAll ( void )
2014-09-09 18:30:40 +04:00
{
// this function drops all bot clients from server (this function removes only yapb's)`q
2016-06-23 15:30:19 +03:00
engine . CenterPrintf ( " Bots are removed from server. " ) ;
2014-09-09 18:30:40 +04:00
m_creationTab . RemoveAll ( ) ;
2016-06-23 15:30:19 +03:00
yb_quota . SetInt ( 0 ) ;
2014-09-09 18:30:40 +04:00
}
void BotManager : : RemoveFromTeam ( Team team , bool removeAll )
{
// this function remove random bot from specified team (if removeAll value = 1 then removes all players from team)
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr & & team = = engine . GetTeam ( m_bots [ i ] - > GetEntity ( ) ) )
2014-09-09 18:30:40 +04:00
{
m_bots [ i ] - > Kick ( ) ;
if ( ! removeAll )
break ;
}
}
}
void BotManager : : RemoveMenu ( edict_t * ent , int selection )
{
// this function displays remove bot menu to specified entity (this function show's only yapb's).
if ( selection > 4 | | selection < 1 )
return ;
static char tempBuffer [ 1024 ] ;
char buffer [ 1024 ] ;
memset ( tempBuffer , 0 , sizeof ( tempBuffer ) ) ;
memset ( buffer , 0 , sizeof ( buffer ) ) ;
int validSlots = ( selection = = 4 ) ? ( 1 < < 9 ) : ( ( 1 < < 8 ) | ( 1 < < 9 ) ) ;
2016-09-22 15:47:35 +03:00
for ( int i = ( selection - 1 ) * 8 ; i < selection * 8 ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-22 15:47:35 +03:00
const Bot * bot = GetBot ( i ) ;
if ( bot ! = nullptr & & ( bot - > pev - > flags & FL_FAKECLIENT ) )
2014-09-09 18:30:40 +04:00
{
validSlots | = 1 < < ( i - ( ( selection - 1 ) * 8 ) ) ;
2016-09-22 15:47:35 +03:00
sprintf ( buffer , " %s %1.1d. %s%s \n " , buffer , i - ( ( selection - 1 ) * 8 ) + 1 , STRING ( bot - > pev - > netname ) , bot - > m_team = = CT ? " \\ y(CT) \\ w " : " \\ r(T) \\ w " ) ;
2014-09-09 18:30:40 +04:00
}
else
2015-06-04 11:52:48 +03:00
sprintf ( buffer , " %s \\ d %1.1d. Not a Bot \\ w \n " , buffer , i - ( ( selection - 1 ) * 8 ) + 1 ) ;
2014-09-09 18:30:40 +04:00
}
2015-06-04 11:52:48 +03:00
sprintf ( tempBuffer , " \\ yBots Remove Menu (%d/4): \\ w \n \n %s \n %s 0. Back " , selection , buffer , ( selection = = 4 ) ? " " : " 9. More... \n " ) ;
2014-09-09 18:30:40 +04:00
2016-09-22 15:47:35 +03:00
// force to clear current menu
DisplayMenuToClient ( ent , BOT_MENU_INVALID ) ;
auto FindMenu = [ ] ( MenuId id )
{
int menuIndex = 0 ;
for ( ; menuIndex < ARRAYSIZE_HLSDK ( g_menus ) ; menuIndex + + )
{
if ( g_menus [ menuIndex ] . id = = id )
break ;
}
return & g_menus [ menuIndex ] ;
} ;
MenuText * menu = nullptr ;
const unsigned int slots = validSlots & static_cast < unsigned int > ( - 1 ) ;
2014-09-09 18:30:40 +04:00
switch ( selection )
{
case 1 :
2016-09-22 15:47:35 +03:00
menu = FindMenu ( BOT_MENU_KICK_PAGE_1 ) ;
menu - > slots = slots ;
menu - > text = tempBuffer ;
2014-09-09 18:30:40 +04:00
2016-09-13 19:09:20 +03:00
DisplayMenuToClient ( ent , BOT_MENU_KICK_PAGE_1 ) ;
2014-09-09 18:30:40 +04:00
break ;
case 2 :
2016-09-22 15:47:35 +03:00
menu = FindMenu ( BOT_MENU_KICK_PAGE_2 ) ;
menu - > slots = slots ;
menu - > text = tempBuffer ;
2014-09-09 18:30:40 +04:00
2016-09-13 19:09:20 +03:00
DisplayMenuToClient ( ent , BOT_MENU_KICK_PAGE_2 ) ;
2014-09-09 18:30:40 +04:00
break ;
case 3 :
2016-09-22 15:47:35 +03:00
menu = FindMenu ( BOT_MENU_KICK_PAGE_3 ) ;
menu - > slots = slots ;
menu - > text = tempBuffer ;
2014-09-09 18:30:40 +04:00
2016-09-13 19:09:20 +03:00
DisplayMenuToClient ( ent , BOT_MENU_KICK_PAGE_3 ) ;
2014-09-09 18:30:40 +04:00
break ;
case 4 :
2016-09-22 15:47:35 +03:00
menu = FindMenu ( BOT_MENU_KICK_PAGE_4 ) ;
menu - > slots = slots ;
menu - > text = tempBuffer ;
2014-09-09 18:30:40 +04:00
2016-09-13 19:09:20 +03:00
DisplayMenuToClient ( ent , BOT_MENU_KICK_PAGE_4 ) ;
2014-09-09 18:30:40 +04:00
break ;
}
}
void BotManager : : KillAll ( int team )
{
// this function kills all bots on server (only this dll controlled bots)
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr )
2014-09-09 18:30:40 +04:00
{
2016-01-16 21:08:22 +03:00
if ( team ! = - 1 & & team ! = m_bots [ i ] - > m_team )
2014-09-09 18:30:40 +04:00
continue ;
m_bots [ i ] - > Kill ( ) ;
}
}
2016-03-01 13:37:10 +03:00
engine . CenterPrintf ( " All Bots died ! " ) ;
2014-09-09 18:30:40 +04:00
}
2016-06-23 15:30:19 +03:00
void BotManager : : RemoveRandom ( bool keepQuota )
2014-09-09 18:30:40 +04:00
{
// this function removes random bot from server (only yapb's)
2016-01-16 21:08:22 +03:00
bool deadBotFound = false ;
// first try to kick the bot that is currently dead
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2016-01-16 21:08:22 +03:00
{
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr & & ! m_bots [ i ] - > m_notKilled ) // is this slot used?
2016-01-16 21:08:22 +03:00
{
2016-06-23 15:30:19 +03:00
m_bots [ i ] - > Kick ( keepQuota ) ;
2016-01-16 21:08:22 +03:00
deadBotFound = true ;
break ;
}
}
if ( deadBotFound )
return ;
// if no dead bots found try to find one with lowest amount of frags
int index = 0 ;
float score = 9999.0f ;
// search bots in this team
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2016-01-16 21:08:22 +03:00
{
Bot * bot = bots . GetBot ( i ) ;
2016-09-11 21:01:06 +03:00
if ( bot ! = nullptr & & bot - > pev - > frags < score )
2016-01-16 21:08:22 +03:00
{
index = i ;
score = bot - > pev - > frags ;
}
}
// if found some bots
if ( index ! = 0 )
{
2016-06-23 15:30:19 +03:00
m_bots [ index ] - > Kick ( keepQuota ) ;
2016-01-16 21:08:22 +03:00
return ;
}
// worst case, just kick some random bot
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr ) // is this slot used?
2014-09-09 18:30:40 +04:00
{
2016-06-23 15:30:19 +03:00
m_bots [ i ] - > Kick ( keepQuota ) ;
2014-09-09 18:30:40 +04:00
break ;
}
}
}
void BotManager : : SetWeaponMode ( int selection )
{
// this function sets bots weapon mode
int tabMapStandart [ 7 ] [ NUM_WEAPONS ] =
{
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // Knife only
{ - 1 , - 1 , - 1 , 2 , 2 , 0 , 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // Pistols only
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 2 , 2 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // Shotgun only
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 2 , 1 , 2 , 0 , 2 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 2 , - 1 } , // Machine Guns only
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 0 , 0 , 1 , 0 , 1 , 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // Rifles only
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 2 , 2 , 0 , 1 , - 1 , - 1 } , // Snipers only
{ - 1 , - 1 , - 1 , 2 , 2 , 0 , 1 , 2 , 2 , 2 , 1 , 2 , 0 , 2 , 0 , 0 , 1 , 0 , 1 , 1 , 2 , 2 , 0 , 1 , 2 , 1 } // Standard
} ;
int tabMapAS [ 7 ] [ NUM_WEAPONS ] =
{
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // Knife only
{ - 1 , - 1 , - 1 , 2 , 2 , 0 , 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // Pistols only
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 1 , 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // Shotgun only
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 1 , 1 , 1 , 0 , 2 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 1 , - 1 } , // Machine Guns only
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 0 , - 1 , 1 , 0 , 1 , 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // Rifles only
{ - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , 0 , 0 , - 1 , 1 , - 1 , - 1 } , // Snipers only
{ - 1 , - 1 , - 1 , 2 , 2 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 2 , 0 , - 1 , 1 , 0 , 1 , 1 , 0 , 0 , - 1 , 1 , 1 , 1 } // Standard
} ;
char modeName [ 7 ] [ 12 ] =
{
{ " Knife " } ,
{ " Pistol " } ,
{ " Shotgun " } ,
{ " Machine Gun " } ,
{ " Rifle " } ,
{ " Sniper " } ,
{ " Standard " }
} ;
selection - - ;
for ( int i = 0 ; i < NUM_WEAPONS ; i + + )
{
g_weaponSelect [ i ] . teamStandard = tabMapStandart [ selection ] [ i ] ;
g_weaponSelect [ i ] . teamAS = tabMapAS [ selection ] [ i ] ;
}
if ( selection = = 0 )
yb_jasonmode . SetInt ( 1 ) ;
else
yb_jasonmode . SetInt ( 0 ) ;
2016-03-01 13:37:10 +03:00
engine . CenterPrintf ( " %s weapon mode selected " , & modeName [ selection ] [ 0 ] ) ;
2014-09-09 18:30:40 +04:00
}
void BotManager : : ListBots ( void )
{
// this function list's bots currently playing on the server
2016-03-01 13:37:10 +03:00
engine . Printf ( " %-3.5s %-9.13s %-17.18s %-3.4s %-3.4s %-3.4s " , " index " , " name " , " personality " , " team " , " difficulty " , " frags " ) ;
2014-09-09 18:30:40 +04:00
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-03-05 21:04:46 +03:00
Bot * bot = GetBot ( i ) ;
2014-09-09 18:30:40 +04:00
// is this player slot valid
2016-09-11 21:01:06 +03:00
if ( bot ! = nullptr )
2016-03-05 21:04:46 +03:00
engine . Printf ( " [%-3.1d] %-9.13s %-17.18s %-3.4s %-3.1d %-3.1d " , i , STRING ( bot - > pev - > netname ) , bot - > m_personality = = PERSONALITY_RUSHER ? " rusher " : bot - > m_personality = = PERSONALITY_NORMAL ? " normal " : " careful " , bot - > m_team = = CT ? " CT " : " T " , bot - > m_difficulty , static_cast < int > ( bot - > pev - > frags ) ) ;
2014-09-09 18:30:40 +04:00
}
}
int BotManager : : GetBotsNum ( void )
{
// this function returns number of yapb's playing on the server
int count = 0 ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr )
2014-09-09 18:30:40 +04:00
count + + ;
}
return count ;
}
Bot * BotManager : : GetHighestFragsBot ( int team )
{
int bestIndex = 0 ;
float bestScore = - 1 ;
// search bots in this team
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-01-16 21:08:22 +03:00
Bot * bot = bots . GetBot ( i ) ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
if ( bot ! = nullptr & & bot - > m_notKilled & & bot - > m_team = = team )
2014-09-09 18:30:40 +04:00
{
2016-01-16 21:08:22 +03:00
if ( bot - > pev - > frags > bestScore )
2014-09-09 18:30:40 +04:00
{
bestIndex = i ;
2016-01-16 21:08:22 +03:00
bestScore = bot - > pev - > frags ;
2014-09-09 18:30:40 +04:00
}
}
}
return GetBot ( bestIndex ) ;
}
2015-06-18 19:29:40 +03:00
void BotManager : : CheckTeamEconomics ( int team , bool setTrue )
2014-09-09 18:30:40 +04:00
{
// this function decides is players on specified team is able to buy primary weapons by calculating players
// that have not enough money to buy primary (with economics), and if this result higher 80%, player is can't
// buy primary weapons.
extern ConVar yb_economics_rounds ;
2016-03-01 22:52:17 +03:00
if ( setTrue | | ! yb_economics_rounds . GetBool ( ) )
2014-09-09 18:30:40 +04:00
{
m_economicsGood [ team ] = true ;
return ; // don't check economics while economics disable
}
int numPoorPlayers = 0 ;
int numTeamPlayers = 0 ;
// start calculating
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-11 21:01:06 +03:00
if ( m_bots [ i ] ! = nullptr & & engine . GetTeam ( m_bots [ i ] - > GetEntity ( ) ) = = team )
2014-09-09 18:30:40 +04:00
{
if ( m_bots [ i ] - > m_moneyAmount < = g_botBuyEconomyTable [ 0 ] )
numPoorPlayers + + ;
numTeamPlayers + + ; // update count of team
}
}
m_economicsGood [ team ] = true ;
if ( numTeamPlayers < = 1 )
return ;
// if 80 percent of team have no enough money to purchase primary weapon
if ( ( numTeamPlayers * 80 ) / 100 < = numPoorPlayers )
m_economicsGood [ team ] = false ;
// winner must buy something!
if ( m_lastWinner = = team )
m_economicsGood [ team ] = true ;
}
void BotManager : : Free ( void )
{
// this function free all bots slots (used on server shutdown)
2016-03-10 00:37:33 +03:00
for ( int i = 0 ; i < MAX_ENGINE_PLAYERS ; i + + )
2015-06-04 11:52:48 +03:00
Free ( i ) ;
2014-09-09 18:30:40 +04:00
}
void BotManager : : Free ( int index )
{
// this function frees one bot selected by index (used on bot disconnect)
delete m_bots [ index ] ;
2016-09-11 21:01:06 +03:00
m_bots [ index ] = nullptr ;
2014-09-09 18:30:40 +04:00
}
2015-06-20 13:38:13 +03:00
Bot : : Bot ( edict_t * bot , int difficulty , int personality , int team , int member , const String & steamId )
2014-09-09 18:30:40 +04:00
{
// this function does core operation of creating bot, it's called by CreateBot (),
// when bot setup completed, (this is a bot class constructor)
char rejectReason [ 128 ] ;
2016-03-05 23:08:07 +03:00
int clientIndex = engine . IndexOfEntity ( bot ) ;
2014-09-09 18:30:40 +04:00
2016-01-27 21:40:47 +03:00
memset ( reinterpret_cast < void * > ( this ) , 0 , sizeof ( * this ) ) ;
2016-09-15 13:26:13 +03:00
2015-06-04 11:52:48 +03:00
pev = & bot - > v ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
if ( bot - > pvPrivateData ! = nullptr )
2014-09-09 18:30:40 +04:00
FREE_PRIVATE ( bot ) ;
2016-09-11 21:01:06 +03:00
bot - > pvPrivateData = nullptr ;
2014-09-09 18:30:40 +04:00
bot - > v . frags = 0 ;
// create the player entity by calling MOD's player function
BotManager : : CallGameEntity ( & bot - > v ) ;
// set all info buffer keys for this bot
2015-06-20 13:38:13 +03:00
char * buffer = GET_INFOKEYBUFFER ( bot ) ;
2014-09-09 18:30:40 +04:00
SET_CLIENT_KEYVALUE ( clientIndex , buffer , " _vgui_menus " , " 0 " ) ;
2016-02-11 21:50:05 +03:00
if ( ! ( g_gameFlags & GAME_LEGACY ) & & yb_latency_display . GetInt ( ) = = 1 )
2015-06-20 14:14:36 +03:00
SET_CLIENT_KEYVALUE ( clientIndex , buffer , " *bot " , " 1 " ) ;
2014-09-09 18:30:40 +04:00
rejectReason [ 0 ] = 0 ; // reset the reject reason template string
2016-03-05 23:08:07 +03:00
MDLL_ClientConnect ( bot , " BOT " , FormatBuffer ( " 127.0.0.%d " , engine . IndexOfEntity ( bot ) + 100 ) , rejectReason ) ;
2015-06-20 14:14:36 +03:00
// should be set after client connect
if ( yb_avatar_display . GetBool ( ) & & ! steamId . IsEmpty ( ) )
SET_CLIENT_KEYVALUE ( clientIndex , buffer , " *sid " , const_cast < char * > ( steamId . GetBuffer ( ) ) ) ;
2014-09-09 18:30:40 +04:00
2015-06-04 11:52:48 +03:00
memset ( & m_pingOffset , 0 , sizeof ( m_pingOffset ) ) ;
memset ( & m_ping , 0 , sizeof ( m_ping ) ) ;
2014-09-09 18:30:40 +04:00
if ( ! IsNullString ( rejectReason ) )
{
AddLogEntry ( true , LL_WARNING , " Server refused '%s' connection (%s) " , STRING ( bot - > v . netname ) , rejectReason ) ;
2016-03-01 13:37:10 +03:00
engine . IssueCmd ( " kick \" %s \" " , STRING ( bot - > v . netname ) ) ; // kick the bot player if the server refused it
2014-09-09 18:30:40 +04:00
bot - > v . flags | = FL_KILLME ;
2016-06-23 15:30:19 +03:00
return ;
2014-09-09 18:30:40 +04:00
}
MDLL_ClientPutInServer ( bot ) ;
bot - > v . flags | = FL_FAKECLIENT ; // set this player as fakeclient
// initialize all the variables for this bot...
m_notStarted = true ; // hasn't joined game yet
2016-09-10 19:31:38 +03:00
m_forceRadio = false ;
2014-09-09 18:30:40 +04:00
2016-10-23 01:49:05 +03:00
m_startAction = GAME_MSG_NONE ;
2016-10-18 20:34:02 +03:00
m_retryJoin = 0 ;
2014-09-09 18:30:40 +04:00
m_moneyAmount = 0 ;
2016-09-11 21:01:06 +03:00
m_logotypeIndex = Random . Int ( 0 , 9 ) ;
2014-09-09 18:30:40 +04:00
// assign how talkative this bot will be
2015-08-15 18:09:15 +03:00
m_sayTextBuffer . chatDelay = Random . Float ( 3.8f , 10.0f ) ;
2016-09-11 21:01:06 +03:00
m_sayTextBuffer . chatProbability = Random . Int ( 1 , 100 ) ;
2014-09-09 18:30:40 +04:00
m_notKilled = false ;
m_weaponBurstMode = BM_OFF ;
2015-06-04 11:52:48 +03:00
m_difficulty = difficulty ;
if ( difficulty < 0 | | difficulty > 4 )
{
2016-09-11 21:01:06 +03:00
difficulty = Random . Int ( 3 , 4 ) ;
2015-06-04 11:52:48 +03:00
yb_difficulty . SetInt ( difficulty ) ;
}
2014-09-09 18:30:40 +04:00
2016-03-01 13:37:10 +03:00
m_lastCommandTime = engine . Time ( ) - 0.1f ;
m_frameInterval = engine . Time ( ) ;
2015-06-04 11:52:48 +03:00
m_timePeriodicUpdate = 0.0f ;
2014-09-09 18:30:40 +04:00
switch ( personality )
{
case 1 :
m_personality = PERSONALITY_RUSHER ;
2015-08-15 18:09:15 +03:00
m_baseAgressionLevel = Random . Float ( 0.7f , 1.0f ) ;
m_baseFearLevel = Random . Float ( 0.0f , 0.4f ) ;
2014-09-09 18:30:40 +04:00
break ;
case 2 :
m_personality = PERSONALITY_CAREFUL ;
2015-08-15 18:09:15 +03:00
m_baseAgressionLevel = Random . Float ( 0.2f , 0.5f ) ;
m_baseFearLevel = Random . Float ( 0.7f , 1.0f ) ;
2014-09-09 18:30:40 +04:00
break ;
default :
m_personality = PERSONALITY_NORMAL ;
2015-08-15 18:09:15 +03:00
m_baseAgressionLevel = Random . Float ( 0.4f , 0.7f ) ;
m_baseFearLevel = Random . Float ( 0.4f , 0.7f ) ;
2014-09-09 18:30:40 +04:00
break ;
}
memset ( & m_ammoInClip , 0 , sizeof ( m_ammoInClip ) ) ;
memset ( & m_ammo , 0 , sizeof ( m_ammo ) ) ;
m_currentWeapon = 0 ; // current weapon is not assigned at start
2016-09-11 21:01:06 +03:00
m_voicePitch = Random . Int ( 80 , 115 ) ; // assign voice pitch
2014-09-09 18:30:40 +04:00
// copy them over to the temp level variables
m_agressionLevel = m_baseAgressionLevel ;
m_fearLevel = m_baseFearLevel ;
2016-03-01 13:37:10 +03:00
m_nextEmotionUpdate = engine . Time ( ) + 0.5f ;
2014-09-09 18:30:40 +04:00
// just to be sure
m_actMessageIndex = 0 ;
m_pushMessageIndex = 0 ;
// assign team and class
m_wantedTeam = team ;
m_wantedClass = member ;
NewRound ( ) ;
}
void Bot : : ReleaseUsedName ( void )
{
2015-06-28 19:43:31 +03:00
FOR_EACH_AE ( g_botNames , j )
2014-09-09 18:30:40 +04:00
{
BotName & name = g_botNames [ j ] ;
2016-09-15 13:26:13 +03:00
if ( name . usedBy = = GetIndex ( ) )
2014-09-09 18:30:40 +04:00
{
2016-09-15 13:26:13 +03:00
name . usedBy = 0 ;
2014-09-09 18:30:40 +04:00
break ;
}
}
}
Bot : : ~ Bot ( void )
{
// this is bot destructor
2016-10-23 01:49:05 +03:00
EnableChatterIcon ( false ) ;
2016-09-15 13:26:13 +03:00
ReleaseUsedName ( ) ;
2016-10-23 01:49:05 +03:00
2014-09-09 18:30:40 +04:00
DeleteSearchNodes ( ) ;
ResetTasks ( ) ;
}
int BotManager : : GetHumansNum ( void )
{
// this function returns number of humans playing on the server
int count = 0 ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-10 19:31:38 +03:00
const Client & client = g_clients [ i ] ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
if ( ( client . flags & CF_USED ) & & m_bots [ i ] = = nullptr & & ! ( client . ent - > v . flags & FL_FAKECLIENT ) )
2014-09-09 18:30:40 +04:00
count + + ;
}
return count ;
}
int BotManager : : GetHumansAliveNum ( void )
{
// this function returns number of humans playing on the server
int count = 0 ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-10 19:31:38 +03:00
const Client & client = g_clients [ i ] ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
if ( ( client . flags & ( CF_USED | CF_ALIVE ) ) & & m_bots [ i ] = = nullptr & & ! ( client . ent - > v . flags & FL_FAKECLIENT ) )
2014-09-09 18:30:40 +04:00
count + + ;
}
return count ;
}
int BotManager : : GetHumansJoinedTeam ( void )
{
// this function returns number of humans playing on the server
int count = 0 ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-09-10 19:31:38 +03:00
const Client & client = g_clients [ i ] ;
2014-09-09 18:30:40 +04:00
2016-11-01 23:57:51 +03:00
if ( ( client . flags & ( CF_USED | CF_ALIVE ) ) & & m_bots [ i ] = = nullptr & & client . team ! = SPECTATOR & & ! ( client . ent - > v . flags & FL_FAKECLIENT ) )
2014-09-09 18:30:40 +04:00
count + + ;
}
return count ;
}
2016-10-18 20:34:02 +03:00
bool BotManager : : IsTeamStacked ( int team )
{
int teamLimit = mp_limitteams . GetInt ( ) ;
if ( ! teamLimit )
return false ;
int teamCount [ SPECTATOR ] = { 0 , } ;
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
{
const Client & client = g_clients [ i ] ;
2016-10-23 01:49:05 +03:00
if ( ( client . flags & CF_USED ) & & client . team2 ! = SPECTATOR )
teamCount [ client . team2 ] + + ;
2016-10-18 20:34:02 +03:00
}
return teamCount [ team ] + 1 > teamCount [ team = = CT ? TERRORIST : CT ] + teamLimit ;
}
2014-09-09 18:30:40 +04:00
void Bot : : NewRound ( void )
{
// this function initializes a bot after creation & at the start of each round
int i = 0 ;
// delete all allocated path nodes
DeleteSearchNodes ( ) ;
2015-08-15 18:09:15 +03:00
m_waypointOrigin . Zero ( ) ;
m_destOrigin . Zero ( ) ;
2014-09-09 18:30:40 +04:00
m_currentWaypointIndex = - 1 ;
2016-09-11 21:01:06 +03:00
m_currentPath = nullptr ;
2014-09-09 18:30:40 +04:00
m_currentTravelFlags = 0 ;
2015-06-09 22:16:08 +03:00
m_goalFailed = 0 ;
2015-08-15 18:09:15 +03:00
m_desiredVelocity . Zero ( ) ;
2014-09-09 18:30:40 +04:00
m_prevGoalIndex = - 1 ;
m_chosenGoalIndex = - 1 ;
m_loosedBombWptIndex = - 1 ;
m_moveToC4 = false ;
m_duckDefuse = false ;
2015-08-15 18:09:15 +03:00
m_duckDefuseCheckTime = 0.0f ;
2014-09-09 18:30:40 +04:00
2015-07-12 17:18:20 +03:00
m_numFriendsLeft = 0 ;
m_numEnemiesLeft = 0 ;
2015-06-28 19:43:31 +03:00
for ( i = 0 ; i < 5 ; i + + )
2015-06-09 22:16:08 +03:00
m_prevWptIndex [ i ] = - 1 ;
2014-09-09 18:30:40 +04:00
2016-03-01 13:37:10 +03:00
m_navTimeset = engine . Time ( ) ;
2016-03-05 23:08:07 +03:00
m_team = engine . GetTeam ( GetEntity ( ) ) ;
2014-09-09 18:30:40 +04:00
switch ( m_personality )
{
case PERSONALITY_NORMAL :
2016-09-11 21:01:06 +03:00
m_pathType = Random . Int ( 0 , 100 ) > 50 ? SEARCH_PATH_SAFEST_FASTER : SEARCH_PATH_SAFEST ;
2014-09-09 18:30:40 +04:00
break ;
case PERSONALITY_RUSHER :
2016-02-29 23:50:16 +03:00
m_pathType = SEARCH_PATH_FASTEST ;
2014-09-09 18:30:40 +04:00
break ;
case PERSONALITY_CAREFUL :
2016-02-29 23:50:16 +03:00
m_pathType = SEARCH_PATH_SAFEST ;
2014-09-09 18:30:40 +04:00
break ;
}
// clear all states & tasks
m_states = 0 ;
ResetTasks ( ) ;
m_isVIP = false ;
m_isLeader = false ;
m_hasProgressBar = false ;
m_canChooseAimDirection = true ;
2015-07-11 19:54:46 +03:00
m_turnAwayFromFlashbang = 0.0f ;
2014-09-09 18:30:40 +04:00
2016-02-06 23:37:58 +03:00
m_maxThrowTimer = 0.0f ;
2015-08-15 18:09:15 +03:00
m_timeTeamOrder = 0.0f ;
2016-03-09 22:34:24 +03:00
m_timeRepotingInDelay = Random . Float ( 40.0f , 240.0f ) ;
2015-08-15 18:09:15 +03:00
m_askCheckTime = 0.0f ;
m_minSpeed = 260.0f ;
m_prevSpeed = 0.0f ;
m_prevOrigin = Vector ( 9999.0f , 9999.0f , 9999.0f ) ;
2016-03-01 13:37:10 +03:00
m_prevTime = engine . Time ( ) ;
m_blindRecognizeTime = engine . Time ( ) ;
m_lookUpdateTime = engine . Time ( ) ;
2014-09-09 18:30:40 +04:00
2015-08-15 18:09:15 +03:00
m_viewDistance = 4096.0f ;
m_maxViewDistance = 4096.0f ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
m_liftEntity = nullptr ;
m_pickupItem = nullptr ;
m_itemIgnore = nullptr ;
2015-08-15 18:09:15 +03:00
m_itemCheckTime = 0.0f ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
m_breakableEntity = nullptr ;
2016-03-12 14:35:44 +03:00
m_breakableOrigin . Zero ( ) ;
2015-08-15 18:09:15 +03:00
m_timeDoorOpen = 0.0f ;
2014-09-09 18:30:40 +04:00
ResetCollideState ( ) ;
ResetDoubleJumpState ( ) ;
2016-09-11 21:01:06 +03:00
m_enemy = nullptr ;
m_lastVictim = nullptr ;
m_lastEnemy = nullptr ;
2015-08-15 18:09:15 +03:00
m_lastEnemyOrigin . Zero ( ) ;
2016-09-11 21:01:06 +03:00
m_trackingEdict = nullptr ;
2015-08-15 18:09:15 +03:00
m_timeNextTracking = 0.0f ;
2014-09-09 18:30:40 +04:00
2015-08-15 18:09:15 +03:00
m_buttonPushTime = 0.0f ;
m_enemyUpdateTime = 0.0f ;
2016-02-11 21:50:05 +03:00
m_enemyIgnoreTimer = 0.0f ;
2015-08-15 18:09:15 +03:00
m_seeEnemyTime = 0.0f ;
m_shootAtDeadTime = 0.0f ;
m_oldCombatDesire = 0.0f ;
m_liftUsageTime = 0.0f ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
m_avoidGrenade = nullptr ;
2014-09-09 18:30:40 +04:00
m_needAvoidGrenade = 0 ;
m_lastDamageType = - 1 ;
m_voteKickIndex = 0 ;
m_lastVoteKick = 0 ;
m_voteMap = 0 ;
m_doorOpenAttempt = 0 ;
m_aimFlags = 0 ;
m_liftState = 0 ;
2015-08-15 18:09:15 +03:00
m_position . Zero ( ) ;
m_liftTravelPos . Zero ( ) ;
2014-09-09 18:30:40 +04:00
2015-06-04 11:52:48 +03:00
SetIdealReactionTimes ( true ) ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
m_targetEntity = nullptr ;
2016-03-05 21:04:46 +03:00
m_tasks . RemoveAll ( ) ;
2015-08-15 18:09:15 +03:00
m_followWaitTime = 0.0f ;
2014-09-09 18:30:40 +04:00
for ( i = 0 ; i < MAX_HOSTAGES ; i + + )
2016-09-11 21:01:06 +03:00
m_hostages [ i ] = nullptr ;
2014-09-09 18:30:40 +04:00
for ( i = 0 ; i < Chatter_Total ; i + + )
2015-08-15 18:09:15 +03:00
m_chatterTimes [ i ] = - 1.0f ;
2014-09-09 18:30:40 +04:00
m_isReloading = false ;
m_reloadState = RELOAD_NONE ;
2015-08-15 18:09:15 +03:00
m_reloadCheckTime = 0.0f ;
2016-03-01 13:37:10 +03:00
m_shootTime = engine . Time ( ) ;
m_playerTargetTime = engine . Time ( ) ;
2015-08-15 18:09:15 +03:00
m_firePause = 0.0f ;
m_timeLastFired = 0.0f ;
2014-09-09 18:30:40 +04:00
2015-08-15 18:09:15 +03:00
m_grenadeCheckTime = 0.0f ;
2014-09-09 18:30:40 +04:00
m_isUsingGrenade = false ;
m_blindButton = 0 ;
2015-08-03 00:05:27 +03:00
m_blindTime = 0.0f ;
m_jumpTime = 0.0f ;
m_jumpStateTimer = 0.0f ;
2014-09-09 18:30:40 +04:00
m_jumpFinished = false ;
m_isStuck = false ;
2016-03-01 13:37:10 +03:00
m_sayTextBuffer . timeNextChat = engine . Time ( ) ;
2014-09-09 18:30:40 +04:00
m_sayTextBuffer . entityIndex = - 1 ;
m_sayTextBuffer . sayText [ 0 ] = 0x0 ;
2016-02-29 22:49:19 +03:00
m_buyState = BUYSTATE_PRIMARY_WEAPON ;
2015-06-04 11:52:48 +03:00
m_lastEquipTime = 0.0f ;
2014-09-09 18:30:40 +04:00
if ( ! m_notKilled ) // if bot died, clear all weapon stuff and force buying again
{
memset ( & m_ammoInClip , 0 , sizeof ( m_ammoInClip ) ) ;
memset ( & m_ammo , 0 , sizeof ( m_ammo ) ) ;
m_currentWeapon = 0 ;
}
2016-03-01 13:37:10 +03:00
m_knifeAttackTime = engine . Time ( ) + Random . Float ( 1.3f , 2.6f ) ;
m_nextBuyTime = engine . Time ( ) + Random . Float ( 0.6f , 2.0f ) ;
2014-09-09 18:30:40 +04:00
m_buyPending = false ;
m_inBombZone = false ;
2014-09-17 20:36:42 +04:00
m_hasC4 = false ;
2014-09-09 18:30:40 +04:00
2015-08-15 18:09:15 +03:00
m_shieldCheckTime = 0.0f ;
m_zoomCheckTime = 0.0f ;
m_strafeSetTime = 0.0f ;
2016-03-09 19:17:56 +03:00
m_combatStrafeDir = STRAFE_DIR_NONE ;
m_fightStyle = FIGHT_NONE ;
2015-08-15 18:09:15 +03:00
m_lastFightStyleCheck = 0.0f ;
2014-09-09 18:30:40 +04:00
m_checkWeaponSwitch = true ;
m_checkKnifeSwitch = true ;
m_buyingFinished = false ;
2016-09-11 21:01:06 +03:00
m_radioEntity = nullptr ;
2014-09-09 18:30:40 +04:00
m_radioOrder = 0 ;
m_defendedBomb = false ;
2015-06-04 11:52:48 +03:00
m_defendHostage = false ;
m_headedTime = 0.0f ;
2014-09-09 18:30:40 +04:00
2016-03-01 13:37:10 +03:00
m_timeLogoSpray = engine . Time ( ) + Random . Float ( 0.5f , 2.0f ) ;
m_spawnTime = engine . Time ( ) ;
m_lastChatTime = engine . Time ( ) ;
2014-09-09 18:30:40 +04:00
m_timeCamping = 0 ;
m_campDirection = 0 ;
m_nextCampDirTime = 0 ;
m_campButtons = 0 ;
2015-08-15 18:09:15 +03:00
m_soundUpdateTime = 0.0f ;
2016-03-01 13:37:10 +03:00
m_heardSoundTime = engine . Time ( ) ;
2014-09-09 18:30:40 +04:00
// clear its message queue
for ( i = 0 ; i < 32 ; i + + )
2016-10-23 01:49:05 +03:00
m_messageQueue [ i ] = GAME_MSG_NONE ;
2014-09-09 18:30:40 +04:00
m_actMessageIndex = 0 ;
m_pushMessageIndex = 0 ;
// and put buying into its message queue
2016-10-23 01:49:05 +03:00
PushMessageQueue ( GAME_MSG_PURCHASE ) ;
2015-08-15 18:09:15 +03:00
PushTask ( TASK_NORMAL , TASKPRI_NORMAL , - 1 , 0.0f , true ) ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 0 , 100 ) < 50 )
2014-09-09 18:30:40 +04:00
ChatterMessage ( Chatter_NewRound ) ;
2015-07-22 23:04:43 +03:00
2016-06-23 15:30:19 +03:00
m_thinkInterval = ( g_gameFlags & GAME_LEGACY ) ? 0.0f : ( 1.0f / 30.0f ) * Random . Float ( 0.95f , 1.05f ) ;
2014-09-09 18:30:40 +04:00
}
void Bot : : Kill ( void )
{
// this function kills a bot (not just using ClientKill, but like the CSBot does)
// base code courtesy of Lazy (from bots-united forums!)
2015-07-26 21:47:29 +03:00
bots . TouchWithKillerEntity ( this ) ;
2014-09-09 18:30:40 +04:00
}
2016-06-23 15:30:19 +03:00
void Bot : : Kick ( bool keepQuota )
2014-09-09 18:30:40 +04:00
{
// this function kick off one bot from the server.
2016-10-29 21:01:41 +03:00
auto username = STRING ( pev - > netname ) ;
if ( ! ( pev - > flags & FL_FAKECLIENT ) | | IsNullString ( username ) )
return ;
2016-09-22 15:47:35 +03:00
// clear fakeclient bit immediately
pev - > flags & = ~ FL_FAKECLIENT ;
2016-10-29 21:01:41 +03:00
engine . IssueCmd ( " kick \" %s \" " , username ) ;
engine . CenterPrintf ( " Bot '%s' kicked " , username ) ;
2014-09-09 18:30:40 +04:00
2016-01-14 23:32:38 +03:00
// keep quota number up to date
2016-06-23 15:30:19 +03:00
if ( ! keepQuota )
2016-09-16 22:55:31 +03:00
yb_quota . SetInt ( A_clamp < int > ( yb_quota . GetInt ( ) - 1 , 0 , yb_quota . GetInt ( ) ) ) ;
2014-09-09 18:30:40 +04:00
}
void Bot : : StartGame ( void )
{
// this function handles the selection of teams & class
2015-12-26 17:19:20 +03:00
# ifdef XASH_CSDM
2016-09-11 21:01:06 +03:00
m_wantedTeam = Random . Int ( 1 , 2 ) ;
2015-12-26 17:19:20 +03:00
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " jointeam %d " , m_wantedTeam ) ;
2015-12-26 17:19:20 +03:00
2016-01-03 23:18:47 +03:00
SET_CLIENT_KEYVALUE ( GetIndex ( ) , GET_INFOKEYBUFFER ( GetEntity ( ) ) , " model " , m_wantedTeam = = 2 ? " Counter-Terrorists " : " Terrorists " ) ;
2015-12-26 17:19:20 +03:00
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 0 , 100 ) < 20 )
2015-12-26 17:19:20 +03:00
ChatMessage ( CHAT_WELCOME ) ;
m_notStarted = false ;
return ;
# endif
2016-09-14 15:40:42 +03:00
// cs prior beta 7.0 uses hud-based motd, so press fire once
if ( g_gameFlags & GAME_LEGACY )
pev - > button | = IN_ATTACK ;
2016-10-18 20:34:02 +03:00
// check if something has assigned team to us
else if ( m_team = = TERRORIST | | m_team = = CT )
m_notStarted = false ;
// if bot was unable to join team, and no menus popups, check for stacked team
2016-10-23 01:49:05 +03:00
if ( m_startAction = = GAME_MSG_NONE & & + + m_retryJoin > 2 )
2016-10-18 20:34:02 +03:00
{
if ( bots . IsTeamStacked ( m_wantedTeam - 1 ) )
{
m_retryJoin = 0 ;
engine . Printf ( " Could not add bot to the game: Team is stacked (to disable this check, set mp_limitteams and mp_autoteambalance to zero and restart the round). " ) ;
Kick ( ) ;
return ;
}
}
2014-09-09 18:30:40 +04:00
// handle counter-strike stuff here...
2016-10-23 01:49:05 +03:00
if ( m_startAction = = GAME_MSG_TEAM_SELECT )
2014-09-09 18:30:40 +04:00
{
2016-10-23 01:49:05 +03:00
m_startAction = GAME_MSG_NONE ; // switch back to idle
2015-06-04 11:52:48 +03:00
char teamJoin = yb_join_team . GetString ( ) [ 0 ] ;
2014-09-09 18:30:40 +04:00
2015-06-04 11:52:48 +03:00
if ( teamJoin = = ' C ' | | teamJoin = = ' c ' )
2014-09-09 18:30:40 +04:00
m_wantedTeam = 2 ;
2015-06-04 11:52:48 +03:00
else if ( teamJoin = = ' T ' | | teamJoin = = ' t ' )
2014-09-09 18:30:40 +04:00
m_wantedTeam = 1 ;
if ( m_wantedTeam ! = 1 & & m_wantedTeam ! = 2 )
m_wantedTeam = 5 ;
// select the team the bot wishes to join...
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , m_wantedTeam ) ;
2014-09-09 18:30:40 +04:00
}
2016-10-23 01:49:05 +03:00
else if ( m_startAction = = GAME_MSG_CLASS_SELECT )
2014-09-09 18:30:40 +04:00
{
2016-10-23 01:49:05 +03:00
m_startAction = GAME_MSG_NONE ; // switch back to idle
2014-09-09 18:30:40 +04:00
2016-10-18 20:34:02 +03:00
// czero has additional models
int maxChoice = ( g_gameFlags & GAME_CZERO ) ? 5 : 4 ;
if ( m_wantedClass < 1 | | m_wantedClass > maxChoice )
m_wantedClass = Random . Int ( 1 , maxChoice ) ; // use random if invalid
2014-09-09 18:30:40 +04:00
// select the class the bot wishes to use...
2016-03-01 22:52:17 +03:00
engine . IssueBotCommand ( GetEntity ( ) , " menuselect %d " , m_wantedClass ) ;
2014-09-09 18:30:40 +04:00
// bot has now joined the game (doesn't need to be started)
m_notStarted = false ;
2016-09-14 15:40:42 +03:00
2014-09-09 18:30:40 +04:00
// check for greeting other players, since we connected
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 0 , 100 ) < 20 )
2014-09-09 18:30:40 +04:00
ChatMessage ( CHAT_WELCOME ) ;
}
}
void BotManager : : CalculatePingOffsets ( void )
{
2016-09-14 11:51:58 +03:00
if ( ! ( g_gameFlags & GAME_SUPPORT_SVC_PINGS ) | | yb_latency_display . GetInt ( ) ! = 2 )
2014-09-09 18:30:40 +04:00
return ;
int averagePing = 0 ;
int numHumans = 0 ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-03-05 23:08:07 +03:00
edict_t * ent = engine . EntityOfIndex ( i + 1 ) ;
2014-09-09 18:30:40 +04:00
if ( ! IsValidPlayer ( ent ) )
continue ;
numHumans + + ;
int ping , loss ;
PLAYER_CNX_STATS ( ent , & ping , & loss ) ;
if ( ping < 0 | | ping > 100 )
2016-09-11 21:01:06 +03:00
ping = Random . Int ( 3 , 15 ) ;
2014-09-09 18:30:40 +04:00
averagePing + = ping ;
}
if ( numHumans > 0 )
averagePing / = numHumans ;
else
2016-09-11 21:01:06 +03:00
averagePing = Random . Int ( 30 , 40 ) ;
2014-09-09 18:30:40 +04:00
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
Bot * bot = GetBot ( i ) ;
2016-09-11 21:01:06 +03:00
if ( bot = = nullptr )
2014-09-09 18:30:40 +04:00
continue ;
2016-09-11 21:01:06 +03:00
int part = static_cast < int > ( averagePing * 0.2f ) ;
int botPing = Random . Int ( averagePing - part , averagePing + part ) + Random . Int ( bot - > m_difficulty + 3 , bot - > m_difficulty + 6 ) + 10 ;
2014-09-09 18:30:40 +04:00
if ( botPing < = 5 )
2016-09-11 21:01:06 +03:00
botPing = Random . Int ( 10 , 23 ) ;
2014-09-09 18:30:40 +04:00
else if ( botPing > 100 )
2016-09-11 21:01:06 +03:00
botPing = Random . Int ( 30 , 40 ) ;
2014-09-09 18:30:40 +04:00
for ( int j = 0 ; j < 2 ; j + + )
{
for ( bot - > m_pingOffset [ j ] = 0 ; bot - > m_pingOffset [ j ] < 4 ; bot - > m_pingOffset [ j ] + + )
{
if ( ( botPing - bot - > m_pingOffset [ j ] ) % 4 = = 0 )
{
bot - > m_ping [ j ] = ( botPing - bot - > m_pingOffset [ j ] ) / 4 ;
break ;
}
}
}
bot - > m_ping [ 2 ] = botPing ;
}
}
void BotManager : : SendPingDataOffsets ( edict_t * to )
{
2016-09-14 11:51:58 +03:00
if ( ! ( g_gameFlags & GAME_SUPPORT_SVC_PINGS ) | | yb_latency_display . GetInt ( ) ! = 2 | | engine . IsNullEntity ( to ) | | ( to - > v . flags & FL_FAKECLIENT ) )
2014-09-09 18:30:40 +04:00
return ;
2015-06-04 11:52:48 +03:00
if ( ! ( to - > v . flags & FL_CLIENT ) & & ! ( ( ( to - > v . button & IN_SCORE ) | | ! ( to - > v . oldbuttons & IN_SCORE ) ) ) )
2014-09-09 18:30:40 +04:00
return ;
static int sending ;
2016-01-16 21:08:22 +03:00
sending = 0 ;
2014-09-09 18:30:40 +04:00
// missing from sdk
static const int SVC_PINGS = 17 ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
{
2016-03-09 22:34:24 +03:00
Bot * bot = m_bots [ i ] ;
2014-09-09 18:30:40 +04:00
2016-09-11 21:01:06 +03:00
if ( bot = = nullptr )
2014-09-09 18:30:40 +04:00
continue ;
switch ( sending )
{
case 0 :
{
// start a new message
2016-09-11 21:01:06 +03:00
MESSAGE_BEGIN ( MSG_ONE_UNRELIABLE , SVC_PINGS , nullptr , to ) ;
2016-01-26 21:18:59 +03:00
WRITE_BYTE ( ( bot - > m_pingOffset [ sending ] * 64 ) + ( 1 + 2 * i ) ) ;
WRITE_SHORT ( bot - > m_ping [ sending ] ) ;
2014-09-09 18:30:40 +04:00
sending + + ;
}
case 1 :
{
// append additional data
2016-01-26 21:18:59 +03:00
WRITE_BYTE ( ( bot - > m_pingOffset [ sending ] * 128 ) + ( 2 + 4 * i ) ) ;
WRITE_SHORT ( bot - > m_ping [ sending ] ) ;
2014-09-09 18:30:40 +04:00
sending + + ;
}
case 2 :
{
// append additional data and end message
WRITE_BYTE ( 4 + 8 * i ) ;
2016-01-26 21:18:59 +03:00
WRITE_SHORT ( bot - > m_ping [ sending ] ) ;
2014-09-09 18:30:40 +04:00
WRITE_BYTE ( 0 ) ;
MESSAGE_END ( ) ;
sending = 0 ;
}
}
}
// end message if not yet sent
if ( sending )
{
WRITE_BYTE ( 0 ) ;
MESSAGE_END ( ) ;
}
}
void BotManager : : SendDeathMsgFix ( void )
{
if ( yb_latency_display . GetInt ( ) = = 2 & & m_deathMsgSent )
{
m_deathMsgSent = false ;
2016-03-01 13:37:10 +03:00
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
2014-09-09 18:30:40 +04:00
SendPingDataOffsets ( g_clients [ i ] . ent ) ;
}
2015-06-11 00:18:49 +03:00
}
void BotManager : : UpdateActiveGrenades ( void )
{
2016-03-25 14:56:40 +03:00
if ( m_grenadeUpdateTime > engine . Time ( ) )
return ;
2016-09-11 21:01:06 +03:00
edict_t * grenade = nullptr ;
2015-06-11 00:18:49 +03:00
// clear previously stored grenades
m_activeGrenades . RemoveAll ( ) ;
// search the map for any type of grenade
2016-03-05 23:08:07 +03:00
while ( ! engine . IsNullEntity ( grenade = FIND_ENTITY_BY_CLASSNAME ( grenade , " grenade " ) ) )
2016-01-27 21:40:47 +03:00
{
// do not count c4 as a grenade
if ( strcmp ( STRING ( grenade - > v . model ) + 9 , " c4.mdl " ) = = 0 )
continue ;
2015-06-11 00:18:49 +03:00
m_activeGrenades . Push ( grenade ) ;
2016-01-27 21:40:47 +03:00
}
2016-03-25 14:56:40 +03:00
m_grenadeUpdateTime = 0.213f + engine . Time ( ) ;
2015-06-11 00:18:49 +03:00
}
2016-03-12 19:56:09 +03:00
const Array < edict_t * > & BotManager : : GetActiveGrenades ( void )
2015-06-11 00:18:49 +03:00
{
return m_activeGrenades ;
2016-02-07 02:34:06 +03:00
}
2016-09-10 19:31:38 +03:00
void BotManager : : SelectLeaderEachTeam ( int team , bool reset )
{
if ( reset )
{
m_leaderChoosen [ team ] = false ;
return ;
}
if ( g_mapType & MAP_AS )
{
if ( team = = CT & & ! m_leaderChoosen [ CT ] )
{
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
{
auto bot = m_bots [ i ] ;
2016-09-11 21:01:06 +03:00
if ( bot ! = nullptr & & bot - > m_isVIP )
2016-09-10 19:31:38 +03:00
{
// vip bot is the leader
bot - > m_isLeader = true ;
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 1 , 100 ) < 50 )
2016-09-10 19:31:38 +03:00
{
bot - > RadioMessage ( Radio_FollowMe ) ;
bot - > m_campButtons = 0 ;
}
}
}
m_leaderChoosen [ CT ] = true ;
}
else if ( team = = TERRORIST & & ! m_leaderChoosen [ TERRORIST ] )
{
auto bot = bots . GetHighestFragsBot ( team ) ;
2016-09-11 21:01:06 +03:00
if ( bot ! = nullptr & & bot - > m_notKilled )
2016-09-10 19:31:38 +03:00
{
bot - > m_isLeader = true ;
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 1 , 100 ) < 45 )
2016-09-10 19:31:38 +03:00
bot - > RadioMessage ( Radio_FollowMe ) ;
}
m_leaderChoosen [ TERRORIST ] = true ;
}
}
else if ( g_mapType & MAP_DE )
{
if ( team = = TERRORIST & & ! m_leaderChoosen [ TERRORIST ] )
{
for ( int i = 0 ; i < engine . MaxClients ( ) ; i + + )
{
auto bot = m_bots [ i ] ;
2016-09-11 21:01:06 +03:00
if ( bot ! = nullptr & & bot - > m_hasC4 )
2016-09-10 19:31:38 +03:00
{
// bot carrying the bomb is the leader
bot - > m_isLeader = true ;
// terrorist carrying a bomb needs to have some company
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 1 , 100 ) < 80 )
2016-09-10 19:31:38 +03:00
{
if ( yb_communication_type . GetInt ( ) = = 2 )
bot - > ChatterMessage ( Chatter_GoingToPlantBomb ) ;
else
bot - > ChatterMessage ( Radio_FollowMe ) ;
bot - > m_campButtons = 0 ;
}
}
m_leaderChoosen [ TERRORIST ] = true ;
}
}
else if ( ! m_leaderChoosen [ CT ] )
{
if ( auto bot = bots . GetHighestFragsBot ( team ) )
{
bot - > m_isLeader = true ;
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 1 , 100 ) < 30 )
2016-09-10 19:31:38 +03:00
bot - > RadioMessage ( Radio_FollowMe ) ;
}
m_leaderChoosen [ CT ] = true ;
}
}
else if ( g_mapType & ( MAP_ES | MAP_KA | MAP_FY ) )
{
if ( auto bot = bots . GetHighestFragsBot ( team ) )
{
bot - > m_isLeader = true ;
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 1 , 100 ) < 30 )
2016-09-10 19:31:38 +03:00
bot - > RadioMessage ( Radio_FollowMe ) ;
}
}
else
{
if ( auto bot = bots . GetHighestFragsBot ( team ) )
{
bot - > m_isLeader = true ;
2016-09-11 21:01:06 +03:00
if ( Random . Int ( 1 , 100 ) < ( team = = TERRORIST ? 30 : 40 ) )
2016-09-10 19:31:38 +03:00
bot - > RadioMessage ( Radio_FollowMe ) ;
}
}
}