cfg: reworked map-specific configs, so they are now located in yapb/conf/maps/*map*.cfg
cmd: fixed handling "yb" & "yapb" command prefixes.
This commit is contained in:
parent
a22cc6db93
commit
c74d8e91e4
14 changed files with 797 additions and 755 deletions
0
cfg/addons/yapb/conf/maps/.gitkeep
Normal file
0
cfg/addons/yapb/conf/maps/.gitkeep
Normal file
|
|
@ -201,9 +201,9 @@ yb_graph_fixcamp "1"
|
|||
//
|
||||
// Specifies the URL from bots will be able to download graph in case of missing local one.
|
||||
// ---
|
||||
// Default: "http://graph.yapb.ru/"
|
||||
// Default: "yapb.ru"
|
||||
//
|
||||
yb_graph_url "http://graph.yapb.ru/"
|
||||
yb_graph_url "yapb.ru"
|
||||
|
||||
//
|
||||
// Kick bots to automatically make room for human players.
|
||||
|
|
|
|||
|
|
@ -119,6 +119,9 @@ public:
|
|||
// load bots difficulty config
|
||||
void loadDifficultyConfig ();
|
||||
|
||||
// loads bots map-specific config
|
||||
void loadMapSpecificConfig ();
|
||||
|
||||
// sets memfile to use engine functions
|
||||
void setupMemoryFiles ();
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ public:
|
|||
bool isDedicated ();
|
||||
|
||||
// get stripped down mod name
|
||||
const char *getModName ();
|
||||
const char *getRunningModName ();
|
||||
|
||||
// get the valid mapname
|
||||
const char *getMapName ();
|
||||
|
|
|
|||
|
|
@ -1141,6 +1141,8 @@ extern ConVar cv_show_latency;
|
|||
extern ConVar cv_enable_query_hook;
|
||||
extern ConVar cv_whose_your_daddy;
|
||||
extern ConVar cv_chatter_path;
|
||||
extern ConVar cv_quota;
|
||||
extern ConVar cv_difficulty;
|
||||
|
||||
// execute client command helper
|
||||
template <typename ...Args> void Bot::issueCommand (const char *fmt, Args &&...args) {
|
||||
|
|
|
|||
|
|
@ -840,7 +840,7 @@ void Bot::updatePickups () {
|
|||
const float highOffset = (m_pickupType == Pickup::Hostage || m_pickupType == Pickup::PlantedC4) ? 50.0f : 20.0f;
|
||||
|
||||
// check if item is too high to reach, check if getting the item would hurt bot
|
||||
if (!game.isNullEntity (m_pickupItem) && pickupPos.z > getEyesPos ().z + highOffset || isDeadlyMove (pickupPos)) {
|
||||
if (pickupPos.z > getEyesPos ().z + highOffset || isDeadlyMove (pickupPos)) {
|
||||
m_itemIgnore = m_pickupItem;
|
||||
m_pickupItem = nullptr;
|
||||
m_pickupType = Pickup::None;
|
||||
|
|
|
|||
766
src/config.cpp
Normal file
766
src/config.cpp
Normal file
|
|
@ -0,0 +1,766 @@
|
|||
//
|
||||
// YaPB - Counter-Strike Bot based on PODBot by Markus Klinge.
|
||||
// Copyright © 2004-2020 YaPB Development Team <team@yapb.ru>.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
#include <yapb.h>
|
||||
|
||||
ConVar cv_bind_menu_key ("yb_bind_menu_key", "=", "Bind's specified key for opening bots menu.", false);
|
||||
ConVar cv_ignore_cvars_on_changelevel ("yb_ignore_cvars_on_changelevel", "yb_quota,yb_autovacate", "Specifies comma separated list of bot cvars, that will not be overriten by config on changelevel.", false);
|
||||
|
||||
BotConfig::BotConfig () {
|
||||
m_chat.resize (Chat::Count);
|
||||
m_chatter.resize (Chatter::Count);
|
||||
|
||||
m_weaponProps.resize (kMaxWeapons);
|
||||
}
|
||||
|
||||
void BotConfig::loadConfigs () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
loadNamesConfig ();
|
||||
loadChatConfig ();
|
||||
loadChatterConfig ();
|
||||
loadWeaponsConfig ();
|
||||
loadLanguageConfig ();
|
||||
loadLogosConfig ();
|
||||
loadAvatarsConfig ();
|
||||
loadDifficultyConfig ();
|
||||
}
|
||||
|
||||
void BotConfig::loadMainConfig () {
|
||||
if (game.is (GameFlags::Legacy) && !game.is (GameFlags::Xash3D)) {
|
||||
util.setNeedForWelcome (true);
|
||||
}
|
||||
setupMemoryFiles ();
|
||||
|
||||
static bool firstLoad = true;
|
||||
|
||||
auto needsToIgnoreVar = [](StringArray &list, const char *needle) {
|
||||
for (const auto &var : list) {
|
||||
if (var == needle) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// this is does the same as exec of engine, but not overwriting values of cvars spcified in cv_ignore_cvars_on_changelevel
|
||||
if (util.openConfig ("yapb.cfg", "YaPB main config file is not found.", &file, false)) {
|
||||
while (file.getLine (line)) {
|
||||
line.trim ();
|
||||
|
||||
if (isCommentLine (line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstLoad) {
|
||||
game.serverCommand (line.chars ());
|
||||
continue;
|
||||
}
|
||||
auto keyval = line.split (" ");
|
||||
|
||||
if (keyval.length () > 1) {
|
||||
auto ignore = String (cv_ignore_cvars_on_changelevel.str ()).split (",");
|
||||
|
||||
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
|
||||
if (strings.matches (cvar->name, "yb_quota") && cv_quota.int_ () <= 0) {
|
||||
engfuncs.pfnCvar_DirectSet (cvar, value);
|
||||
continue;
|
||||
}
|
||||
game.print ("Bot CVAR '%s' differs from the stored in the config (%s/%s). Ignoring.", cvar->name, cvar->string, value);
|
||||
|
||||
// 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 ();
|
||||
}
|
||||
firstLoad = false;
|
||||
|
||||
// android is abit hard to play, lower the difficulty by default
|
||||
if (plat.android && cv_difficulty.int_ () > 3) {
|
||||
cv_difficulty.set (3);
|
||||
}
|
||||
|
||||
// bind the correct menu key for bot menu...
|
||||
if (!game.isDedicated () && !strings.isEmpty (cv_bind_menu_key.str ())) {
|
||||
game.serverCommand ("bind \"%s\" \"yb menu\"", cv_bind_menu_key.str ());
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadNamesConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// naming initialization
|
||||
if (util.openConfig ("names.cfg", "Name configuration file not found.", &file, true)) {
|
||||
m_botNames.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
line.trim ();
|
||||
|
||||
if (isCommentLine (line)) {
|
||||
continue;
|
||||
}
|
||||
// max botname is 32 characters
|
||||
if (line.length () > 32) {
|
||||
line[32] = kNullChar;
|
||||
}
|
||||
m_botNames.emplace (line, -1);
|
||||
}
|
||||
file.close ();
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadWeaponsConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
auto addWeaponEntries = [](SmallArray <WeaponInfo> &weapons, bool as, StringRef name, const StringArray &data) {
|
||||
|
||||
// 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) {
|
||||
weapons[i].teamAS = data[i].int_ ();
|
||||
}
|
||||
else {
|
||||
weapons[i].teamStandard = data[i].int_ ();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto addIntEntries = [](SmallArray <int32> &to, StringRef name, const StringArray &data) {
|
||||
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) {
|
||||
to[i] = data[i].int_ ();
|
||||
}
|
||||
};
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// weapon data initialization
|
||||
if (util.openConfig ("weapon.cfg", "Weapon configuration file not found. Loading defaults", &file)) {
|
||||
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 ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// chatter initialization
|
||||
if (game.is (GameFlags::HasBotVoice) && cv_radio_mode.int_ () == 2 && util.openConfig ("chatter.cfg", "Couldn't open chatter system configuration", &file)) {
|
||||
m_chatter.clear ();
|
||||
|
||||
struct EventMap {
|
||||
String str;
|
||||
int code;
|
||||
float repeat;
|
||||
} chatterEventMap[] = {
|
||||
{ "Radio_CoverMe", Radio::CoverMe, kMaxChatterRepeatInteval },
|
||||
{ "Radio_YouTakePoint", Radio::YouTakeThePoint, kMaxChatterRepeatInteval },
|
||||
{ "Radio_HoldPosition", Radio::HoldThisPosition, 10.0f },
|
||||
{ "Radio_RegroupTeam", Radio::RegroupTeam, 10.0f },
|
||||
{ "Radio_FollowMe", Radio::FollowMe, 15.0f },
|
||||
{ "Radio_TakingFire", Radio::TakingFireNeedAssistance, 5.0f },
|
||||
{ "Radio_GoGoGo", Radio::GoGoGo, kMaxChatterRepeatInteval },
|
||||
{ "Radio_Fallback", Radio::TeamFallback, kMaxChatterRepeatInteval },
|
||||
{ "Radio_StickTogether", Radio::StickTogetherTeam, kMaxChatterRepeatInteval },
|
||||
{ "Radio_GetInPosition", Radio::GetInPositionAndWaitForGo, kMaxChatterRepeatInteval },
|
||||
{ "Radio_StormTheFront", Radio::StormTheFront, kMaxChatterRepeatInteval },
|
||||
{ "Radio_ReportTeam", Radio::ReportInTeam, kMaxChatterRepeatInteval },
|
||||
{ "Radio_Affirmative", Radio::RogerThat, kMaxChatterRepeatInteval },
|
||||
{ "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 },
|
||||
{ "Radio_ShesGonnaBlow", Radio::ShesGonnaBlow, kMaxChatterRepeatInteval },
|
||||
{ "Radio_Negative", Radio::Negative, kMaxChatterRepeatInteval },
|
||||
{ "Radio_EnemyDown", Radio::EnemyDown, 10.0f },
|
||||
{ "Chatter_DiePain", Chatter::DiePain, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_GoingToPlantBomb", Chatter::GoingToPlantBomb, 5.0f },
|
||||
{ "Chatter_GoingToGuardVIPSafety", Chatter::GoingToGuardVIPSafety, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_RescuingHostages", Chatter::RescuingHostages, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_TeamKill", Chatter::FriendlyFire, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_GuardingVipSafety", Chatter::GuardingVIPSafety, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_PlantingC4", Chatter::PlantingBomb, 10.0f },
|
||||
{ "Chatter_InCombat", Chatter::InCombat, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_SeeksEnemy", Chatter::SeekingEnemies, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_Nothing", Chatter::Nothing, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_EnemyDown", Chatter::EnemyDown, 10.0f },
|
||||
{ "Chatter_UseHostage", Chatter::UsingHostages, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_WonTheRound", Chatter::WonTheRound, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_QuicklyWonTheRound", Chatter::QuickWonRound, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_NoEnemiesLeft", Chatter::NoEnemiesLeft, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_FoundBombPlace", Chatter::FoundC4Plant, 15.0f },
|
||||
{ "Chatter_WhereIsTheBomb", Chatter::WhereIsTheC4, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_DefendingBombSite", Chatter::DefendingBombsite, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_BarelyDefused", Chatter::BarelyDefused, kMaxChatterRepeatInteval },
|
||||
{ "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 },
|
||||
{ "Chatter_GuardDroppedC4", Chatter::GuardingDroppedC4, 3.0f },
|
||||
{ "Chatter_DefusingC4", Chatter::DefusingBomb, 3.0f },
|
||||
{ "Chatter_FoundC4", Chatter::FoundC4, 5.5f },
|
||||
{ "Chatter_ScaredEmotion", Chatter::ScaredEmotion, 6.1f },
|
||||
{ "Chatter_HeardEnemy", Chatter::ScaredEmotion, 12.8f },
|
||||
{ "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 },
|
||||
{ "Chatter_GoingToGuardDoppedBomb", Chatter::GoingToGuardDroppedC4, 6.0f },
|
||||
{ "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 },
|
||||
};
|
||||
|
||||
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) {
|
||||
if (event.str == items[0]) {
|
||||
// this does common work of parsing comma-separated chatter line
|
||||
auto sounds = items[1].split (",");
|
||||
|
||||
for (auto &sound : sounds) {
|
||||
sound.trim ().trim ("\"");
|
||||
float duration = game.getWaveLen (sound.chars ());
|
||||
|
||||
if (duration > 0.0f) {
|
||||
m_chatter[event.code].emplace (cr::move (sound), event.repeat, duration);
|
||||
}
|
||||
}
|
||||
sounds.clear ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file.close ();
|
||||
}
|
||||
else {
|
||||
cv_radio_mode.set (1);
|
||||
logger.message ("Bots chatter communication disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadChatConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// chat config initialization
|
||||
if (util.openConfig ("chat.cfg", "Chat file not found.", &file, true)) {
|
||||
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]")) {
|
||||
chat = &m_chat[Chat::Kill];
|
||||
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 ();
|
||||
|
||||
if (game.isDedicated () || game.is (GameFlags::Legacy)) {
|
||||
|
||||
if (game.is (GameFlags::Legacy)) {
|
||||
logger.message ("Bots multilingual system disabled, due to your Counter-Strike version!");
|
||||
}
|
||||
return; // dedicated server will use only english translation
|
||||
}
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// localizer inititalization
|
||||
if (util.openConfig ("lang.cfg", "Specified language not found.", &file, true)) {
|
||||
String temp;
|
||||
Twin <String, String> lang;
|
||||
|
||||
// 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 ()) {
|
||||
m_language.push (lang.first.trim (), lang.second.trim ());
|
||||
}
|
||||
}
|
||||
else if (line.startsWith ("[TRANSLATED]") && !temp.empty ()) {
|
||||
lang.first = cr::move (temp);
|
||||
}
|
||||
else {
|
||||
temp += line;
|
||||
}
|
||||
}
|
||||
file.close ();
|
||||
}
|
||||
else if (strcmp (cv_language.str (), "en") != 0) {
|
||||
logger.error ("Couldn't load language configuration");
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadAvatarsConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
if (game.is (GameFlags::Legacy)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// avatars inititalization
|
||||
if (util.openConfig ("avatars.cfg", "Avatars config file not found. Avatars will not be displayed.", &file)) {
|
||||
m_avatars.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
if (isCommentLine (line)) {
|
||||
continue;
|
||||
}
|
||||
m_avatars.push (cr::move (line.trim ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadDifficultyConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// initialize defaults
|
||||
m_difficulty[Difficulty::Noob] = {
|
||||
{ 0.8f, 1.0f }, 5, 0, 0
|
||||
};
|
||||
|
||||
m_difficulty[Difficulty::Easy] = {
|
||||
{ 0.6f, 0.8f }, 30, 10, 10
|
||||
};
|
||||
|
||||
m_difficulty[Difficulty::Normal] = {
|
||||
{ 0.4f, 0.6f }, 50, 30, 40
|
||||
};
|
||||
|
||||
m_difficulty[Difficulty::Hard] = {
|
||||
{ 0.2f, 0.4f }, 75, 60, 70
|
||||
};
|
||||
|
||||
m_difficulty[Difficulty::Expert] = {
|
||||
{ 0.1f, 0.2f }, 100, 90, 90
|
||||
};
|
||||
|
||||
// currently, mindelay, maxdelay, headprob, seenthruprob, heardthruprob
|
||||
constexpr uint32 kMaxDifficultyValues = 5;
|
||||
|
||||
// helper for parsing each level
|
||||
auto parseLevel = [&] (int32 level, StringRef data) {
|
||||
auto values = data.split <String> (",");
|
||||
|
||||
if (values.length () != kMaxDifficultyValues) {
|
||||
logger.error ("Bad value for difficulty level #%d.", level);
|
||||
return;
|
||||
}
|
||||
auto diff = &m_difficulty[level];
|
||||
|
||||
diff->reaction[0] = values[0].float_ ();
|
||||
diff->reaction[1] = values[1].float_ ();
|
||||
diff->headshotPct = values[2].int_ ();
|
||||
diff->seenThruPct = values[3].int_ ();
|
||||
diff->hearThruPct = values[4].int_ ();
|
||||
};
|
||||
|
||||
// avatars inititalization
|
||||
if (util.openConfig ("difficulty.cfg", "Difficulty config file not found. Defaults loaded.", &file)) {
|
||||
|
||||
while (file.getLine (line)) {
|
||||
if (isCommentLine (line)) {
|
||||
continue;
|
||||
}
|
||||
auto items = line.split ("=");
|
||||
|
||||
if (items.length () != 2) {
|
||||
logger.error ("Error in difficulty config file syntax... Please correct all errors.");
|
||||
continue;
|
||||
}
|
||||
auto key = items[0].trim ();
|
||||
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadMapSpecificConfig () {
|
||||
auto mapSpecificConfig = strings.format ("addons/%s/conf/maps/%s.cfg", product.folder, game.getMapName ());
|
||||
|
||||
// check existence of file
|
||||
if (File::exists (strings.format ("%s/%s", game.getRunningModName (), mapSpecificConfig))) {
|
||||
game.serverCommand ("exec %s", mapSpecificConfig);
|
||||
|
||||
game.print ("Executed map-specific config: %s", mapSpecificConfig);
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadLogosConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// logos inititalization
|
||||
if (util.openConfig ("logos.cfg", "Logos config file not found. Loading defaults.", &file)) {
|
||||
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) {
|
||||
MemFileStorage::instance ().initizalize (wrapLoadFile, wrapFreeFile);
|
||||
setMemoryPointers = false;
|
||||
}
|
||||
}
|
||||
|
||||
BotName *BotConfig::pickBotName () {
|
||||
if (m_botNames.empty ()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_botNames.length () * 2; ++i) {
|
||||
auto botName = &m_botNames.random ();
|
||||
|
||||
if (botName->name.length () < 3 || botName->usedBy != -1) {
|
||||
continue;
|
||||
}
|
||||
return botName;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BotConfig::clearUsedName (Bot *bot) {
|
||||
for (auto &name : m_botNames) {
|
||||
if (name.usedBy == bot->index ()) {
|
||||
name.usedBy = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::initWeapons () {
|
||||
m_weapons.clear ();
|
||||
|
||||
// fill array with available weapons
|
||||
m_weapons.emplace (Weapon::Knife, "weapon_knife", "knife.mdl", 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, true);
|
||||
m_weapons.emplace (Weapon::USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, 12, false);
|
||||
m_weapons.emplace (Weapon::Glock18, "weapon_glock18", "glock18.mdl", 400, 1, -1, -1, 1, 2, 1, 1, 0, 20, false);
|
||||
m_weapons.emplace (Weapon::Deagle, "weapon_deagle", "deagle.mdl", 650, 1, 2, 2, 1, 3, 4, 4, 2, 7, false);
|
||||
m_weapons.emplace (Weapon::P228, "weapon_p228", "p228.mdl", 600, 1, 2, 2, 1, 4, 3, 3, 0, 13, false);
|
||||
m_weapons.emplace (Weapon::Elite, "weapon_elite", "elite.mdl", 800, 1, 0, 0, 1, 5, 5, 5, 0, 30, false);
|
||||
m_weapons.emplace (Weapon::FiveSeven, "weapon_fiveseven", "fiveseven.mdl", 750, 1, 1, 1, 1, 6, 5, 5, 0, 20, false);
|
||||
m_weapons.emplace (Weapon::M3, "weapon_m3", "m3.mdl", 1700, 1, 2, -1, 2, 1, 1, 1, 0, 8, false);
|
||||
m_weapons.emplace (Weapon::XM1014, "weapon_xm1014", "xm1014.mdl", 3000, 1, 2, -1, 2, 2, 2, 2, 0, 7, false);
|
||||
m_weapons.emplace (Weapon::MP5, "weapon_mp5navy", "mp5.mdl", 1500, 1, 2, 1, 3, 1, 2, 2, 0, 30, true);
|
||||
m_weapons.emplace (Weapon::TMP, "weapon_tmp", "tmp.mdl", 1250, 1, 1, 1, 3, 2, 1, 1, 0, 30, true);
|
||||
m_weapons.emplace (Weapon::P90, "weapon_p90", "p90.mdl", 2350, 1, 2, 1, 3, 3, 4, 4, 0, 50, true);
|
||||
m_weapons.emplace (Weapon::MAC10, "weapon_mac10", "mac10.mdl", 1400, 1, 0, 0, 3, 4, 1, 1, 0, 30, true);
|
||||
m_weapons.emplace (Weapon::UMP45, "weapon_ump45", "ump45.mdl", 1700, 1, 2, 2, 3, 5, 3, 3, 0, 25, true);
|
||||
m_weapons.emplace (Weapon::AK47, "weapon_ak47", "ak47.mdl", 2500, 1, 0, 0, 4, 1, 2, 2, 2, 30, true);
|
||||
m_weapons.emplace (Weapon::SG552, "weapon_sg552", "sg552.mdl", 3500, 1, 0, -1, 4, 2, 4, 4, 2, 30, true);
|
||||
m_weapons.emplace (Weapon::M4A1, "weapon_m4a1", "m4a1.mdl", 3100, 1, 1, 1, 4, 3, 3, 3, 2, 30, true);
|
||||
m_weapons.emplace (Weapon::Galil, "weapon_galil", "galil.mdl", 2000, 1, 0, 0, 4, -1, 1, 1, 2, 35, true);
|
||||
m_weapons.emplace (Weapon::Famas, "weapon_famas", "famas.mdl", 2250, 1, 1, 1, 4, -1, 1, 1, 2, 25, true);
|
||||
m_weapons.emplace (Weapon::AUG, "weapon_aug", "aug.mdl", 3500, 1, 1, 1, 4, 4, 4, 4, 2, 30, true);
|
||||
m_weapons.emplace (Weapon::Scout, "weapon_scout", "scout.mdl", 2750, 1, 2, 0, 4, 5, 3, 2, 3, 10, false);
|
||||
m_weapons.emplace (Weapon::AWP, "weapon_awp", "awp.mdl", 4750, 1, 2, 0, 4, 6, 5, 6, 3, 10, false);
|
||||
m_weapons.emplace (Weapon::G3SG1, "weapon_g3sg1", "g3sg1.mdl", 5000, 1, 0, 2, 4, 7, 6, 6, 3, 20, false);
|
||||
m_weapons.emplace (Weapon::SG550, "weapon_sg550", "sg550.mdl", 4200, 1, 1, 1, 4, 8, 5, 5, 3, 30, false);
|
||||
m_weapons.emplace (Weapon::M249, "weapon_m249", "m249.mdl", 5750, 1, 2, 1, 5, 1, 1, 1, 2, 100, true);
|
||||
m_weapons.emplace (Weapon::Shield, "weapon_shield", "shield.mdl", 2200, 0, 1, 1, 8, -1, 8, 8, 0, 0, false);
|
||||
|
||||
// not needed actually, but cause too much refactoring for now. todo
|
||||
m_weapons.emplace (0, "", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if (game.isDedicated ()) {
|
||||
return input.chars ();
|
||||
}
|
||||
static String result;
|
||||
result.clear ();
|
||||
|
||||
if (m_language.find (input, result)) {
|
||||
return result.chars ();
|
||||
}
|
||||
return input.chars (); // nothing found
|
||||
}
|
||||
|
|
@ -204,7 +204,7 @@ int BotControl::cmdCvars () {
|
|||
|
||||
// if save requested, dump cvars to yapb.cfg
|
||||
if (isSave) {
|
||||
cfg.open (strings.format ("%s/addons/%s/conf/%s.cfg", game.getModName (), product.folder, product.folder), "wt");
|
||||
cfg.open (strings.format ("%s/addons/%s/conf/%s.cfg", game.getRunningModName (), product.folder, product.folder), "wt");
|
||||
cfg.puts ("// Configuration file for %s\n\n", product.name);
|
||||
}
|
||||
|
||||
|
|
@ -1550,6 +1550,12 @@ bool BotControl::executeCommands () {
|
|||
if (m_args.empty ()) {
|
||||
return false;
|
||||
}
|
||||
const auto &prefix = m_args[0];
|
||||
|
||||
// no handling if not for us
|
||||
if (prefix != "yb" && prefix != "yapb") {
|
||||
return false;
|
||||
}
|
||||
Client &client = util.getClient (game.indexOfPlayer (m_ent));
|
||||
|
||||
// do not allow to execute stuff for non admins
|
||||
|
|
@ -1557,7 +1563,6 @@ bool BotControl::executeCommands () {
|
|||
msg ("Access to %s commands is restricted.", product.name);
|
||||
return true;
|
||||
}
|
||||
const auto &prefix = m_args[0];
|
||||
|
||||
auto aliasMatch = [] (String &test, const String &cmd, String &aliasName) -> bool {
|
||||
for (auto &alias : test.split ("/")) {
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ void Game::testHull (const Vector &start, const Vector &end, int ignoreFlags, in
|
|||
}
|
||||
|
||||
float Game::getWaveLen (const char *fileName) {
|
||||
auto filePath = strings.format ("%s/%s/%s.wav", getModName (), cv_chatter_path.str (), fileName);
|
||||
auto filePath = strings.format ("%s/%s/%s.wav", getRunningModName (), cv_chatter_path.str (), fileName);
|
||||
|
||||
File fp (filePath, "rb");
|
||||
|
||||
|
|
@ -316,7 +316,7 @@ bool Game::isDedicated () {
|
|||
return dedicated;
|
||||
}
|
||||
|
||||
const char *Game::getModName () {
|
||||
const char *Game::getRunningModName () {
|
||||
// this function returns mod name without path
|
||||
|
||||
static String name;
|
||||
|
|
@ -631,7 +631,7 @@ void Game::registerCvars (bool gameVars) {
|
|||
}
|
||||
|
||||
bool Game::loadCSBinary () {
|
||||
auto modname = getModName ();
|
||||
auto modname = getRunningModName ();
|
||||
|
||||
if (!modname) {
|
||||
return false;
|
||||
|
|
@ -735,7 +735,7 @@ bool Game::postload () {
|
|||
|
||||
// ensure we're have all needed directories
|
||||
for (const auto &dir : StringArray { "conf/lang", "data/train", "data/graph", "data/logs" }) {
|
||||
File::createPath (strings.format ("%s/addons/%s/%s", getModName (), product.folder, dir));
|
||||
File::createPath (strings.format ("%s/addons/%s/%s", getRunningModName (), product.folder, dir));
|
||||
}
|
||||
|
||||
// set out user agent for http stuff
|
||||
|
|
@ -815,7 +815,7 @@ bool Game::postload () {
|
|||
auto gamedll = strings.format ("%s/%s", plat.env ("XASH3D_GAMELIBDIR"), plat.hfp ? "libserver_hardfp.so" : "libserver.so");
|
||||
|
||||
if (!m_gameLib.load (gamedll)) {
|
||||
logger.fatal ("Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", gamedll, getModName ());
|
||||
logger.fatal ("Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", gamedll, getRunningModName ());
|
||||
}
|
||||
displayCSVersion ();
|
||||
}
|
||||
|
|
@ -823,7 +823,7 @@ bool Game::postload () {
|
|||
bool binaryLoaded = loadCSBinary ();
|
||||
|
||||
if (!binaryLoaded && !is (GameFlags::Metamod)) {
|
||||
logger.fatal ("Mod that you has started, not supported by this bot (gamedir: %s)", getModName ());
|
||||
logger.fatal ("Mod that you has started, not supported by this bot (gamedir: %s)", getRunningModName ());
|
||||
}
|
||||
displayCSVersion ();
|
||||
|
||||
|
|
|
|||
|
|
@ -2719,7 +2719,7 @@ const char *BotGraph::getDataDirectory (bool isMemoryFile) {
|
|||
buffer.assignf ("addons/%s/data/", product.folder);
|
||||
}
|
||||
else {
|
||||
buffer.assignf ("%s/addons/%s/data/", game.getModName (), product.folder);
|
||||
buffer.assignf ("%s/addons/%s/data/", game.getRunningModName (), product.folder);
|
||||
}
|
||||
return buffer.chars ();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,10 +309,10 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int) {
|
|||
// execute main config
|
||||
conf.loadMainConfig ();
|
||||
|
||||
if (File::exists (strings.format ("%s/maps/%s_%s.cfg", game.getModName (), game.getMapName (), product.folder))) {
|
||||
game.serverCommand ("exec maps/%s_%s.cfg", game.getMapName (), product.folder);
|
||||
game.print ("Executing Map-Specific config file");
|
||||
}
|
||||
// load map-specific config
|
||||
conf.loadMapSpecificConfig ();
|
||||
|
||||
// initialize quota management
|
||||
bots.initQuota ();
|
||||
|
||||
if (game.is (GameFlags::Metamod)) {
|
||||
|
|
|
|||
738
src/manager.cpp
738
src/manager.cpp
|
|
@ -16,7 +16,6 @@
|
|||
#include <yapb.h>
|
||||
|
||||
ConVar cv_autovacate ("yb_autovacate", "1", "Kick bots to automatically make room for human players.");
|
||||
ConVar cv_bind_menu_key ("yb_bind_menu_key", "=", "Bind's specified key for opening bots menu.", false);
|
||||
|
||||
ConVar cv_quota ("yb_quota", "0", "Specifies the number bots to be added to the game.", true, 0.0f, static_cast <float> (kGameMaxPlayers));
|
||||
ConVar cv_quota_mode ("yb_quota_mode", "normal", "Specifies the type of quota.\nAllowed values: 'normal', 'fill', and 'match'.\nIf 'fill', the server will adjust bots to keep N players in the game, where N is yb_quota.\nIf 'match', the server will maintain a 1:N ratio of humans to bots, where N is yb_quota_match.", false);
|
||||
|
|
@ -35,7 +34,6 @@ ConVar cv_show_avatars ("yb_show_avatars", "1", "Enables or disabels displaying
|
|||
ConVar cv_show_latency ("yb_show_latency", "2", "Enables latency display in scoreboard.\nAllowed values: '0', '1', '2'.\nIf '0', there is nothing displayed.\nIf '1', there is a 'BOT' is displayed.\nIf '2' fake ping is displayed.", true, 0.0f, 2.0f);
|
||||
|
||||
ConVar cv_language ("yb_language", "en", "Specifies the language for bot messages and menus.", false);
|
||||
ConVar cv_ignore_cvars_on_changelevel ("yb_ignore_cvars_on_changelevel", "yb_quota,yb_autovacate", "Specifies comma separated list of bot cvars, that will not be overriten by config on changelevel.", false);
|
||||
|
||||
ConVar mp_limitteams ("mp_limitteams", nullptr, Var::GameRef);
|
||||
ConVar mp_autoteambalance ("mp_autoteambalance", nullptr, Var::GameRef);
|
||||
|
|
@ -1696,739 +1694,3 @@ void BotManager::setBombPlanted (bool isPlanted) {
|
|||
}
|
||||
m_bombPlanted = isPlanted;
|
||||
}
|
||||
|
||||
BotConfig::BotConfig () {
|
||||
m_chat.resize (Chat::Count);
|
||||
m_chatter.resize (Chatter::Count);
|
||||
|
||||
m_weaponProps.resize (kMaxWeapons);
|
||||
}
|
||||
|
||||
void BotConfig::loadConfigs () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
loadNamesConfig ();
|
||||
loadChatConfig ();
|
||||
loadChatterConfig ();
|
||||
loadWeaponsConfig ();
|
||||
loadLanguageConfig ();
|
||||
loadLogosConfig ();
|
||||
loadAvatarsConfig ();
|
||||
loadDifficultyConfig ();
|
||||
}
|
||||
|
||||
void BotConfig::loadMainConfig () {
|
||||
if (game.is (GameFlags::Legacy) && !game.is (GameFlags::Xash3D)) {
|
||||
util.setNeedForWelcome (true);
|
||||
}
|
||||
setupMemoryFiles ();
|
||||
|
||||
static bool firstLoad = true;
|
||||
|
||||
auto needsToIgnoreVar = [] (StringArray &list, const char *needle) {
|
||||
for (const auto &var : list) {
|
||||
if (var == needle) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// this is does the same as exec of engine, but not overwriting values of cvars spcified in cv_ignore_cvars_on_changelevel
|
||||
if (util.openConfig ("yapb.cfg", "YaPB main config file is not found.", &file, false)) {
|
||||
while (file.getLine (line)) {
|
||||
line.trim ();
|
||||
|
||||
if (isCommentLine (line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstLoad) {
|
||||
game.serverCommand (line.chars ());
|
||||
continue;
|
||||
}
|
||||
auto keyval = line.split (" ");
|
||||
|
||||
if (keyval.length () > 1) {
|
||||
auto ignore = String (cv_ignore_cvars_on_changelevel.str ()).split (",");
|
||||
|
||||
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
|
||||
if (strings.matches (cvar->name, "yb_quota") && cv_quota.int_ () <= 0) {
|
||||
engfuncs.pfnCvar_DirectSet (cvar, value);
|
||||
continue;
|
||||
}
|
||||
game.print ("Bot CVAR '%s' differs from the stored in the config (%s/%s). Ignoring.", cvar->name, cvar->string, value);
|
||||
|
||||
// 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 ();
|
||||
}
|
||||
firstLoad = false;
|
||||
|
||||
// android is abit hard to play, lower the difficulty by default
|
||||
if (plat.android && cv_difficulty.int_ () > 3) {
|
||||
cv_difficulty.set (3);
|
||||
}
|
||||
|
||||
// bind the correct menu key for bot menu...
|
||||
if (!game.isDedicated () && !strings.isEmpty (cv_bind_menu_key.str ())) {
|
||||
game.serverCommand ("bind \"%s\" \"yb menu\"", cv_bind_menu_key.str ());
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadNamesConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// naming initialization
|
||||
if (util.openConfig ("names.cfg", "Name configuration file not found.", &file, true)) {
|
||||
m_botNames.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
line.trim ();
|
||||
|
||||
if (isCommentLine (line)) {
|
||||
continue;
|
||||
}
|
||||
// max botname is 32 characters
|
||||
if (line.length () > 32) {
|
||||
line[32] = kNullChar;
|
||||
}
|
||||
m_botNames.emplace (line, -1);
|
||||
}
|
||||
file.close ();
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadWeaponsConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
auto addWeaponEntries = [] (SmallArray <WeaponInfo> &weapons, bool as, StringRef name, const StringArray &data) {
|
||||
|
||||
// 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) {
|
||||
weapons[i].teamAS = data[i].int_ ();
|
||||
}
|
||||
else {
|
||||
weapons[i].teamStandard = data[i].int_ ();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto addIntEntries = [] (SmallArray <int32> &to, StringRef name, const StringArray &data) {
|
||||
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) {
|
||||
to[i] = data[i].int_ ();
|
||||
}
|
||||
};
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// weapon data initialization
|
||||
if (util.openConfig ("weapon.cfg", "Weapon configuration file not found. Loading defaults", &file)) {
|
||||
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 ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// chatter initialization
|
||||
if (game.is (GameFlags::HasBotVoice) && cv_radio_mode.int_ () == 2 && util.openConfig ("chatter.cfg", "Couldn't open chatter system configuration", &file)) {
|
||||
m_chatter.clear ();
|
||||
|
||||
struct EventMap {
|
||||
String str;
|
||||
int code;
|
||||
float repeat;
|
||||
} chatterEventMap[] = {
|
||||
{ "Radio_CoverMe", Radio::CoverMe, kMaxChatterRepeatInteval },
|
||||
{ "Radio_YouTakePoint", Radio::YouTakeThePoint, kMaxChatterRepeatInteval },
|
||||
{ "Radio_HoldPosition", Radio::HoldThisPosition, 10.0f },
|
||||
{ "Radio_RegroupTeam", Radio::RegroupTeam, 10.0f },
|
||||
{ "Radio_FollowMe", Radio::FollowMe, 15.0f },
|
||||
{ "Radio_TakingFire", Radio::TakingFireNeedAssistance, 5.0f },
|
||||
{ "Radio_GoGoGo", Radio::GoGoGo, kMaxChatterRepeatInteval },
|
||||
{ "Radio_Fallback", Radio::TeamFallback, kMaxChatterRepeatInteval },
|
||||
{ "Radio_StickTogether", Radio::StickTogetherTeam, kMaxChatterRepeatInteval },
|
||||
{ "Radio_GetInPosition", Radio::GetInPositionAndWaitForGo, kMaxChatterRepeatInteval },
|
||||
{ "Radio_StormTheFront", Radio::StormTheFront, kMaxChatterRepeatInteval },
|
||||
{ "Radio_ReportTeam", Radio::ReportInTeam, kMaxChatterRepeatInteval },
|
||||
{ "Radio_Affirmative", Radio::RogerThat, kMaxChatterRepeatInteval },
|
||||
{ "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 },
|
||||
{ "Radio_ShesGonnaBlow", Radio::ShesGonnaBlow, kMaxChatterRepeatInteval },
|
||||
{ "Radio_Negative", Radio::Negative, kMaxChatterRepeatInteval },
|
||||
{ "Radio_EnemyDown", Radio::EnemyDown, 10.0f },
|
||||
{ "Chatter_DiePain", Chatter::DiePain, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_GoingToPlantBomb", Chatter::GoingToPlantBomb, 5.0f },
|
||||
{ "Chatter_GoingToGuardVIPSafety", Chatter::GoingToGuardVIPSafety, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_RescuingHostages", Chatter::RescuingHostages, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_TeamKill", Chatter::FriendlyFire, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_GuardingVipSafety", Chatter::GuardingVIPSafety, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_PlantingC4", Chatter::PlantingBomb, 10.0f },
|
||||
{ "Chatter_InCombat", Chatter::InCombat, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_SeeksEnemy", Chatter::SeekingEnemies, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_Nothing", Chatter::Nothing, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_EnemyDown", Chatter::EnemyDown, 10.0f },
|
||||
{ "Chatter_UseHostage", Chatter::UsingHostages, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_WonTheRound", Chatter::WonTheRound, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_QuicklyWonTheRound", Chatter::QuickWonRound, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_NoEnemiesLeft", Chatter::NoEnemiesLeft, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_FoundBombPlace", Chatter::FoundC4Plant, 15.0f },
|
||||
{ "Chatter_WhereIsTheBomb", Chatter::WhereIsTheC4, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_DefendingBombSite", Chatter::DefendingBombsite, kMaxChatterRepeatInteval },
|
||||
{ "Chatter_BarelyDefused", Chatter::BarelyDefused, kMaxChatterRepeatInteval },
|
||||
{ "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 },
|
||||
{ "Chatter_GuardDroppedC4", Chatter::GuardingDroppedC4, 3.0f },
|
||||
{ "Chatter_DefusingC4", Chatter::DefusingBomb, 3.0f },
|
||||
{ "Chatter_FoundC4", Chatter::FoundC4, 5.5f },
|
||||
{ "Chatter_ScaredEmotion", Chatter::ScaredEmotion, 6.1f },
|
||||
{ "Chatter_HeardEnemy", Chatter::ScaredEmotion, 12.8f },
|
||||
{ "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 },
|
||||
{ "Chatter_GoingToGuardDoppedBomb", Chatter::GoingToGuardDroppedC4, 6.0f },
|
||||
{ "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 },
|
||||
};
|
||||
|
||||
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) {
|
||||
if (event.str == items[0]) {
|
||||
// this does common work of parsing comma-separated chatter line
|
||||
auto sounds = items[1].split (",");
|
||||
|
||||
for (auto &sound : sounds) {
|
||||
sound.trim ().trim ("\"");
|
||||
float duration = game.getWaveLen (sound.chars ());
|
||||
|
||||
if (duration > 0.0f) {
|
||||
m_chatter[event.code].emplace (cr::move (sound), event.repeat, duration);
|
||||
}
|
||||
}
|
||||
sounds.clear ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file.close ();
|
||||
}
|
||||
else {
|
||||
cv_radio_mode.set (1);
|
||||
logger.message ("Bots chatter communication disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadChatConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// chat config initialization
|
||||
if (util.openConfig ("chat.cfg", "Chat file not found.", &file, true)) {
|
||||
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]")) {
|
||||
chat = &m_chat[Chat::Kill];
|
||||
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 ();
|
||||
|
||||
if (game.isDedicated () || game.is (GameFlags::Legacy)) {
|
||||
|
||||
if (game.is (GameFlags::Legacy)) {
|
||||
logger.message ("Bots multilingual system disabled, due to your Counter-Strike version!");
|
||||
}
|
||||
return; // dedicated server will use only english translation
|
||||
}
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// localizer inititalization
|
||||
if (util.openConfig ("lang.cfg", "Specified language not found.", &file, true)) {
|
||||
String temp;
|
||||
Twin <String, String> lang;
|
||||
|
||||
// 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 ()) {
|
||||
m_language.push (lang.first.trim (), lang.second.trim ());
|
||||
}
|
||||
}
|
||||
else if (line.startsWith ("[TRANSLATED]") && !temp.empty ()) {
|
||||
lang.first = cr::move (temp);
|
||||
}
|
||||
else {
|
||||
temp += line;
|
||||
}
|
||||
}
|
||||
file.close ();
|
||||
}
|
||||
else if (strcmp (cv_language.str (), "en") != 0) {
|
||||
logger.error ("Couldn't load language configuration");
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadAvatarsConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
if (game.is (GameFlags::Legacy)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// avatars inititalization
|
||||
if (util.openConfig ("avatars.cfg", "Avatars config file not found. Avatars will not be displayed.", &file)) {
|
||||
m_avatars.clear ();
|
||||
|
||||
while (file.getLine (line)) {
|
||||
if (isCommentLine (line)) {
|
||||
continue;
|
||||
}
|
||||
m_avatars.push (cr::move (line.trim ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadDifficultyConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// initialize defaults
|
||||
m_difficulty[Difficulty::Noob] = {
|
||||
{ 0.8f, 1.0f }, 5, 0, 0
|
||||
};
|
||||
|
||||
m_difficulty[Difficulty::Easy] = {
|
||||
{ 0.6f, 0.8f }, 30, 10, 10
|
||||
};
|
||||
|
||||
m_difficulty[Difficulty::Normal] = {
|
||||
{ 0.4f, 0.6f }, 50, 30, 40
|
||||
};
|
||||
|
||||
m_difficulty[Difficulty::Hard] = {
|
||||
{ 0.2f, 0.4f }, 75, 60, 70
|
||||
};
|
||||
|
||||
m_difficulty[Difficulty::Expert] = {
|
||||
{ 0.1f, 0.2f }, 100, 90, 90
|
||||
};
|
||||
|
||||
// currently, mindelay, maxdelay, headprob, seenthruprob, heardthruprob
|
||||
constexpr uint32 kMaxDifficultyValues = 5;
|
||||
|
||||
// helper for parsing each level
|
||||
auto parseLevel = [&] (int32 level, StringRef data) {
|
||||
auto values = data.split <String> (",");
|
||||
|
||||
if (values.length () != kMaxDifficultyValues) {
|
||||
logger.error ("Bad value for difficulty level #%d.", level);
|
||||
return;
|
||||
}
|
||||
auto diff = &m_difficulty[level];
|
||||
|
||||
diff->reaction[0] = values[0].float_ ();
|
||||
diff->reaction[1] = values[1].float_ ();
|
||||
diff->headshotPct = values[2].int_ ();
|
||||
diff->seenThruPct = values[3].int_ ();
|
||||
diff->hearThruPct = values[4].int_ ();
|
||||
};
|
||||
|
||||
// avatars inititalization
|
||||
if (util.openConfig ("difficulty.cfg", "Difficulty config file not found. Defaults loaded.", &file)) {
|
||||
|
||||
while (file.getLine (line)) {
|
||||
if (isCommentLine (line)) {
|
||||
continue;
|
||||
}
|
||||
auto items = line.split ("=");
|
||||
|
||||
if (items.length () != 2) {
|
||||
logger.error ("Error in difficulty config file syntax... Please correct all errors.");
|
||||
continue;
|
||||
}
|
||||
auto key = items[0].trim ();
|
||||
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::loadLogosConfig () {
|
||||
setupMemoryFiles ();
|
||||
|
||||
String line;
|
||||
MemFile file;
|
||||
|
||||
// logos inititalization
|
||||
if (util.openConfig ("logos.cfg", "Logos config file not found. Loading defaults.", &file)) {
|
||||
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) {
|
||||
MemFileStorage::instance ().initizalize (wrapLoadFile, wrapFreeFile);
|
||||
setMemoryPointers = false;
|
||||
}
|
||||
}
|
||||
|
||||
BotName *BotConfig::pickBotName () {
|
||||
if (m_botNames.empty ()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_botNames.length () * 2; ++i) {
|
||||
auto botName = &m_botNames.random ();
|
||||
|
||||
if (botName->name.length () < 3 || botName->usedBy != -1) {
|
||||
continue;
|
||||
}
|
||||
return botName;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BotConfig::clearUsedName (Bot *bot) {
|
||||
for (auto &name : m_botNames) {
|
||||
if (name.usedBy == bot->index ()) {
|
||||
name.usedBy = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BotConfig::initWeapons () {
|
||||
m_weapons.clear ();
|
||||
|
||||
// fill array with available weapons
|
||||
m_weapons.emplace (Weapon::Knife, "weapon_knife", "knife.mdl", 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, true );
|
||||
m_weapons.emplace (Weapon::USP, "weapon_usp", "usp.mdl", 500, 1, -1, -1, 1, 1, 2, 2, 0, 12, false );
|
||||
m_weapons.emplace (Weapon::Glock18, "weapon_glock18", "glock18.mdl", 400, 1, -1, -1, 1, 2, 1, 1, 0, 20, false );
|
||||
m_weapons.emplace (Weapon::Deagle, "weapon_deagle", "deagle.mdl", 650, 1, 2, 2, 1, 3, 4, 4, 2, 7, false );
|
||||
m_weapons.emplace (Weapon::P228, "weapon_p228", "p228.mdl", 600, 1, 2, 2, 1, 4, 3, 3, 0, 13, false );
|
||||
m_weapons.emplace (Weapon::Elite, "weapon_elite", "elite.mdl", 800, 1, 0, 0, 1, 5, 5, 5, 0, 30, false );
|
||||
m_weapons.emplace (Weapon::FiveSeven, "weapon_fiveseven", "fiveseven.mdl", 750, 1, 1, 1, 1, 6, 5, 5, 0, 20, false );
|
||||
m_weapons.emplace (Weapon::M3, "weapon_m3", "m3.mdl", 1700, 1, 2, -1, 2, 1, 1, 1, 0, 8, false );
|
||||
m_weapons.emplace (Weapon::XM1014, "weapon_xm1014", "xm1014.mdl", 3000, 1, 2, -1, 2, 2, 2, 2, 0, 7, false );
|
||||
m_weapons.emplace (Weapon::MP5, "weapon_mp5navy", "mp5.mdl", 1500, 1, 2, 1, 3, 1, 2, 2, 0, 30, true );
|
||||
m_weapons.emplace (Weapon::TMP, "weapon_tmp", "tmp.mdl", 1250, 1, 1, 1, 3, 2, 1, 1, 0, 30, true );
|
||||
m_weapons.emplace (Weapon::P90, "weapon_p90", "p90.mdl", 2350, 1, 2, 1, 3, 3, 4, 4, 0, 50, true );
|
||||
m_weapons.emplace (Weapon::MAC10, "weapon_mac10", "mac10.mdl", 1400, 1, 0, 0, 3, 4, 1, 1, 0, 30, true );
|
||||
m_weapons.emplace (Weapon::UMP45, "weapon_ump45", "ump45.mdl", 1700, 1, 2, 2, 3, 5, 3, 3, 0, 25, true );
|
||||
m_weapons.emplace (Weapon::AK47, "weapon_ak47", "ak47.mdl", 2500, 1, 0, 0, 4, 1, 2, 2, 2, 30, true );
|
||||
m_weapons.emplace (Weapon::SG552, "weapon_sg552", "sg552.mdl", 3500, 1, 0, -1, 4, 2, 4, 4, 2, 30, true );
|
||||
m_weapons.emplace (Weapon::M4A1, "weapon_m4a1", "m4a1.mdl", 3100, 1, 1, 1, 4, 3, 3, 3, 2, 30, true );
|
||||
m_weapons.emplace (Weapon::Galil, "weapon_galil", "galil.mdl", 2000, 1, 0, 0, 4, -1, 1, 1, 2, 35, true );
|
||||
m_weapons.emplace (Weapon::Famas, "weapon_famas", "famas.mdl", 2250, 1, 1, 1, 4, -1, 1, 1, 2, 25, true );
|
||||
m_weapons.emplace (Weapon::AUG, "weapon_aug", "aug.mdl", 3500, 1, 1, 1, 4, 4, 4, 4, 2, 30, true );
|
||||
m_weapons.emplace (Weapon::Scout, "weapon_scout", "scout.mdl", 2750, 1, 2, 0, 4, 5, 3, 2, 3, 10, false );
|
||||
m_weapons.emplace (Weapon::AWP, "weapon_awp", "awp.mdl", 4750, 1, 2, 0, 4, 6, 5, 6, 3, 10, false );
|
||||
m_weapons.emplace (Weapon::G3SG1, "weapon_g3sg1", "g3sg1.mdl", 5000, 1, 0, 2, 4, 7, 6, 6, 3, 20, false );
|
||||
m_weapons.emplace (Weapon::SG550, "weapon_sg550", "sg550.mdl", 4200, 1, 1, 1, 4, 8, 5, 5, 3, 30, false );
|
||||
m_weapons.emplace (Weapon::M249, "weapon_m249", "m249.mdl", 5750, 1, 2, 1, 5, 1, 1, 1, 2, 100, true );
|
||||
m_weapons.emplace (Weapon::Shield, "weapon_shield", "shield.mdl", 2200, 0, 1, 1, 8, -1, 8, 8, 0, 0, false );
|
||||
|
||||
// not needed actually, but cause too much refactoring for now. todo
|
||||
m_weapons.emplace (0, "", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false );
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if (game.isDedicated ()) {
|
||||
return input.chars ();
|
||||
}
|
||||
static String result;
|
||||
result.clear ();
|
||||
|
||||
if (m_language.find (input, result)) {
|
||||
return result.chars ();
|
||||
}
|
||||
return input.chars (); // nothing found
|
||||
}
|
||||
|
|
@ -57,6 +57,7 @@
|
|||
<ClCompile Include="..\src\botlib.cpp" />
|
||||
<ClCompile Include="..\src\chatlib.cpp" />
|
||||
<ClCompile Include="..\src\combat.cpp" />
|
||||
<ClCompile Include="..\src\config.cpp" />
|
||||
<ClCompile Include="..\src\control.cpp" />
|
||||
<ClCompile Include="..\src\engine.cpp" />
|
||||
<ClCompile Include="..\src\graph.cpp" />
|
||||
|
|
|
|||
|
|
@ -179,6 +179,9 @@
|
|||
<ClCompile Include="..\src\support.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\config.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="yapb.rc">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue