mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
I've had this problem for a long time, my bots only speak English even though I'm playing on a French client. I suppose this must be the case for some other people who do not have a large number of players with the same local client. If we use French DBCs, the bots bug because they only recognize US DBCs. From what I understand, the language is chosen as follows: On load, the module reads the entire `ai_playerbot_texts` table and stores each text variant in a dictionary indexed by the locale ID: the `text` column remains the default value (English), and the `text_loc1` to `text_loc8` columns fill slots 1 through 8. Whenever a real player connects, the module increments a counter for that player's DBC locale using `AddLocalePriority(player->GetSession()->GetSessionDbcLocale())`. When a bot needs a text, `GetLocalePriority()` returns the most frequently used locale index among currently connected players. The corresponding string is then retrieved. if the box is empty, we fall back to the English version (text[0]). ### This PR improve language detection. **Summary** - log both the client DBC locale and the account database locale when a player logs in - fall back to the account locale when the client reports enUS but the account is configured for another locale - keep the existing vote-based selection so bots always speak the majority language among connected players **Therefore, the original behavior is maintained. Bots still choose the most represented language among connected players (the counter is simply more efficient by prioritizing the account's locale when it differs from the client's English). For example, if more English-speaking players are connected, the language will revert to English, as the bots always share the majority locale.**
229 lines
6.4 KiB
C++
229 lines
6.4 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
|
*/
|
|
|
|
#include "PlayerbotTextMgr.h"
|
|
|
|
#include "Playerbots.h"
|
|
#include "WorldSessionMgr.h"
|
|
|
|
void PlayerbotTextMgr::replaceAll(std::string& str, const std::string& from, const std::string& to)
|
|
{
|
|
if (from.empty())
|
|
return;
|
|
size_t start_pos = 0;
|
|
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
|
|
{
|
|
str.replace(start_pos, from.length(), to);
|
|
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
|
}
|
|
}
|
|
|
|
void PlayerbotTextMgr::LoadBotTexts()
|
|
{
|
|
LOG_INFO("playerbots", "Loading playerbots texts...");
|
|
|
|
uint32 count = 0;
|
|
if (PreparedQueryResult result =
|
|
PlayerbotsDatabase.Query(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_TEXT)))
|
|
{
|
|
do
|
|
{
|
|
std::map<uint32, std::string> text;
|
|
Field* fields = result->Fetch();
|
|
std::string name = fields[0].Get<std::string>();
|
|
text[0] = fields[1].Get<std::string>();
|
|
uint8 sayType = fields[2].Get<uint8>();
|
|
uint8 replyType = fields[3].Get<uint8>();
|
|
for (uint8 i = 1; i < MAX_LOCALES; ++i)
|
|
{
|
|
text[i] = fields[i + 3].Get<std::string>();
|
|
}
|
|
|
|
botTexts[name].push_back(BotTextEntry(name, text, sayType, replyType));
|
|
++count;
|
|
} while (result->NextRow());
|
|
}
|
|
|
|
LOG_INFO("playerbots", "{} playerbots texts loaded", count);
|
|
}
|
|
|
|
void PlayerbotTextMgr::LoadBotTextChance()
|
|
{
|
|
if (botTextChance.empty())
|
|
{
|
|
QueryResult results = PlayerbotsDatabase.Query("SELECT name, probability FROM ai_playerbot_texts_chance");
|
|
if (results)
|
|
{
|
|
do
|
|
{
|
|
Field* fields = results->Fetch();
|
|
std::string name = fields[0].Get<std::string>();
|
|
uint32 probability = fields[1].Get<uint32>();
|
|
|
|
botTextChance[name] = probability;
|
|
} while (results->NextRow());
|
|
}
|
|
}
|
|
}
|
|
|
|
// general texts
|
|
|
|
std::string PlayerbotTextMgr::GetBotText(std::string name)
|
|
{
|
|
if (botTexts.empty())
|
|
{
|
|
LOG_ERROR("playerbots", "Can't get bot text {}! No bots texts loaded!", name);
|
|
return "";
|
|
}
|
|
|
|
if (botTexts[name].empty())
|
|
{
|
|
LOG_ERROR("playerbots", "Can't get bot text {}! No bots texts for this name!", name);
|
|
return "";
|
|
}
|
|
|
|
std::vector<BotTextEntry>& list = botTexts[name];
|
|
BotTextEntry textEntry = list[urand(0, list.size() - 1)];
|
|
return !textEntry.m_text[GetLocalePriority()].empty() ? textEntry.m_text[GetLocalePriority()] : textEntry.m_text[0];
|
|
}
|
|
|
|
std::string PlayerbotTextMgr::GetBotText(std::string name, std::map<std::string, std::string> placeholders)
|
|
{
|
|
std::string botText = GetBotText(name);
|
|
if (botText.empty())
|
|
return "";
|
|
|
|
for (std::map<std::string, std::string>::iterator i = placeholders.begin(); i != placeholders.end(); ++i)
|
|
replaceAll(botText, i->first, i->second);
|
|
|
|
return botText;
|
|
}
|
|
|
|
std::string PlayerbotTextMgr::GetBotTextOrDefault(std::string name, std::string defaultText,
|
|
std::map<std::string, std::string> placeholders)
|
|
{
|
|
std::string botText = GetBotText(name, placeholders);
|
|
if (botText.empty())
|
|
{
|
|
for (std::map<std::string, std::string>::iterator i = placeholders.begin(); i != placeholders.end(); ++i)
|
|
{
|
|
replaceAll(defaultText, i->first, i->second);
|
|
}
|
|
return defaultText;
|
|
}
|
|
|
|
return botText;
|
|
}
|
|
|
|
// chat replies
|
|
|
|
std::string PlayerbotTextMgr::GetBotText(ChatReplyType replyType, std::map<std::string, std::string> placeholders)
|
|
{
|
|
if (botTexts.empty())
|
|
{
|
|
LOG_ERROR("playerbots", "Can't get bot text reply {}! No bots texts loaded!", replyType);
|
|
return "";
|
|
}
|
|
if (botTexts["reply"].empty())
|
|
{
|
|
LOG_ERROR("playerbots", "Can't get bot text reply {}! No bots texts replies!", replyType);
|
|
return "";
|
|
}
|
|
|
|
std::vector<BotTextEntry>& list = botTexts["reply"];
|
|
std::vector<BotTextEntry> proper_list;
|
|
for (auto text : list)
|
|
{
|
|
if (text.m_replyType == replyType)
|
|
proper_list.push_back(text);
|
|
}
|
|
|
|
if (proper_list.empty())
|
|
return "";
|
|
|
|
BotTextEntry textEntry = proper_list[urand(0, proper_list.size() - 1)];
|
|
std::string botText =
|
|
!textEntry.m_text[GetLocalePriority()].empty() ? textEntry.m_text[GetLocalePriority()] : textEntry.m_text[0];
|
|
for (auto& placeholder : placeholders)
|
|
replaceAll(botText, placeholder.first, placeholder.second);
|
|
|
|
return botText;
|
|
}
|
|
|
|
std::string PlayerbotTextMgr::GetBotText(ChatReplyType replyType, std::string name)
|
|
{
|
|
std::map<std::string, std::string> placeholders;
|
|
placeholders["%s"] = name;
|
|
|
|
return GetBotText(replyType, placeholders);
|
|
}
|
|
|
|
// probabilities
|
|
|
|
bool PlayerbotTextMgr::rollTextChance(std::string name)
|
|
{
|
|
if (!botTextChance[name])
|
|
return true;
|
|
|
|
return urand(0, 100) < botTextChance[name];
|
|
}
|
|
|
|
bool PlayerbotTextMgr::GetBotText(std::string name, std::string& text)
|
|
{
|
|
if (!rollTextChance(name))
|
|
return false;
|
|
|
|
text = GetBotText(name);
|
|
return !text.empty();
|
|
}
|
|
|
|
bool PlayerbotTextMgr::GetBotText(std::string name, std::string& text, std::map<std::string, std::string> placeholders)
|
|
{
|
|
if (!rollTextChance(name))
|
|
return false;
|
|
|
|
text = GetBotText(name, placeholders);
|
|
return !text.empty();
|
|
}
|
|
|
|
void PlayerbotTextMgr::AddLocalePriority(uint32 locale)
|
|
{
|
|
if (locale >= MAX_LOCALES)
|
|
{
|
|
LOG_WARN("playerbots", "Ignoring locale {} for bot texts because it exceeds MAX_LOCALES ({})", locale, MAX_LOCALES - 1);
|
|
return;
|
|
}
|
|
|
|
botTextLocalePriority[locale]++;
|
|
}
|
|
|
|
uint32 PlayerbotTextMgr::GetLocalePriority()
|
|
{
|
|
// if no real players online, reset top locale
|
|
uint32 const activeSessions = sWorldSessionMgr->GetActiveSessionCount();
|
|
if (!activeSessions)
|
|
{
|
|
ResetLocalePriority();
|
|
return 0;
|
|
}
|
|
|
|
uint32 topLocale = 0;
|
|
for (uint8 i = 0; i < MAX_LOCALES; ++i)
|
|
{
|
|
if (botTextLocalePriority[i] > botTextLocalePriority[topLocale])
|
|
topLocale = i;
|
|
}
|
|
|
|
return topLocale;
|
|
}
|
|
|
|
void PlayerbotTextMgr::ResetLocalePriority()
|
|
{
|
|
for (uint8 i = 0; i < MAX_LOCALES; ++i)
|
|
{
|
|
botTextLocalePriority[i] = 0;
|
|
}
|
|
}
|