Merge pull request #386 from atidot3/locale_fix

Playerbot functionnality to use (channels / emote / say / yell / guild) chat
This commit is contained in:
Yunfan Li
2024-07-28 18:36:53 +08:00
committed by GitHub
22 changed files with 399 additions and 202 deletions

View File

@@ -1238,6 +1238,19 @@ AiPlayerbot.CommandPrefix = ""
# Separator for bot chat commands
AiPlayerbot.CommandSeparator = "\\\\"
# Enable playerbot talk (say / yell / general chatting / lfg)
AiPlayerbot.RandomBotTalk = 0
# Enable playerbot emote
AiPlayerbot.RandomBotEmote = 0
# Enable dungeon suggestions for random bots
AiPlayerbot.RandomBotSuggestDungeons = 1
# Bots greet to the players
AiPlayerbot.EnableGreet = 0
#
#
#
@@ -1276,9 +1289,6 @@ AiPlayerbot.RandomBotLoginAtStartup = 1
# Guild Task system
AiPlayerbot.EnableGuildTasks = 0
# Enable dungeon suggestions for random bots
AiPlayerbot.RandomBotSuggestDungeons = 1
# Enable dungeon suggestions in lower case randomly
AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0
@@ -1297,9 +1307,6 @@ AiPlayerbot.RandombotsWalkingRPG = 0
# Set randombots movement speed to walking only inside buildings
AiPlayerbot.RandombotsWalkingRPG.InDoors = 0
# Bots greet to the players
AiPlayerbot.EnableGreet = 0
# Specify percent of active bots
# The default is 10. With 10% of all bots going active or inactive each minute.
AiPlayerbot.BotActiveAlone = 100

View File

@@ -264,7 +264,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (!player->InBattleground())
{
engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", nullptr);
engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", "emote", nullptr);
}
if (sPlayerbotAIConfig->autoSaveMana)
{
@@ -548,7 +548,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
if (!player->InBattleground())
{
nonCombatEngine->addStrategies("nc", "food", "chat", "follow",
"default", "quest", "loot", "gather", "duel", "buff", "mount", nullptr);
"default", "quest", "loot", "gather", "duel", "buff", "mount", "emote", nullptr);
}
if (sPlayerbotAIConfig->autoSaveMana) {
nonCombatEngine->addStrategy("auto save mana");
@@ -636,7 +636,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
// Battleground switch
if (player->InBattleground() && player->GetBattleground())
{
nonCombatEngine->addStrategies("nc", "chat", "default", "buff", "food", "mount", "pvp", "dps assist", "attack tagged", nullptr);
nonCombatEngine->addStrategies("nc", "chat", "default", "buff", "food", "mount", "pvp", "dps assist", "attack tagged", "emote", nullptr);
nonCombatEngine->removeStrategy("custom::say");
nonCombatEngine->removeStrategy("travel");
nonCombatEngine->removeStrategy("rpg");

View File

@@ -288,7 +288,7 @@ std::string const ChatHelper::FormatQuest(Quest const* quest)
std::string const ChatHelper::FormatGameobject(GameObject* go)
{
std::ostringstream out;
out << "|cFFFFFF00|Hfound:" << go->GetGUID().GetRawValue() << ":" << go->GetEntry() << ":" << "|h[" << go->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()) << "]|h|r";
out << "|cFFFFFF00|Hfound:" << go->GetGUID().GetRawValue() << ":" << go->GetEntry() << ":" << "|h[" << go->GetNameForLocaleIdx(LOCALE_enUS) << "]|h|r";
return out.str();
}
@@ -296,7 +296,7 @@ std::string const ChatHelper::FormatWorldobject(WorldObject* wo)
{
std::ostringstream out;
out << "|cFFFFFF00|Hfound:" << wo->GetGUID().GetRawValue() << ":" << wo->GetEntry() << ":" << "|h[";
out << (wo->ToGameObject() ? ((GameObject*)wo)->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()) : wo->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale())) << "]|h|r";
out << (wo->ToGameObject() ? ((GameObject*)wo)->GetNameForLocaleIdx(LOCALE_enUS) : wo->GetNameForLocaleIdx(LOCALE_enUS)) << "]|h|r";
return out.str();
}
@@ -327,7 +327,7 @@ std::string const ChatHelper::FormatWorldEntry(int32 entry)
std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
{
std::ostringstream out;
out << "|cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()] << "]|h|r";
out << "|cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[LOCALE_enUS] << "]|h|r";
return out.str();
}
@@ -336,7 +336,7 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count
char color[32];
sprintf(color, "%x", ItemQualityColors[proto->Quality]);
// const std::string &name = sObjectMgr->GetItemLocale(proto->ItemId)->Name[sWorld->GetDefaultDbcLocale()];
// const std::string &name = sObjectMgr->GetItemLocale(proto->ItemId)->Name[LOCALE_enUS];
std::ostringstream out;
out << "|c" << color << "|Hitem:" << proto->ItemId

View File

@@ -140,6 +140,8 @@ bool PlayerbotAIConfig::Initialize()
minRandomBotsPriceChangeInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBotsPriceChangeInterval", 2 * HOUR);
maxRandomBotsPriceChangeInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotsPriceChangeInterval", 48 * HOUR);
randomBotJoinLfg = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinLfg", true);
randomBotTalk = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotTalk", false);
randomBotEmote = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotEmote", false);
randomBotSuggestDungeons = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotSuggestDungeons", true);
suggestDungeonsInLowerCaseRandomly = sConfigMgr->GetOption<bool>("AiPlayerbot.SuggestDungeonsInLowerCaseRandomly", false);
randomBotJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinBG", true);

View File

@@ -95,6 +95,8 @@ class PlayerbotAIConfig
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
bool randomBotJoinLfg;
bool randomBotTalk;
bool randomBotEmote;
bool randomBotSuggestDungeons;
bool suggestDungeonsInLowerCaseRandomly;
bool randomBotJoinBG;

View File

@@ -85,7 +85,8 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
uint32 botAccountId = holder.GetAccountId();
WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), LOCALE_enUS, 0, false, false, 0, true);
// At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this allows channels to work as intended)
WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), sWorld->GetDefaultDbcLocale(), 0, false, false, 0, true);
botSession->HandlePlayerLoginFromDB(holder); // will delete lqh
@@ -544,13 +545,13 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
Channel* new_channel = nullptr;
if (isLfg)
{
std::string lfgChannelName = channel->pattern[0];
std::string lfgChannelName = channel->pattern[sWorld->GetDefaultDbcLocale()];
new_channel = cMgr->GetJoinChannel("LookingForGroup", channel->ChannelID);
}
else
{
char new_channel_name_buf[100];
snprintf(new_channel_name_buf, 100, channel->pattern[0], current_zone_name.c_str());
snprintf(new_channel_name_buf, 100, channel->pattern[sWorld->GetDefaultDbcLocale()], current_zone_name.c_str());
new_channel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID);
}
if (new_channel && new_channel->GetName().length() > 0)

View File

@@ -28,12 +28,13 @@ void PlayerbotTextMgr::LoadBotTexts()
Field* fields = result->Fetch();
std::string name = fields[0].Get<std::string>();
text[0] = fields[1].Get<std::string>();
uint32 sayType = fields[2].Get<uint32>();
uint32 replyType = fields[3].Get<uint32>();
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;
}
@@ -191,6 +192,7 @@ uint32 PlayerbotTextMgr::GetLocalePriority()
if (botTextLocalePriority[i] > topLocale)
topLocale = i;
}
return topLocale;
}

View File

@@ -1228,8 +1228,8 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
if (bot->GetLevel() <= 18 && (loc.GetMapId() != pInfo->mapId || dis > 10000.0f)) {
continue;
}
LocaleConstant locale = sWorld->GetDefaultDbcLocale();
const LocaleConstant& locale = sWorld->GetDefaultDbcLocale();
LOG_INFO("playerbots", "Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) {},{},{} ({}/{} locations)",
bot->GetName().c_str(), bot->GetLevel(),
map->GetId(), map->GetMapName(),

View File

@@ -172,6 +172,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal)
if (!action)
{
//LOG_ERROR("playerbots", "Action: {} - is UNKNOWN - c:{} l:{}", actionNode->getName().c_str(), botAI->GetBot()->getClass(), botAI->GetBot()->GetLevel());
LogAction("A:%s - UNKNOWN", actionNode->getName().c_str());
}
else if (action->isUseful())

View File

@@ -123,6 +123,7 @@ class ActionContext : public NamedObjectContext<Action>
creators["talk"] = &ActionContext::talk;
creators["suggest what to do"] = &ActionContext::suggest_what_to_do;
creators["suggest trade"] = &ActionContext::suggest_trade;
creators["suggest dungeon"] = &ActionContext::suggest_dungeon;
creators["return"] = &ActionContext::_return;
creators["move to loot"] = &ActionContext::move_to_loot;
creators["open loot"] = &ActionContext::open_loot;
@@ -280,6 +281,7 @@ class ActionContext : public NamedObjectContext<Action>
static Action* talk(PlayerbotAI* botAI) { return new TalkAction(botAI); }
static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); }
static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); }
static Action* suggest_dungeon(PlayerbotAI* botAI) { return new SuggestDungeonAction(botAI); }
static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); }
static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); }
static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); }

View File

@@ -6,6 +6,7 @@
#include "Event.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "GuildMgr.h"
bool AutoLearnSpellAction::Execute(Event event)
{
@@ -27,7 +28,6 @@ bool AutoLearnSpellAction::Execute(Event event)
return true;
}
void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
{
if (sPlayerbotAIConfig->autoLearnTrainerSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
@@ -35,6 +35,21 @@ void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
if (sPlayerbotAIConfig->autoLearnQuestSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
LearnQuestSpells(out);
if (sPlayerbotAIConfig->randomBotTalk)
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
{
std::map<std::string, std::string> placeholders;
placeholders["%level"] = std::to_string(bot->GetLevel());
if (urand(0, 3))
guild->BroadcastToGuild(bot->GetSession(), false, BOT_TEXT2("Ding!", placeholders), LANG_UNIVERSAL);
else
guild->BroadcastToGuild(bot->GetSession(), false, BOT_TEXT2("Yay level %level!", placeholders), LANG_UNIVERSAL);
}
}
}
void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out)

View File

@@ -671,9 +671,9 @@ bool EmoteAction::Execute(Event event)
if (pSource && (pSource->GetGUID() != bot->GetGUID()) && ((urand(0, 1) && bot->HasInArc(static_cast<float>(M_PI), pSource, 10.0f)) ||
(namlen > 1 && strstri(bot->GetName().c_str(), nam.c_str()))))
{
LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_TEXT_EMOTE {} from player {} <{}>",
/*LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_TEXT_EMOTE {} from player {} <{}>",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
bot->GetName(), text_emote, pSource->GetGUID().ToString().c_str(), pSource->GetName());
bot->GetName(), text_emote, pSource->GetGUID().ToString().c_str(), pSource->GetName());*/
emote = text_emote;
}
@@ -693,9 +693,9 @@ bool EmoteAction::Execute(Event event)
if ((pSource->GetGUID() != bot->GetGUID()) && (pSource->GetTarget() == bot->GetGUID() ||
(urand(0, 1) && bot->HasInArc(static_cast<float>(M_PI), pSource, 10.0f))))
{
LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_EMOTE {} from player {} <{}>",
/*LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_EMOTE {} from player {} <{}>",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName(),
emoteId, pSource->GetGUID().ToString().c_str(), pSource->GetName());
emoteId, pSource->GetGUID().ToString().c_str(), pSource->GetName());*/
std::vector<uint32> types;
for (int32 i = sEmotesTextStore.GetNumRows(); i >= 0; --i)

View File

@@ -105,7 +105,7 @@ bool LfgJoinAction::JoinLFG()
const auto& botLevel = bot->GetLevel();
/*LFG_TYPE_RANDOM on classic is 15-58 so bot over level 25 will never queue*/
/*LFG_TYPE_RANDOM on classic is 15-58 so bot over level 25 will never queue*/
if (dungeon->MinLevel && (botLevel < dungeon->MinLevel || botLevel > dungeon->MaxLevel)
||
(botLevel > dungeon->MinLevel + 10 && dungeon->TypeID == LFG_TYPE_DUNGEON)

View File

@@ -12,6 +12,7 @@
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "GuildMgr.h"
bool LootAction::Execute(Event event)
{
@@ -416,6 +417,22 @@ bool StoreLootAction::Execute(Event event)
if (proto->Quality >= ITEM_QUALITY_RARE && !urand(0, 1) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT))
botAI->PlayEmote(TEXT_EMOTE_CHEER);
if (sPlayerbotAIConfig->randomBotTalk && bot->GetGuildId() && urand(0, 10) && proto->Quality >= ITEM_QUALITY_RARE)
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
{
std::map<std::string, std::string> placeholders;
placeholders["%name"] = chat->FormatItem(proto);
if (urand(0, 3))
guild->BroadcastToGuild(bot->GetSession(), false, BOT_TEXT2("Yay I looted %name!", placeholders), LANG_UNIVERSAL);
else
guild->BroadcastToGuild(bot->GetSession(), false, BOT_TEXT2("Guess who got a %name? Me!", placeholders), LANG_UNIVERSAL);
}
}
// std::ostringstream out;
// out << "Looting " << chat->FormatItem(proto);
// botAI->TellMasterNoFacing(out.str());

View File

@@ -1570,7 +1570,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj()
return false;
}
std::ostringstream name;
name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (aura)";
name << spellInfo->SpellName[LOCALE_enUS]; // << "] (aura)";
if (FleePosition(dynOwner->GetPosition(), radius)) {
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
lastTellTimer = time(NULL);
@@ -1628,7 +1628,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage()
continue;
}
std::ostringstream name;
name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (object)";
name << spellInfo->SpellName[LOCALE_enUS]; // << "] (object)";
if (FleePosition(go->GetPosition(), radius)) {
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
lastTellTimer = time(NULL);
@@ -1677,7 +1677,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
break;
}
std::ostringstream name;
name << triggerSpellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; //<< "] (unit)";
name << triggerSpellInfo->SpellName[LOCALE_enUS]; //<< "] (unit)";
if (FleePosition(unit->GetPosition(), radius)) {
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
lastTellTimer = time(NULL);

View File

@@ -10,6 +10,16 @@
#include "GuildMgr.h"
#include <regex>
static const std::unordered_set<std::string> noReplyMsgs = {
"join", "leave", "follow", "attack", "pull", "flee", "reset", "reset ai",
"all ?", "talents", "talents list", "talents auto", "talk", "stay", "stats",
"who", "items", "leave", "join", "repair", "summon", "nc ?", "co ?", "de ?",
"dead ?", "follow", "los", "guard", "do accept invitation", "stats", "react ?",
"reset strats", "home",
};
static const std::unordered_set<std::string> noReplyMsgParts = { "+", "-","@" , "follow target", "focus heal", "cast ", "accept [", "e [", "destroy [", "go zone" };
static const std::unordered_set<std::string> noReplyMsgStarts = { "e ", "accept ", "cast ", "destroy " };
SayAction::SayAction(PlayerbotAI* botAI) : Action(botAI, "say"), Qualified()
{
}
@@ -105,6 +115,9 @@ bool SayAction::isUseful()
if (!botAI->AllowActivity())
return false;
if (botAI->HasStrategy("silent", BotState::BOT_STATE_NON_COMBAT))
return false;
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
return (time(nullptr) - lastSaid) > 30;
}
@@ -114,6 +127,35 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
ChatReplyType replyType = REPLY_NOT_UNDERSTAND; // default not understand
std::string respondsText = "";
// if we're just commanding bots around, don't respond...
// first one is for exact word matches
if (noReplyMsgs.find(msg) != noReplyMsgs.end()) {
/*std::ostringstream out;
out << "DEBUG ChatReplyDo decided to ignore exact blocklist match" << msg;
bot->Say(out.str(), LANG_UNIVERSAL);*/
return;
}
// second one is for partial matches like + or - where we change strats
if (std::any_of(noReplyMsgParts.begin(), noReplyMsgParts.end(), [&msg](const std::string& part) { return msg.find(part) != std::string::npos; })) {
/*std::ostringstream out;
out << "DEBUG ChatReplyDo decided to ignore partial blocklist match" << msg;
bot->Say(out.str(), LANG_UNIVERSAL);*/
return;
}
if (std::any_of(noReplyMsgStarts.begin(), noReplyMsgStarts.end(), [&msg](const std::string& start) {
return msg.find(start) == 0; // Check if the start matches the beginning of msg
})) {
/*std::ostringstream out;
out << "DEBUG ChatReplyDo decided to ignore start blocklist match" << msg;
bot->Say(out.str(), LANG_UNIVERSAL);*/
return;
}
ObjectGuid receiver = sCharacterCache->GetCharacterGuidByName(name);
Player* plr = ObjectAccessor::FindPlayer(receiver);
// Chat Logic
int32 verb_pos = -1;
int32 verb_type = -1;
@@ -149,18 +191,16 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
// Responds
for (uint32 i = 0; i < 8; i++)
{
// // blame gm with chat tag
// if (Player* plr = sObjectMgr->GetPlayer(ObjectGuid(HIGHGUID_PLAYER, guid1)))
// {
// if (plr->isGMChat())
// {
// replyType = REPLY_ADMIN_ABUSE;
// found = true;
// break;
// }
// }
//
if (word[i] == "hi" || word[i] == "hey" || word[i] == "hello" || word[i] == "wazzup")
// blame gm with chat tag
if (plr && plr->isGMChat())
{
replyType = REPLY_ADMIN_ABUSE;
found = true;
break;
}
if (word[i] == "hi" || word[i] == "hey" || word[i] == "hello" || word[i] == "wazzup"
|| word[i] == "salut" || word[i] == "plop" || word[i] == "yo")
{
replyType = REPLY_HELLO;
found = true;
@@ -169,22 +209,25 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
if (verb_type < 4)
{
if (word[i] == "am" || word[i] == "are" || word[i] == "is")
if (word[i] == "am" || word[i] == "are" || word[i] == "is" || word[i] == "suis" || word[i] == "as" || word[i] == "est"
|| word[i] == "dois" || word[i] == "doit")
{
verb_pos = i;
verb_type = 2; // present
if (verb_pos == 0)
is_quest = 1;
}
else if (word[i] == "will")
else if (word[i] == "will" || word[i] == "vais" || word[i] == "sera")
{
verb_pos = i;
verb_type = 3; // future
}
else if (word[i] == "was" || word[i] == "were")
else if (word[i] == "was" || word[i] == "were" || word[i] == "été" || word[i] == "ai" || word[i] == "eu" || word[i] == "étions" || word[i] == "etion" )
{
verb_pos = i;
verb_type = 1; // past
}
else if (word[i] == "shut" || word[i] == "noob")
else if (word[i] == "shut" || word[i] == "noob" || word[i] == "tg")
{
if (msg.find(bot->GetName()) == std::string::npos)
{
@@ -600,22 +643,20 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
{
if (type == CHAT_MSG_WHISPER)
{
ObjectGuid receiver = sCharacterCache->GetCharacterGuidByName(name);
if (!receiver || !receiver.IsPlayer() || !ObjectAccessor::FindPlayer(receiver))
if (plr)
{
return;
}
if (bot->GetTeamId() == TEAM_ALLIANCE)
{
bot->Whisper(c, LANG_COMMON, ObjectAccessor::FindPlayer(receiver));
}
else
{
bot->Whisper(c, LANG_ORCISH, ObjectAccessor::FindPlayer(receiver));
if (bot->GetTeamId() == TEAM_ALLIANCE)
{
bot->Whisper(c, LANG_COMMON, plr);
}
else
{
bot->Whisper(c, LANG_ORCISH, plr);
}
}
}
if (type == CHAT_MSG_SAY)
else if (type == CHAT_MSG_SAY)
{
if (bot->GetTeamId() == TEAM_ALLIANCE)
bot->Say(respondsText, LANG_COMMON);
@@ -623,7 +664,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
bot->Say(respondsText, LANG_ORCISH);
}
if (type == CHAT_MSG_YELL)
else if (type == CHAT_MSG_YELL)
{
if (bot->GetTeamId() == TEAM_ALLIANCE)
bot->Yell(respondsText, LANG_COMMON);
@@ -631,7 +672,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
bot->Yell(respondsText, LANG_ORCISH);
}
if (type == CHAT_MSG_GUILD)
else if (type == CHAT_MSG_GUILD)
{
if (!bot->GetGuildId())
return;
@@ -645,4 +686,4 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
}
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(nullptr) + urand(5, 25));
}
}
}

View File

@@ -3,6 +3,7 @@
*/
#include "SuggestWhatToDoAction.h"
#include "ServerFacade.h"
#include "ChannelMgr.h"
#include "Event.h"
#include "ItemVisitors.h"
@@ -11,24 +12,47 @@
#include "Playerbots.h"
#include "PlayerbotTextMgr.h"
#include "GuildMgr.h"
#include "Config.h"
std::map<std::string, uint8> SuggestWhatToDoAction::instances;
#include <functional>
enum eTalkType
{
General = ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
Trade = ChannelFlags::CHANNEL_FLAG_CITY | ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG | ChannelFlags::CHANNEL_FLAG_TRADE,
LocalDefence = ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
GuildRecruitment = ChannelFlags::CHANNEL_FLAG_CITY | ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
LookingForGroup = ChannelFlags::CHANNEL_FLAG_LFG | ChannelFlags::CHANNEL_FLAG_GENERAL
};
std::map<std::string, uint8> SuggestDungeonAction::instances;
std::map<std::string, uint8> SuggestWhatToDoAction::factions;
SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string const name) : InventoryAction(botAI, name)
SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string const name)
: InventoryAction{ botAI, name }
, _dbc_locale{ sWorld->GetDefaultDbcLocale() }
{
suggestions.push_back(&SuggestWhatToDoAction::specificQuest);
suggestions.push_back(&SuggestWhatToDoAction::grindReputation);
suggestions.push_back(&SuggestWhatToDoAction::something);
suggestions.push_back(std::bind(&SuggestWhatToDoAction::specificQuest, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindReputation, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::something, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindMaterials, this));
}
bool SuggestWhatToDoAction::Execute(Event event)
bool SuggestWhatToDoAction::isUseful()
{
if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId())
return false;
std::string qualifier = "suggest what to do";
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
return (time(0) - lastSaid) > 30;
}
bool SuggestWhatToDoAction::Execute(Event event)
{
uint32 index = rand() % suggestions.size();
(this->*suggestions[index])();
auto fnct_ptr = suggestions[index];
fnct_ptr();
std::string const qualifier = "suggest what to do";
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
@@ -37,73 +61,6 @@ bool SuggestWhatToDoAction::Execute(Event event)
return true;
}
void SuggestWhatToDoAction::instance()
{
if (instances.empty())
{
instances["Ragefire Chasm"] = 15;
instances["Deadmines"] = 18;
instances["Wailing Caverns"] = 18;
instances["Shadowfang Keep"] = 25;
instances["Blackfathom Deeps"] = 20;
instances["Stockade"] = 20;
instances["Gnomeregan"] = 35;
instances["Razorfen Kraul"] = 35;
instances["Maraudon"] = 50;
instances["Scarlet Monestery"] = 40;
instances["Uldaman"] = 45;
instances["Dire Maul"] = 58;
instances["Scholomance"] = 59;
instances["Razorfen Downs"] = 40;
instances["Strathholme"] = 59;
instances["Zul'Farrak"] = 45;
instances["Blackrock Depths"] = 55;
instances["Temple of Atal'Hakkar"] = 55;
instances["Lower Blackrock Spire"] = 57;
instances["Hellfire Citidel"] = 65;
instances["Coilfang Reservoir"] = 65;
instances["Auchindoun"] = 65;
instances["Cavens of Time"] = 68;
instances["Tempest Keep"] = 69;
instances["Magister's Terrace"] = 70;
instances["Utgarde Keep"] = 75;
instances["The Nexus"] = 75;
instances["Ahn'kahet: The Old Kingdom"] = 75;
instances["Azjol-Nerub"] = 75;
instances["Drak'Tharon Keep"] = 75;
instances["Violet Hold"] = 80;
instances["Gundrak"] = 77;
instances["Halls of Stone"] = 77;
instances["Halls of Lightning"] = 77;
instances["Oculus"] = 77;
instances["Utgarde Pinnacle"] = 77;
instances["Trial of the Champion"] = 80;
instances["Forge of Souls"] = 80;
instances["Pit of Saron"] = 80;
instances["Halls of Reflection"] = 80;
}
std::vector<std::string> allowedInstances;
for (auto & instance : instances)
{
if (bot->GetLevel() >= instance.second) allowedInstances.push_back(instance.first);
}
if (allowedInstances.empty()) return;
std::map<std::string, std::string> placeholders;
placeholders["%role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
std::ostringstream itemout;
//itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r";
itemout << allowedInstances[urand(0, allowedInstances.size() - 1)];
placeholders["%instance"] = itemout.str();
spam(BOT_TEXT2("suggest_instance", placeholders), urand(0, 1) ? 0x50 : 0, urand(0, 2), urand(0, 2));
}
std::vector<uint32> SuggestWhatToDoAction::GetIncompletedQuests()
{
std::vector<uint32> result;
@@ -136,7 +93,52 @@ void SuggestWhatToDoAction::specificQuest()
placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%quest"] = chat->FormatQuest(quest);
spam(BOT_TEXT2("suggest_quest", placeholders), urand(0, 1) ? 0x18 : 0, urand(0, 2), urand(0, 2));
spam(BOT_TEXT2("suggest_quest", placeholders), urand(0, 1) ? eTalkType::General : 0, urand(0, 2), urand(0, 2));
}
void SuggestWhatToDoAction::grindMaterials()
{
/*if (bot->GetLevel() <= 5)
return;
auto result = CharacterDatabase.Query("SELECT distinct category, multiplier FROM ahbot_category where category not in ('other', 'quest', 'trade', 'reagent') and multiplier > 3 order by multiplier desc limit 10");
if (!result)
return;
std::map<std::string, double> categories;
do
{
Field* fields = result->Fetch();
categories[fields[0].Get<std::string>()] = fields[1].Get<float>();
} while (result->NextRow());
for (std::map<std::string, double>::iterator i = categories.begin(); i != categories.end(); ++i)
{
if (urand(0, 10) < 3) {
std::string name = i->first;
double multiplier = i->second;
for (int j = 0; j < ahbot::CategoryList::instance.size(); j++)
{
ahbot::Category* category = ahbot::CategoryList::instance[j];
if (name == category->GetName())
{
std::string item = category->GetLabel();
transform(item.begin(), item.end(), item.begin(), ::tolower);
std::ostringstream itemout;
itemout << "|c0000b000" << item << "|r";
item = itemout.str();
std::map<std::string, std::string> placeholders;
placeholders["%role"] = chat->formatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%category"] = item;
spam(BOT_TEXT2("suggest_trade", placeholders), urand(0, 1) ? 0x3C : 0x18, !urand(0, 2), !urand(0, 3));
return;
}
}
}
}*/
}
void SuggestWhatToDoAction::grindReputation()
@@ -185,12 +187,11 @@ void SuggestWhatToDoAction::grindReputation()
levels.push_back("exalted");
std::vector<std::string> allowedFactions;
for (std::map<std::string, uint8>::iterator i = factions.begin(); i != factions.end(); ++i)
for (const auto& i : factions)
{
if (bot->GetLevel() >= i->second)
allowedFactions.push_back(i->first);
if (bot->GetLevel() >= i.second)
allowedFactions.push_back(i.first);
}
if (allowedFactions.empty())
return;
@@ -207,7 +208,7 @@ void SuggestWhatToDoAction::grindReputation()
itemout << allowedFactions[urand(0, allowedFactions.size() - 1)];
placeholders["%faction"] = itemout.str();
spam(BOT_TEXT2("suggest_faction", placeholders), 0x18, true);
spam(BOT_TEXT2("suggest_faction", placeholders), eTalkType::General, true);
}
void SuggestWhatToDoAction::something()
@@ -221,10 +222,10 @@ void SuggestWhatToDoAction::something()
std::ostringstream out;
// out << "|cffb04040" << entry->area_name[0] << "|r";
out << entry->area_name[0];
out << entry->area_name[_dbc_locale];
placeholders["%zone"] = out.str();
spam(BOT_TEXT2("suggest_something", placeholders), urand(0, 1) ? 0x18 : 0, urand(0, 2), urand(0, 2));
spam(BOT_TEXT2("suggest_something", placeholders), urand(0, 1) ? eTalkType::General : 0, urand(0, 2), urand(0, 2));
}
void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, bool guild)
@@ -237,58 +238,63 @@ void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, b
if (!cMgr)
return;
for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i)
{
ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i);
if (!channel) continue;
for (AreaTableEntry const* current_zone : sAreaTableStore)
AreaTableEntry const* current_zone = GetAreaEntryByAreaID(bot->GetAreaId());
if (!current_zone)
continue;
// combine full channel name
char channelName[100];
Channel* chn = nullptr;
if ((channel->flags & CHANNEL_DBC_FLAG_LFG) != 0)
{
if (!current_zone)
continue;
std::string chanName = channel->pattern[_dbc_locale];
chn = cMgr->GetChannel(chanName, bot);
}
else
{
snprintf(channelName, 100, channel->pattern[_dbc_locale], current_zone->area_name[_dbc_locale]);
chn = cMgr->GetChannel(channelName, bot);
}
if (!chn)
continue;
// skip world chat here
if (chn->GetName() == "World")
continue;
if (flags != 0 && chn->GetFlags() != flags)
continue;
// combine full channel name
char channelName[100];
Channel* chn = nullptr;
if ((channel->flags & CHANNEL_DBC_FLAG_LFG) != 0)
{
std::string chanName = channel->pattern[0];
chn = cMgr->GetChannel(chanName, bot);
}
else
{
snprintf(channelName, 100, channel->pattern[0], current_zone->area_name[0]);
chn = cMgr->GetChannel(channelName, bot);
}
if (!chn)
continue;
// skip world chat here
if (chn->GetName() == "World")
continue;
// skip local defense
if (chn->GetChannelId() == 22)
continue;
if (flags != 0 && chn->GetFlags() != flags)
continue;
// skip local defense
//if (chn->GetFlags() == 0x18)
// continue;
// no filter, pick several options
if (flags == CHANNEL_FLAG_NONE)
{
channelNames.push_back(chn->GetName());
}
else
chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
// no filter, pick several options
if (flags == CHANNEL_FLAG_NONE)
{
channelNames.push_back(chn->GetName());
}
else
{
if (!bot->IsInChannel(chn))
chn->JoinChannel(bot, "");
chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
}
if (!channelNames.empty())
{
std::string randomName = channelNames[urand(0, channelNames.size() - 1)];
if (Channel* chn = cMgr->GetChannel(randomName, bot))
{
if (!bot->IsInChannel(chn))
chn->JoinChannel(bot, "");
chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
}
}
if (worldChat)
@@ -337,6 +343,80 @@ class FindTradeItemsVisitor : public IterateItemsVisitor
uint32 quality;
};
SuggestDungeonAction::SuggestDungeonAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest dungeon")
{
}
bool SuggestDungeonAction::Execute(Event event)
{
// TODO: use sPlayerbotDungeonSuggestionMgr
if (instances.empty())
{
instances["Ragefire Chasm"] = 15;
instances["Deadmines"] = 18;
instances["Wailing Caverns"] = 18;
instances["Shadowfang Keep"] = 25;
instances["Blackfathom Deeps"] = 20;
instances["Stockade"] = 20;
instances["Gnomeregan"] = 35;
instances["Razorfen Kraul"] = 35;
instances["Maraudon"] = 50;
instances["Scarlet Monestery"] = 40;
instances["Uldaman"] = 45;
instances["Dire Maul"] = 58;
instances["Scholomance"] = 59;
instances["Razorfen Downs"] = 40;
instances["Strathholme"] = 59;
instances["Zul'Farrak"] = 45;
instances["Blackrock Depths"] = 55;
instances["Temple of Atal'Hakkar"] = 55;
instances["Lower Blackrock Spire"] = 57;
instances["Hellfire Citidel"] = 65;
instances["Coilfang Reservoir"] = 65;
instances["Auchindoun"] = 65;
instances["Cavens of Time"] = 68;
instances["Tempest Keep"] = 69;
instances["Magister's Terrace"] = 70;
instances["Utgarde Keep"] = 75;
instances["The Nexus"] = 75;
instances["Ahn'kahet: The Old Kingdom"] = 75;
instances["Azjol-Nerub"] = 75;
instances["Drak'Tharon Keep"] = 75;
instances["Violet Hold"] = 80;
instances["Gundrak"] = 77;
instances["Halls of Stone"] = 77;
instances["Halls of Lightning"] = 77;
instances["Oculus"] = 77;
instances["Utgarde Pinnacle"] = 77;
instances["Trial of the Champion"] = 80;
instances["Forge of Souls"] = 80;
instances["Pit of Saron"] = 80;
instances["Halls of Reflection"] = 80;
}
std::vector<std::string> allowedInstances;
for (const auto& instance : instances)
{
if (bot->GetLevel() >= instance.second)
allowedInstances.push_back(instance.first);
}
if (allowedInstances.empty()) return false;
std::map<std::string, std::string> placeholders;
placeholders["%role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
std::ostringstream itemout;
//itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r";
itemout << allowedInstances[urand(0, allowedInstances.size() - 1)];
placeholders["%instance"] = itemout.str();
spam(BOT_TEXT2("suggest_instance", placeholders), urand(0, 1) ? eTalkType::LookingForGroup : 0, urand(0, 2), urand(0, 2));
return true;
}
SuggestTradeAction::SuggestTradeAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest trade")
{
}
@@ -400,13 +480,6 @@ bool SuggestTradeAction::Execute(Event event)
placeholders["%item"] = chat->FormatItem(proto, count);
placeholders["%gold"] = chat->formatMoney(price);
spam(BOT_TEXT2("suggest_sell", placeholders), urand(0, 1) ? 0x3C : 0, urand(0, 1), urand(0, 5));
spam(BOT_TEXT2("suggest_sell", placeholders), urand(0, 1) ? eTalkType::Trade : 0, urand(0, 1), urand(0, 5));
return true;
}
bool SuggestWhatToDoAction::isUseful()
{
std::string const qualifier = "suggest what to do";
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
return (time(nullptr) - lastSaid) > 30;
}

View File

@@ -18,19 +18,19 @@ class SuggestWhatToDoAction : public InventoryAction
bool isUseful() override;
protected:
typedef void (SuggestWhatToDoAction::*Suggestion)();
using Suggestion = std::function<void()>;
std::vector<Suggestion> suggestions;
void instance();
void specificQuest();
void grindReputation();
void grindMaterials();
void something();
void spam(std::string msg, uint8 flags = 0, bool worldChat = false, bool guild = false);
std::vector<uint32> GetIncompletedQuests();
private:
static std::map<std::string, uint8> instances;
static std::map<std::string, uint8> factions;
const int32_t _dbc_locale;
};
class SuggestTradeAction : public SuggestWhatToDoAction
@@ -42,4 +42,15 @@ class SuggestTradeAction : public SuggestWhatToDoAction
bool isUseful() override { return true; }
};
class SuggestDungeonAction : public SuggestWhatToDoAction
{
public:
SuggestDungeonAction(PlayerbotAI* botAI);
bool Execute(Event event) override;
bool isUseful() override { return true; }
private:
static std::map<std::string, uint8> instances;
};
#endif

View File

@@ -138,4 +138,4 @@ bool TellExpectedDpsAction::Execute(Event event)
float dps = AI_VALUE(float, "expected group dps");
botAI->TellMaster("Expected Group DPS: " + std::to_string(dps));
return true;
}
}

View File

@@ -6,6 +6,7 @@
#include "Event.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "GuildMgr.h"
bool XpGainAction::Execute(Event event)
{
@@ -32,6 +33,25 @@ bool XpGainAction::Execute(Event event)
p >> groupBonus; // 8 group bonus
}
if (sPlayerbotAIConfig->randomBotTalk && bot->GetGuildId() && urand(0, 10))
{
Creature* creature = botAI->GetCreature(guid);
if (creature && (creature->isElite() || creature->isWorldBoss() || creature->GetLevel() > 61 || creature->GetLevel() > bot->GetLevel() + 4))
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
{
std::map<std::string, std::string> placeholders;
placeholders["%name"] = creature->GetName();
if (urand(0, 3))
guild->BroadcastToGuild(bot->GetSession(), false, BOT_TEXT2("Wow I just killed %name!", placeholders), LANG_UNIVERSAL);
else
guild->BroadcastToGuild(bot->GetSession(), false, BOT_TEXT2("Awesome that %name went down quickly!", placeholders), LANG_UNIVERSAL);
}
}
}
Unit* victim = nullptr;
if (guid)
victim = botAI->GetUnit(guid);

View File

@@ -7,22 +7,25 @@
void EmoteStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("emote", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest what to do", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest trade", 1.0f), nullptr)));
if (sPlayerbotAIConfig->randomBotEmote)
{
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("emote", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("receive text emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr)));
triggers.push_back(new TriggerNode("receive emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr)));
}
if (sPlayerbotAIConfig->randomBotTalk)
{
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest what to do", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest trade", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("talk", 1.0f), nullptr)));
}
if (sPlayerbotAIConfig->randomBotSuggestDungeons)
{
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("suggest dungeon", 1.0f), nullptr)));
}
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest dungeon", 1.0f), nullptr)));
if (sPlayerbotAIConfig->enableGreet)
{
triggers.push_back(new TriggerNode("new player nearby", NextAction::array(0, new NextAction("greet", 1.0f), nullptr)));
}
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("talk", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("receive text emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr)));
triggers.push_back(new TriggerNode("receive emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("rpg mount anim", 1.0f), nullptr)));
}

View File

@@ -190,7 +190,7 @@ uint32 VehicleSpellIdValue::Calculate()
char firstSymbol = tolower(namepart[0]);
int spellLength = wnamepart.length();
int loc = bot->GetSession()->GetSessionDbcLocale();
const int loc = LocaleConstant::LOCALE_enUS;
Creature* creature = vehicleBase->ToCreature();
for (uint32 x = 0; x < MAX_CREATURE_SPELLS; ++x)