2020-06-15 11:28:42 +03:00
//
2023-05-24 23:41:23 +03:00
// YaPB, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright © YaPB Project Developers <yapb@jeefo.net>.
2020-06-15 11:28:42 +03:00
//
2020-11-03 08:57:12 +03:00
// SPDX-License-Identifier: MIT
2020-06-15 11:28:42 +03:00
//
# include <yapb.h>
2023-07-21 21:43:36 +03:00
ConVar cv_bind_menu_key ( " bind_menu_key " , " = " , " Binds specified key for opening bots menu. " , false ) ;
ConVar cv_ignore_cvars_on_changelevel ( " ignore_cvars_on_changelevel " , " yb_quota,yb_autovacate " , " Specifies comma separated list of bot cvars, that will not be overwritten by config on changelevel. " , false ) ;
ConVar cv_logger_disable_logfile ( " logger_disable_logfile " , " 0 " , " Disables logger to write anything to log file. Just spew content to the console. " ) ;
2020-06-15 11:28:42 +03:00
BotConfig : : BotConfig ( ) {
m_chat . resize ( Chat : : Count ) ;
m_chatter . resize ( Chatter : : Count ) ;
m_weaponProps . resize ( kMaxWeapons ) ;
}
void BotConfig : : loadConfigs ( ) {
setupMemoryFiles ( ) ;
2023-04-11 22:32:28 +03:00
loadCustomConfig ( ) ;
2020-06-15 11:28:42 +03:00
loadNamesConfig ( ) ;
loadChatConfig ( ) ;
loadChatterConfig ( ) ;
loadWeaponsConfig ( ) ;
loadLanguageConfig ( ) ;
loadLogosConfig ( ) ;
loadAvatarsConfig ( ) ;
loadDifficultyConfig ( ) ;
}
2020-10-04 00:54:34 +03:00
void BotConfig : : loadMainConfig ( bool isFirstLoad ) {
2023-06-13 19:55:03 +03:00
if ( game . is ( GameFlags : : Legacy ) ) {
2020-06-15 11:28:42 +03:00
util . setNeedForWelcome ( true ) ;
}
setupMemoryFiles ( ) ;
2023-03-13 15:39:15 +03:00
auto needsToIgnoreVar = [ ] ( StringArray & list , const char * needle ) {
2020-06-15 11:28:42 +03:00
for ( const auto & var : list ) {
if ( var = = needle ) {
return true ;
}
}
return false ;
} ;
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
2023-05-12 20:00:06 +03:00
// this is does the same as exec of engine, but not overwriting values of cvars specified in cv_ignore_cvars_on_changelevel
2023-05-14 14:48:06 +03:00
if ( openConfig ( product . nameLower , " Bot main config file is not found. " , & file , false ) ) {
2020-06-15 11:28:42 +03:00
while ( file . getLine ( line ) ) {
line . trim ( ) ;
if ( isCommentLine ( line ) ) {
continue ;
}
2020-10-04 00:54:34 +03:00
if ( isFirstLoad ) {
2020-06-15 11:28:42 +03:00
game . serverCommand ( line . chars ( ) ) ;
continue ;
}
auto keyval = line . split ( " " ) ;
if ( keyval . length ( ) > 1 ) {
2024-04-25 15:03:39 +03:00
auto ignore = String ( cv_ignore_cvars_on_changelevel . as < StringRef > ( ) ) . split ( " , " ) ;
2020-06-15 11:28:42 +03:00
auto key = keyval [ 0 ] . trim ( ) . chars ( ) ;
auto cvar = engfuncs . pfnCVarGetPointer ( key ) ;
if ( cvar ! = nullptr ) {
auto value = const_cast < char * > ( keyval [ 1 ] . trim ( ) . trim ( " \" " ) . trim ( ) . chars ( ) ) ;
if ( needsToIgnoreVar ( ignore , key ) & & ! strings . matches ( value , cvar - > string ) ) {
// preserve quota number if it's zero
2024-04-25 15:03:39 +03:00
if ( cv_quota . name ( ) = = cvar - > name & & cv_quota . as < int > ( ) < = 0 ) {
2020-06-15 11:28:42 +03:00
engfuncs . pfnCvar_DirectSet ( cvar , value ) ;
continue ;
}
2020-10-12 20:59:48 +03:00
ctrl . msg ( " Bot CVAR '%s' differs from the stored in the config (%s/%s). Ignoring. " , cvar - > name , cvar - > string , value ) ;
2020-06-15 11:28:42 +03:00
// ensure cvar will have old value
engfuncs . pfnCvar_DirectSet ( cvar , cvar - > string ) ;
}
else {
engfuncs . pfnCvar_DirectSet ( cvar , value ) ;
}
}
else {
game . serverCommand ( line . chars ( ) ) ;
}
}
}
file . close ( ) ;
}
2023-04-02 12:17:12 +03:00
else {
game . serverCommand ( strings . format ( " %s cvars save " , product . cmdPri ) ) ;
}
2020-06-15 11:28:42 +03:00
2023-06-04 22:19:08 +03:00
// android is a bit hard to play, lower the difficulty by default
2024-04-25 15:03:39 +03:00
if ( plat . android & & cv_difficulty . as < int > ( ) > 3 ) {
2020-06-15 11:28:42 +03:00
cv_difficulty . set ( 3 ) ;
}
// bind the correct menu key for bot menu...
2023-06-20 15:18:35 +03:00
if ( ! game . isDedicated ( ) ) {
2024-04-25 15:03:39 +03:00
auto val = cv_bind_menu_key . as < StringRef > ( ) ;
2023-06-20 15:18:35 +03:00
if ( ! val . empty ( ) ) {
game . serverCommand ( " bind \" %s \" \" yb menu \" " , val ) ;
}
2020-06-15 11:28:42 +03:00
}
2023-06-04 22:19:08 +03:00
// disable logger if requested
2024-04-25 15:03:39 +03:00
logger . disableLogWrite ( cv_logger_disable_logfile ) ;
2020-06-15 11:28:42 +03:00
}
void BotConfig : : loadNamesConfig ( ) {
setupMemoryFiles ( ) ;
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
2023-08-08 11:48:37 +03:00
constexpr auto kMaxNameLen = 32 ;
2020-06-15 11:28:42 +03:00
// naming initialization
2023-05-14 14:48:06 +03:00
if ( openConfig ( " names " , " Name configuration file not found. " , & file , true ) ) {
2020-06-15 11:28:42 +03:00
m_botNames . clear ( ) ;
while ( file . getLine ( line ) ) {
line . trim ( ) ;
if ( isCommentLine ( line ) ) {
continue ;
}
// max botname is 32 characters
2023-08-08 11:48:37 +03:00
if ( line . length ( ) > kMaxNameLen - 1 ) {
line [ kMaxNameLen - 1 ] = kNullChar ;
2020-06-15 11:28:42 +03:00
}
m_botNames . emplace ( line , - 1 ) ;
}
file . close ( ) ;
}
2024-01-19 00:03:45 +03:00
m_botNames . shuffle ( ) ;
2020-06-15 11:28:42 +03:00
}
void BotConfig : : loadWeaponsConfig ( ) {
setupMemoryFiles ( ) ;
2023-03-13 15:39:15 +03:00
auto addWeaponEntries = [ ] ( SmallArray < WeaponInfo > & weapons , bool as , StringRef name , const StringArray & data ) {
2020-06-15 11:28:42 +03:00
// we're have null terminator element in weapons array...
if ( data . length ( ) + 1 ! = weapons . length ( ) ) {
logger . error ( " %s entry in weapons config is not valid or malformed (%d/%d). " , name , data . length ( ) , weapons . length ( ) ) ;
return ;
}
for ( size_t i = 0 ; i < data . length ( ) ; + + i ) {
if ( as ) {
2024-04-25 15:03:39 +03:00
weapons [ i ] . teamAS = data [ i ] . as < int > ( ) ;
2020-06-15 11:28:42 +03:00
}
else {
2024-04-25 15:03:39 +03:00
weapons [ i ] . teamStandard = data [ i ] . as < int > ( ) ;
2020-06-15 11:28:42 +03:00
}
}
} ;
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
auto addIntEntries = [ ] ( SmallArray < int32_t > & to , StringRef name , const StringArray & data ) {
2020-06-15 11:28:42 +03:00
if ( data . length ( ) ! = to . length ( ) ) {
logger . error ( " %s entry in weapons config is not valid or malformed (%d/%d). " , name , data . length ( ) , to . length ( ) ) ;
return ;
}
for ( size_t i = 0 ; i < to . length ( ) ; + + i ) {
2024-04-25 15:03:39 +03:00
to [ i ] = data [ i ] . as < int > ( ) ;
2020-06-15 11:28:42 +03:00
}
} ;
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
// weapon data initialization
2023-05-14 14:48:06 +03:00
if ( openConfig ( " weapon " , " Weapon configuration file not found. Loading defaults. " , & file ) ) {
2020-06-15 11:28:42 +03:00
while ( file . getLine ( line ) ) {
line . trim ( ) ;
if ( isCommentLine ( line ) ) {
continue ;
}
auto pair = line . split ( " = " ) ;
if ( pair . length ( ) ! = 2 ) {
continue ;
}
for ( auto & trim : pair ) {
trim . trim ( ) ;
}
auto splitted = pair [ 1 ] . split ( " , " ) ;
if ( pair [ 0 ] . startsWith ( " MapStandard " ) ) {
addWeaponEntries ( m_weapons , false , pair [ 0 ] , splitted ) ;
}
else if ( pair [ 0 ] . startsWith ( " MapAS " ) ) {
addWeaponEntries ( m_weapons , true , pair [ 0 ] , splitted ) ;
}
else if ( pair [ 0 ] . startsWith ( " GrenadePercent " ) ) {
addIntEntries ( m_grenadeBuyPrecent , pair [ 0 ] , splitted ) ;
}
else if ( pair [ 0 ] . startsWith ( " Economics " ) ) {
addIntEntries ( m_botBuyEconomyTable , pair [ 0 ] , splitted ) ;
}
else if ( pair [ 0 ] . startsWith ( " PersonalityNormal " ) ) {
addIntEntries ( m_normalWeaponPrefs , pair [ 0 ] , splitted ) ;
}
else if ( pair [ 0 ] . startsWith ( " PersonalityRusher " ) ) {
addIntEntries ( m_rusherWeaponPrefs , pair [ 0 ] , splitted ) ;
}
else if ( pair [ 0 ] . startsWith ( " PersonalityCareful " ) ) {
addIntEntries ( m_carefulWeaponPrefs , pair [ 0 ] , splitted ) ;
}
}
file . close ( ) ;
}
}
void BotConfig : : loadChatterConfig ( ) {
setupMemoryFiles ( ) ;
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
// chatter initialization
2024-04-25 15:03:39 +03:00
if ( game . is ( GameFlags : : HasBotVoice ) & & cv_radio_mode . as < int > ( ) = = 2 & & openConfig ( " chatter " , " Couldn't open chatter system configuration " , & file ) ) {
2020-06-15 11:28:42 +03:00
m_chatter . clear ( ) ;
struct EventMap {
String str ;
int code ;
float repeat ;
} chatterEventMap [ ] = {
2023-01-24 14:38:08 +00:00
{ " Radio_CoverMe " , Radio : : CoverMe , kMaxChatterRepeatInterval } ,
{ " Radio_YouTakePoint " , Radio : : YouTakeThePoint , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
{ " Radio_HoldPosition " , Radio : : HoldThisPosition , 10.0f } ,
{ " Radio_RegroupTeam " , Radio : : RegroupTeam , 10.0f } ,
{ " Radio_FollowMe " , Radio : : FollowMe , 15.0f } ,
{ " Radio_TakingFire " , Radio : : TakingFireNeedAssistance , 5.0f } ,
2023-01-24 14:38:08 +00:00
{ " Radio_GoGoGo " , Radio : : GoGoGo , kMaxChatterRepeatInterval } ,
{ " Radio_Fallback " , Radio : : TeamFallback , kMaxChatterRepeatInterval } ,
{ " Radio_StickTogether " , Radio : : StickTogetherTeam , kMaxChatterRepeatInterval } ,
{ " Radio_GetInPosition " , Radio : : GetInPositionAndWaitForGo , kMaxChatterRepeatInterval } ,
{ " Radio_StormTheFront " , Radio : : StormTheFront , kMaxChatterRepeatInterval } ,
{ " Radio_ReportTeam " , Radio : : ReportInTeam , kMaxChatterRepeatInterval } ,
{ " Radio_Affirmative " , Radio : : RogerThat , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
{ " Radio_EnemySpotted " , Radio : : EnemySpotted , 4.0f } ,
{ " Radio_NeedBackup " , Radio : : NeedBackup , 5.0f } ,
{ " Radio_SectorClear " , Radio : : SectorClear , 10.0f } ,
{ " Radio_InPosition " , Radio : : ImInPosition , 10.0f } ,
{ " Radio_ReportingIn " , Radio : : ReportingIn , 3.0f } ,
2023-01-24 14:38:08 +00:00
{ " Radio_ShesGonnaBlow " , Radio : : ShesGonnaBlow , kMaxChatterRepeatInterval } ,
{ " Radio_Negative " , Radio : : Negative , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
{ " Radio_EnemyDown " , Radio : : EnemyDown , 10.0f } ,
2023-01-24 14:38:08 +00:00
{ " Chatter_DiePain " , Chatter : : DiePain , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
{ " Chatter_GoingToPlantBomb " , Chatter : : GoingToPlantBomb , 5.0f } ,
2024-04-22 14:23:56 +00:00
{ " Chatter_GoingToGuardEscapeZone " , Chatter : : GoingToGuardEscapeZone , kMaxChatterRepeatInterval } ,
{ " Chatter_GoingToGuardRescueZone " , Chatter : : GoingToGuardRescueZone , kMaxChatterRepeatInterval } ,
2023-01-24 14:38:08 +00:00
{ " Chatter_GoingToGuardVIPSafety " , Chatter : : GoingToGuardVIPSafety , kMaxChatterRepeatInterval } ,
{ " Chatter_RescuingHostages " , Chatter : : RescuingHostages , kMaxChatterRepeatInterval } ,
{ " Chatter_TeamKill " , Chatter : : TeamKill , kMaxChatterRepeatInterval } ,
2024-04-22 14:23:56 +00:00
{ " Chatter_GuardingEscapeZone " , Chatter : : GuardingEscapeZone , kMaxChatterRepeatInterval } ,
2023-01-24 14:38:08 +00:00
{ " Chatter_GuardingVipSafety " , Chatter : : GuardingVIPSafety , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
{ " Chatter_PlantingC4 " , Chatter : : PlantingBomb , 10.0f } ,
2023-01-24 14:38:08 +00:00
{ " Chatter_InCombat " , Chatter : : InCombat , kMaxChatterRepeatInterval } ,
{ " Chatter_SeeksEnemy " , Chatter : : SeekingEnemies , kMaxChatterRepeatInterval } ,
{ " Chatter_Nothing " , Chatter : : Nothing , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
{ " Chatter_EnemyDown " , Chatter : : EnemyDown , 10.0f } ,
2023-01-24 14:38:08 +00:00
{ " Chatter_UseHostage " , Chatter : : UsingHostages , kMaxChatterRepeatInterval } ,
{ " Chatter_WonTheRound " , Chatter : : WonTheRound , kMaxChatterRepeatInterval } ,
{ " Chatter_QuicklyWonTheRound " , Chatter : : QuickWonRound , kMaxChatterRepeatInterval } ,
{ " Chatter_NoEnemiesLeft " , Chatter : : NoEnemiesLeft , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
{ " Chatter_FoundBombPlace " , Chatter : : FoundC4Plant , 15.0f } ,
2023-01-24 14:38:08 +00:00
{ " Chatter_WhereIsTheBomb " , Chatter : : WhereIsTheC4 , kMaxChatterRepeatInterval } ,
{ " Chatter_DefendingBombSite " , Chatter : : DefendingBombsite , kMaxChatterRepeatInterval } ,
{ " Chatter_BarelyDefused " , Chatter : : BarelyDefused , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
{ " Chatter_NiceshotCommander " , Chatter : : NiceShotCommander , 10.0f } ,
{ " Chatter_ReportingIn " , Chatter : : ReportingIn , 10.0f } ,
{ " Chatter_SpotTheBomber " , Chatter : : SpotTheBomber , 4.3f } ,
{ " Chatter_VIPSpotted " , Chatter : : VIPSpotted , 5.3f } ,
{ " Chatter_FriendlyFire " , Chatter : : FriendlyFire , 2.1f } ,
{ " Chatter_GotBlinded " , Chatter : : Blind , 12.0f } ,
2024-04-30 15:49:24 +00:00
{ " Chatter_GuardingPlantedC4 " , Chatter : : GuardingPlantedC4 , 3.0f } ,
2020-06-15 11:28:42 +03:00
{ " Chatter_DefusingC4 " , Chatter : : DefusingBomb , 3.0f } ,
{ " Chatter_FoundC4 " , Chatter : : FoundC4 , 5.5f } ,
{ " Chatter_ScaredEmotion " , Chatter : : ScaredEmotion , 6.1f } ,
2024-04-21 05:43:14 +00:00
{ " Chatter_HeardEnemy " , Chatter : : HeardTheEnemy , 12.8f } ,
2024-05-03 17:23:36 +00:00
{ " Chatter_SpottedOneEnemy " , Chatter : : SpottedOneEnemy , 4.0f } ,
{ " Chatter_SpottedTwoEnemies " , Chatter : : SpottedTwoEnemies , 4.0f } ,
{ " Chatter_SpottedThreeEnemies " , Chatter : : SpottedThreeEnemies , 4.0f } ,
{ " Chatter_TooManyEnemies " , Chatter : : TooManyEnemies , 4.0f } ,
2020-06-15 11:28:42 +03:00
{ " Chatter_SniperWarning " , Chatter : : SniperWarning , 14.3f } ,
{ " Chatter_SniperKilled " , Chatter : : SniperKilled , 12.1f } ,
{ " Chatter_OneEnemyLeft " , Chatter : : OneEnemyLeft , 12.5f } ,
{ " Chatter_TwoEnemiesLeft " , Chatter : : TwoEnemiesLeft , 12.5f } ,
{ " Chatter_ThreeEnemiesLeft " , Chatter : : ThreeEnemiesLeft , 12.5f } ,
{ " Chatter_NiceshotPall " , Chatter : : NiceShotPall , 2.0f } ,
{ " Chatter_GoingToGuardHostages " , Chatter : : GoingToGuardHostages , 3.0f } ,
2024-04-30 15:49:24 +00:00
{ " Chatter_GoingToGuardDroppedBomb " , Chatter : : GoingToGuardDroppedC4 , 6.0f } ,
2020-06-15 11:28:42 +03:00
{ " Chatter_OnMyWay " , Chatter : : OnMyWay , 1.5f } ,
{ " Chatter_LeadOnSir " , Chatter : : LeadOnSir , 5.0f } ,
{ " Chatter_Pinned_Down " , Chatter : : PinnedDown , 5.0f } ,
{ " Chatter_GottaFindTheBomb " , Chatter : : GottaFindC4 , 3.0f } ,
{ " Chatter_You_Heard_The_Man " , Chatter : : YouHeardTheMan , 3.0f } ,
{ " Chatter_Lost_The_Commander " , Chatter : : LostCommander , 4.5f } ,
{ " Chatter_NewRound " , Chatter : : NewRound , 3.5f } ,
{ " Chatter_CoverMe " , Chatter : : CoverMe , 3.5f } ,
{ " Chatter_BehindSmoke " , Chatter : : BehindSmoke , 3.5f } ,
{ " Chatter_BombSiteSecured " , Chatter : : BombsiteSecured , 3.5f } ,
{ " Chatter_GoingToCamp " , Chatter : : GoingToCamp , 30.0f } ,
{ " Chatter_Camp " , Chatter : : Camping , 10.0f } ,
2024-05-20 19:58:59 +00:00
{ " Chatter_OnARoll " , Chatter : : OnARoll , kMaxChatterRepeatInterval } ,
2020-06-15 11:28:42 +03:00
} ;
while ( file . getLine ( line ) ) {
line . trim ( ) ;
if ( isCommentLine ( line ) ) {
continue ;
}
StringRef rewriteKey = " RewritePath " ;
StringRef eventKey = " Event " ;
if ( line . startsWith ( rewriteKey ) ) {
cv_chatter_path . set ( line . substr ( rewriteKey . length ( ) ) . trim ( ) . chars ( ) ) ;
}
else if ( line . startsWith ( eventKey ) ) {
auto items = line . substr ( eventKey . length ( ) ) . split ( " = " ) ;
if ( items . length ( ) ! = 2 ) {
logger . error ( " Error in chatter config file syntax... Please correct all errors. " ) ;
continue ;
}
for ( auto & item : items ) {
item . trim ( ) ;
}
items [ 1 ] . trim ( " (;) " ) ;
for ( const auto & event : chatterEventMap ) {
2023-05-12 20:00:06 +03:00
if ( event . str = = items . first ( ) ) {
2020-06-15 11:28:42 +03:00
// this does common work of parsing comma-separated chatter line
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
auto sentences = items [ 1 ] . split ( " , " ) ;
2024-01-19 00:03:45 +03:00
sentences . shuffle ( ) ;
2020-06-15 11:28:42 +03:00
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
for ( auto & sound : sentences ) {
2020-06-15 11:28:42 +03:00
sound . trim ( ) . trim ( " \" " ) ;
2023-08-22 09:45:29 +03:00
const auto duration = util . getWaveLength ( sound . chars ( ) ) ;
2020-06-15 11:28:42 +03:00
if ( duration > 0.0f ) {
m_chatter [ event . code ] . emplace ( cr : : move ( sound ) , event . repeat , duration ) ;
}
}
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
sentences . clear ( ) ;
2020-06-15 11:28:42 +03:00
}
}
}
}
file . close ( ) ;
}
else {
cv_radio_mode . set ( 1 ) ;
2023-06-13 19:55:03 +03:00
game . print ( " Bots chatter communication disabled. " ) ;
2020-06-15 11:28:42 +03:00
}
}
void BotConfig : : loadChatConfig ( ) {
setupMemoryFiles ( ) ;
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
// chat config initialization
2023-05-14 14:48:06 +03:00
if ( openConfig ( " chat " , " Chat file not found. " , & file , true ) ) {
2020-06-15 11:28:42 +03:00
StringArray * chat = nullptr ;
StringArray keywords { } ;
StringArray replies { } ;
// clear all the stuff before loading new one
for ( auto & item : m_chat ) {
item . clear ( ) ;
}
m_replies . clear ( ) ;
while ( file . getLine ( line ) ) {
line . trim ( ) ;
if ( isCommentLine ( line ) ) {
continue ;
}
if ( line . startsWith ( " [KILLED] " ) ) {
chat = & m_chat [ Chat : : Kill ] ;
continue ;
}
else if ( line . startsWith ( " [BOMBPLANT] " ) ) {
2022-01-19 01:11:01 +03:00
chat = & m_chat [ Chat : : Plant ] ;
2020-06-15 11:28:42 +03:00
continue ;
}
else if ( line . startsWith ( " [DEADCHAT] " ) ) {
chat = & m_chat [ Chat : : Dead ] ;
continue ;
}
else if ( line . startsWith ( " [REPLIES] " ) ) {
chat = nullptr ;
continue ;
}
else if ( line . startsWith ( " [UNKNOWN] " ) ) {
chat = & m_chat [ Chat : : NoKeyword ] ;
continue ;
}
else if ( line . startsWith ( " [TEAMATTACK] " ) ) {
chat = & m_chat [ Chat : : TeamAttack ] ;
continue ;
}
else if ( line . startsWith ( " [WELCOME] " ) ) {
chat = & m_chat [ Chat : : Hello ] ;
continue ;
}
else if ( line . startsWith ( " [TEAMKILL] " ) ) {
chat = & m_chat [ Chat : : TeamKill ] ;
continue ;
}
if ( chat ! = nullptr ) {
chat - > push ( line ) ;
}
else {
if ( line . startsWith ( " @KEY " ) ) {
if ( ! keywords . empty ( ) & & ! replies . empty ( ) ) {
m_replies . emplace ( keywords , replies ) ;
keywords . clear ( ) ;
replies . clear ( ) ;
}
keywords . clear ( ) ;
for ( const auto & key : line . substr ( 4 ) . split ( " , " ) ) {
keywords . emplace ( utf8tools . strToUpper ( key ) ) ;
}
for ( auto & keyword : keywords ) {
keyword . trim ( ) . trim ( " \" " ) ;
}
}
else if ( ! keywords . empty ( ) & & ! line . empty ( ) ) {
replies . push ( line ) ;
}
}
}
// shuffle chat a bit
for ( auto & item : m_chat ) {
item . shuffle ( ) ;
item . shuffle ( ) ;
}
file . close ( ) ;
}
else {
cv_chat . set ( 0 ) ;
}
}
void BotConfig : : loadLanguageConfig ( ) {
setupMemoryFiles ( ) ;
2020-10-08 09:40:55 +03:00
if ( game . is ( GameFlags : : Legacy ) ) {
2023-06-13 19:55:03 +03:00
return ; // legacy versions will use only english translation
2020-06-15 11:28:42 +03:00
}
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
2023-06-13 19:55:03 +03:00
// localizer initialization
2023-05-14 14:48:06 +03:00
if ( openConfig ( " lang " , " Specified language not found. " , & file , true ) ) {
2024-05-16 21:15:41 +03:00
String temp { } ;
Twin < String , String > lang { } ;
2020-06-15 11:28:42 +03:00
2024-05-15 23:57:23 +03:00
auto trimWithoutWs = [ ] ( String in ) - > String {
return in . trim ( " \r \n " ) ;
} ;
2020-06-15 11:28:42 +03:00
2024-01-19 00:03:45 +03:00
auto pushTranslatedMsg = [ & ] ( ) {
2024-05-15 23:57:23 +03:00
m_language [ hashLangString ( trimWithoutWs ( lang . first ) . chars ( ) ) ] = trimWithoutWs ( lang . second ) ;
2024-01-19 00:03:45 +03:00
} ;
2020-06-15 11:28:42 +03:00
// clear all the translations before new load
m_language . clear ( ) ;
while ( file . getLine ( line ) ) {
if ( isCommentLine ( line ) ) {
continue ;
}
if ( line . startsWith ( " [ORIGINAL] " ) ) {
if ( ! temp . empty ( ) ) {
lang . second = cr : : move ( temp ) ;
}
if ( ! lang . second . empty ( ) & & ! lang . first . empty ( ) ) {
2024-01-19 00:03:45 +03:00
pushTranslatedMsg ( ) ;
2020-06-15 11:28:42 +03:00
}
}
else if ( line . startsWith ( " [TRANSLATED] " ) & & ! temp . empty ( ) ) {
lang . first = cr : : move ( temp ) ;
}
else {
temp + = line ;
}
2024-01-19 00:03:45 +03:00
// make sure last string is translated
if ( file . eof ( ) & & ! lang . first . empty ( ) ) {
2024-05-15 23:57:23 +03:00
lang . second = trimWithoutWs ( line ) ;
2024-01-19 00:03:45 +03:00
pushTranslatedMsg ( ) ;
}
2020-06-15 11:28:42 +03:00
}
file . close ( ) ;
}
2024-04-25 15:03:39 +03:00
else if ( cv_language . as < StringRef > ( ) ! = " en " ) {
2020-06-15 11:28:42 +03:00
logger . error ( " Couldn't load language configuration " ) ;
}
}
void BotConfig : : loadAvatarsConfig ( ) {
setupMemoryFiles ( ) ;
2023-06-13 19:55:03 +03:00
if ( game . is ( GameFlags : : Legacy ) | | game . is ( GameFlags : : Xash3D ) ) {
2020-06-15 11:28:42 +03:00
return ;
}
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
2023-06-13 19:55:03 +03:00
// avatars initialization
2023-05-14 14:48:06 +03:00
if ( openConfig ( " avatars " , " Avatars config file not found. Avatars will not be displayed. " , & file ) ) {
2020-06-15 11:28:42 +03:00
m_avatars . clear ( ) ;
while ( file . getLine ( line ) ) {
if ( isCommentLine ( line ) ) {
continue ;
}
m_avatars . push ( cr : : move ( line . trim ( ) ) ) ;
}
}
}
void BotConfig : : loadDifficultyConfig ( ) {
setupMemoryFiles ( ) ;
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
// initialize defaults
m_difficulty [ Difficulty : : Noob ] = {
2023-04-11 22:32:28 +03:00
{ 0.8f , 1.0f } , 5 , 0 , 0 , 38 , { 30.0f , 30.0f , 40.0f }
2020-06-15 11:28:42 +03:00
} ;
m_difficulty [ Difficulty : : Easy ] = {
2023-04-11 22:32:28 +03:00
{ 0.6f , 0.8f } , 30 , 10 , 10 , 32 , { 15.0f , 15.0f , 24.0f }
2020-06-15 11:28:42 +03:00
} ;
m_difficulty [ Difficulty : : Normal ] = {
2023-04-11 22:32:28 +03:00
{ 0.4f , 0.6f } , 50 , 30 , 40 , 26 , { 5.0f , 5.0f , 10.0f }
2020-06-15 11:28:42 +03:00
} ;
m_difficulty [ Difficulty : : Hard ] = {
2023-04-11 22:32:28 +03:00
{ 0.2f , 0.4f } , 75 , 60 , 70 , 23 , { 0.0f , 0.0f , 0.0f }
2020-06-15 11:28:42 +03:00
} ;
m_difficulty [ Difficulty : : Expert ] = {
2023-04-11 22:32:28 +03:00
{ 0.1f , 0.2f } , 100 , 90 , 90 , 21 , { 0.0f , 0.0f , 0.0f }
2020-06-15 11:28:42 +03:00
} ;
2023-04-11 22:32:28 +03:00
// currently, mindelay, maxdelay, headprob, seenthruprob, heardthruprob, recoil, aim_error {x,y,z}
constexpr uint32_t kMaxDifficultyValues = 9 ;
2020-06-15 11:28:42 +03:00
2024-01-26 19:52:00 +03:00
// has errors ?
int errorCount = 0 ;
2020-06-15 11:28:42 +03:00
// helper for parsing each level
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
auto parseLevel = [ & ] ( int32_t level , StringRef data ) {
2020-06-15 11:28:42 +03:00
auto values = data . split < String > ( " , " ) ;
if ( values . length ( ) ! = kMaxDifficultyValues ) {
2024-01-26 19:52:00 +03:00
+ + errorCount ;
2020-06-15 11:28:42 +03:00
return ;
}
auto diff = & m_difficulty [ level ] ;
2024-04-25 15:03:39 +03:00
diff - > reaction [ 0 ] = values [ 0 ] . as < float > ( ) ;
diff - > reaction [ 1 ] = values [ 1 ] . as < float > ( ) ;
diff - > headshotPct = values [ 2 ] . as < int > ( ) ;
diff - > seenThruPct = values [ 3 ] . as < int > ( ) ;
diff - > hearThruPct = values [ 4 ] . as < int > ( ) ;
diff - > maxRecoil = values [ 5 ] . as < int > ( ) ;
diff - > aimError . x = values [ 6 ] . as < float > ( ) ;
diff - > aimError . y = values [ 7 ] . as < float > ( ) ;
diff - > aimError . z = values [ 8 ] . as < float > ( ) ;
2020-06-15 11:28:42 +03:00
} ;
2023-08-08 11:48:37 +03:00
// difficulty initialization
2023-05-14 14:48:06 +03:00
if ( openConfig ( " difficulty " , " Difficulty config file not found. Loading defaults. " , & file ) ) {
2020-06-15 11:28:42 +03:00
while ( file . getLine ( line ) ) {
2020-07-11 20:34:26 +03:00
if ( isCommentLine ( line ) | | line . length ( ) < 3 ) {
2020-06-15 11:28:42 +03:00
continue ;
}
auto items = line . split ( " = " ) ;
if ( items . length ( ) ! = 2 ) {
logger . error ( " Error in difficulty config file syntax... Please correct all errors. " ) ;
continue ;
}
2021-09-16 16:09:51 +03:00
const auto & key = items [ 0 ] . trim ( ) ;
2020-06-15 11:28:42 +03:00
// get our keys
if ( key = = " Noob " ) {
parseLevel ( Difficulty : : Noob , items [ 1 ] ) ;
}
else if ( key = = " Easy " ) {
parseLevel ( Difficulty : : Easy , items [ 1 ] ) ;
}
else if ( key = = " Normal " ) {
parseLevel ( Difficulty : : Normal , items [ 1 ] ) ;
}
else if ( key = = " Hard " ) {
parseLevel ( Difficulty : : Hard , items [ 1 ] ) ;
}
else if ( key = = " Expert " ) {
parseLevel ( Difficulty : : Expert , items [ 1 ] ) ;
}
}
2024-01-26 19:52:00 +03:00
// if some errors occurred, notify user
if ( errorCount > 0 ) {
logger . error ( " Config file: difficulty.%s has a bad syntax. Probably out of date. " , kConfigExtension ) ;
}
2020-06-15 11:28:42 +03:00
}
}
void BotConfig : : loadMapSpecificConfig ( ) {
2023-12-20 00:06:45 +03:00
auto mapSpecificConfig = strings . joinPath ( folders . config , " maps " , strings . format ( " %s.%s " , game . getMapName ( ) , kConfigExtension ) ) ;
2020-06-15 11:28:42 +03:00
// check existence of file
2023-12-20 00:06:45 +03:00
if ( plat . fileExists ( strings . joinPath ( bstor . getRunningPath ( ) , mapSpecificConfig ) . chars ( ) ) ) {
auto mapSpecificConfigForExec = strings . joinPath ( bstor . getRunningPathVFS ( ) , mapSpecificConfig ) ;
mapSpecificConfigForExec . replace ( " \\ " , " / " ) ;
2020-06-15 11:28:42 +03:00
2023-12-20 00:06:45 +03:00
game . serverCommand ( " exec %s " , mapSpecificConfigForExec ) ;
ctrl . msg ( " Executed map-specific config: %s " , mapSpecificConfigForExec ) ;
2020-06-15 11:28:42 +03:00
}
}
2020-12-15 15:28:58 +03:00
void BotConfig : : loadCustomConfig ( ) {
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-12-15 15:28:58 +03:00
2024-01-22 08:50:03 +03:00
auto setDefaults = [ & ] ( ) {
m_custom [ " C4ModelName " ] = " c4.mdl " ;
m_custom [ " AMXParachuteCvar " ] = " sv_parachute " ;
m_custom [ " CustomCSDMSpawnPoint " ] = " view_spawn " ;
2024-05-15 22:56:35 +03:00
m_custom [ " CSDMDetectCvar " ] = " csdm_active " ;
m_custom [ " ZMDetectCvar " ] = " zp_delay " ;
m_custom [ " ZMDelayCvar " ] = " zp_delay " ;
m_custom [ " ZMInfectedTeam " ] = " T " ;
2024-05-24 14:17:37 +03:00
m_custom [ " EnableFakeBotFeatures " ] = " no " ;
2024-01-22 08:50:03 +03:00
} ;
2024-05-16 21:15:41 +03:00
2024-01-22 08:50:03 +03:00
setDefaults ( ) ;
2020-12-15 15:28:58 +03:00
2024-01-26 19:52:00 +03:00
// has errors ?
int errorCount = 0 ;
2023-06-13 19:55:03 +03:00
// custom initialization
2023-05-14 14:48:06 +03:00
if ( openConfig ( " custom " , " Custom config file not found. Loading defaults. " , & file ) ) {
2020-12-15 15:28:58 +03:00
m_custom . clear ( ) ;
2024-01-22 08:50:03 +03:00
// set defaults anyway
setDefaults ( ) ;
2020-12-15 15:28:58 +03:00
while ( file . getLine ( line ) ) {
line . trim ( ) ;
if ( isCommentLine ( line ) ) {
continue ;
}
auto values = line . split ( " = " ) ;
if ( values . length ( ) ! = 2 ) {
2024-01-26 19:52:00 +03:00
+ + errorCount ;
continue ;
2020-12-15 15:28:58 +03:00
}
auto kv = Twin < String , String > ( values [ 0 ] . trim ( ) , values [ 1 ] . trim ( ) ) ;
if ( ! kv . first . empty ( ) & & ! kv . second . empty ( ) ) {
m_custom [ kv . first ] = kv . second ;
}
}
2024-01-26 19:52:00 +03:00
// if some errors occurred, notify user
if ( errorCount > 0 ) {
logger . error ( " Config file: custom.%s has a bad syntax. Probably out of date. " , kConfigExtension ) ;
}
2020-12-15 15:28:58 +03:00
}
}
2020-06-15 11:28:42 +03:00
void BotConfig : : loadLogosConfig ( ) {
setupMemoryFiles ( ) ;
2024-05-16 21:15:41 +03:00
String line { } ;
MemFile file { } ;
2020-06-15 11:28:42 +03:00
2023-06-13 19:55:03 +03:00
// logos initialization
2023-05-14 14:48:06 +03:00
if ( openConfig ( " logos " , " Logos config file not found. Loading defaults. " , & file ) ) {
2020-06-15 11:28:42 +03:00
m_logos . clear ( ) ;
while ( file . getLine ( line ) ) {
if ( isCommentLine ( line ) ) {
continue ;
}
m_logos . push ( cr : : move ( line . trim ( ) ) ) ;
}
}
else {
m_logos = cr : : move ( String { " {biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r " } . split ( " ; " ) ) ;
}
}
void BotConfig : : setupMemoryFiles ( ) {
static bool setMemoryPointers = true ;
auto wrapLoadFile = [ ] ( const char * filename , int * length ) {
return engfuncs . pfnLoadFileForMe ( filename , length ) ;
} ;
auto wrapFreeFile = [ ] ( void * buffer ) {
engfuncs . pfnFreeFile ( buffer ) ;
} ;
if ( setMemoryPointers ) {
2023-05-12 20:00:06 +03:00
MemFileStorage : : instance ( ) . initizalize ( cr : : move ( wrapLoadFile ) , cr : : move ( wrapFreeFile ) ) ;
2020-06-15 11:28:42 +03:00
setMemoryPointers = false ;
}
}
BotName * BotConfig : : pickBotName ( ) {
if ( m_botNames . empty ( ) ) {
return nullptr ;
}
for ( size_t i = 0 ; i < m_botNames . length ( ) * 2 ; + + i ) {
2023-08-08 11:48:37 +03:00
auto bn = & m_botNames . random ( ) ;
2020-06-15 11:28:42 +03:00
2024-02-19 23:16:03 +03:00
if ( bn - > name . empty ( ) | | bn - > usedBy ! = - 1 ) {
2020-06-15 11:28:42 +03:00
continue ;
}
2023-08-08 11:48:37 +03:00
return bn ;
2020-06-15 11:28:42 +03:00
}
return nullptr ;
}
void BotConfig : : clearUsedName ( Bot * bot ) {
2023-08-08 11:48:37 +03:00
for ( auto & bn : m_botNames ) {
if ( bn . usedBy = = bot - > index ( ) ) {
bn . usedBy = - 1 ;
2020-06-15 11:28:42 +03:00
break ;
}
}
}
2023-03-13 15:39:15 +03:00
void BotConfig : : setBotNameUsed ( const int index , StringRef name ) {
2023-02-01 21:19:17 +03:00
for ( auto & bn : m_botNames ) {
if ( bn . name = = name ) {
bn . usedBy = index ;
break ;
}
}
}
2020-06-15 11:28:42 +03:00
void BotConfig : : initWeapons ( ) {
m_weapons . clear ( ) ;
// fill array with available weapons
2024-03-09 01:06:11 +03:00
m_weapons = {
{ Weapon : : Knife , " weapon_knife " , " knife.mdl " , 0 , 0 , - 1 , - 1 , 0 , 0 , 0 , 0 , 0 , 0 , WeaponType : : Melee , true } ,
{ Weapon : : USP , " weapon_usp " , " usp.mdl " , 500 , 1 , - 1 , - 1 , 1 , 1 , 2 , 2 , 0 , 12 , WeaponType : : Pistol , false } ,
{ Weapon : : Glock18 , " weapon_glock18 " , " glock18.mdl " , 400 , 1 , - 1 , - 1 , 1 , 2 , 1 , 1 , 0 , 20 , WeaponType : : Pistol , false } ,
{ Weapon : : Deagle , " weapon_deagle " , " deagle.mdl " , 650 , 1 , 2 , 2 , 1 , 3 , 4 , 4 , 2 , 7 , WeaponType : : Pistol , false } ,
{ Weapon : : P228 , " weapon_p228 " , " p228.mdl " , 600 , 1 , 2 , 2 , 1 , 4 , 3 , 3 , 0 , 13 , WeaponType : : Pistol , false } ,
{ Weapon : : Elite , " weapon_elite " , " elite.mdl " , 800 , 1 , 0 , 0 , 1 , 5 , 5 , 5 , 0 , 30 , WeaponType : : Pistol , false } ,
{ Weapon : : FiveSeven , " weapon_fiveseven " , " fiveseven.mdl " , 750 , 1 , 1 , 1 , 1 , 6 , 5 , 5 , 0 , 20 , WeaponType : : Pistol , false } ,
{ Weapon : : M3 , " weapon_m3 " , " m3.mdl " , 1700 , 1 , 2 , - 1 , 2 , 1 , 1 , 1 , 0 , 8 , WeaponType : : Shotgun , false } ,
{ Weapon : : XM1014 , " weapon_xm1014 " , " xm1014.mdl " , 3000 , 1 , 2 , - 1 , 2 , 2 , 2 , 2 , 0 , 7 , WeaponType : : Shotgun , false } ,
{ Weapon : : MP5 , " weapon_mp5navy " , " mp5.mdl " , 1500 , 1 , 2 , 1 , 3 , 1 , 2 , 2 , 0 , 30 , WeaponType : : SMG , true } ,
{ Weapon : : TMP , " weapon_tmp " , " tmp.mdl " , 1250 , 1 , 1 , 1 , 3 , 2 , 1 , 1 , 0 , 30 , WeaponType : : SMG , true } ,
{ Weapon : : P90 , " weapon_p90 " , " p90.mdl " , 2350 , 1 , 2 , 1 , 3 , 3 , 4 , 4 , 0 , 50 , WeaponType : : SMG , true } ,
{ Weapon : : MAC10 , " weapon_mac10 " , " mac10.mdl " , 1400 , 1 , 0 , 0 , 3 , 4 , 1 , 1 , 0 , 30 , WeaponType : : SMG , true } ,
{ Weapon : : UMP45 , " weapon_ump45 " , " ump45.mdl " , 1700 , 1 , 2 , 2 , 3 , 5 , 3 , 3 , 0 , 25 , WeaponType : : SMG , true } ,
{ Weapon : : AK47 , " weapon_ak47 " , " ak47.mdl " , 2500 , 1 , 0 , 0 , 4 , 1 , 2 , 2 , 2 , 30 , WeaponType : : Rifle , true } ,
{ Weapon : : SG552 , " weapon_sg552 " , " sg552.mdl " , 3500 , 1 , 0 , - 1 , 4 , 2 , 4 , 4 , 2 , 30 , WeaponType : : ZoomRifle , true } ,
{ Weapon : : M4A1 , " weapon_m4a1 " , " m4a1.mdl " , 3100 , 1 , 1 , 1 , 4 , 3 , 3 , 3 , 2 , 30 , WeaponType : : Rifle , true } ,
{ Weapon : : Galil , " weapon_galil " , " galil.mdl " , 2000 , 1 , 0 , 0 , 4 , - 1 , 1 , 1 , 2 , 35 , WeaponType : : Rifle , true } ,
{ Weapon : : Famas , " weapon_famas " , " famas.mdl " , 2250 , 1 , 1 , 1 , 4 , - 1 , 1 , 1 , 2 , 25 , WeaponType : : Rifle , true } ,
{ Weapon : : AUG , " weapon_aug " , " aug.mdl " , 3500 , 1 , 1 , 1 , 4 , 4 , 4 , 4 , 2 , 30 , WeaponType : : ZoomRifle , true } ,
{ Weapon : : Scout , " weapon_scout " , " scout.mdl " , 2750 , 1 , 2 , 0 , 4 , 5 , 3 , 2 , 3 , 10 , WeaponType : : Sniper , false } ,
{ Weapon : : AWP , " weapon_awp " , " awp.mdl " , 4750 , 1 , 2 , 0 , 4 , 6 , 5 , 6 , 3 , 10 , WeaponType : : Sniper , false } ,
{ Weapon : : G3SG1 , " weapon_g3sg1 " , " g3sg1.mdl " , 5000 , 1 , 0 , 2 , 4 , 7 , 6 , 6 , 3 , 20 , WeaponType : : Sniper , false } ,
{ Weapon : : SG550 , " weapon_sg550 " , " sg550.mdl " , 4200 , 1 , 1 , 1 , 4 , 8 , 5 , 5 , 3 , 30 , WeaponType : : Sniper , false } ,
{ Weapon : : M249 , " weapon_m249 " , " m249.mdl " , 5750 , 1 , 2 , 1 , 5 , 1 , 1 , 1 , 2 , 100 , WeaponType : : Heavy , true } ,
{ Weapon : : Shield , " weapon_shield " , " shield.mdl " , 2200 , 0 , 1 , 1 , 8 , - 1 , 8 , 8 , 0 , 0 , WeaponType : : Pistol , false } ,
// not needed actually, but cause too much refactoring for now. todo
{ 0 , " " , " " , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , WeaponType : : None , false }
} ;
2020-06-15 11:28:42 +03:00
}
void BotConfig : : adjustWeaponPrices ( ) {
// elite price is 1000$ on older versions of cs...
if ( ! ( game . is ( GameFlags : : Legacy ) ) ) {
return ;
}
for ( auto & weapon : m_weapons ) {
if ( weapon . id = = Weapon : : Elite ) {
weapon . price = 1000 ;
break ;
}
}
}
WeaponInfo & BotConfig : : findWeaponById ( const int id ) {
for ( auto & weapon : m_weapons ) {
if ( weapon . id = = id ) {
return weapon ;
}
}
return m_weapons . at ( 0 ) ;
}
const char * BotConfig : : translate ( StringRef input ) {
// this function translate input string into needed language
2020-11-03 08:57:12 +03:00
if ( ctrl . ignoreTranslate ( ) ) {
2020-06-15 11:28:42 +03:00
return input . chars ( ) ;
}
2020-06-21 14:59:33 +03:00
auto hash = hashLangString ( input . chars ( ) ) ;
2020-06-15 11:28:42 +03:00
2023-04-15 04:10:09 +03:00
if ( m_language . exists ( hash ) ) {
2020-06-21 14:59:33 +03:00
return m_language [ hash ] . chars ( ) ;
2020-06-15 11:28:42 +03:00
}
return input . chars ( ) ; // nothing found
2020-06-21 14:59:33 +03:00
}
2020-12-15 15:28:58 +03:00
void BotConfig : : showCustomValues ( ) {
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
ctrl . msg ( " Current values for custom config items: " ) ;
2020-12-15 15:28:58 +03:00
2023-03-13 15:39:15 +03:00
m_custom . foreach ( [ & ] ( const String & key , const String & val ) {
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
ctrl . msg ( " %s = %s " , key , val ) ;
2020-12-15 15:28:58 +03:00
} ) ;
}
aim: verify camp angles from nav data before using them
aim: tweaked a bit grenade handling, so bots should use them more
aim: reduce time between selecting grenade and throwing it away
aim: removed hacks in look angles code, due to removing yb_whoose_your_daddy cvar
aim: use direct enemy origin from visibility check, and not re-calculate it
aim: update enemy prediction, so it now depends on frame interval for a bot
aim: additional height offset are tweaked, and now used only for difficulty 4
nav: tweaked a bit player avoidance code, and it's not preventing bot from checking terrain
nav: do not check banned nodes, when bucket sizes re too low
nav: cover nodes are now selected depending on total bots on server
nav: let bot enter pause task after long jump
nav: extend velocity by a little for a jump, like it was in first versions of bot
nav: stuck checking is now taken in account lower minimal speed if bot is ducking
fix: navigation reachability timers, so bots will have correct current node index while camping
fix: bots are unable to finish pickup or destroy breakable task, if target is not reachable
fix: cover nodes are now calculated as they should
fix: manual calling bots add_[t/ct] now ignores yb_join_team cvar
bot: tweaked a little difficulty levels, so level 4 is now nightmare level, and 3 is very heard
bot: minor refactoring and moving functions to correct source file
bot: add yb_economics_disrespect_percent, so bots can ignore economics and buy more different guns
bot: add yb_check_darkness that allows to disable darkness checks for bot, thus disallowing usage of flashlight
bot: camp buttons are now lightly depends on bot health
chat: welcome chat message from bots is now sent during first freeze time period
crlib: switch over to stdint.h and remove crlib-own types
crlib: fixed alignment in sse code
2023-04-07 14:46:49 +03:00
uint32_t BotConfig : : hashLangString ( StringRef str ) {
2020-09-11 16:01:33 +03:00
auto test = [ ] ( const char ch ) {
return ( ch > = ' 0 ' & & ch < = ' 9 ' ) | | ( ch > = ' A ' & & ch < = ' Z ' ) | | ( ch > = ' a ' & & ch < = ' z ' ) ;
} ;
2024-05-16 21:15:41 +03:00
String res { } ;
2020-06-21 14:59:33 +03:00
2020-09-11 16:01:33 +03:00
for ( const auto & ch : str ) {
if ( ! test ( ch ) ) {
2020-06-21 14:59:33 +03:00
continue ;
}
2020-09-11 16:01:33 +03:00
res + = ch ;
2020-06-21 14:59:33 +03:00
}
2020-09-11 16:01:33 +03:00
return res . empty ( ) ? 0 : res . hash ( ) ;
2020-06-21 14:59:33 +03:00
}
2023-05-14 14:48:06 +03:00
bool BotConfig : : openConfig ( StringRef fileName , StringRef errorIfNotExists , MemFile * outFile , bool languageDependant /*= false*/ ) {
if ( * outFile ) {
outFile - > close ( ) ;
}
// save config dir
2023-12-20 00:06:45 +03:00
auto configDir = strings . joinPath ( bstor . getRunningPathVFS ( ) , folders . config ) ;
2023-05-14 14:48:06 +03:00
if ( languageDependant ) {
2024-04-25 15:03:39 +03:00
if ( fileName . startsWith ( " lang " ) & & cv_language . as < StringRef > ( ) = = " en " ) {
2023-05-14 14:48:06 +03:00
return false ;
}
2024-04-25 15:03:39 +03:00
auto langConfig = strings . joinPath ( configDir , folders . lang , strings . format ( " %s_%s.%s " , cv_language . as < StringRef > ( ) , fileName , kConfigExtension ) ) ;
2023-05-14 14:48:06 +03:00
// check is file is exists for this language
if ( ! outFile - > open ( langConfig ) ) {
outFile - > open ( strings . joinPath ( configDir , folders . lang , strings . format ( " en_%s.%s " , fileName , kConfigExtension ) ) ) ;
}
}
else {
outFile - > open ( strings . joinPath ( configDir , strings . format ( " %s.%s " , fileName , kConfigExtension ) ) ) ;
}
if ( ! * outFile ) {
logger . error ( errorIfNotExists . chars ( ) ) ;
return false ;
}
return true ;
}