Merge branch 'master' into eots-fixes

This commit is contained in:
Fuzz
2024-07-29 22:33:33 +10:00
46 changed files with 746 additions and 554 deletions

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,12 +548,12 @@ 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");
}
if ((facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
{
Player* master = facade->GetMaster();
@@ -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->GetGOInfo()->name << "]|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)->GetGOInfo()->name : wo->GetName()) << "]|h|r";
out << (wo->ToGameObject() ? ((GameObject*)wo)->GetNameForLocaleIdx(LOCALE_enUS) : wo->GetNameForLocaleIdx(LOCALE_enUS)) << "]|h|r";
return out.str();
}
@@ -336,6 +336,8 @@ 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[LOCALE_enUS];
std::ostringstream out;
out << "|c" << color << "|Hitem:" << proto->ItemId
<< ":0:0:0:0:0:0:0" << "|h[" << proto->Name1

View File

@@ -96,7 +96,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
if (IsNeededForQuest(bot, itemId))
{
this->guid = guid;
this->guid = lootGUID;
return;
}
isQuestItemOnly |= itemId > 0;

View File

@@ -191,7 +191,7 @@ PlayerbotAI::~PlayerbotAI()
delete aiObjectContext;
if (bot)
sPlayerbotsMgr->RemovePlayerBotData(bot->GetGUID());
sPlayerbotsMgr->RemovePlayerBotData(bot->GetGUID(), true);
}
void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
@@ -625,10 +625,15 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro
return;
}
if (!IsAllowedCommand(filtered) && !GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, type != CHAT_MSG_WHISPER, fromPlayer))
if (!IsAllowedCommand(filtered) &&
(master != fromPlayer || !GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, type != CHAT_MSG_WHISPER, fromPlayer)))
return;
if (type == CHAT_MSG_RAID_WARNING && filtered.find(bot->GetName()) != std::string::npos && filtered.find("award") == std::string::npos)
if (!IsAllowedCommand(filtered) && master != fromPlayer)
return;
if (type == CHAT_MSG_RAID_WARNING && filtered.find(bot->GetName()) != std::string::npos &&
filtered.find("award") == std::string::npos)
{
ChatCommandHolder cmd("warning", fromPlayer, type);
chatCommands.push(cmd);

View File

@@ -140,7 +140,10 @@ 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);
randomBotGuildTalk = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGuildTalk", false);
suggestDungeonsInLowerCaseRandomly = sConfigMgr->GetOption<bool>("AiPlayerbot.SuggestDungeonsInLowerCaseRandomly", false);
randomBotJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinBG", true);
randomBotAutoJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutoJoinBG", false);
@@ -279,7 +282,7 @@ bool PlayerbotAIConfig::Initialize()
allowSummonInCombat = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonInCombat", true);
allowSummonWhenMasterIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenMasterIsDead", true);
allowSummonWhenBotIsDead = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowSummonWhenBotIsDead", true);
reviveBotWhenSummoned = sConfigMgr->GetOption<bool>("AiPlayerbot.ReviveBotWhenSummoned", true);
reviveBotWhenSummoned = sConfigMgr->GetOption<int32>("AiPlayerbot.ReviveBotWhenSummoned", 1);
botRepairWhenSummon = sConfigMgr->GetOption<bool>("AiPlayerbot.BotRepairWhenSummon", true);
autoInitOnly = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoInitOnly", false);
autoInitEquipLevelLimitRatio = sConfigMgr->GetOption<float>("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0);

View File

@@ -95,7 +95,10 @@ class PlayerbotAIConfig
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
bool randomBotJoinLfg;
bool randomBotTalk;
bool randomBotEmote;
bool randomBotSuggestDungeons;
bool randomBotGuildTalk;
bool suggestDungeonsInLowerCaseRandomly;
bool randomBotJoinBG;
bool randomBotAutoJoinBG;
@@ -220,7 +223,7 @@ class PlayerbotAIConfig
bool allowSummonInCombat;
bool allowSummonWhenMasterIsDead;
bool allowSummonWhenBotIsDead;
bool reviveBotWhenSummoned;
int reviveBotWhenSummoned;
bool botRepairWhenSummon;
bool autoInitOnly;
float autoInitEquipLevelLimitRatio;

View File

@@ -136,6 +136,9 @@ void PlayerbotFactory::Init()
continue;
}
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId);
if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE) { // unique gem
continue;
}
if (!proto || !sGemPropertiesStore.LookupEntry(proto->GemProperties)) {
continue;
}
@@ -644,6 +647,7 @@ void PlayerbotFactory::InitPetTalents()
spells_row.erase(spells_row.begin() + index);
}
}
bot->SendTalentsInfoData(true);
}
void PlayerbotFactory::InitPet()
@@ -870,6 +874,7 @@ void PlayerbotFactory::InitTalentsTree(bool increment/*false*/, bool use_templat
if (bot->GetFreeTalentPoints())
InitTalents((specTab + 1) % 3);
}
bot->SendTalentsInfoData(false);
}
void PlayerbotFactory::InitTalentsBySpecNo(Player* bot, int specNo, bool reset)
@@ -933,6 +938,7 @@ void PlayerbotFactory::InitTalentsBySpecNo(Player* bot, int specNo, bool reset)
break;
}
}
bot->SendTalentsInfoData(false);
}
void PlayerbotFactory::InitTalentsByParsedSpecLink(Player* bot, std::vector<std::vector<uint32>> parsedSpecLink, bool reset)
@@ -983,6 +989,7 @@ void PlayerbotFactory::InitTalentsByParsedSpecLink(Player* bot, std::vector<std:
break;
}
}
bot->SendTalentsInfoData(false);
}
class DestroyItemsVisitor : public IterateItemsVisitor
@@ -1734,7 +1741,7 @@ void PlayerbotFactory::InitBags(bool destroyOld)
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
}
if (old_bag) {
return;
continue;
}
Item* newItem = bot->EquipNewItem(dest, newItemId, true);
if (newItem)
@@ -2567,7 +2574,7 @@ void PlayerbotFactory::InitMounts()
break;
case RACE_NIGHTELF:
slow = { 10789, 8394, 10793 };
fast = { 24252, 63637, 22723 };
fast = { 23219, 23220, 63637 };
break;
case RACE_UNDEAD_PLAYER:
slow = { 17463, 17464, 17462 };
@@ -2616,6 +2623,16 @@ void PlayerbotFactory::InitMounts()
for (uint32 type = 0; type < 4; type++)
{
bool hasMount = false;
for (uint32 &spell : mounts[bot->getRace()][type]) {
if (bot->HasSpell(spell)) {
hasMount = true;
break;
}
}
if (hasMount)
continue;
if (bot->GetLevel() < secondmount && type == 1)
continue;
@@ -2844,7 +2861,29 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (!increment) {
for (uint32 slotIndex = 0; slotIndex < MAX_GLYPH_SLOT_INDEX; ++slotIndex)
{
bot->SetGlyph(slotIndex, 0, true);
uint32 glyph = bot->GetGlyph(slotIndex);
if (GlyphPropertiesEntry const* glyphEntry = sGlyphPropertiesStore.LookupEntry(glyph))
{
bot->RemoveAurasDueToSpell(glyphEntry->SpellId);
// Removed any triggered auras
Unit::AuraMap& ownedAuras = bot->GetOwnedAuras();
for (Unit::AuraMap::iterator iter = ownedAuras.begin(); iter != ownedAuras.end();)
{
Aura* aura = iter->second;
if (SpellInfo const* triggeredByAuraSpellInfo = aura->GetTriggeredByAuraSpellInfo())
{
if (triggeredByAuraSpellInfo->Id == glyphEntry->SpellId)
{
bot->RemoveOwnedAura(iter);
continue;
}
}
++iter;
}
bot->SetGlyph(slotIndex, 0, true);
}
}
}
@@ -2943,6 +2982,8 @@ void PlayerbotFactory::InitGlyphs(bool increment)
if (!glyph) {
continue;
}
GlyphPropertiesEntry const* glyphEntry = sGlyphPropertiesStore.LookupEntry(glyph);
bot->CastSpell(bot, glyphEntry->SpellId, TriggerCastFlags(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_SHAPESHIFT | TRIGGERED_IGNORE_CASTER_AURASTATE)));
bot->SetGlyph(realSlot, glyph, true);
chosen.insert(glyph);
} else {
@@ -2976,13 +3017,16 @@ void PlayerbotFactory::InitGlyphs(bool increment)
continue;
chosen.insert(id);
GlyphPropertiesEntry const* glyphEntry = sGlyphPropertiesStore.LookupEntry(id);
bot->CastSpell(bot, glyphEntry->SpellId, TriggerCastFlags(TRIGGERED_FULL_MASK & ~(TRIGGERED_IGNORE_SHAPESHIFT | TRIGGERED_IGNORE_CASTER_AURASTATE)));
bot->SetGlyph(realSlot, id, true);
found = true;
break;
}
}
}
bot->SendTalentsInfoData(false);
}
void PlayerbotFactory::CancelAuras()

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
@@ -525,7 +526,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
// join standard channels
AreaTableEntry const* current_zone = sAreaTableStore.LookupEntry(bot->GetAreaId());
ChannelMgr* cMgr = ChannelMgr::forTeam(bot->GetTeamId());
std::string current_zone_name = current_zone ? current_zone->area_name[0] : "";
std::string current_zone_name = current_zone ? current_zone->area_name[sWorld->GetDefaultDbcLocale()] : "";
if (current_zone && cMgr)
{
@@ -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)
@@ -736,7 +737,7 @@ bool PlayerbotMgr::HandlePlayerbotMgrCommand(ChatHandler* handler, char const* a
for (std::vector<std::string>::iterator i = messages.begin(); i != messages.end(); ++i)
{
handler->PSendSysMessage("%s", i->c_str());
handler->PSendSysMessage("{}", i->c_str());
}
return true;
@@ -748,8 +749,8 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
if (!*args)
{
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME");
messages.push_back(" addclass CLASSNAME");
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME\n");
messages.push_back("usage: addclass CLASSNAME");
return messages;
}
@@ -842,7 +843,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
if (GET_PLAYERBOT_AI(master))
{
messages.push_back("Disable player botAI");
DisablePlayerBot(master->GetGUID());
delete GET_PLAYERBOT_AI(master);
}
else if (sPlayerbotAIConfig->selfBotLevel == 0)
messages.push_back("Self-bot is disabled");
@@ -851,7 +852,8 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
else
{
messages.push_back("Enable player botAI");
OnBotLogin(master);
sPlayerbotsMgr->AddPlayerbotData(master, true);
GET_PLAYERBOT_AI(master)->SetMaster(master);
}
return messages;
@@ -938,20 +940,22 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
race_limit = "2, 5, 6, 8, 10";
break;
}
uint32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back();
// find a bot fit conditions and not in any guild
QueryResult results = CharacterDatabase.Query("SELECT guid FROM characters "
"WHERE name IN (SELECT name FROM playerbots_names) AND class = '{}' AND online = 0 AND race IN ({}) AND guid NOT IN ( SELECT guid FROM guild_member ) "
"ORDER BY account DESC LIMIT 1", claz, race_limit);
"AND account <= {} "
"ORDER BY account DESC LIMIT 1", claz, race_limit, maxAccountId);
if (results)
{
Field* fields = results->Fetch();
ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get<uint32>());
AddPlayerBot(guid, master->GetSession()->GetAccountId());
messages.push_back("addclass " + std::string(charname) + " ok");
messages.push_back("Add class " + std::string(charname));
return messages;
}
messages.push_back("addclass failed.");
messages.push_back("Add class failed.");
return messages;
}
@@ -1214,7 +1218,7 @@ PlayerbotMgr::PlayerbotMgr(Player* const master) : PlayerbotHolder(), master(ma
PlayerbotMgr::~PlayerbotMgr()
{
if (master)
sPlayerbotsMgr->RemovePlayerBotData(master->GetGUID());
sPlayerbotsMgr->RemovePlayerBotData(master->GetGUID(), false);
}
void PlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
@@ -1427,31 +1431,45 @@ void PlayerbotsMgr::AddPlayerbotData(Player* player, bool isBotAI)
return;
}
// If the guid already exists in the map, remove it
std::unordered_map<ObjectGuid, PlayerbotAIBase*>::iterator itr = _playerbotsMap.find(player->GetGUID());
if (itr != _playerbotsMap.end())
{
_playerbotsMap.erase(itr);
}
if (!isBotAI)
{
std::unordered_map<ObjectGuid, PlayerbotAIBase*>::iterator itr = _playerbotsMgrMap.find(player->GetGUID());
if (itr != _playerbotsMgrMap.end())
{
_playerbotsMgrMap.erase(itr);
}
PlayerbotMgr* playerbotMgr = new PlayerbotMgr(player);
ASSERT(_playerbotsMap.emplace(player->GetGUID(), playerbotMgr).second);
ASSERT(_playerbotsMgrMap.emplace(player->GetGUID(), playerbotMgr).second);
playerbotMgr->OnPlayerLogin(player);
}
else
{
std::unordered_map<ObjectGuid, PlayerbotAIBase*>::iterator itr = _playerbotsAIMap.find(player->GetGUID());
if (itr != _playerbotsAIMap.end())
{
_playerbotsAIMap.erase(itr);
}
PlayerbotAI* botAI = new PlayerbotAI(player);
ASSERT(_playerbotsMap.emplace(player->GetGUID(), botAI).second);
ASSERT(_playerbotsAIMap.emplace(player->GetGUID(), botAI).second);
}
}
void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid)
void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid, bool is_AI)
{
std::unordered_map<ObjectGuid, PlayerbotAIBase*>::iterator itr = _playerbotsMap.find(guid);
if (itr != _playerbotsMap.end())
{
_playerbotsMap.erase(itr);
if (is_AI) {
std::unordered_map<ObjectGuid, PlayerbotAIBase*>::iterator itr = _playerbotsAIMap.find(guid);
if (itr != _playerbotsAIMap.end())
{
_playerbotsAIMap.erase(itr);
}
} else {
std::unordered_map<ObjectGuid, PlayerbotAIBase*>::iterator itr = _playerbotsMgrMap.find(guid);
if (itr != _playerbotsMgrMap.end())
{
_playerbotsMgrMap.erase(itr);
}
}
}
@@ -1464,8 +1482,8 @@ PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
// if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
// return nullptr;
// }
auto itr = _playerbotsMap.find(player->GetGUID());
if (itr != _playerbotsMap.end())
auto itr = _playerbotsAIMap.find(player->GetGUID());
if (itr != _playerbotsAIMap.end())
{
if (itr->second->IsBotAI())
return reinterpret_cast<PlayerbotAI*>(itr->second);
@@ -1480,8 +1498,8 @@ PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player)
{
return nullptr;
}
auto itr = _playerbotsMap.find(player->GetGUID());
if (itr != _playerbotsMap.end())
auto itr = _playerbotsMgrMap.find(player->GetGUID());
if (itr != _playerbotsMgrMap.end())
{
if (!itr->second->IsBotAI())
return reinterpret_cast<PlayerbotMgr*>(itr->second);

View File

@@ -99,13 +99,14 @@ class PlayerbotsMgr
}
void AddPlayerbotData(Player* player, bool isBotAI);
void RemovePlayerBotData(ObjectGuid const& guid);
void RemovePlayerBotData(ObjectGuid const& guid, bool is_AI);
PlayerbotAI* GetPlayerbotAI(Player* player);
PlayerbotMgr* GetPlayerbotMgr(Player* player);
private:
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsMap;
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsAIMap;
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsMgrMap;
};
#define sPlayerbotsMgr PlayerbotsMgr::instance()

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

@@ -299,7 +299,7 @@ class PlayerbotsScript : public PlayerbotScript
{
botAI->HandleBotOutgoingPacket(*packet);
}
else if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
playerbotMgr->HandleMasterOutgoingPacket(*packet);
}

View File

@@ -14,6 +14,7 @@
#include "RandomPlayerbotMgr.h"
#include "SharedValueContext.h"
#include "Spell.h"
#include "SpellMgr.h"
#include "TravelNode.h"
std::vector<std::string> split(std::string const s, char delim);

View File

@@ -2211,8 +2211,8 @@ void RandomItemMgr::BuildAmmoCache()
{
for (uint32 subClass = ITEM_SUBCLASS_ARROW; subClass <= ITEM_SUBCLASS_BULLET; subClass++)
{
QueryResult results = WorldDatabase.Query("SELECT entry, Flags FROM item_template WHERE class = {} AND subclass = {} AND RequiredLevel <= {} AND stackable = 1000 "
"ORDER BY RequiredLevel DESC", ITEM_CLASS_PROJECTILE, subClass, level);
QueryResult results = WorldDatabase.Query("SELECT entry, Flags FROM item_template WHERE class = {} AND subclass = {} AND RequiredLevel <= {} "
"ORDER BY stackable DESC, RequiredLevel DESC", ITEM_CLASS_PROJECTILE, subClass, level);
if (!results)
continue;
do {

View File

@@ -375,7 +375,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
LOG_INFO("playerbots", "Creating random bot accounts...");
std::vector<std::future<void>> account_creations;
bool account_creation = false;
int account_creation = 0;
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
{
std::ostringstream out;
@@ -389,7 +389,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
{
continue;
}
account_creation = true;
account_creation++;
std::string password = "";
if (sPlayerbotAIConfig->randomBotRandomPassword)
{
@@ -408,6 +408,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
if (account_creation) {
/* wait for async accounts create to make character create correctly, same as account delete */
LOG_INFO("playerbots", "Waiting for {} accounts loading into database...", account_creation);
std::this_thread::sleep_for(10ms * sPlayerbotAIConfig->randomBotAccountCount);
}
@@ -418,7 +419,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
std::unordered_map<uint8,std::vector<std::string>> names;
std::vector<std::pair<Player*, uint32>> playerBots;
std::vector<WorldSession*> sessionBots;
bool bot_creation = false;
int bot_creation = 0;
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
{
std::ostringstream out;
@@ -441,7 +442,6 @@ void RandomPlayerbotFactory::CreateRandomBots()
{
continue;
}
bot_creation = true;
LOG_INFO("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1, sPlayerbotAIConfig->randomBotAccountCount);
RandomPlayerbotFactory factory(accountId);
@@ -462,19 +462,23 @@ void RandomPlayerbotFactory::CreateRandomBots()
continue;
}
if (cls != 10)
if (cls != 10) {
if (Player* playerBot = factory.CreateRandomBot(session, cls, names)) {
playerBot->SaveToDB(true, false);
sCharacterCache->AddCharacterCacheEntry(playerBot->GetGUID(), accountId, playerBot->GetName(),
playerBot->getGender(), playerBot->getRace(), playerBot->getClass(), playerBot->GetLevel());
playerBot->CleanupsBeforeDelete();
delete playerBot;
bot_creation++;
} else {
LOG_ERROR("playerbots", "Fail to create character for account {}", accountId);
}
}
}
}
if (bot_creation) {
LOG_INFO("playerbots", "Waiting for {} characters loading into database...", totalCharCount);
LOG_INFO("playerbots", "Waiting for {} characters loading into database...", bot_creation);
/* wait for characters load into database, or characters will fail to loggin */
std::this_thread::sleep_for(10s);
}

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(),
@@ -1501,7 +1501,6 @@ void RandomPlayerbotMgr::Randomize(Player* bot)
else {
RandomizeFirst(bot);
}
RandomTeleportForLevel(bot);
}
void RandomPlayerbotMgr::IncreaseLevel(Player* bot)
@@ -1590,6 +1589,8 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
if (pmo)
pmo->finish();
RandomTeleportForLevel(bot);
}
void RandomPlayerbotMgr::RandomizeMin(Player* bot)

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

@@ -4,17 +4,24 @@
#include "AcceptInvitationAction.h"
#include "Event.h"
#include "ObjectAccessor.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "PlayerbotSecurity.h"
#include "WorldPacket.h"
bool AcceptInvitationAction::Execute(Event event)
{
Group* grp = bot->GetGroupInvite();
if (!grp)
return false;
WorldPacket packet = event.getPacket();
uint8 flag;
std::string name;
packet >> flag >> name;
Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID());
// Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID());
Player* inviter = ObjectAccessor::FindPlayerByName(name, true);
if (!inviter)
return false;

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,22 @@ void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
if (sPlayerbotAIConfig->autoLearnQuestSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
LearnQuestSpells(out);
if (sPlayerbotAIConfig->randomBotGuildTalk)
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
{
std::string toSay = "";
if (urand(0, 3))
toSay = "Ding !";
else
toSay = "Yay level " + std::to_string(bot->GetLevel()) + " !";
guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL);
}
}
}
void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out)

View File

@@ -2386,11 +2386,11 @@ static std::pair<uint32, uint32> AV_AllianceDefendObjectives[] =
static uint32 AB_AttackObjectives[] =
{
// Attack
{ BG_AB_NODE_STABLES },
{ BG_AB_NODE_BLACKSMITH },
{ BG_AB_NODE_FARM },
{ BG_AB_NODE_LUMBER_MILL },
{ BG_AB_NODE_GOLD_MINE }
BG_AB_NODE_STABLES,
BG_AB_NODE_BLACKSMITH,
BG_AB_NODE_FARM,
BG_AB_NODE_LUMBER_MILL,
BG_AB_NODE_GOLD_MINE
};
static std::tuple<uint32, uint32, uint32> EY_AttackObjectives[] =

View File

@@ -447,7 +447,7 @@ bool EmoteActionBase::ReceiveEmote(Player* source, uint32 emote, bool verbal)
case TEXT_EMOTE_RASP:
emoteId = EMOTE_ONESHOT_RUDE;
textEmote = TEXT_EMOTE_RASP;
emoteText = "Right back at you, bub!", LANG_UNIVERSAL;
emoteText = "Right back at you, bub!"; // , LANG_UNIVERSAL;
break;
case TEXT_EMOTE_ROAR:
case TEXT_EMOTE_THREATEN:
@@ -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

@@ -20,6 +20,7 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) {
if (!pet) {
return false;
}
bool toggled = false;
for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
{
if(itr->second.state == PETSPELL_REMOVED)
@@ -29,17 +30,29 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) {
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (spellInfo->IsPassive())
continue;
bool shouldApply = true;
// imp's spell, felhunte's intelligence, ghoul's leap, cat stealth
if (spellId == 4511 || spellId == 1742 ||
spellId == 54424 || spellId == 57564 || spellId == 57565 || spellId == 57566 || spellId == 57567 ||
spellId == 47482 || spellId == 24450) {
pet->ToggleAutocast(spellInfo, false);
} else {
pet->ToggleAutocast(spellInfo, true);
shouldApply = false;
}
bool isAutoCast = false;
for (unsigned int &m_autospell : pet->m_autospells)
{
if (m_autospell == spellId)
{
isAutoCast = true;
break;
}
}
if (shouldApply != isAutoCast) {
pet->ToggleAutocast(spellInfo, shouldApply);
toggled = true;
}
}
return true;
return toggled;
}
bool PetAttackAction::Execute(Event event)

View File

@@ -19,7 +19,7 @@ bool InviteToGroupAction::Execute(Event event)
bool InviteToGroupAction::Invite(Player* player)
{
if (!player)
if (!player || !player->IsInWorld())
return false;
if (!GET_PLAYERBOT_AI(player) && !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, true, player))

View File

@@ -103,14 +103,13 @@ bool LfgJoinAction::JoinLFG()
if (!dungeon || (dungeon->TypeID != LFG_TYPE_RANDOM && dungeon->TypeID != LFG_TYPE_DUNGEON && dungeon->TypeID != LFG_TYPE_HEROIC && dungeon->TypeID != LFG_TYPE_RAID))
continue;
uint32 botLevel = bot->GetLevel();
if (dungeon->MinLevel && botLevel < dungeon->MinLevel)
continue;
const auto& botLevel = bot->GetLevel();
if (dungeon->MinLevel && botLevel > dungeon->MinLevel + 10)
continue;
if (dungeon->MaxLevel && botLevel > dungeon->MaxLevel)
/*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)
)
continue;
selected.push_back(dungeon->ID);
@@ -126,41 +125,8 @@ bool LfgJoinAction::JoinLFG()
bool many = list.size() > 1;
LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(*list.begin());
/*for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i)
{
LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i);
if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM_DUNGEON && dungeon->type != LFG_TYPE_DUNGEON && dungeon->type != LFG_TYPE_HEROIC_DUNGEON &&
dungeon->type != LFG_TYPE_RAID))
continue;
uint32 botLevel = bot->GetLevel();
if (dungeon->MinLevel && botLevel < dungeon->MinLevel)
continue;
if (dungeon->MinLevel && botLevel > dungeon->MinLevel + 10)
continue;
if (dungeon->MaxLevel && botLevel > dungeon->MaxLevel)
continue;
if (heroic && !dungeon->difficulty)
continue;
if (rbotAId && dungeon->type != LFG_TYPE_RAID)
continue;
if (!random && !rbotAId && !heroic && dungeon->type != LFG_TYPE_DUNGEON)
continue;
list.insert(dungeon);
}
if (list.empty() && !random)
return false;*/
// check role for console msg
std::string _roles = "multiple roles";
uint32 roleMask = GetRoles();
if (roleMask & PLAYER_ROLE_TANK)
_roles = "TANK";
@@ -175,70 +141,8 @@ bool LfgJoinAction::JoinLFG()
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H",
bot->GetLevel(), bot->GetName().c_str(), _roles, many ? "several dungeons" : dungeon->Name[0]);
/*if (lfgState->IsSingleRole())
{
if (lfgState->HasRole(ROLE_TANK))
_roles = "TANK";
if (lfgState->HasRole(ROLE_HEALER))
_roles = "HEAL";
if (lfgState->HasRole(ROLE_DAMAGE))
_roles = "DPS";
}*/
/*LFGDungeonEntry const* dungeon;
if(!random)
dungeon = *list.begin();
bool many = list.size() > 1;
if (random)
{
LFGDungeonSet randList = sLFGMgr->GetRandomDungeonsForPlayer(bot);
for (LFGDungeonSet::const_iterator itr = randList.begin(); itr != randList.end(); ++itr)
{
LFGDungeonEntry const* dungeon = *itr;
if (!dungeon)
continue;
idx.push_back(dungeon->ID);
}
if (idx.empty())
return false;
// choose random dungeon
dungeon = sLFGDungeonStore.LookupEntry(idx[urand(0, idx.size() - 1)]);
list.insert(dungeon);
pState->SetType(LFG_TYPE_RANDOM_DUNGEON);
LOG_INFO("playerbots", "Bot {} {}:{} <{}>: queues LFG, Random Dungeon as {} ({})",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str(), _roles, dungeon->Name[0]);
return true;
}
else if (heroic)
{
pState->SetType(LFG_TYPE_HEROIC_DUNGEON);
LOG_INFO("playerbots", "Bot {} {}:{} <{}>: queues LFG, Heroic Dungeon as {} ({})",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H",
bot->GetLevel(), bot->GetName().c_str(), _roles, many ? "several dungeons" : dungeon->Name[0]);
}
else if (rbotAId)
{
pState->SetType(LFG_TYPE_RAID);
LOG_INFO("playerbots", "Bot {} {}:{} <{}>: queues LFG, RbotAId as {} ({})",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str(), _roles, many ? "several dungeons" : dungeon->Name[0]);
}
else
{
pState->SetType(LFG_TYPE_DUNGEON);
LOG_INFO("playerbots", "Bot {} {}:{} <{}>: queues LFG, Dungeon as {} ({})",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str(), _roles, many ? "several dungeons" : dungeon->Name[0]);
}*/
// Set RbotAId Browser comment
std::string const _gs = std::to_string(botAI->GetEquipGearScore(bot, false, false));
sLFGMgr->JoinLfg(bot, roleMask, list, _gs);
return true;

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,23 @@ 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->randomBotGuildTalk && bot->GetGuildId() && urand(0, 10) && proto->Quality >= ITEM_QUALITY_RARE)
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
{
std::string toSay = "";
if (urand(0, 3))
toSay = "Yay I looted " + chat->FormatItem(proto) + " !";
else
toSay = "Guess who got a " + chat->FormatItem(proto) + " ? Me !";
guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL);
}
}
// std::ostringstream out;
// out << "Looting " << chat->FormatItem(proto);
// botAI->TellMasterNoFacing(out.str());

View File

@@ -859,6 +859,21 @@ void MovementAction::UpdateMovementState()
if (bot->IsFlying())
bot->UpdateSpeed(MOVE_FLIGHT, true);
Transport* newTransport = bot->GetMap()->GetTransportForPos(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot);
if (newTransport != bot->GetTransport())
{
LOG_DEBUG("playerbots", "Bot {} is on a transport", bot->GetName());
if (bot->GetTransport())
bot->GetTransport()->RemovePassenger(bot, true);
if (newTransport)
newTransport->AddPassenger(bot, true);
bot->StopMovingOnCurrentPos();
}
// Temporary speed increase in group
//if (botAI->HasRealPlayerMaster())
//bot->SetSpeedRate(MOVE_RUN, 1.1f);
@@ -1565,7 +1580,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);
@@ -1605,7 +1620,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage()
continue;
}
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (spellInfo->IsPositive()) {
if (!spellInfo || spellInfo->IsPositive()) {
continue;
}
float radius = (float)goInfo->trap.diameter / 2;
@@ -1623,7 +1638,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);
@@ -1672,7 +1687,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,9 +672,9 @@ 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())
if (!bot->GetGuildId() || !sPlayerbotAIConfig->randomBotGuildTalk)
return;
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
@@ -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

@@ -74,7 +74,7 @@ bool SellAction::Execute(Event event)
return true;
}
if (text == "all")
if (text != "")
{
std::vector<Item *> items = parseItems(text, ITERATE_ITEMS_IN_BAGS);
for (Item *item : items)
@@ -84,7 +84,7 @@ bool SellAction::Execute(Event event)
return true;
}
botAI->TellError("Usage: s gray/*/vendor/all");
botAI->TellError("Usage: s gray/*/vendor/[item link]");
return false;
}

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
{
/*0x18*/ General = ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
/*0x3C*/ Trade = ChannelFlags::CHANNEL_FLAG_CITY | ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG | ChannelFlags::CHANNEL_FLAG_TRADE,
/*0x18*/ LocalDefence = ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
/*x038*/ GuildRecruitment = ChannelFlags::CHANNEL_FLAG_CITY | ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
/*0x50*/ 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, 3));
}
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, 3));
}
void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, bool guild)
@@ -237,58 +238,65 @@ void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, b
if (!cMgr)
return;
AreaTableEntry const* zone = sAreaTableStore.LookupEntry(bot->GetMap()->GetZoneId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()));
if (!zone) return;
/*AreaTableEntry const* area = sAreaTableStore.LookupEntry(bot->GetMap()->GetAreaId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()));
if (!area) return;*/
for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i)
{
ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i);
if (!channel) continue;
for (AreaTableEntry const* current_zone : sAreaTableStore)
// combine full channel name
char channelName[100];
Channel* chn = nullptr;
if (channel->ChannelID == 24 || (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], zone->area_name[_dbc_locale]);
chn = cMgr->GetChannel(channelName, bot);
}
if (!chn)
continue;
// skip world chat here
if (chn->GetName() == "World")
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;
if (flags != 0 && chn->GetFlags() != flags)
continue;
if (flags != 0 && chn->GetFlags() != flags)
continue;
// skip local defense and universal defense
if (chn->GetChannelId() == 22 || chn->GetChannelId() == 23)
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)
@@ -296,13 +304,13 @@ void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, b
if (Channel* worldChannel = cMgr->GetChannel("World", bot))
worldChannel->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
}
}
if (guild && bot->GetGuildId())
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
guild->BroadcastToGuild(bot->GetSession(), false, msg.c_str(), LANG_UNIVERSAL);
}
if (sPlayerbotAIConfig->randomBotGuildTalk && guild && bot->GetGuildId())
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
guild->BroadcastToGuild(bot->GetSession(), false, msg.c_str(), LANG_UNIVERSAL);
}
}
@@ -337,15 +345,88 @@ class FindTradeItemsVisitor : public IterateItemsVisitor
uint32 quality;
};
SuggestDungeonAction::SuggestDungeonAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest dungeon")
{
}
bool SuggestDungeonAction::Execute(Event event)
{
// TODO: use sPlayerbotDungeonSuggestionMgr
if (!sPlayerbotAIConfig->randomBotSuggestDungeons || bot->GetGroup()) return false;
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, 3));
return true;
}
SuggestTradeAction::SuggestTradeAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest trade")
{
}
bool SuggestTradeAction::Execute(Event event)
{
if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId())
return false;
uint32 quality = urand(0, 100);
if (quality > 95)
quality = ITEM_QUALITY_LEGENDARY;
@@ -400,13 +481,7 @@ 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, 2), 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

@@ -6,6 +6,7 @@
#include "Event.h"
#include "ChatHelper.h"
#include "Playerbots.h"
#include "World.h"
bool TellLosAction::Execute(Event event)
{
@@ -52,7 +53,7 @@ void TellLosAction::ListUnits(std::string const title, GuidVector units)
for (ObjectGuid const guid : units)
{
if (Unit* unit = botAI->GetUnit(guid)) {
botAI->TellMaster(unit->GetName());
botAI->TellMaster(unit->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()));
}
}
@@ -137,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

@@ -172,6 +172,7 @@ bool MaintenanceAction::Execute(Event event)
factory.ApplyEnchantAndGemsNew();
}
bot->DurabilityRepairAll(false, 1.0f, false);
bot->SendTalentsInfoData(false);
return true;
}
@@ -181,6 +182,7 @@ bool RemoveGlyphAction::Execute(Event event)
{
bot->SetGlyph(slotIndex, 0, true);
}
bot->SendTalentsInfoData(false);
return true;
}

View File

@@ -164,15 +164,15 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
bool SummonAction::Teleport(Player* summoner, Player* player)
{
Player* master = GetMaster();
if (master->GetMap() && master->GetMap()->IsDungeon()) {
InstanceMap* map = master->GetMap()->ToInstanceMap();
if (map) {
if (map->CannotEnter(player) == Map::CANNOT_ENTER_MAX_PLAYERS) {
botAI->TellError("I can not enter this dungeon");
return false;
}
}
}
// if (master->GetMap() && master->GetMap()->IsDungeon()) {
// InstanceMap* map = master->GetMap()->ToInstanceMap();
// if (map) {
// if (map->CannotEnter(player, true) == Map::CANNOT_ENTER_MAX_PLAYERS) {
// botAI->TellError("I can not enter this dungeon");
// return false;
// }
// }
// }
if (!summoner->IsBeingTeleported() && !player->IsBeingTeleported())
{
float followAngle = GetFollowAngle();
@@ -206,7 +206,8 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
return false;
}
if (bot->isDead() && sPlayerbotAIConfig->reviveBotWhenSummoned)
bool revive = sPlayerbotAIConfig->reviveBotWhenSummoned == 2 || (sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !master->IsInCombat() && master->IsAlive());
if (bot->isDead() && revive)
{
bot->ResurrectPlayer(1.0f, false);
botAI->TellMasterNoFacing("I live, again!");

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,26 @@ bool XpGainAction::Execute(Event event)
p >> groupBonus; // 8 group bonus
}
if (sPlayerbotAIConfig->randomBotGuildTalk && 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::string toSay = "";
if (urand(0, 3))
toSay = "Wow I just killed " + creature->GetName() + " !";
else
toSay = "Awesome that " + creature->GetName() + " went down quickly !";
guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL);
}
}
}
Unit* victim = nullptr;
if (guid)
victim = botAI->GetUnit(guid);

View File

@@ -72,12 +72,12 @@ UnholyDKStrategy::UnholyDKStrategy(PlayerbotAI* botAI) : GenericDKStrategy(botAI
NextAction** UnholyDKStrategy::getDefaultActions()
{
return NextAction::array(0,
new NextAction("death and decay", ACTION_DEFAULT + 1.0f),
new NextAction("scourge strike", ACTION_DEFAULT + 0.8f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.6f),
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.4f),
new NextAction("death and decay", ACTION_DEFAULT + 0.5f),
new NextAction("horn of winter", ACTION_DEFAULT + 0.4f),
new NextAction("summon gargoyle", ACTION_DEFAULT + 0.3f),
new NextAction("death coil", ACTION_DEFAULT + 0.2f),
new NextAction("melee", ACTION_DEFAULT),
new NextAction("scourge strike", ACTION_NORMAL + 0.1f),
new NextAction("melee", ACTION_DEFAULT),
nullptr);
}
@@ -91,8 +91,8 @@ void UnholyDKStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("no desolation", NextAction::array(0, new NextAction("blood strike", ACTION_HIGH + 4), nullptr)));
triggers.push_back(new TriggerNode("death and decay cooldown",
NextAction::array(0,
new NextAction("ghoul frenzy", ACTION_DEFAULT + 5.0f),
new NextAction("scourge strike", ACTION_DEFAULT + 4.0f),
new NextAction("ghoul frenzy", ACTION_NORMAL + 5.0f),
new NextAction("scourge strike", ACTION_NORMAL + 4.0f),
new NextAction("blood boil", ACTION_NORMAL + 3.0f),
new NextAction("icy touch", ACTION_NORMAL + 2.0f),
new NextAction("plague strike", ACTION_NORMAL + 1.0f),

View File

@@ -7,22 +7,24 @@
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->randomBotSuggestDungeons)
if (sPlayerbotAIConfig->randomBotEmote)
{
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("suggest dungeon", 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", 10.0f),
new NextAction("suggest dungeon", 3.0f),
new NextAction("suggest trade", 3.0f),
new NextAction("talk", 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

@@ -88,10 +88,10 @@ class FreezingTrapTrigger : public HasCcTargetTrigger
FreezingTrapTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "freezing trap") { }
};
class RapidFireTrigger : public BuffTrigger
class RapidFireTrigger : public BoostTrigger
{
public:
RapidFireTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "rapid fire") { }
RapidFireTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "rapid fire") { }
};
class TrueshotAuraTrigger : public BuffTrigger

View File

@@ -180,7 +180,7 @@ void MageBoostStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode("icy veins", NextAction::array(0, new NextAction("icy veins", 50.0f), nullptr)));
triggers.push_back(new TriggerNode("presence of mind", NextAction::array(0, new NextAction("presence of mind", 42.0f), nullptr)));
// triggers.push_back(new TriggerNode("arcane power", NextAction::array(0, new NextAction("arcane power", 41.0f), nullptr)));
// triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 41.0f), nullptr)));
triggers.push_back(new TriggerNode("mirror image", NextAction::array(0, new NextAction("mirror image", 41.0f), nullptr)));
}
void MageCcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -5,9 +5,7 @@
#ifndef _PLAYERBOT_SHAMANACTIONS_H
#define _PLAYERBOT_SHAMANACTIONS_H
#include "Define.h"
#include "GenericSpellActions.h"
#include "Playerbots.h"
#include "SharedDefines.h"
class PlayerbotAI;
@@ -362,31 +360,11 @@ class CastWindShearOnEnemyHealerAction : public CastSpellOnEnemyHealerAction
CastWindShearOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "wind shear") { }
};
// class CastCurePoisonAction : public CastCureSpellAction
// {
// public:
// CastCurePoisonAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "cure poison") { }
// };
CURE_ACTION(CastCurePoisonActionSham, "cure disease");
CURE_PARTY_ACTION(CastCurePoisonOnPartyActionSham, "cure poison", DISPEL_POISON);
// class CastCurePoisonOnPartyAction : public CurePartyMemberAction
// {
// public:
// CastCurePoisonOnPartyAction(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, "cure poison", DISPEL_POISON) { }
// };
// class CastCureDiseaseAction : public CastCureSpellAction
// {
// public:
// CastCureDiseaseAction(PlayerbotAI* botAI) : CastCureSpellAction(botAI, "cure disease") { }
// };
// class CastCureDiseaseOnPartyAction : public CurePartyMemberAction
// {
// public:
// CastCureDiseaseOnPartyAction(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, "cure disease", DISPEL_DISEASE) { }
// std::string const getName() override { return "cure disease on party"; }
// };
CURE_ACTION(CastCureDiseaseActionSham, "cure disease");
CURE_PARTY_ACTION(CastCureDiseaseOnPartyActionSham, "cure disease", DISPEL_DISEASE);
class CastLavaBurstAction : public CastSpellAction
{

View File

@@ -213,10 +213,10 @@ class ShamanAiObjectContextInternal : public NamedObjectContext<Action>
creators["heroism"] = &ShamanAiObjectContextInternal::heroism;
creators["bloodlust"] = &ShamanAiObjectContextInternal::bloodlust;
creators["elemental mastery"] = &ShamanAiObjectContextInternal::elemental_mastery;
// creators["cure disease"] = &ShamanAiObjectContextInternal::cure_disease;
// creators["cure disease on party"] = &ShamanAiObjectContextInternal::cure_disease_on_party;
// creators["cure poison"] = &ShamanAiObjectContextInternal::cure_poison;
// creators["cure poison on party"] = &ShamanAiObjectContextInternal::cure_poison_on_party;
creators["cure disease"] = &ShamanAiObjectContextInternal::cure_disease;
creators["cure disease on party"] = &ShamanAiObjectContextInternal::cure_disease_on_party;
creators["cure poison"] = &ShamanAiObjectContextInternal::cure_poison;
creators["cure poison on party"] = &ShamanAiObjectContextInternal::cure_poison_on_party;
creators["lava burst"] = &ShamanAiObjectContextInternal::lava_burst;
creators["earth shield on main tank"] = &ShamanAiObjectContextInternal::earth_shield_on_main_tank;
creators["fire elemental totem"] = &ShamanAiObjectContextInternal::fire_elemental_totem;
@@ -277,10 +277,10 @@ class ShamanAiObjectContextInternal : public NamedObjectContext<Action>
static Action* lava_lash(PlayerbotAI* botAI) { return new CastLavaLashAction(botAI); }
static Action* ancestral_spirit(PlayerbotAI* botAI) { return new CastAncestralSpiritAction(botAI); }
static Action* wind_shear_on_enemy_healer(PlayerbotAI* botAI) { return new CastWindShearOnEnemyHealerAction(botAI); }
// static Action* cure_poison(PlayerbotAI* botAI) { return new CastCurePoisonAction(botAI); }
// static Action* cure_poison_on_party(PlayerbotAI* botAI) { return new CastCurePoisonOnPartyAction(botAI); }
// static Action* cure_disease(PlayerbotAI* botAI) { return new CastCureDiseaseAction(botAI); }
// static Action* cure_disease_on_party(PlayerbotAI* botAI) { return new CastCureDiseaseOnPartyAction(botAI); }
static Action* cure_poison(PlayerbotAI* botAI) { return new CastCurePoisonActionSham(botAI); }
static Action* cure_poison_on_party(PlayerbotAI* botAI) { return new CastCurePoisonOnPartyActionSham(botAI); }
static Action* cure_disease(PlayerbotAI* botAI) { return new CastCureDiseaseActionSham(botAI); }
static Action* cure_disease_on_party(PlayerbotAI* botAI) { return new CastCureDiseaseOnPartyActionSham(botAI); }
static Action* lava_burst(PlayerbotAI* ai) { return new CastLavaBurstAction(ai); }
static Action* earth_shield_on_main_tank(PlayerbotAI* ai) { return new CastEarthShieldOnMainTankAction(ai); }
static Action* totem_of_wrath(PlayerbotAI* ai) { return new CastTotemOfWrathAction(ai); }

View File

@@ -160,7 +160,7 @@ bool GrindTargetValue::needForQuest(Unit* target)
{
QuestStatusData* questStatus = sTravelMgr->getQuestStatus(bot, questId);
if (questTemplate->GetQuestLevel() > bot->GetLevel())
if (questTemplate->GetQuestLevel() > bot->GetLevel()+5)
continue;
for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)

View File

@@ -6,6 +6,7 @@
#include "ChatHelper.h"
#include "Playerbots.h"
#include "Vehicle.h"
#include "World.h"
SpellIdValue::SpellIdValue(PlayerbotAI* botAI) : CalculatedValue<uint32>(botAI, "spell id", 20 * 1000)
{
@@ -34,7 +35,7 @@ uint32 SpellIdValue::Calculate()
char firstSymbol = tolower(namepart[0]);
int spellLength = wnamepart.length();
LocaleConstant loc = bot->GetSession()->GetSessionDbcLocale();
LocaleConstant loc = LOCALE_enUS;
std::set<uint32> spellIds;
for (PlayerSpellMap::iterator itr = bot->GetSpellMap().begin(); itr != bot->GetSpellMap().end(); ++itr)
@@ -189,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)