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 # Separator for bot chat commands
AiPlayerbot.CommandSeparator = "\\\\" 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 # Guild Task system
AiPlayerbot.EnableGuildTasks = 0 AiPlayerbot.EnableGuildTasks = 0
# Enable dungeon suggestions for random bots
AiPlayerbot.RandomBotSuggestDungeons = 1
# Enable dungeon suggestions in lower case randomly # Enable dungeon suggestions in lower case randomly
AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0 AiPlayerbot.SuggestDungeonsInLowerCaseRandomly = 0
@@ -1297,9 +1307,6 @@ AiPlayerbot.RandombotsWalkingRPG = 0
# Set randombots movement speed to walking only inside buildings # Set randombots movement speed to walking only inside buildings
AiPlayerbot.RandombotsWalkingRPG.InDoors = 0 AiPlayerbot.RandombotsWalkingRPG.InDoors = 0
# Bots greet to the players
AiPlayerbot.EnableGreet = 0
# Specify percent of active bots # Specify percent of active bots
# The default is 10. With 10% of all bots going active or inactive each minute. # The default is 10. With 10% of all bots going active or inactive each minute.
AiPlayerbot.BotActiveAlone = 100 AiPlayerbot.BotActiveAlone = 100

View File

@@ -264,7 +264,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (!player->InBattleground()) 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) if (sPlayerbotAIConfig->autoSaveMana)
{ {
@@ -548,7 +548,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
if (!player->InBattleground()) if (!player->InBattleground())
{ {
nonCombatEngine->addStrategies("nc", "food", "chat", "follow", 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) { if (sPlayerbotAIConfig->autoSaveMana) {
nonCombatEngine->addStrategy("auto save mana"); nonCombatEngine->addStrategy("auto save mana");
@@ -636,7 +636,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
// Battleground switch // Battleground switch
if (player->InBattleground() && player->GetBattleground()) 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("custom::say");
nonCombatEngine->removeStrategy("travel"); nonCombatEngine->removeStrategy("travel");
nonCombatEngine->removeStrategy("rpg"); 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::string const ChatHelper::FormatGameobject(GameObject* go)
{ {
std::ostringstream out; 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(); return out.str();
} }
@@ -296,7 +296,7 @@ std::string const ChatHelper::FormatWorldobject(WorldObject* wo)
{ {
std::ostringstream out; std::ostringstream out;
out << "|cFFFFFF00|Hfound:" << wo->GetGUID().GetRawValue() << ":" << wo->GetEntry() << ":" << "|h["; 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(); return out.str();
} }
@@ -327,7 +327,7 @@ std::string const ChatHelper::FormatWorldEntry(int32 entry)
std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo) std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
{ {
std::ostringstream out; 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(); return out.str();
} }
@@ -336,7 +336,7 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count
char color[32]; char color[32];
sprintf(color, "%x", ItemQualityColors[proto->Quality]); 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; std::ostringstream out;
out << "|c" << color << "|Hitem:" << proto->ItemId out << "|c" << color << "|Hitem:" << proto->ItemId

View File

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

View File

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

View File

@@ -85,7 +85,8 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
uint32 botAccountId = holder.GetAccountId(); 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 botSession->HandlePlayerLoginFromDB(holder); // will delete lqh
@@ -544,13 +545,13 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
Channel* new_channel = nullptr; Channel* new_channel = nullptr;
if (isLfg) if (isLfg)
{ {
std::string lfgChannelName = channel->pattern[0]; std::string lfgChannelName = channel->pattern[sWorld->GetDefaultDbcLocale()];
new_channel = cMgr->GetJoinChannel("LookingForGroup", channel->ChannelID); new_channel = cMgr->GetJoinChannel("LookingForGroup", channel->ChannelID);
} }
else else
{ {
char new_channel_name_buf[100]; 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); new_channel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID);
} }
if (new_channel && new_channel->GetName().length() > 0) if (new_channel && new_channel->GetName().length() > 0)

View File

@@ -28,12 +28,13 @@ void PlayerbotTextMgr::LoadBotTexts()
Field* fields = result->Fetch(); Field* fields = result->Fetch();
std::string name = fields[0].Get<std::string>(); std::string name = fields[0].Get<std::string>();
text[0] = fields[1].Get<std::string>(); text[0] = fields[1].Get<std::string>();
uint32 sayType = fields[2].Get<uint32>(); uint8 sayType = fields[2].Get<uint8>();
uint32 replyType = fields[3].Get<uint32>(); uint8 replyType = fields[3].Get<uint8>();
for (uint8 i = 1; i < MAX_LOCALES; ++i) for (uint8 i = 1; i < MAX_LOCALES; ++i)
{ {
text[i] = fields[i + 3].Get<std::string>(); text[i] = fields[i + 3].Get<std::string>();
} }
botTexts[name].push_back(BotTextEntry(name, text, sayType, replyType)); botTexts[name].push_back(BotTextEntry(name, text, sayType, replyType));
++count; ++count;
} }
@@ -191,6 +192,7 @@ uint32 PlayerbotTextMgr::GetLocalePriority()
if (botTextLocalePriority[i] > topLocale) if (botTextLocalePriority[i] > topLocale)
topLocale = i; topLocale = i;
} }
return topLocale; 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)) { if (bot->GetLevel() <= 18 && (loc.GetMapId() != pInfo->mapId || dis > 10000.0f)) {
continue; continue;
} }
LocaleConstant locale = sWorld->GetDefaultDbcLocale();
const LocaleConstant& locale = sWorld->GetDefaultDbcLocale();
LOG_INFO("playerbots", "Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) {},{},{} ({}/{} locations)", LOG_INFO("playerbots", "Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) {},{},{} ({}/{} locations)",
bot->GetName().c_str(), bot->GetLevel(), bot->GetName().c_str(), bot->GetLevel(),
map->GetId(), map->GetMapName(), map->GetId(), map->GetMapName(),

View File

@@ -172,6 +172,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal)
if (!action) 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()); LogAction("A:%s - UNKNOWN", actionNode->getName().c_str());
} }
else if (action->isUseful()) else if (action->isUseful())

View File

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

View File

@@ -6,6 +6,7 @@
#include "Event.h" #include "Event.h"
#include "PlayerbotFactory.h" #include "PlayerbotFactory.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "GuildMgr.h"
bool AutoLearnSpellAction::Execute(Event event) bool AutoLearnSpellAction::Execute(Event event)
{ {
@@ -27,7 +28,6 @@ bool AutoLearnSpellAction::Execute(Event event)
return true; return true;
} }
void AutoLearnSpellAction::LearnSpells(std::ostringstream* out) void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
{ {
if (sPlayerbotAIConfig->autoLearnTrainerSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot))) 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))) if (sPlayerbotAIConfig->autoLearnQuestSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
LearnQuestSpells(out); 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) 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)) || 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())))) (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->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; emote = text_emote;
} }
@@ -693,9 +693,9 @@ bool EmoteAction::Execute(Event event)
if ((pSource->GetGUID() != bot->GetGUID()) && (pSource->GetTarget() == bot->GetGUID() || if ((pSource->GetGUID() != bot->GetGUID()) && (pSource->GetTarget() == bot->GetGUID() ||
(urand(0, 1) && bot->HasInArc(static_cast<float>(M_PI), pSource, 10.0f)))) (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(), 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; std::vector<uint32> types;
for (int32 i = sEmotesTextStore.GetNumRows(); i >= 0; --i) for (int32 i = sEmotesTextStore.GetNumRows(); i >= 0; --i)

View File

@@ -105,7 +105,7 @@ bool LfgJoinAction::JoinLFG()
const auto& botLevel = bot->GetLevel(); 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) if (dungeon->MinLevel && (botLevel < dungeon->MinLevel || botLevel > dungeon->MaxLevel)
|| ||
(botLevel > dungeon->MinLevel + 10 && dungeon->TypeID == LFG_TYPE_DUNGEON) (botLevel > dungeon->MinLevel + 10 && dungeon->TypeID == LFG_TYPE_DUNGEON)

View File

@@ -12,6 +12,7 @@
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "ServerFacade.h" #include "ServerFacade.h"
#include "GuildMgr.h"
bool LootAction::Execute(Event event) 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)) if (proto->Quality >= ITEM_QUALITY_RARE && !urand(0, 1) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT))
botAI->PlayEmote(TEXT_EMOTE_CHEER); 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; // std::ostringstream out;
// out << "Looting " << chat->FormatItem(proto); // out << "Looting " << chat->FormatItem(proto);
// botAI->TellMasterNoFacing(out.str()); // botAI->TellMasterNoFacing(out.str());

View File

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

View File

@@ -10,6 +10,16 @@
#include "GuildMgr.h" #include "GuildMgr.h"
#include <regex> #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() SayAction::SayAction(PlayerbotAI* botAI) : Action(botAI, "say"), Qualified()
{ {
} }
@@ -105,6 +115,9 @@ bool SayAction::isUseful()
if (!botAI->AllowActivity()) if (!botAI->AllowActivity())
return false; return false;
if (botAI->HasStrategy("silent", BotState::BOT_STATE_NON_COMBAT))
return false;
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
return (time(nullptr) - lastSaid) > 30; 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 ChatReplyType replyType = REPLY_NOT_UNDERSTAND; // default not understand
std::string respondsText = ""; 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 // Chat Logic
int32 verb_pos = -1; int32 verb_pos = -1;
int32 verb_type = -1; int32 verb_type = -1;
@@ -149,18 +191,16 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
// Responds // Responds
for (uint32 i = 0; i < 8; i++) for (uint32 i = 0; i < 8; i++)
{ {
// // blame gm with chat tag // blame gm with chat tag
// if (Player* plr = sObjectMgr->GetPlayer(ObjectGuid(HIGHGUID_PLAYER, guid1))) if (plr && plr->isGMChat())
// { {
// if (plr->isGMChat()) replyType = REPLY_ADMIN_ABUSE;
// { found = true;
// replyType = REPLY_ADMIN_ABUSE; break;
// 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")
//
if (word[i] == "hi" || word[i] == "hey" || word[i] == "hello" || word[i] == "wazzup")
{ {
replyType = REPLY_HELLO; replyType = REPLY_HELLO;
found = true; found = true;
@@ -169,22 +209,25 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
if (verb_type < 4) 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_pos = i;
verb_type = 2; // present 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_pos = i;
verb_type = 3; // future 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_pos = i;
verb_type = 1; // past 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) 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) if (type == CHAT_MSG_WHISPER)
{ {
ObjectGuid receiver = sCharacterCache->GetCharacterGuidByName(name); if (plr)
if (!receiver || !receiver.IsPlayer() || !ObjectAccessor::FindPlayer(receiver))
{ {
return; if (bot->GetTeamId() == TEAM_ALLIANCE)
} {
if (bot->GetTeamId() == TEAM_ALLIANCE) bot->Whisper(c, LANG_COMMON, plr);
{ }
bot->Whisper(c, LANG_COMMON, ObjectAccessor::FindPlayer(receiver)); else
} {
else bot->Whisper(c, LANG_ORCISH, plr);
{ }
bot->Whisper(c, LANG_ORCISH, ObjectAccessor::FindPlayer(receiver));
} }
} }
if (type == CHAT_MSG_SAY) else if (type == CHAT_MSG_SAY)
{ {
if (bot->GetTeamId() == TEAM_ALLIANCE) if (bot->GetTeamId() == TEAM_ALLIANCE)
bot->Say(respondsText, LANG_COMMON); bot->Say(respondsText, LANG_COMMON);
@@ -623,7 +664,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
bot->Say(respondsText, LANG_ORCISH); bot->Say(respondsText, LANG_ORCISH);
} }
if (type == CHAT_MSG_YELL) else if (type == CHAT_MSG_YELL)
{ {
if (bot->GetTeamId() == TEAM_ALLIANCE) if (bot->GetTeamId() == TEAM_ALLIANCE)
bot->Yell(respondsText, LANG_COMMON); bot->Yell(respondsText, LANG_COMMON);
@@ -631,7 +672,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
bot->Yell(respondsText, LANG_ORCISH); bot->Yell(respondsText, LANG_ORCISH);
} }
if (type == CHAT_MSG_GUILD) else if (type == CHAT_MSG_GUILD)
{ {
if (!bot->GetGuildId()) if (!bot->GetGuildId())
return; 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)); 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 "SuggestWhatToDoAction.h"
#include "ServerFacade.h"
#include "ChannelMgr.h" #include "ChannelMgr.h"
#include "Event.h" #include "Event.h"
#include "ItemVisitors.h" #include "ItemVisitors.h"
@@ -11,24 +12,47 @@
#include "Playerbots.h" #include "Playerbots.h"
#include "PlayerbotTextMgr.h" #include "PlayerbotTextMgr.h"
#include "GuildMgr.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; 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(std::bind(&SuggestWhatToDoAction::specificQuest, this));
suggestions.push_back(&SuggestWhatToDoAction::grindReputation); suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindReputation, this));
suggestions.push_back(&SuggestWhatToDoAction::something); 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()) if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId())
return false; 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(); uint32 index = rand() % suggestions.size();
(this->*suggestions[index])(); auto fnct_ptr = suggestions[index];
fnct_ptr();
std::string const qualifier = "suggest what to do"; std::string const qualifier = "suggest what to do";
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
@@ -37,73 +61,6 @@ bool SuggestWhatToDoAction::Execute(Event event)
return true; 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> SuggestWhatToDoAction::GetIncompletedQuests()
{ {
std::vector<uint32> result; std::vector<uint32> result;
@@ -136,7 +93,52 @@ void SuggestWhatToDoAction::specificQuest()
placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%quest"] = chat->FormatQuest(quest); 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() void SuggestWhatToDoAction::grindReputation()
@@ -185,12 +187,11 @@ void SuggestWhatToDoAction::grindReputation()
levels.push_back("exalted"); levels.push_back("exalted");
std::vector<std::string> allowedFactions; 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) if (bot->GetLevel() >= i.second)
allowedFactions.push_back(i->first); allowedFactions.push_back(i.first);
} }
if (allowedFactions.empty()) if (allowedFactions.empty())
return; return;
@@ -207,7 +208,7 @@ void SuggestWhatToDoAction::grindReputation()
itemout << allowedFactions[urand(0, allowedFactions.size() - 1)]; itemout << allowedFactions[urand(0, allowedFactions.size() - 1)];
placeholders["%faction"] = itemout.str(); placeholders["%faction"] = itemout.str();
spam(BOT_TEXT2("suggest_faction", placeholders), 0x18, true); spam(BOT_TEXT2("suggest_faction", placeholders), eTalkType::General, true);
} }
void SuggestWhatToDoAction::something() void SuggestWhatToDoAction::something()
@@ -221,10 +222,10 @@ void SuggestWhatToDoAction::something()
std::ostringstream out; std::ostringstream out;
// out << "|cffb04040" << entry->area_name[0] << "|r"; // out << "|cffb04040" << entry->area_name[0] << "|r";
out << entry->area_name[0]; out << entry->area_name[_dbc_locale];
placeholders["%zone"] = out.str(); 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) 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) if (!cMgr)
return; return;
for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i)
{ {
ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i); ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i);
if (!channel) continue; 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) std::string chanName = channel->pattern[_dbc_locale];
continue; 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 // skip local defense
char channelName[100]; if (chn->GetChannelId() == 22)
Channel* chn = nullptr; continue;
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;
if (flags != 0 && chn->GetFlags() != flags) // no filter, pick several options
continue; if (flags == CHANNEL_FLAG_NONE)
{
// skip local defense channelNames.push_back(chn->GetName());
//if (chn->GetFlags() == 0x18) }
// continue; else
{
// no filter, pick several options if (!bot->IsInChannel(chn))
if (flags == CHANNEL_FLAG_NONE) chn->JoinChannel(bot, "");
{ chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
channelNames.push_back(chn->GetName());
}
else
chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
} }
if (!channelNames.empty()) if (!channelNames.empty())
{ {
std::string randomName = channelNames[urand(0, channelNames.size() - 1)]; std::string randomName = channelNames[urand(0, channelNames.size() - 1)];
if (Channel* chn = cMgr->GetChannel(randomName, bot)) if (Channel* chn = cMgr->GetChannel(randomName, bot))
{
if (!bot->IsInChannel(chn))
chn->JoinChannel(bot, "");
chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
}
} }
if (worldChat) if (worldChat)
@@ -337,6 +343,80 @@ class FindTradeItemsVisitor : public IterateItemsVisitor
uint32 quality; 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") SuggestTradeAction::SuggestTradeAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest trade")
{ {
} }
@@ -400,13 +480,6 @@ bool SuggestTradeAction::Execute(Event event)
placeholders["%item"] = chat->FormatItem(proto, count); placeholders["%item"] = chat->FormatItem(proto, count);
placeholders["%gold"] = chat->formatMoney(price); 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; 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; bool isUseful() override;
protected: protected:
typedef void (SuggestWhatToDoAction::*Suggestion)(); using Suggestion = std::function<void()>;
std::vector<Suggestion> suggestions; std::vector<Suggestion> suggestions;
void instance();
void specificQuest(); void specificQuest();
void grindReputation(); void grindReputation();
void grindMaterials();
void something(); void something();
void spam(std::string msg, uint8 flags = 0, bool worldChat = false, bool guild = false); void spam(std::string msg, uint8 flags = 0, bool worldChat = false, bool guild = false);
std::vector<uint32> GetIncompletedQuests(); std::vector<uint32> GetIncompletedQuests();
private: private:
static std::map<std::string, uint8> instances;
static std::map<std::string, uint8> factions; static std::map<std::string, uint8> factions;
const int32_t _dbc_locale;
}; };
class SuggestTradeAction : public SuggestWhatToDoAction class SuggestTradeAction : public SuggestWhatToDoAction
@@ -42,4 +42,15 @@ class SuggestTradeAction : public SuggestWhatToDoAction
bool isUseful() override { return true; } 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 #endif

View File

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

View File

@@ -6,6 +6,7 @@
#include "Event.h" #include "Event.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "GuildMgr.h"
bool XpGainAction::Execute(Event event) bool XpGainAction::Execute(Event event)
{ {
@@ -32,6 +33,25 @@ bool XpGainAction::Execute(Event event)
p >> groupBonus; // 8 group bonus 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; Unit* victim = nullptr;
if (guid) if (guid)
victim = botAI->GetUnit(guid); victim = botAI->GetUnit(guid);

View File

@@ -7,22 +7,25 @@
void EmoteStrategy::InitTriggers(std::vector<TriggerNode*>& triggers) void EmoteStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{ {
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("emote", 1.0f), nullptr))); if (sPlayerbotAIConfig->randomBotEmote)
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("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) if (sPlayerbotAIConfig->randomBotSuggestDungeons)
{ triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest dungeon", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("suggest dungeon", 1.0f), nullptr)));
}
if (sPlayerbotAIConfig->enableGreet) 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("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))); 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]); char firstSymbol = tolower(namepart[0]);
int spellLength = wnamepart.length(); int spellLength = wnamepart.length();
int loc = bot->GetSession()->GetSessionDbcLocale(); const int loc = LocaleConstant::LOCALE_enUS;
Creature* creature = vehicleBase->ToCreature(); Creature* creature = vehicleBase->ToCreature();
for (uint32 x = 0; x < MAX_CREATURE_SPELLS; ++x) for (uint32 x = 0; x < MAX_CREATURE_SPELLS; ++x)