Merge pull request #433 from atidot3/bot_questing

Bot questing fixes
This commit is contained in:
Yunfan Li
2024-08-14 11:50:06 +08:00
committed by GitHub
61 changed files with 7477 additions and 1988 deletions

View File

@@ -128,6 +128,15 @@ AiPlayerbot.AllowPlayerBots = 0
# Allow/deny bots from your guild
AiPlayerbot.AllowGuildBots = 1
# Randombots will invite nearby bots to guilds
AiPlayerbot.RandomBotGuildNearby = 0
# Randombots will invite players to groups/raids/guilds.
AiPlayerbot.RandomBotInvitePlayer = 0
# Bots will chat in say/guild when they invite other bots to groups/raids/guilds
AiPlayerbot.InviteChat = 0
# Bots will be summoned to player when accept group invitation
AiPlayerbot.SummonWhenGroup = 1
@@ -458,9 +467,6 @@ AiPlayerbot.SyncLevelWithPlayers = 0
# Mark many quests <= Bot level as complete (slows down bot creation)
AiPlayerbot.PreQuests = 0
# Bots without a master will say their lines
AiPlayerbot.RandomBotSayWithoutMaster = 0
# Enable LFG for random bots
AiPlayerbot.RandomBotJoinLfg = 1
@@ -1266,27 +1272,107 @@ AiPlayerbot.CommandPrefix = ""
# Separator for bot chat commands
AiPlayerbot.CommandSeparator = "\\\\"
# Enable playerbot to talk in guild
AiPlayerbot.RandomBotGuildTalk = 1
# 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
# Chance to reply to toxic links with toxic links (0-100)
AiPlayerbot.ToxicLinksRepliesChance = 30
# Chance to reply to thunderfury with thunderfury (0-100)
AiPlayerbot.ThunderfuryRepliesChance = 40
# Bots will chat in guild about certain events (int) (0-100)
AiPlayerbot.GuildRepliesRate = 100
# Bots will chat in guild about certain events
AIPlayerbot.GuildFeedback = 1
# Bots without a master will say their lines
AiPlayerbot.RandomBotSayWithoutMaster = 0
#
#
#
####################################################################################################
####################################################################################################
#
# Broadcast rates
#
# 1 - to enable broadcasts globally, 0 - to disable (default 1)
# AiPlayerbot.EnableBroadcasts = 1
#
# all broadcast chances should be in range 0-30000
#
# value of 0 will disable this particular broadcast
# setting value to 30000 does not guarantee the broadcast, as there are some internal randoms as well
#
# setting channel broadcast chance to 0, will re-route most broadcasts to other available channels
# setting all channel broadcasts to 0 will disable most broadcasts
# AiPlayerbot.BroadcastToGuildGlobalChance = 30000
# AiPlayerbot.BroadcastToWorldGlobalChance = 30000
# AiPlayerbot.BroadcastToGeneralGlobalChance = 30000
# AiPlayerbot.BroadcastToTradeGlobalChance = 30000
# AiPlayerbot.BroadcastToLFGGlobalChance = 30000
# AiPlayerbot.BroadcastToLocalDefenseGlobalChance = 30000
# AiPlayerbot.BroadcastToWorldDefenseGlobalChance = 30000
# AiPlayerbot.BroadcastToGuildRecruitmentGlobalChance = 30000
#
# individual settings
# setting one of these to 0 will disable the particular broadcast
# AiPlayerbot.BroadcastChanceLootingItemPoor = 30
# AiPlayerbot.BroadcastChanceLootingItemNormal = 150
# AiPlayerbot.BroadcastChanceLootingItemUncommon = 10000
# AiPlayerbot.BroadcastChanceLootingItemRare = 20000
# AiPlayerbot.BroadcastChanceLootingItemEpic = 30000
# AiPlayerbot.BroadcastChanceLootingItemLegendary = 30000
# AiPlayerbot.BroadcastChanceLootingItemArtifact = 30000
#
# AiPlayerbot.BroadcastChanceQuestAccepted = 6000
# AiPlayerbot.BroadcastChanceQuestUpdateObjectiveCompleted = 300
# AiPlayerbot.BroadcastChanceQuestUpdateObjectiveProgress = 300
# AiPlayerbot.BroadcastChanceQuestUpdateFailedTimer = 300
# AiPlayerbot.BroadcastChanceQuestUpdateComplete = 1000
# AiPlayerbot.BroadcastChanceQuestTurnedIn = 10000
#
# AiPlayerbot.BroadcastChanceKillNormal = 30
# AiPlayerbot.BroadcastChanceKillElite = 300
# AiPlayerbot.BroadcastChanceKillRareelite = 3000
# AiPlayerbot.BroadcastChanceKillWorldboss = 20000
# AiPlayerbot.BroadcastChanceKillRare = 10000
# AiPlayerbot.BroadcastChanceKillUnknown = 100
# AiPlayerbot.BroadcastChanceKillPet = 10
# AiPlayerbot.BroadcastChanceKillPlayer = 30
#
# AiPlayerbot.BroadcastChanceLevelupGeneric = 20000
# AiPlayerbot.BroadcastChanceLevelupTenX = 30000
# AiPlayerbot.BroadcastChanceLevelupMaxLevel = 30000
#
# AiPlayerbot.BroadcastChanceSuggestInstance = 5000
# AiPlayerbot.BroadcastChanceSuggestQuest = 10000
# AiPlayerbot.BroadcastChanceSuggestGrindMaterials = 5000
# AiPlayerbot.BroadcastChanceSuggestGrindReputation = 5000
# AiPlayerbot.BroadcastChanceSuggestSell = 300
# AiPlayerbot.BroadcastChanceSuggestSomething = 30000
#
# Very rude speeches
# AiPlayerbot.BroadcastChanceSuggestSomethingToxic = 0
#
# Specifically for "<word> [item link]"
# AiPlayerbot.BroadcastChanceSuggestToxicLinks = 0
#
# prefix is used as a word in "<word> [item link]"
# AiPlayerbot.ToxicLinksPrefix = gnomes
#
# chance to suggest thunderfury
# AiPlayerbot.BroadcastChanceSuggestThunderfury = 1
#
# does not depend on global chance
# AiPlayerbot.BroadcastChanceGuildManagement = 30000
#
####################################################################################################
####################################################################################################
# LOGS
#

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -276,7 +276,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
if (!player->InBattleground())
{
engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", "emote", nullptr);
engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", nullptr);
}
if (sPlayerbotAIConfig->autoSaveMana)
{
@@ -596,6 +596,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategies("nc", "food", "chat", "follow", "default", "quest", "loot", "gather", "duel",
"buff", "mount", "emote", nullptr);
}
if (sPlayerbotAIConfig->autoSaveMana)
{
nonCombatEngine->addStrategy("auto save mana");

920
src/BroadcastHelper.cpp Normal file
View File

@@ -0,0 +1,920 @@
#include "Playerbots.h"
#include "BroadcastHelper.h"
#include "ServerFacade.h"
#include "Channel.h"
#include "AiFactory.h"
BroadcastHelper::BroadcastHelper() {}
uint8 BroadcastHelper::GetLocale()
{
uint8 locale = sWorld->GetDefaultDbcLocale();
// -- In case we're using auto detect on config file^M
if (locale >= MAX_LOCALES)
locale = LocaleConstant::LOCALE_enUS;
return locale;
}
bool BroadcastHelper::BroadcastTest(PlayerbotAI* ai, Player* bot)
{
//return something to ignore the logic
return false;
std::map<std::string, std::string> placeholders;
placeholders["%rand1"] = std::to_string(urand(0, 1));
placeholders["%rand2"] = std::to_string(urand(0, 1));
placeholders["%rand3"] = std::to_string(urand(0, 1));
int32 rand = urand(0, 1);
if (rand == 1 && ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE))
return true;
else if (ai->SayToChannel(BOT_TEXT2("Posted to GuildRecruitment, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GUILD_RECRUITMENT))
return true;
return ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE);
//int32 rand = urand(1, 8);
if (rand == 1 && ai->SayToGuild(BOT_TEXT2("Posted to guild, %rand1, %rand2, %rand3", placeholders)))
return true;
else if (rand == 2 && ai->SayToWorld(BOT_TEXT2("Posted to world, %rand1, %rand2, %rand3", placeholders)))
return true;
else if (rand == 3 && ai->SayToChannel(BOT_TEXT2("Posted to general, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GENERAL))
return true;
else if (rand == 4 && ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE))
return true;
else if (rand == 5 && ai->SayToChannel(BOT_TEXT2("Posted to LFG, %rand1, %rand2, %rand3", placeholders), ChatChannelId::LOOKING_FOR_GROUP))
return true;
else if (rand == 6 && ai->SayToChannel(BOT_TEXT2("Posted to LocalDefense, %rand1, %rand2, %rand3", placeholders), ChatChannelId::LOCAL_DEFENSE))
return true;
else if (rand == 7 && ai->SayToChannel(BOT_TEXT2("Posted to WorldDefense, %rand1, %rand2, %rand3", placeholders), ChatChannelId::WORLD_DEFENSE))
return true;
else if (rand == 8 && ai->SayToChannel(BOT_TEXT2("Posted to GuildRecruitment, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GUILD_RECRUITMENT))
return true;
return false;
}
/**
@param toChannels - map of (ToChannel, chance), where chance is in range 0-100 as uint32 (unless global chance is not 100%)
@return true if said to the channel, false otherwise
*/
bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::string message, std::list<std::pair<ToChannel, uint32>> toChannels)
{
if (message.empty())
{
return false;
}
for (const auto& pair : toChannels)
{
uint32 roll = urand(1, 100);
uint32 chance = pair.second;
uint32 broadcastRoll = urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue);
switch (pair.first)
{
case TO_GUILD:
{
if (roll <= chance
&& broadcastRoll <= sPlayerbotAIConfig->broadcastToGuildGlobalChance
&& ai->SayToGuild(message))
{
return true;
}
break;
}
case TO_WORLD:
{
if (roll <= chance
&& broadcastRoll <= sPlayerbotAIConfig->broadcastToWorldGlobalChance
&& ai->SayToWorld(message))
{
return true;
}
break;
}
case TO_GENERAL:
{
if (roll <= chance
&& broadcastRoll <= sPlayerbotAIConfig->broadcastToGeneralGlobalChance
&& ai->SayToChannel(message, ChatChannelId::GENERAL))
{
return true;
}
break;
}
case TO_TRADE:
{
if (roll <= chance
&& broadcastRoll <= sPlayerbotAIConfig->broadcastToTradeGlobalChance
&& ai->SayToChannel(message, ChatChannelId::TRADE))
{
return true;
}
break;
}
case TO_LOOKING_FOR_GROUP:
{
if (roll <= chance
&& broadcastRoll <= sPlayerbotAIConfig->broadcastToLFGGlobalChance
&& ai->SayToChannel(message, ChatChannelId::LOOKING_FOR_GROUP))
{
return true;
}
break;
}
case TO_LOCAL_DEFENSE:
{
if (roll <= chance
&& broadcastRoll <= sPlayerbotAIConfig->broadcastToLocalDefenseGlobalChance
&& ai->SayToChannel(message, ChatChannelId::LOCAL_DEFENSE))
{
return true;
}
break;
}
case TO_WORLD_DEFENSE:
{
if (roll <= chance
&& broadcastRoll <= sPlayerbotAIConfig->broadcastToWorldDefenseGlobalChance
&& ai->SayToChannel(message, ChatChannelId::WORLD_DEFENSE))
{
return true;
}
break;
}
case TO_GUILD_RECRUITMENT:
{
if (roll <= chance
&& broadcastRoll <= sPlayerbotAIConfig->broadcastToGuildRecruitmentGlobalChance
&& ai->SayToChannel(message, ChatChannelId::GUILD_RECRUITMENT))
{
return true;
}
break;
}
default:
break;
}
}
return false;
}
bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, const ItemTemplate *proto)
{
std::map<std::string, std::string> placeholders;
placeholders["%item_link"] = ai->GetChatHelper()->FormatItem(proto);
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
switch (proto->Quality)
{
case ITEM_QUALITY_POOR:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemPoor)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_looting_item_poor", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case ITEM_QUALITY_NORMAL:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemNormal)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_looting_item_normal", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case ITEM_QUALITY_UNCOMMON:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemUncommon)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_looting_item_uncommon", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case ITEM_QUALITY_RARE:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemRare)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_looting_item_rare", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case ITEM_QUALITY_EPIC:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemEpic)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_looting_item_epic", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case ITEM_QUALITY_LEGENDARY:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemLegendary)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_looting_item_legendary", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case ITEM_QUALITY_ARTIFACT:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemArtifact)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_looting_item_artifact", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
default:
break;
}
return false;
}
bool BroadcastHelper::BroadcastQuestAccepted(PlayerbotAI* ai, Player* bot, const Quest* quest)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestAccepted)
{
std::map<std::string, std::string> placeholders;
placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest);
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_accepted_generic", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastQuestUpdateAddKill(PlayerbotAI* ai, Player* bot, Quest const* quest, uint32 availableCount, uint32 requiredCount, std::string obectiveName)
{
std::map<std::string, std::string> placeholders;
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest);
placeholders["%quest_obj_name"] = obectiveName;
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
placeholders["%quest_obj_available"] = std::to_string(availableCount);
placeholders["%quest_obj_required"] = std::to_string(requiredCount);
placeholders["%quest_obj_missing"] = std::to_string(requiredCount - std::min(availableCount, requiredCount));
placeholders["%quest_obj_full_formatted"] = ai->GetChatHelper()->FormatQuestObjective(obectiveName, availableCount, requiredCount);
if (availableCount < requiredCount
&& urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateObjectiveProgress)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_update_add_kill_objective_progress", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
else if (availableCount == requiredCount
&& urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateObjectiveCompleted)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_update_add_kill_objective_completed", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastQuestUpdateAddItem(PlayerbotAI* ai, Player* bot, Quest const* quest, uint32 availableCount, uint32 requiredCount, const ItemTemplate* proto)
{
std::map<std::string, std::string> placeholders;
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest);
std::string itemLinkFormatted = ai->GetChatHelper()->FormatItem(proto);
placeholders["%item_link"] = itemLinkFormatted;
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
placeholders["%quest_obj_available"] = std::to_string(availableCount);
placeholders["%quest_obj_required"] = std::to_string(requiredCount);
placeholders["%quest_obj_missing"] = std::to_string(requiredCount - std::min(availableCount, requiredCount));
placeholders["%quest_obj_full_formatted"] = ai->GetChatHelper()->FormatQuestObjective(itemLinkFormatted, availableCount, requiredCount);
if (availableCount < requiredCount
&& urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateObjectiveProgress)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_update_add_item_objective_progress", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
else if (availableCount == requiredCount
&& urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateObjectiveCompleted)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_update_add_item_objective_completed", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastQuestUpdateFailedTimer(PlayerbotAI* ai, Player* bot, Quest const* quest)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateFailedTimer)
{
std::map<std::string, std::string> placeholders;
placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest);
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_update_failed_timer", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastQuestUpdateComplete(PlayerbotAI* ai, Player* bot, Quest const* quest)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateComplete)
{
std::map<std::string, std::string> placeholders;
placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest);
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_update_complete", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastQuestTurnedIn(PlayerbotAI* ai, Player* bot, Quest const* quest)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestTurnedIn)
{
std::map<std::string, std::string> placeholders;
placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest);
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_turned_in", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastKill(PlayerbotAI* ai, Player* bot, Creature *creature)
{
std::map<std::string, std::string> placeholders;
placeholders["%victim_name"] = creature->GetName();
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%victim_level"] = creature->GetLevel();
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
//if ((creature->IsElite() && !creature->GetMap()->IsDungeon())
//if creature->IsWorldBoss()
//if creature->GetLevel() > DEFAULT_MAX_LEVEL + 1
//if creature->GetLevel() > bot->GetLevel() + 4
if (creature->IsPet())
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillPet)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_killed_pet", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
}
else if (creature->IsPlayer())
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillPlayer)
{
placeholders["%victim_class"] = ai->GetChatHelper()->FormatClass(creature->getClass());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_killed_player", placeholders),
{ {TO_WORLD_DEFENSE, 50}, {TO_LOCAL_DEFENSE, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
}
else
{
switch (creature->GetCreatureTemplate()->rank)
{
case CREATURE_ELITE_NORMAL:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillNormal)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_killed_normal", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case CREATURE_ELITE_ELITE:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillElite)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_killed_elite", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case CREATURE_ELITE_RAREELITE:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillRareelite)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_killed_rareelite", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case CREATURE_ELITE_WORLDBOSS:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillWorldboss)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_killed_worldboss", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case CREATURE_ELITE_RARE:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillRare)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_killed_rare", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
case CREATURE_UNKNOWN:
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillUnknown)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_killed_unknown", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
break;
default:
break;
}
}
return false;
}
bool BroadcastHelper::BroadcastLevelup(PlayerbotAI* ai, Player* bot)
{
uint32 level = bot->GetLevel();
std::map<std::string, std::string> placeholders;
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(level);
if (level == sPlayerbotAIConfig->randomBotMaxLevel
&& urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLevelupMaxLevel)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_levelup_max_level", placeholders),
{ {TO_GUILD, 30}, {TO_WORLD, 90}, {TO_GENERAL, 100} }
);
}
// It's divisible by 10
else if (level % 10 == 0
&& urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLevelupTenX)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_levelup_10x", placeholders),
{ {TO_GUILD, 50}, {TO_WORLD, 90}, {TO_GENERAL, 100} }
);
}
else if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLevelupGeneric)
{
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_levelup_generic", placeholders),
{ {TO_GUILD, 90}, {TO_WORLD, 90}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastGuildMemberPromotion(PlayerbotAI* ai, Player* bot, Player* player)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceGuildManagement)
{
std::map<std::string, std::string> placeholders;
placeholders["%other_name"] = player->GetName();
placeholders["%other_class"] = ai->GetChatHelper()->FormatClass(player->getClass());
placeholders["%other_race"] = ai->GetChatHelper()->FormatRace(player->getRace());
placeholders["%other_level"] = std::to_string(player->GetLevel());
return ai->SayToGuild(BOT_TEXT2("broadcast_guild_promotion", placeholders));
}
return false;
}
bool BroadcastHelper::BroadcastGuildMemberDemotion(PlayerbotAI* ai, Player* bot, Player* player)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceGuildManagement)
{
std::map<std::string, std::string> placeholders;
placeholders["%other_name"] = player->GetName();
placeholders["%other_class"] = ai->GetChatHelper()->FormatClass(player->getClass());
placeholders["%other_race"] = ai->GetChatHelper()->FormatRace(player->getRace());
placeholders["%other_level"] = std::to_string(player->GetLevel());
return ai->SayToGuild(BOT_TEXT2("broadcast_guild_demotion", placeholders));
}
return false;
}
bool BroadcastHelper::BroadcastGuildGroupOrRaidInvite(PlayerbotAI* ai, Player* bot, Player* player, Group* group)
{
std::map<std::string, std::string> placeholders;
placeholders["%name"] = player->GetName();
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
//TODO move texts to sql!
if (group && group->isRaidGroup())
{
if (urand(0, 3))
{
return ai->SayToGuild(BOT_TEXT2("Hey anyone want to raid in %zone_name", placeholders));
}
else
{
return ai->SayToGuild(BOT_TEXT2("Hey %name I'm raiding in %zone_name do you wan to join me?", placeholders));
}
}
else
{
//(bot->GetTeam() == ALLIANCE ? LANG_COMMON : LANG_ORCISH)
if (urand(0, 3))
{
return ai->SayToGuild(BOT_TEXT2("Hey anyone wanna group up in %zone_name?", placeholders));
}
else
{
return ai->SayToGuild(BOT_TEXT2("Hey %name do you want join my group? I'm heading for %zone_name", placeholders));
}
}
return false;
}
bool BroadcastHelper::BroadcastSuggestInstance(PlayerbotAI* ai, std::vector<std::string>& allowedInstances, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestInstance)
{
std::map<std::string, std::string> placeholders;
placeholders["%my_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_name"] = itemout.str();
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("suggest_instance", placeholders),
{ {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastSuggestQuest(PlayerbotAI* ai, std::vector<uint32>& quests, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestQuest)
{
int index = rand() % quests.size();
Quest const* quest = sObjectMgr->GetQuestTemplate(quests[index]);
std::map<std::string, std::string> placeholders;
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest);
placeholders["%quest_level"] = std::to_string(quest->GetQuestLevel());
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("suggest_quest", placeholders),
{ {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastSuggestGrindMaterials(PlayerbotAI* ai, std::string item, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestGrindMaterials)
{
std::map<std::string, std::string> placeholders;
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%category"] = item;
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("suggest_trade", placeholders),
{ {TO_TRADE, 50}, {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastSuggestGrindReputation(PlayerbotAI* ai, std::vector<std::string> levels, std::vector<std::string> allowedFactions, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestGrindReputation)
{
std::map<std::string, std::string> placeholders;
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%rep_level"] = levels[urand(0, 2)];
std::ostringstream rnd; rnd << urand(1, 5) << "K";
placeholders["%rndK"] = rnd.str();
std::ostringstream itemout;
//itemout << "|c004040b0" << allowedFactions[urand(0, allowedFactions.size() - 1)] << "|r";
itemout << allowedFactions[urand(0, allowedFactions.size() - 1)];
placeholders["%faction"] = itemout.str();
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("suggest_faction", placeholders),
{ {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastSuggestSell(PlayerbotAI* ai, const ItemTemplate* proto, uint32 count, uint32 price, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestSell)
{
std::map<std::string, std::string> placeholders;
placeholders["%item_link"] = ai->GetChatHelper()->FormatItem(proto, 0);
placeholders["%item_formatted_link"] = ai->GetChatHelper()->FormatItem(proto, count);
placeholders["%item_count"] = std::to_string(count);
placeholders["%cost_gold"] = ai->GetChatHelper()->formatMoney(price);
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("suggest_sell", placeholders),
{ {TO_TRADE, 90}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastSuggestSomething(PlayerbotAI* ai, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestSomething)
{
std::map<std::string, std::string> placeholders;
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("suggest_something", placeholders),
{ {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastSuggestSomethingToxic(PlayerbotAI* ai, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestSomethingToxic)
{
//items
std::vector<Item*> botItems = ai->GetInventoryAndEquippedItems();
std::map<std::string, std::string> placeholders;
placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? ai->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : BOT_TEXT1("string_empty_link");
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("suggest_something_toxic", placeholders),
{ {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastSuggestToxicLinks(PlayerbotAI* ai, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestToxicLinks)
{
//quests
std::vector<uint32> incompleteQuests;
for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 questId = bot->GetQuestSlotQuestId(slot);
if (!questId)
continue;
QuestStatus status = bot->GetQuestStatus(questId);
if (status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_NONE)
incompleteQuests.push_back(questId);
}
//items
std::vector<Item*> botItems = ai->GetInventoryAndEquippedItems();
//spells
//?
std::map<std::string, std::string> placeholders;
placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? ai->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : BOT_TEXT1("string_empty_link");
placeholders["%prefix"] = sPlayerbotAIConfig->toxicLinksPrefix;
if (incompleteQuests.size() > 0)
{
Quest const* quest = sObjectMgr->GetQuestTemplate(incompleteQuests[rand() % incompleteQuests.size()]);
placeholders["%random_taken_quest_or_item_link"] = ai->GetChatHelper()->FormatQuest(quest);
}
else
{
placeholders["%random_taken_quest_or_item_link"] = placeholders["%random_inventory_item_link"];
}
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
AreaTableEntry const* current_area = ai->GetCurrentArea();
AreaTableEntry const* current_zone = ai->GetCurrentZone();
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("suggest_toxic_links", placeholders),
{ {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} }
);
}
return false;
}
bool BroadcastHelper::BroadcastSuggestThunderfury(PlayerbotAI* ai, Player* bot)
{
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestThunderfury)
{
std::map<std::string, std::string> placeholders;
ItemTemplate const* thunderfuryProto = sObjectMgr->GetItemTemplate(19019);
placeholders["%thunderfury_link"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(thunderfuryProto);
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("thunderfury_spam", placeholders),
{ {TO_WORLD, 70}, {TO_GENERAL, 100} }
);
}
return false;
}

148
src/BroadcastHelper.h Normal file
View File

@@ -0,0 +1,148 @@
#pragma once
class PlayerbotAI;
class Player;
class ItemTemplate;
class Quest;
class Creature;
class Group;
class BroadcastHelper
{
public:
BroadcastHelper();
public:
enum ToChannel
{
TO_GUILD = 1,
TO_WORLD = 2,
TO_GENERAL = 3,
TO_TRADE = 4,
TO_LOOKING_FOR_GROUP = 5,
TO_LOCAL_DEFENSE = 6,
TO_WORLD_DEFENSE = 7,
TO_GUILD_RECRUITMENT = 8
};
static uint8_t GetLocale();
static bool BroadcastTest(
PlayerbotAI* ai,
Player* bot
);
static bool BroadcastToChannelWithGlobalChance(
PlayerbotAI* ai,
std::string message,
std::list<std::pair<ToChannel, uint32_t>> toChannels
);
static bool BroadcastLootingItem(
PlayerbotAI* ai,
Player* bot,
const ItemTemplate* proto
);
static bool BroadcastQuestAccepted(
PlayerbotAI* ai,
Player* bot,
const Quest* quest
);
static bool BroadcastQuestUpdateAddKill(
PlayerbotAI* ai,
Player* bot,
Quest const* quest,
uint32_t availableCount,
uint32_t requiredCount,
std::string obectiveName
);
static bool BroadcastQuestUpdateAddItem(
PlayerbotAI* ai,
Player* bot,
Quest const* quest,
uint32_t availableCount,
uint32_t requiredCount,
const ItemTemplate* proto
);
static bool BroadcastQuestUpdateFailedTimer(
PlayerbotAI* ai,
Player* bot,
Quest const* quest
);
static bool BroadcastQuestUpdateComplete(
PlayerbotAI* ai,
Player* bot,
Quest const* quest
);
static bool BroadcastQuestTurnedIn(
PlayerbotAI* ai,
Player* bot,
Quest const* quest
);
static bool BroadcastKill(
PlayerbotAI* ai,
Player* bot,
Creature* creature
);
static bool BroadcastLevelup(
PlayerbotAI* ai,
Player* bot
);
static bool BroadcastGuildMemberPromotion(
PlayerbotAI* ai,
Player* bot,
Player* player
);
static bool BroadcastGuildMemberDemotion(
PlayerbotAI* ai,
Player* bot,
Player* player
);
static bool BroadcastGuildGroupOrRaidInvite(
PlayerbotAI* ai,
Player* bot,
Player* player,
Group* group
);
static bool BroadcastSuggestInstance(
PlayerbotAI* ai,
std::vector<std::string>& allowedInstances,
Player* bot
);
static bool BroadcastSuggestQuest(
PlayerbotAI* ai,
std::vector<uint32>& quests,
Player* bot
);
static bool BroadcastSuggestGrindMaterials(
PlayerbotAI* ai,
std::string item,
Player* bot
);
static bool BroadcastSuggestGrindReputation(
PlayerbotAI* ai,
std::vector<std::string> levels,
std::vector<std::string> allowedFactions,
Player* bot
);
static bool BroadcastSuggestSell(
PlayerbotAI* ai,
const ItemTemplate* proto,
uint32_t count,
uint32_t price,
Player* bot
);
static bool BroadcastSuggestSomething(
PlayerbotAI* ai,
Player* bot
);
static bool BroadcastSuggestSomethingToxic(
PlayerbotAI* ai,
Player* bot
);
static bool BroadcastSuggestToxicLinks(
PlayerbotAI* ai,
Player* bot
);
static bool BroadcastSuggestThunderfury(
PlayerbotAI* ai,
Player* bot
);
};

View File

@@ -9,6 +9,8 @@
#include "Playerbots.h"
#include "SpellInfo.h"
#include <regex>
std::map<std::string, uint32> ChatHelper::consumableSubClasses;
std::map<std::string, uint32> ChatHelper::tradeSubClasses;
std::map<std::string, uint32> ChatHelper::itemQualities;
@@ -219,6 +221,26 @@ std::string const ChatHelper::formatMoney(uint32 copper)
return out.str();
}
std::string ChatHelper::parseValue(const std::string& type, const std::string& text)
{
std::string retString;
std::string pattern = "Hvalue:" + type + ":";
int pos = text.find(pattern, 0);
if (pos == -1)
return retString;
pos += pattern.size();
int endPos = text.find('|', pos);
if (endPos == -1)
return retString;
retString = text.substr(pos, endPos - pos);
return retString;
}
uint32 ChatHelper::parseMoney(std::string const text)
{
// if user specified money in ##g##s##c format
@@ -570,3 +592,35 @@ void ChatHelper::eraseAllSubStr(std::string& mainStr, std::string const toErase)
mainStr.erase(pos, toErase.length());
}
}
std::set<uint32> ChatHelper::ExtractAllQuestIds(const std::string& text)
{
std::set<uint32> ids;
std::regex rgx("Hquest:[0-9]+");
auto begin = std::sregex_iterator(text.begin(), text.end(), rgx);
auto end = std::sregex_iterator();
for (std::sregex_iterator i = begin; i != end; ++i)
{
std::smatch match = *i;
ids.insert(std::stoi(match.str().erase(0, 7)));
}
return ids;
}
std::set<uint32> ChatHelper::ExtractAllItemIds(const std::string& text)
{
std::set<uint32> ids;
std::regex rgx("Hitem:[0-9]+");
auto begin = std::sregex_iterator(text.begin(), text.end(), rgx);
auto end = std::sregex_iterator();
for (std::sregex_iterator i = begin; i != end; ++i)
{
std::smatch match = *i;
ids.insert(std::stoi(match.str().erase(0, 6)));
}
return ids;
}

View File

@@ -34,6 +34,8 @@ public:
static uint32 parseMoney(std::string const text);
static ItemIds parseItems(std::string const text);
uint32 parseSpell(std::string const text);
static std::string parseValue(const std::string& type, const std::string& text);
static std::string const FormatQuest(Quest const* quest);
static std::string const FormatItem(ItemTemplate const* proto, uint32 count = 0, uint32 total = 0);
static std::string const FormatQItem(uint32 itemId);
@@ -62,6 +64,9 @@ public:
void eraseAllSubStr(std::string& mainStr, std::string const toErase);
static std::set<uint32> ExtractAllQuestIds(const std::string& text);
static std::set<uint32> ExtractAllItemIds(const std::string& text);
private:
static std::map<std::string, uint32> consumableSubClasses;
static std::map<std::string, uint32> tradeSubClasses;

File diff suppressed because it is too large Load Diff

View File

@@ -106,6 +106,58 @@ public:
void* param;
float minValue;
};
enum ChatChannelSource
{
SRC_GUILD,
SRC_WORLD,
SRC_GENERAL,
SRC_TRADE,
SRC_LOOKING_FOR_GROUP,
SRC_LOCAL_DEFENSE,
SRC_WORLD_DEFENSE,
SRC_GUILD_RECRUITMENT,
SRC_SAY,
SRC_WHISPER,
SRC_EMOTE,
SRC_TEXT_EMOTE,
SRC_YELL,
SRC_PARTY,
SRC_RAID,
SRC_UNDEFINED
};
static std::map<ChatChannelSource, std::string> ChatChannelSourceStr = {
{ SRC_GUILD, "SRC_GUILD"},
{ SRC_WORLD, "SRC_WORLD"},
{ SRC_GENERAL, "SRC_GENERAL"},
{ SRC_TRADE, "SRC_TRADE"},
{ SRC_LOOKING_FOR_GROUP, "SRC_LOOKING_FOR_GROUP"},
{ SRC_LOCAL_DEFENSE, "SRC_LOCAL_DEFENSE"},
{ SRC_WORLD_DEFENSE, "SRC_WORLD_DEFENSE"},
{ SRC_GUILD_RECRUITMENT, "SRC_GUILD_RECRUITMENT"},
{ SRC_SAY, "SRC_SAY"},
{ SRC_WHISPER, "SRC_WHISPER"},
{ SRC_EMOTE, "SRC_EMOTE"},
{ SRC_TEXT_EMOTE, "SRC_TEXT_EMOTE"},
{ SRC_YELL, "SRC_YELL"},
{ SRC_PARTY, "SRC_PARTY"},
{ SRC_RAID, "SRC_RAID"},
{ SRC_UNDEFINED, "SRC_UNDEFINED"}
};
enum ChatChannelId
{
GENERAL = 1,
TRADE = 2,
LOCAL_DEFENSE = 22,
WORLD_DEFENSE = 23,
LOOKING_FOR_GROUP = 26,
GUILD_RECRUITMENT = 25,
};
enum RoguePoisonDisplayId
{
@@ -307,10 +359,10 @@ public:
{
}
std::string const GetCommand() { return command; }
const std::string& GetCommand() { return command; }
Player* GetOwner() { return owner; }
uint32 GetType() { return type; }
time_t GetTime() { return time; }
uint32& GetType() { return type; }
time_t& GetTime() { return time; }
private:
std::string const command;
@@ -331,16 +383,14 @@ public:
std::string const HandleRemoteCommand(std::string const command);
void HandleCommand(uint32 type, std::string const text, Player* fromPlayer);
void QueueChatResponse(uint8 msgtype, ObjectGuid guid1, ObjectGuid guid2, std::string message, std::string chanName,
std::string name);
void HandleBotOutgoingPacket(WorldPacket const& packet);
void QueueChatResponse(const ChatQueuedReply reply);
void HandleBotOutgoingPacket(WorldPacket const& packet);
void HandleMasterIncomingPacket(WorldPacket const& packet);
void HandleMasterOutgoingPacket(WorldPacket const& packet);
void HandleTeleportAck();
void ChangeEngine(BotState type);
void DoNextAction(bool minimal = false);
virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false,
std::string const qualifier = "");
virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false, std::string const qualifier = "");
void ChangeStrategy(std::string const name, BotState type);
void ClearStrategies(BotState type);
std::vector<std::string> GetStrategies(BotState type);
@@ -377,13 +427,25 @@ public:
GameObject* GetGameObject(ObjectGuid guid);
// static GameObject* GetGameObject(GameObjectData const* gameObjectData);
WorldObject* GetWorldObject(ObjectGuid guid);
std::vector<Player*> GetPlayersInGroup();
const AreaTableEntry* GetCurrentArea();
const AreaTableEntry* GetCurrentZone();
std::string GetLocalizedAreaName(const AreaTableEntry* entry);
bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMasterNoFacing(std::ostringstream& stream,
PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMasterNoFacing(std::string const text,
PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMasterNoFacing(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMasterNoFacing(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellError(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool SayToGuild(const std::string& msg);
bool SayToWorld(const std::string& msg);
bool SayToChannel(const std::string& msg, const ChatChannelId& chanId);
bool SayToParty(const std::string& msg);
bool SayToRaid(const std::string& msg);
bool Yell(const std::string& msg);
bool Say(const std::string& msg);
bool Whisper(const std::string& msg, const std::string& receiverName);
void SpellInterrupted(uint32 spellid);
int32 CalculateGlobalCooldown(uint32 spellid);
void InterruptSpell();
@@ -461,11 +523,12 @@ public:
bool AllowActive(ActivityType activityType);
bool AllowActivity(ActivityType activityType = ALL_ACTIVITY, bool checkNow = false);
bool HasCheat(BotCheatMask mask)
{
return ((uint32)mask & (uint32)cheatMask) != 0 ||
((uint32)mask & (uint32)sPlayerbotAIConfig->botCheatMask) != 0;
}
//Check if player is safe to use.
bool IsSafe(Player* player);
bool IsSafe(WorldObject* obj);
ChatChannelSource GetChatChannelSource(Player* bot, uint32 type, std::string channelName);
bool HasCheat(BotCheatMask mask) { return ((uint32)mask & (uint32)cheatMask) != 0 || ((uint32)mask & (uint32)sPlayerbotAIConfig->botCheatMask) != 0; }
BotCheatMask GetCheat() { return cheatMask; }
void SetCheat(BotCheatMask mask) { cheatMask = mask; }
@@ -486,23 +549,34 @@ public:
bool EqualLowercaseName(std::string s1, std::string s2);
InventoryResult CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool not_loading = true) const;
uint8 FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) const;
std::vector<Item*> GetInventoryAndEquippedItems();
std::vector<Item*> GetInventoryItems();
uint32 GetInventoryItemsCountWithId(uint32 itemId);
bool HasItemInInventory(uint32 itemId);
std::vector<std::pair<const Quest*, uint32>> GetCurrentQuestsRequiringItemId(uint32 itemId);
std::vector<const Quest*> GetAllCurrentQuests();
std::vector<const Quest*> GetCurrentIncompleteQuests();
std::set<uint32> GetAllCurrentQuestIds();
std::set<uint32> GetCurrentIncompleteQuestIds();
private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
bool mixed = false);
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore, bool mixed = false);
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
void HandleCommands();
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
protected:
Player* bot;
Player* master;
uint32 accountId;
Player* master;
uint32 accountId;
AiObjectContext* aiObjectContext;
Engine* currentEngine;
Engine* engines[BOT_STATE_MAX];
BotState currentState;
ChatHelper chatHelper;
std::queue<ChatCommandHolder> chatCommands;
std::queue<ChatQueuedReply> chatReplies;
std::list<ChatCommandHolder> chatCommands;
std::list<ChatQueuedReply> chatReplies;
PacketHandlingHelper botOutgoingPacketHandlers;
PacketHandlingHelper masterIncomingPacketHandlers;
PacketHandlingHelper masterOutgoingPacketHandlers;

View File

@@ -107,6 +107,9 @@ bool PlayerbotAIConfig::Initialize()
iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 100);
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
randomBotGuildNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGuildNearby", false);
randomBotInvitePlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotInvitePlayer", false);
inviteChat = sConfigMgr->GetOption<bool>("AiPlayerbot.InviteChat", false);
allowPlayerBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowPlayerBots", false);
randomBotMapsAsString = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotMaps", "0,1,530,571");
@@ -160,12 +163,100 @@ bool PlayerbotAIConfig::Initialize()
maxRandomBotsPriceChangeInterval =
sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotsPriceChangeInterval", 48 * HOUR);
randomBotJoinLfg = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinLfg", true);
//////////////////////////// CHAT
enableBroadcasts = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableBroadcasts", 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);
randomBotSayWithoutMaster = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotSayWithoutMaster", false);
// broadcastChanceMaxValue is used in urand(1, broadcastChanceMaxValue) for broadcasts,
// lowering it will increase the chance, setting it to 0 will disable broadcasts
// for internal use, not intended to be change by the user
broadcastChanceMaxValue = enableBroadcasts ? 30000 : 0;
// all broadcast chances should be in range 1-broadcastChanceMaxValue, value of 0 will disable this particular
// broadcast setting value to max does not guarantee the broadcast, as there are some internal randoms as well
broadcastToGuildGlobalChance = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastToGuildGlobalChance", 30000);
broadcastToWorldGlobalChance = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastToWorldGlobalChance", 30000);
broadcastToGeneralGlobalChance = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastToGeneralGlobalChance", 30000);
broadcastToTradeGlobalChance = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastToTradeGlobalChance", 30000);
broadcastToLFGGlobalChance = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastToLFGGlobalChance", 30000);
broadcastToLocalDefenseGlobalChance =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastToLocalDefenseGlobalChance", 30000);
broadcastToWorldDefenseGlobalChance =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastToWorldDefenseGlobalChance", 30000);
broadcastToGuildRecruitmentGlobalChance =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastToGuildRecruitmentGlobalChance", 30000);
broadcastChanceLootingItemPoor = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLootingItemPoor", 30);
broadcastChanceLootingItemNormal =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLootingItemNormal", 300);
broadcastChanceLootingItemUncommon =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLootingItemUncommon", 10000);
broadcastChanceLootingItemRare = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLootingItemRare", 20000);
broadcastChanceLootingItemEpic = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLootingItemEpic", 30000);
broadcastChanceLootingItemLegendary =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLootingItemLegendary", 30000);
broadcastChanceLootingItemArtifact =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLootingItemArtifact", 30000);
broadcastChanceQuestAccepted = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceQuestAccepted", 6000);
broadcastChanceQuestUpdateObjectiveCompleted =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceQuestUpdateObjectiveCompleted", 300);
broadcastChanceQuestUpdateObjectiveProgress =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceQuestUpdateObjectiveProgress", 300);
broadcastChanceQuestUpdateFailedTimer =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceQuestUpdateFailedTimer", 300);
broadcastChanceQuestUpdateComplete =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceQuestUpdateComplete", 1000);
broadcastChanceQuestTurnedIn = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceQuestTurnedIn", 10000);
broadcastChanceKillNormal = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceKillNormal", 30);
broadcastChanceKillElite = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceKillElite", 300);
broadcastChanceKillRareelite = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceKillRareelite", 3000);
broadcastChanceKillWorldboss = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceKillWorldboss", 20000);
broadcastChanceKillRare = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceKillRare", 10000);
broadcastChanceKillUnknown = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceKillUnknown", 100);
broadcastChanceKillPet = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceKillPet", 10);
broadcastChanceKillPlayer = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceKillPlayer", 30);
broadcastChanceLevelupGeneric = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLevelupGeneric", 20000);
broadcastChanceLevelupTenX = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLevelupTenX", 30000);
broadcastChanceLevelupMaxLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceLevelupMaxLevel", 30000);
broadcastChanceSuggestInstance = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestInstance", 5000);
broadcastChanceSuggestQuest = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestQuest", 10000);
broadcastChanceSuggestGrindMaterials =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestGrindMaterials", 5000);
broadcastChanceSuggestGrindReputation =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestGrindReputation", 5000);
broadcastChanceSuggestSell = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestSell", 300);
broadcastChanceSuggestSomething =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestSomething", 30000);
broadcastChanceSuggestSomethingToxic =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestSomethingToxic", 0);
broadcastChanceSuggestToxicLinks = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestToxicLinks", 0);
toxicLinksPrefix = sConfigMgr->GetOption<std::string>("AiPlayerbot.ToxicLinksPrefix", "gnomes");
broadcastChanceSuggestThunderfury =
sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceSuggestThunderfury", 1);
// does not depend on global chance
broadcastChanceGuildManagement = sConfigMgr->GetOption<int32>("AiPlayerbot.BroadcastChanceGuildManagement", 30000);
////////////////////////////
toxicLinksRepliesChance = sConfigMgr->GetOption<int32>("AiPlayerbot.ToxicLinksRepliesChance", 30); // 0-100
thunderfuryRepliesChance = sConfigMgr->GetOption<int32>("AiPlayerbot.ThunderfuryRepliesChance", 40); // 0-100
guildRepliesRate = sConfigMgr->GetOption<int32>("AiPlayerbot.GuildRepliesRate", 100); // 0-100
suggestDungeonsInLowerCaseRandomly =
sConfigMgr->GetOption<bool>("AiPlayerbot.SuggestDungeonsInLowerCaseRandomly", false);
////////////////////////// !CHAT
randomBotJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinBG", true);
randomBotAutoJoinBG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutoJoinBG", false);
randomBotAutoJoinWarsongBracket = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAutoJoinWarsongBracket", 14);
@@ -368,8 +459,6 @@ bool PlayerbotAIConfig::Initialize()
autoDoQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoDoQuests", false);
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
freeFood = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeFood", true);
randomBotSayWithoutMaster = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotSayWithoutMaster", false);
sayWhenCollectingItems = sConfigMgr->GetOption<bool>("AiPlayerbot.SayWhenCollectingItems", true);
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", true);
// arena

View File

@@ -55,6 +55,7 @@ public:
bool enabled;
bool allowGuildBots, allowPlayerBots;
bool randomBotGuildNearby, randomBotInvitePlayer, inviteChat;
uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, expireActionTime,
dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance,
@@ -95,11 +96,78 @@ public:
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
bool randomBotJoinLfg;
// chat
bool randomBotTalk;
bool randomBotEmote;
bool randomBotSuggestDungeons;
bool randomBotGuildTalk;
bool enableBroadcasts;
bool enableGreet;
bool randomBotSayWithoutMaster;
uint32 broadcastChanceMaxValue;
uint32 broadcastToGuildGlobalChance;
uint32 broadcastToWorldGlobalChance;
uint32 broadcastToGeneralGlobalChance;
uint32 broadcastToTradeGlobalChance;
uint32 broadcastToLFGGlobalChance;
uint32 broadcastToLocalDefenseGlobalChance;
uint32 broadcastToWorldDefenseGlobalChance;
uint32 broadcastToGuildRecruitmentGlobalChance;
uint32 broadcastChanceLootingItemPoor;
uint32 broadcastChanceLootingItemNormal;
uint32 broadcastChanceLootingItemUncommon;
uint32 broadcastChanceLootingItemRare;
uint32 broadcastChanceLootingItemEpic;
uint32 broadcastChanceLootingItemLegendary;
uint32 broadcastChanceLootingItemArtifact;
uint32 broadcastChanceQuestAccepted;
uint32 broadcastChanceQuestUpdateObjectiveCompleted;
uint32 broadcastChanceQuestUpdateObjectiveProgress;
uint32 broadcastChanceQuestUpdateFailedTimer;
uint32 broadcastChanceQuestUpdateComplete;
uint32 broadcastChanceQuestTurnedIn;
uint32 broadcastChanceKillNormal;
uint32 broadcastChanceKillElite;
uint32 broadcastChanceKillRareelite;
uint32 broadcastChanceKillWorldboss;
uint32 broadcastChanceKillRare;
uint32 broadcastChanceKillUnknown;
uint32 broadcastChanceKillPet;
uint32 broadcastChanceKillPlayer;
uint32 broadcastChanceLevelupGeneric;
uint32 broadcastChanceLevelupTenX;
uint32 broadcastChanceLevelupMaxLevel;
uint32 broadcastChanceSuggestInstance;
uint32 broadcastChanceSuggestQuest;
uint32 broadcastChanceSuggestGrindMaterials;
uint32 broadcastChanceSuggestGrindReputation;
uint32 broadcastChanceSuggestSell;
uint32 broadcastChanceSuggestSomething;
uint32 broadcastChanceSuggestSomethingToxic;
uint32 broadcastChanceSuggestToxicLinks;
std::string toxicLinksPrefix;
uint32 toxicLinksRepliesChance;
uint32 broadcastChanceSuggestThunderfury;
uint32 thunderfuryRepliesChance;
uint32 broadcastChanceGuildManagement;
uint32 guildRepliesRate;
bool suggestDungeonsInLowerCaseRandomly;
// --
bool randomBotJoinBG;
bool randomBotAutoJoinBG;
uint32 randomBotAutoJoinWarsongBracket;
@@ -181,8 +249,6 @@ public:
uint32 commandServerPort;
bool perfMonEnabled;
bool enableGreet;
bool summonWhenGroup;
bool randomBotShowHelmet;
bool randomBotShowCloak;
@@ -212,8 +278,6 @@ public:
bool freeFood;
bool autoLearnQuestSpells;
bool autoTeleportForLevel;
bool randomBotSayWithoutMaster;
bool sayWhenCollectingItems;
bool randomBotGroupNearby;
uint32 tweakValue; // Debugging config

View File

@@ -26,9 +26,10 @@
#include "Playerbots.h"
#include "SharedDefines.h"
#include "WorldSession.h"
#include "ChannelMgr.h"
#include "BroadcastHelper.h"
PlayerbotHolder::PlayerbotHolder() : PlayerbotAIBase(false) {}
class PlayerbotLoginQueryHolder : public LoginQueryHolder
{
private:
@@ -562,10 +563,12 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
pkt << ""; // Pass
bot->GetSession()->HandleJoinChannel(pkt);
}
// join standard channels
AreaTableEntry const* current_zone = sAreaTableStore.LookupEntry(bot->GetAreaId());
uint8 locale = BroadcastHelper::GetLocale();
AreaTableEntry const* current_zone = GET_PLAYERBOT_AI(bot)->GetCurrentZone();
ChannelMgr* cMgr = ChannelMgr::forTeam(bot->GetTeamId());
std::string current_zone_name = current_zone ? current_zone->area_name[sWorld->GetDefaultDbcLocale()] : "";
std::string current_zone_name = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : "";
if (current_zone && cMgr)
{
@@ -575,27 +578,40 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
if (!channel)
continue;
bool isLfg = (channel->flags & CHANNEL_DBC_FLAG_LFG) != 0;
// skip non built-in channels or global channel without zone name in pattern
if (!isLfg && (!channel || (channel->flags & 4) == 4))
continue;
// new channel
Channel* new_channel = nullptr;
if (isLfg)
switch (channel->ChannelID)
{
std::string lfgChannelName = channel->pattern[sWorld->GetDefaultDbcLocale()];
new_channel = cMgr->GetJoinChannel("LookingForGroup", channel->ChannelID);
case ChatChannelId::GENERAL:
case ChatChannelId::LOCAL_DEFENSE:
{
char new_channel_name_buf[100];
snprintf(new_channel_name_buf, 100, channel->pattern[locale], current_zone_name.c_str());
new_channel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID);
break;
}
case ChatChannelId::TRADE:
case ChatChannelId::GUILD_RECRUITMENT:
{
char new_channel_name_buf[100];
//3459 is ID for a zone named "City" (only exists for the sake of using its name)
//Currently in magons TBC, if you switch zones, then you join "Trade - <zone>" and "GuildRecruitment - <zone>"
//which is a core bug, should be "Trade - City" and "GuildRecruitment - City" in both 1.12 and TBC
//but if you (actual player) logout in a city and log back in - you join "City" versions
snprintf(new_channel_name_buf, 100, channel->pattern[locale], GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(GetAreaEntryByAreaID(3459)).c_str());
new_channel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID);
break;
}
case ChatChannelId::LOOKING_FOR_GROUP:
case ChatChannelId::WORLD_DEFENSE:
{
new_channel = cMgr->GetJoinChannel(channel->pattern[locale], channel->ChannelID);
break;
}
default:
break;
}
else
{
char new_channel_name_buf[100];
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)
if (new_channel)
new_channel->JoinChannel(bot, "");
}
}

View File

@@ -68,3 +68,8 @@ Unit* ServerFacade::GetChaseTarget(Unit* target)
return nullptr;
}
void ServerFacade::SendPacket(Player *player, WorldPacket *packet)
{
return player->GetSession()->SendPacket(packet);
}

View File

@@ -11,6 +11,7 @@
class Player;
class Unit;
class WorldObject;
class WorldPacket;
class ServerFacade
{
@@ -32,8 +33,9 @@ public:
bool IsDistanceLessOrEqualThan(float dist1, float dist2);
void SetFacingTo(Player* bot, WorldObject* wo, bool force = false);
Unit* GetChaseTarget(Unit* target);
void SendPacket(Player *player, WorldPacket* packet);
};
#define sServerFacade ServerFacade::instance()

View File

@@ -121,7 +121,20 @@ WorldPosition::WorldPosition(uint32 mapid, mGridCoord grid)
void WorldPosition::set(const WorldLocation& pos) { WorldRelocate(pos); }
void WorldPosition::setMapId(uint32 id) { m_mapId = id; }
void WorldPosition::set(const WorldPosition& pos)
{
WorldRelocate(pos.m_mapId, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
}
void WorldPosition::set(const WorldObject* pos)
{
WorldRelocate(pos->GetMapId(), pos->GetPositionX(), pos->GetPositionY(), pos->GetPositionZ(), pos->GetOrientation());
}
void WorldPosition::setMapId(uint32 id)
{
m_mapId = id;
}
void WorldPosition::setX(float x) { m_positionX = x; }
@@ -1045,30 +1058,42 @@ std::string const QuestTravelDestination::getTitle() { return ChatHelper::Format
bool QuestRelationTravelDestination::isActive(Player* bot)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
if (botAI && !botAI->HasStrategy("rpg quest", BOT_STATE_NON_COMBAT))
return false;
if (relation == 0)
{
if (questTemplate->GetQuestLevel() >= bot->GetLevel() + 5)
if ((int32)questTemplate->GetQuestLevel() >= (int32)bot->GetLevel() + (int32)5)
return false;
// if (questTemplate->XPValue(bot) == 0)
// return false;
// skip for now this quest
if (getPoints().front()->GetMapId() != bot->GetMapId())
return false;
if (!bot->GetMap()->GetEntry()->IsWorldMap() || !bot->CanTakeQuest(questTemplate, false))
return false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
uint32 dialogStatus = sTravelMgr->getDialogStatus(bot, entry, questTemplate);
if (AI_VALUE(bool, "can fight equal"))
{
if (dialogStatus != DIALOG_STATUS_AVAILABLE)
if (AI_VALUE(uint8, "free quest log slots") < 5)
return false;
//None has yellow exclamation mark.
if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest npc::" + std::to_string(entry)))
if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest low level npc::" + std::to_string(entry) + "need quest objective::" + std::to_string(questId))) //Noone can do this quest for a usefull reward.
return false;
}
else
{
if (dialogStatus != DIALOG_STATUS_LOW_LEVEL_AVAILABLE)
if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest low level npc::" + std::to_string(entry))) //Noone can pick up this quest for money.
return false;
if (AI_VALUE(uint8, "free quest log slots") < 10)
return false;
}
@@ -1079,27 +1104,14 @@ bool QuestRelationTravelDestination::isActive(Player* bot)
}
else
{
if (!bot->IsActiveQuest(questId))
if (!AI_VALUE2(bool, "group or", "following party,near leader,can turn in quest npc::" + std::to_string(entry)))
return false;
if (!bot->CanRewardQuest(questTemplate, false))
return false;
uint32 dialogStatus = sTravelMgr->getDialogStatus(bot, entry, questTemplate);
if (dialogStatus != DIALOG_STATUS_REWARD2 && dialogStatus != DIALOG_STATUS_REWARD &&
dialogStatus != DIALOG_STATUS_REWARD_REP)
return false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
AiObjectContext* context = botAI->GetAiObjectContext();
// Do not try to hand-in dungeon/elite quests in instances without a group.
if ((questTemplate->GetType() == QUEST_TYPE_ELITE || questTemplate->GetType() == QUEST_TYPE_DUNGEON) &&
!AI_VALUE(bool, "can fight boss"))
//Do not try to hand-in dungeon/elite quests in instances without a group.
if ((questTemplate->GetType() == QUEST_TYPE_ELITE || questTemplate->GetType() == QUEST_TYPE_DUNGEON) && !AI_VALUE(bool, "can fight boss"))
{
WorldPosition pos(bot);
if (!this->nearestPoint(const_cast<WorldPosition*>(&pos))->isOverworld())
if (!this->nearestPoint(&pos)->isOverworld())
return false;
}
}
@@ -1233,7 +1245,8 @@ std::string const RpgTravelDestination::getTitle()
{
std::ostringstream out;
out << "rpg npc ";
if(entry > 0)
out << "rpg npc ";
out << " " << ChatHelper::FormatWorldEntry(entry);
@@ -3880,7 +3893,45 @@ std::vector<TravelDestination*> TravelMgr::getQuestTravelDestinations(Player* bo
std::vector<TravelDestination*> retTravelLocations;
if (questId == -1)
if (!questId)
{
for (auto& dest : questGivers)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
for (auto& quest : quests)
{
for (auto& dest : quest.second->questTakers)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
if (!ignoreObjectives)
for (auto& dest : quest.second->questObjectives)
{
if (!ignoreInactive && !dest->isActive(bot))
continue;
if (maxDistance > 0 && dest->distanceTo(&botLocation) > maxDistance)
continue;
retTravelLocations.push_back(dest);
}
}
}
else if (questId == -1)
{
for (auto& dest : questGivers)
{

View File

@@ -97,8 +97,10 @@ public:
WorldPosition(uint32 mapid, CellCoord cell);
WorldPosition(uint32 mapid, mGridCoord grid);
// Setters
//Setters
void set(const WorldLocation& pos);
void set(const WorldObject* wo);
void set(const WorldPosition& pos);
void setMapId(uint32 id);
void setX(float x);
void setY(float y);
@@ -404,11 +406,10 @@ private:
class GuidPosition : public ObjectGuid, public WorldPosition
{
public:
GuidPosition() : ObjectGuid(), WorldPosition(), loadedFromDB(false) {}
GuidPosition() : ObjectGuid(), WorldPosition(), loadedFromDB(false) { }
GuidPosition(WorldObject* wo);
GuidPosition(CreatureData const& creData);
GuidPosition(GameObjectData const& goData);
CreatureTemplate const* GetCreatureTemplate();
GameObjectTemplate const* GetGameObjectTemplate();

View File

@@ -14,13 +14,30 @@ void Qualified::Qualify(int qual)
qualifier = out.str();
}
std::string const Qualified::MultiQualify(std::vector<std::string> qualifiers)
std::string const Qualified::MultiQualify(std::vector<std::string> qualifiers, const std::string& separator, const std::string_view brackets)
{
std::ostringstream out;
for (auto& qualifier : qualifiers)
out << qualifier << (&qualifier != &qualifiers.back() ? " " : "");
std::stringstream out;
for (uint8 i = 0; i < qualifiers.size(); i++)
{
const std::string& qualifier = qualifiers[i];
if (i == qualifiers.size() - 1)
{
out << qualifier;
}
else
{
out << qualifier << separator;
}
}
return out.str();
if (brackets.empty())
{
return out.str();
}
else
{
return brackets[0] + out.str() + brackets[1];
}
}
std::vector<std::string> Qualified::getMultiQualifiers(std::string const qualifier1)

View File

@@ -29,7 +29,7 @@ public:
std::string const getQualifier() { return qualifier; }
static std::string const MultiQualify(std::vector<std::string> qualifiers);
static std::string const MultiQualify(std::vector<std::string> qualifiers, const std::string& separator, const std::string_view brackets = "{}");
static std::vector<std::string> getMultiQualifiers(std::string const qualifier1);
static int32 getMultiQualifier(std::string const qualifier1, uint32 pos);

View File

@@ -107,6 +107,7 @@ public:
creators["debug move"] = &StrategyContext::debug_move;
creators["debug rpg"] = &StrategyContext::debug_rpg;
creators["debug spell"] = &StrategyContext::debug_spell;
creators["debug quest"] = &StrategyContext::debug_quest;
creators["maintenance"] = &StrategyContext::maintenance;
creators["group"] = &StrategyContext::group;
creators["guild"] = &StrategyContext::guild;
@@ -170,9 +171,10 @@ private:
static Strategy* debug_move(PlayerbotAI* botAI) { return new DebugMoveStrategy(botAI); }
static Strategy* debug_rpg(PlayerbotAI* botAI) { return new DebugRpgStrategy(botAI); }
static Strategy* debug_spell(PlayerbotAI* botAI) { return new DebugSpellStrategy(botAI); }
static Strategy* debug_quest(PlayerbotAI* botAI) { return new DebugQuestStrategy(botAI); }
static Strategy* maintenance(PlayerbotAI* botAI) { return new MaintenanceStrategy(botAI); }
static Strategy* group(PlayerbotAI* botAI) { return new GroupStrategy(botAI); }
static Strategy* guild(PlayerbotAI* botAI) { return new GuildStrategy(botAI); }
static Strategy* guild (PlayerbotAI* botAI) { return new GuildStrategy(botAI); }
static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); }
static Strategy* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeStrategy(botAI); }
static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); }

View File

@@ -8,54 +8,63 @@
#include "Event.h"
#include "Playerbots.h"
void AcceptAllQuestsAction::ProcessQuest(Quest const* quest, Object* questGiver)
bool AcceptAllQuestsAction::ProcessQuest(Quest const* quest, Object* questGiver)
{
AcceptQuest(quest, questGiver->GetGUID());
if (!AcceptQuest(quest, questGiver->GetGUID())) return false;
auto text_quest = ChatHelper::FormatQuest(quest);
bot->PlayDistanceSound(620);
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
LOG_INFO("playerbots", "{} => Quest [ {} ] accepted", bot->GetName(), quest->GetTitle());
bot->Say("Quest [ " + text_quest + " ] accepted", LANG_UNIVERSAL);
}
return true;
}
bool AcceptQuestAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
if (!requester)
return false;
Player* bot = botAI->GetBot();
ObjectGuid guid;
uint64_t guid;
uint32 quest = 0;
std::string const text = event.getParam();
PlayerbotChatHandler ch(master);
PlayerbotChatHandler ch(requester);
quest = ch.extractQuestId(text);
bool hasAccept = false;
if (event.getPacket().empty())
{
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++)
for (auto i = npcs.begin(); i != npcs.end(); i++)
{
Unit* unit = botAI->GetUnit(*i);
if (unit && quest && unit->hasQuest(quest))
{
guid = unit->GetGUID();
guid = unit->GetGUID().GetRawValue();
break;
}
if (unit && text == "*" && bot->GetDistance(unit) <= INTERACTION_DISTANCE)
QuestAction::ProcessQuests(unit);
if (unit && text == "*" && sqrt(bot->GetDistance(unit)) <= INTERACTION_DISTANCE)
hasAccept |= QuestAction::ProcessQuests(unit);
}
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects");
for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++)
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
for (auto i = gos.begin(); i != gos.end(); i++)
{
GameObject* go = botAI->GetGameObject(*i);
if (go && quest && go->hasQuest(quest))
{
guid = go->GetGUID();
guid = go->GetGUID().GetRawValue();
break;
}
if (go && text == "*" && bot->GetDistance(go) <= INTERACTION_DISTANCE)
QuestAction::ProcessQuests(go);
if (go && text == "*" && sqrt(bot->GetDistance(go)) <= INTERACTION_DISTANCE)
hasAccept |= QuestAction::ProcessQuests(go);
}
}
else
@@ -72,7 +81,17 @@ bool AcceptQuestAction::Execute(Event event)
if (!qInfo)
return false;
return AcceptQuest(qInfo, guid);
hasAccept |= AcceptQuest(qInfo, ObjectGuid(guid));
if (hasAccept)
{
std::stringstream ss;
ss << "AcceptQuestAction {" << qInfo->GetTitle() << "} - {" << std::to_string(qInfo->GetQuestId()) << "}";
LOG_INFO("playerbots", "{}", ss.str().c_str());
botAI->TellMaster(ss.str());
}
return hasAccept;
}
bool AcceptQuestShareAction::Execute(Event event)
@@ -136,3 +155,41 @@ bool AcceptQuestShareAction::Execute(Event event)
return false;
}
bool ConfirmQuestAction::Execute(Event event)
{
Player* bot = botAI->GetBot();
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
WorldPacket& p = event.getPacket();
p.rpos(0);
uint32 quest;
p >> quest;
Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
quest = qInfo->GetQuestId();
if (!bot->CanTakeQuest(qInfo, false))
{
// can't take quest
botAI->TellError("quest_cant_take");
return false;
}
if (bot->CanAddQuest(qInfo, false))
{
bot->AddQuest(qInfo, requester);
if (bot->CanCompleteQuest(quest))
bot->CompleteQuest(quest);
if (qInfo->GetSrcSpell() > 0)
{
bot->CastSpell(bot, qInfo->GetSrcSpell(), true);
}
botAI->TellMaster("quest_accept");
return true;
}
return false;
}

View File

@@ -20,7 +20,7 @@ public:
}
protected:
void ProcessQuest(Quest const* quest, Object* questGiver) override;
bool ProcessQuest(Quest const* quest, Object* questGiver) override;
};
class AcceptQuestAction : public AcceptAllQuestsAction
@@ -37,4 +37,10 @@ public:
bool Execute(Event event) override;
};
class ConfirmQuestAction : public Action {
public:
ConfirmQuestAction(PlayerbotAI* ai) : Action(ai, "confirm quest") {}
bool Execute(Event event);
};
#endif

View File

@@ -9,6 +9,8 @@
#include "AddLootAction.h"
#include "AttackAction.h"
#include "AutoLearnSpellAction.h"
#include "ShareQuestAction.h"
#include "BattleGroundTactics.h"
#include "AutoTeleportForLevelAction.h"
#include "BattleGroundJoinAction.h"
#include "BattleGroundTactics.h"
@@ -43,6 +45,7 @@
#include "NonCombatActions.h"
#include "OutfitAction.h"
#include "PositionAction.h"
#include "DropQuestAction.h"
#include "RaidNaxxActions.h"
#include "RandomBotUpdateAction.h"
#include "ReachTargetActions.h"
@@ -153,6 +156,7 @@ public:
creators["war stomp"] = &ActionContext::war_stomp;
creators["auto talents"] = &ActionContext::auto_talents;
creators["auto learn spell"] = &ActionContext::auto_learn_spell;
creators["auto share quest"] = &ActionContext::auto_share_quest;
creators["auto teleport for level"] = &ActionContext::auto_teleport_for_level;
creators["auto upgrade equip"] = &ActionContext::auto_upgrade_equip;
creators["xp gain"] = &ActionContext::xp_gain;
@@ -179,6 +183,7 @@ public:
creators["turn in petition"] = &ActionContext::turn_in_petition;
creators["buy tabard"] = &ActionContext::buy_tabard;
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
creators["clean quest log"] = &ActionContext::clean_quest_log;
// BG Tactics
creators["bg tactics"] = &ActionContext::bg_tactics;
@@ -206,7 +211,7 @@ public:
creators["blade salvo"] = &ActionContext::blade_salvo;
creators["glaive throw"] = &ActionContext::glaive_throw;
// Rpg
//Rpg
creators["rpg stay"] = &ActionContext::rpg_stay;
creators["rpg work"] = &ActionContext::rpg_work;
creators["rpg emote"] = &ActionContext::rpg_emote;
@@ -231,7 +236,7 @@ public:
creators["rpg mount anim"] = &ActionContext::rpg_mount_anim;
creators["toggle pet spell"] = &ActionContext::toggle_pet_spell;
creators["pet attack"] = &ActionContext::pet_attack;
creators["pet attack"] = &ActionContext::pet_attack;
}
private:
@@ -265,10 +270,7 @@ private:
static Action* ReachSpell(PlayerbotAI* botAI) { return new ReachSpellAction(botAI); }
static Action* ReachMelee(PlayerbotAI* botAI) { return new ReachMeleeAction(botAI); }
static Action* reach_party_member_to_heal(PlayerbotAI* botAI) { return new ReachPartyMemberToHealAction(botAI); }
static Action* reach_party_member_to_resurrect(PlayerbotAI* botAI)
{
return new ReachPartyMemberToResurrectAction(botAI);
}
static Action* reach_party_member_to_resurrect(PlayerbotAI* botAI) { return new ReachPartyMemberToResurrectAction(botAI); }
static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); }
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); }
static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); }
@@ -324,6 +326,7 @@ private:
static Action* war_stomp(PlayerbotAI* botAI) { return new CastWarStompAction(botAI); }
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); }
static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); }
static Action* auto_teleport_for_level(PlayerbotAI* botAI) { return new AutoTeleportForLevelAction(botAI); }
static Action* auto_upgrade_equip(PlayerbotAI* botAI) { return new AutoUpgradeEquipAction(botAI); }
static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); }
@@ -351,6 +354,7 @@ private:
static Action* turn_in_petition(PlayerbotAI* botAI) { return new PetitionTurnInAction(botAI); }
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
// BG Tactics
static Action* bg_tactics(PlayerbotAI* botAI) { return new BGTactics(botAI); }

View File

@@ -9,6 +9,7 @@
#include "GuildMgr.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "BroadcastHelper.h"
bool AutoLearnSpellAction::Execute(Event event)
{
@@ -32,29 +33,12 @@ bool AutoLearnSpellAction::Execute(Event event)
void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
{
if (sPlayerbotAIConfig->autoLearnTrainerSpells &&
sRandomPlayerbotMgr->IsRandomBot(bot)) // || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
BroadcastHelper::BroadcastLevelup(botAI, bot);
if (sPlayerbotAIConfig->autoLearnTrainerSpells)
LearnTrainerSpells(out);
if (sPlayerbotAIConfig->autoLearnQuestSpells &&
sRandomPlayerbotMgr->IsRandomBot(bot)) // || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
if (sPlayerbotAIConfig->autoLearnQuestSpells)
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

@@ -172,6 +172,7 @@ public:
creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut;
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
creators["tell expected dps"] = &ChatActionContext::tell_expected_dps;
creators["join"] = &ChatActionContext::join;
creators["calc"] = &ChatActionContext::calc;
}
@@ -269,6 +270,7 @@ private:
static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); }
static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); }
static Action* tell_expected_dps(PlayerbotAI* ai) { return new TellExpectedDpsAction(ai); }
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }
static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); }
};

View File

@@ -3,10 +3,9 @@
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ChooseRpgTargetAction.h"
#include <random>
#include "ChooseRpgTargetAction.h"
#include "BattlegroundMgr.h"
#include "BudgetValues.h"
#include "ChatHelper.h"
@@ -14,6 +13,9 @@
#include "Formations.h"
#include "GuildCreateActions.h"
#include "Playerbots.h"
#include "RpgSubActions.h"
#include "Util.h"
#include "ServerFacade.h"
#include "PossibleRpgTargetsValue.h"
bool ChooseRpgTargetAction::HasSameTarget(ObjectGuid guid, uint32 max, GuidVector const& nearGuids)
@@ -52,52 +54,94 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP)
GuidPosition currentRpgTarget = AI_VALUE(GuidPosition, "rpg target");
SET_AI_VALUE(GuidPosition, "rpg target", guidP);
Strategy* rpgStrategy = botAI->GetAiObjectContext()->GetStrategy("rpg");
Strategy* rpgStrategy;
std::vector<TriggerNode*> triggerNodes;
rpgStrategy->InitTriggers(triggerNodes);
float maxRelevance = 0.0f;
for (auto& triggerNode : triggerNodes)
for (auto& strategy : botAI->GetAiObjectContext()->GetSupportedStrategies())
{
Trigger* trigger = context->GetTrigger(triggerNode->getName());
if (trigger)
if (strategy.find("rpg") == std::string::npos)
continue;
if (!botAI->HasStrategy(strategy, BotState::BOT_STATE_NON_COMBAT))
continue;
rpgStrategy = botAI->GetAiObjectContext()->GetStrategy(strategy);
rpgStrategy->InitTriggers(triggerNodes);
for (auto triggerNode : triggerNodes)
{
triggerNode->setTrigger(trigger);
Trigger* trigger = context->GetTrigger(triggerNode->getName());
if (triggerNode->getFirstRelevance() < maxRelevance || triggerNode->getFirstRelevance() > 2.0f)
continue;
if (trigger)
{
triggerNode->setTrigger(trigger);
trigger = triggerNode->getTrigger();
if (!trigger->IsActive())
continue;
if (triggerNode->getFirstRelevance() < maxRelevance || triggerNode->getFirstRelevance() > 2.0f)
continue;
maxRelevance = triggerNode->getFirstRelevance();
Trigger* trigger = triggerNode->getTrigger();
if (!trigger->IsActive())
continue;
NextAction** nextActions = triggerNode->getHandlers();
bool isRpg = false;
for (int32 i = 0; i < NextAction::size(nextActions); i++)
{
NextAction* nextAction = nextActions[i];
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
if (dynamic_cast<RpgEnabled*>(action))
isRpg = true;
}
NextAction::destroy(nextActions);
if (isRpg)
{
maxRelevance = triggerNode->getFirstRelevance();
rgpActionReason[guidP] = triggerNode->getName();
}
}
}
for (auto trigger : triggerNodes)
{
delete trigger;
}
triggerNodes.clear();
}
SET_AI_VALUE(GuidPosition, "rpg target", currentRpgTarget);
for (std::vector<TriggerNode*>::iterator i = triggerNodes.begin(); i != triggerNodes.end(); i++)
{
TriggerNode* trigger = *i;
delete trigger;
}
if (!maxRelevance)
return 0.0;
triggerNodes.clear();
return (maxRelevance - 1.0) * 1000.0f;
return floor((maxRelevance - 1.0) * 1000.0f);
}
bool ChooseRpgTargetAction::Execute(Event event)
{
TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target");
uint32 num = 0;
Player* master = botAI->GetMaster();
GuidPosition masterRpgTarget;
if (master && master != bot && GET_PLAYERBOT_AI(master) && master->GetMapId() == bot->GetMapId() && !master->IsBeingTeleported())
{
Player* player = botAI->GetMaster();
GuidPosition masterRpgTarget = PAI_VALUE(GuidPosition, "rpg target");
}
else
master = nullptr;
std::unordered_map<ObjectGuid, uint32> targets;
uint32 num = 0;
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
GuidVector possibleObjects = AI_VALUE(GuidVector, "nearest game objects no los");
GuidVector possiblePlayers = AI_VALUE(GuidVector, "nearest friendly players");
@@ -180,18 +224,22 @@ bool ChooseRpgTargetAction::Execute(Event event)
for (auto it = begin(targets); it != end(targets);)
{
if (it->second == 0 || (hasGoodRelevance && it->second <= 1.0))
{
//Remove empty targets.
if (it->second == 0)
it = targets.erase(it);
//Remove useless targets if there's any good ones
else if (hasGoodRelevance && it->second <= 1.0)
it = targets.erase(it);
//Remove useless targets if it's not masters target.
else if (!hasGoodRelevance && master && (!masterRpgTarget || it->first != masterRpgTarget))
it = targets.erase(it);
}
else
++it;
}
if (targets.empty())
{
LOG_DEBUG("playerbots", "{} can't choose RPG target: all {} are not available", bot->GetName().c_str(),
possibleTargets.size());
LOG_DEBUG("playerbots", "{} can't choose RPG target: all {} targets are not available", bot->GetName().c_str(), possibleTargets.size());
RESET_AI_VALUE(GuidSet&, "ignore rpg target");
RESET_AI_VALUE(GuidPosition, "rpg target");
return false;
@@ -244,17 +292,20 @@ bool ChooseRpgTargetAction::isUseful()
if (!botAI->AllowActivity(RPG_ACTIVITY))
return false;
if (AI_VALUE(GuidPosition, "rpg target"))
GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target");
if (guidP && guidP.distance(bot) < sPlayerbotAIConfig->reactDistance * 2)
return false;
TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target");
if (travelTarget->isTraveling() && isFollowValid(bot, *travelTarget->getPosition()))
return false;
//if (travelTarget->isTraveling() && AI_VALUE2(bool, "can free move to", *travelTarget->getPosition()))
//return false;
if (AI_VALUE(GuidVector, "possible rpg targets").empty())
return false;
//Not stay, not guard, not combat, not trading and group ready.
if (!AI_VALUE(bool, "can move around"))
return false;

View File

@@ -28,7 +28,9 @@ public:
private:
float getMaxRelevance(GuidPosition guidP);
bool HasSameTarget(ObjectGuid guid, uint32 max, GuidVector const& nearGuids);
bool HasSameTarget(ObjectGuid guid, uint32 max, GuidVector const& nearGuids);
std::unordered_map <ObjectGuid, std::string> rgpActionReason;
};
class ClearRpgTargetAction : public ChooseRpgTargetAction

View File

@@ -11,19 +11,26 @@
bool ChooseTravelTargetAction::Execute(Event event)
{
// Get the current travel target. This target is no longer active.
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
//Get the current travel target. This target is no longer active.
TravelTarget* oldTarget = context->GetValue<TravelTarget*>("travel target")->Get();
// Select a new target to travel to.
//Select a new target to travel to.
TravelTarget newTarget = TravelTarget(botAI);
getNewTarget(&newTarget, oldTarget);
// If the new target is not active we failed.
if (!newTarget.isActive())
if (!oldTarget) return false;
if (!oldTarget->isForced() || oldTarget->getStatus() == TravelStatus::TRAVEL_STATUS_EXPIRED)
getNewTarget(&newTarget, oldTarget);
else
newTarget.copyTarget(oldTarget);
//If the new target is not active we failed.
if (!newTarget.isActive() && !newTarget.isForced())
return false;
setNewTarget(&newTarget, oldTarget);
return true;
}
@@ -34,64 +41,141 @@ bool ChooseTravelTargetAction::Execute(Event event)
// Eventually we want to rewrite this to be more intelligent.
void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
{
bool foundTarget = false;
// Join groups members
bool foundTarget = foundTarget = SetGroupTarget(newTarget);
foundTarget = SetGroupTarget(newTarget); // Join groups members
// Enpty bags/repair
if (!foundTarget && urand(1, 100) > 10) // 90% chance
if (AI_VALUE2(bool, "group or", "should sell,can sell,following party,near leader") ||
AI_VALUE2(bool, "group or", "should repair,can repair,following party,near leader"))
foundTarget = SetRpgTarget(newTarget); // Go to town to sell items or repair
// Rpg in city
if (!foundTarget && urand(1, 100) > 90) // 10% chance
foundTarget =
SetNpcFlagTarget(newTarget, {UNIT_NPC_FLAG_BANKER, UNIT_NPC_FLAG_BATTLEMASTER, UNIT_NPC_FLAG_AUCTIONEER});
// Grind for money
if (!foundTarget && AI_VALUE(bool, "should get money"))
//Empty bags/repair
if (!foundTarget && urand(1, 100) > 10 && bot->GetLevel() > 5) //90% chance
{
if (urand(1, 100) > 66)
if (AI_VALUE2(bool, "group or", "should sell,can sell,following party,near leader") ||
AI_VALUE2(bool, "group or", "should repair,can repair,following party,near leader")
)
{
foundTarget = SetQuestTarget(newTarget, true); // Turn in quests for money.
if (!foundTarget)
foundTarget = SetQuestTarget(newTarget); // Do low level quests
}
else if (urand(1, 100) > 50)
{
foundTarget = SetGrindTarget(newTarget); // Go grind mobs for money
}
else
{
foundTarget = SetNewQuestTarget(newTarget); // Find a low level quest to do
foundTarget = SetRpgTarget(newTarget); //Go to town to sell items or repair
}
}
// Continue
if (!foundTarget && urand(1, 100) > 10) // 90% chance
foundTarget = SetCurrentTarget(newTarget, oldTarget); // Extend current target.
//Rpg in city
if (!foundTarget && urand(1, 100) > 90 && bot->GetLevel() > 5) //10% chance
{
foundTarget = SetNpcFlagTarget(newTarget, { UNIT_NPC_FLAG_BANKER,UNIT_NPC_FLAG_BATTLEMASTER,UNIT_NPC_FLAG_AUCTIONEER });
}
// Dungeon in group
if (!foundTarget && urand(1, 100) > 50) // 50% chance
// PvP activities
bool pvpActivate = false;
if (pvpActivate && !foundTarget && urand(0, 4) && bot->GetLevel() > 50)
{
WorldPosition pos = WorldPosition(bot);
WorldPosition* botPos = &pos;
TravelTarget* target = context->GetValue<TravelTarget*>("travel target")->Get();
TravelDestination* dest = ChooseTravelTargetAction::FindDestination(bot, "Tarren Mill");
if (dest)
{
std::vector<WorldPosition*> points = dest->nextPoint(botPos, true);
if (!points.empty())
{
target->setTarget(dest, points.front());
target->setForced(true);
std::ostringstream out; out << "Traveling to " << dest->getTitle();
botAI->TellMasterNoFacing(out.str());
foundTarget = true;
}
}
}
//Grind for money
if (!foundTarget && AI_VALUE(bool, "should get money"))
{
//Empty mail for money
//if (AI_VALUE(bool, "can get mail"))
//{
// foundTarget = SetGOTypeTarget(requester, newTarget, GAMEOBJECT_TYPE_MAILBOX, "", false); //Find a mailbox
//}
if (!foundTarget)
{
// 50% Focus on active quests for money.
if (urand(1, 100) > 50)
{
// 50% Focus on active quests for money.
if (urand(1, 100) > 50)
{
// Turn in quests for money.
foundTarget = SetQuestTarget(newTarget, true, false, true, true);
}
if (!foundTarget)
{
// Find new (low) level quests
foundTarget = SetQuestTarget(newTarget, false, true, false, false);
}
}
else
{
// Go grind mobs for money
foundTarget = SetGrindTarget(newTarget);
}
}
}
//Continue current target. 90% chance
if (!foundTarget && urand(1, 100) > 10)
{
// Extend current target.
foundTarget = SetCurrentTarget(newTarget, oldTarget);
}
//Get mail 30% chance
//if (!foundTarget && urand(1, 100) > 70)
//{
// if (AI_VALUE(bool, "can get mail"))
// {
// foundTarget = SetGOTypeTarget(requester, newTarget, GAMEOBJECT_TYPE_MAILBOX, "", false); //Find a mailbox
// }
//}
//Dungeon in group. 50% chance
if (!foundTarget && urand(1, 100) > 50)
{
if (AI_VALUE(bool, "can fight boss"))
foundTarget = SetBossTarget(newTarget); // Go fight a (dungeon boss)
if (!foundTarget && urand(1, 100) > 5) // 95% chance
foundTarget = SetQuestTarget(newTarget); // Do a target of an active quest.
{
// Go fight a (dungeon boss)
foundTarget = SetBossTarget(newTarget);
}
}
//Do quests (start, do, end) 95% chance
if (!foundTarget && urand(1, 100) > 5)
foundTarget = SetNewQuestTarget(newTarget); // Find a new quest to do.
{
// Do any nearby
foundTarget = SetQuestTarget(newTarget, false, true, true, true);
}
if (!foundTarget && botAI->HasStrategy("explore", BOT_STATE_NON_COMBAT)) // Explore a unexplored sub-zone.
//Explore a nearby unexplored area.
if (!foundTarget && botAI->HasStrategy("explore", BotState::BOT_STATE_NON_COMBAT) && urand(1, 100) > 90) //10% chance Explore a unexplored sub-zone.
{
foundTarget = SetExploreTarget(newTarget);
}
// if (!foundTarget)
// foundTarget = SetRpgTarget(target);
//Just hang with an npc 50% chance
if (!foundTarget && urand(1, 100) > 50)
{
foundTarget = SetRpgTarget(newTarget);
if (foundTarget)
newTarget->setForced(true);
}
if (!foundTarget)
SetNullTarget(newTarget); // Idle a bit.
{
foundTarget = SetGrindTarget(newTarget);
}
// Idle a bit.
if (!foundTarget)
SetNullTarget(newTarget);
}
void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
@@ -395,45 +479,57 @@ bool ChooseTravelTargetAction::SetCurrentTarget(TravelTarget* target, TravelTarg
return target->isActive();
}
bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool onlyCompleted)
bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool onlyCompleted, bool newQuests, bool activeQuests, bool completedQuests)
{
std::vector<TravelDestination*> activeDestinations;
std::vector<WorldPosition*> activePoints;
QuestStatusMap& questMap = bot->getQuestStatusMap();
WorldPosition botLocation(bot);
bool onlyClassQuest = !urand(0, 10);
// Find destinations related to the active quests.
for (auto& quest : questMap)
if (newQuests)
{
if (bot->IsQuestRewarded(quest.first))
continue;
uint32 questId = quest.first;
QuestStatusData* questStatus = &quest.second;
if (onlyCompleted && sObjectMgr->GetQuestTemplate(questId) &&
!bot->CanRewardQuest(sObjectMgr->GetQuestTemplate(questId), false))
continue;
std::vector<TravelDestination*> questDestinations =
sTravelMgr->getQuestTravelDestinations(bot, questId, botAI->HasRealPlayerMaster(), false, 5000);
std::vector<WorldPosition*> questPoints;
for (auto& questDestination : questDestinations)
// Prefer new quests near the player at lower levels.
activeDestinations = sTravelMgr->getQuestTravelDestinations(bot, -1, true, false, 400 + bot->GetLevel() * 10);
}
if (activeQuests || completedQuests)
{
QuestStatusMap& questMap = bot->getQuestStatusMap();
//Find destinations related to the active quests.
for (auto& quest : questMap)
{
std::vector<WorldPosition*> destinationPoints = questDestination->getPoints();
if (!destinationPoints.empty())
questPoints.insert(questPoints.end(), destinationPoints.begin(), destinationPoints.end());
}
if (bot->IsQuestRewarded(quest.first))
continue;
if (getBestDestination(&questDestinations, &questPoints))
{
activeDestinations.push_back(questDestinations.front());
activePoints.push_back(questPoints.front());
uint32 questId = quest.first;
QuestStatusData* questStatus = &quest.second;
const auto questTemplate = sObjectMgr->GetQuestTemplate(questId);
if (!activeQuests && !bot->CanRewardQuest(questTemplate, false))
continue;
if (!completedQuests && bot->CanRewardQuest(questTemplate, false))
continue;
//Find quest takers or objectives
std::vector<TravelDestination*> questDestinations = sTravelMgr->getQuestTravelDestinations(bot, questId, true, false, 0);
if (onlyClassQuest && activeDestinations.size() && questDestinations.size()) //Only do class quests if we have any.
{
if (activeDestinations.front()->GetQuestTemplate()->GetRequiredClasses() && !questTemplate->GetRequiredClasses())
continue;
if (!activeDestinations.front()->GetQuestTemplate()->GetRequiredClasses() && questTemplate->GetRequiredClasses())
activeDestinations.clear();
}
activeDestinations.insert(activeDestinations.end(), questDestinations.begin(), questDestinations.end());
}
}
if (newQuests && activeDestinations.empty())
activeDestinations = sTravelMgr->getQuestTravelDestinations(bot, -1, true, false); //If we really don't find any new quests look futher away.
if (botAI->HasStrategy("debug travel", BotState::BOT_STATE_NON_COMBAT))
botAI->TellMasterNoFacing(std::to_string(activeDestinations.size()) + " quest destinations found.");
if (!getBestDestination(&activeDestinations, &activePoints))
return false;
@@ -726,7 +822,7 @@ bool ChooseTravelTargetAction::SetNullTarget(TravelTarget* target)
std::vector<std::string> split(std::string const s, char delim);
char* strstri(char const* haystack, char const* needle);
TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::string const name)
TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::string const name, bool zones, bool npcs, bool quests, bool mobs, bool bosses)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
@@ -734,32 +830,54 @@ TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::s
std::vector<TravelDestination*> dests;
// Zones
for (auto& d : sTravelMgr->getExploreTravelDestinations(bot, true, true))
//Quests
if (quests)
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
for (auto& d : sTravelMgr->getQuestTravelDestinations(bot, 0, true, true))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
}
// Npcs
for (auto& d : sTravelMgr->getRpgTravelDestinations(bot, true, true))
//Zones
if (zones)
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
for (auto& d : sTravelMgr->getExploreTravelDestinations(bot, true, true))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
}
// Mobs
for (auto& d : sTravelMgr->getGrindTravelDestinations(bot, true, true))
//Npcs
if (npcs)
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
for (auto& d : sTravelMgr->getRpgTravelDestinations(bot, true, true))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
}
// Bosses
for (auto& d : sTravelMgr->getBossTravelDestinations(bot, true, true))
//Mobs
if (mobs)
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
for (auto& d : sTravelMgr->getGrindTravelDestinations(bot, true, true, 5000.0f))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
}
//Bosses
if (bosses)
{
for (auto& d : sTravelMgr->getBossTravelDestinations(bot, true, true))
{
if (strstri(d->getTitle().c_str(), name.c_str()))
dests.push_back(d);
}
}
WorldPosition botPos(bot);
@@ -767,13 +885,11 @@ TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::s
if (dests.empty())
return nullptr;
TravelDestination* dest = *std::min_element(dests.begin(), dests.end(),
[botPos](TravelDestination* i, TravelDestination* j) {
return i->distanceTo(const_cast<WorldPosition*>(&botPos)) <
j->distanceTo(const_cast<WorldPosition*>(&botPos));
});
return dest;
return *std::min_element(dests.begin(), dests.end(),
[botPos](TravelDestination* i, TravelDestination* j)
{
return i->distanceTo(const_cast<WorldPosition*>(&botPos)) < j->distanceTo(const_cast<WorldPosition*>(&botPos));
});
};
bool ChooseTravelTargetAction::isUseful()

View File

@@ -26,26 +26,23 @@ public:
bool Execute(Event event) override;
bool isUseful() override;
static TravelDestination* FindDestination(Player* bot, std::string const name);
static TravelDestination* FindDestination(Player* bot, std::string const name, bool zones = true, bool npcs = true, bool quests = true, bool mobs = true, bool bosses = true);
protected:
void getNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget);
void setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget);
void ReportTravelTarget(TravelTarget* newTarget, TravelTarget* oldTarget);
bool getBestDestination(std::vector<TravelDestination*>* activeDestinations,
std::vector<WorldPosition*>* activePoints);
bool getBestDestination(std::vector<TravelDestination*>* activeDestinations, std::vector<WorldPosition*>* activePoints);
bool SetGroupTarget(TravelTarget* target);
bool SetCurrentTarget(TravelTarget* target, TravelTarget* oldTarget);
bool SetQuestTarget(TravelTarget* target, bool onlyCompleted = false);
bool SetQuestTarget(TravelTarget* target, bool onlyCompleted = false, bool newQuests = true, bool activeQuests = true, bool completedQuests = true);
bool SetNewQuestTarget(TravelTarget* target);
bool SetRpgTarget(TravelTarget* target);
bool SetGrindTarget(TravelTarget* target);
bool SetBossTarget(TravelTarget* target);
bool SetExploreTarget(TravelTarget* target);
bool SetNpcFlagTarget(TravelTarget* target, std::vector<NPCFlags> flags, std::string const name = "",
std::vector<uint32> items = {});
bool SetNpcFlagTarget(TravelTarget* target, std::vector<NPCFlags> flags, std::string const name = "", std::vector<uint32> items = { });
bool SetNullTarget(TravelTarget* target);
private:

View File

@@ -46,28 +46,44 @@ bool DropQuestAction::Execute(Event event)
bot->RemoveRewardedQuest(entry);
bot->RemoveActiveQuest(entry, false);
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
const Quest* pQuest = sObjectMgr->GetQuestTemplate(entry);
const std::string text_quest = ChatHelper::FormatQuest(pQuest);
LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), pQuest->GetTitle());
bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL);
}
botAI->TellMaster("Quest removed");
return true;
}
bool CleanQuestLogAction::Execute(Event event)
{
std::string const link = event.getParam();
if (botAI->HasActivePlayerMaster())
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
std::string link = event.getParam();
if (botAI->HasActivePlayerMaster() || !sRandomPlayerbotMgr->IsRandomBot(bot))
return false;
uint8 totalQuests = 0;
DropQuestType(totalQuests); // Count the total quests
// Count the total quests
DropQuestType(totalQuests);
if (MAX_QUEST_LOG_SIZE - totalQuests > 6)
return true;
if (AI_VALUE(bool, "can fight equal")) // Only drop gray quests when able to fight proper lvl quests.
{
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6); // Drop gray/red quests.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6, false, true); // Drop gray/red quests with progress.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6, false, true, true); // Drop gray/red completed quests.
// Drop failed quests
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE, true, true);
return true;
}
// Only drop gray quests when able to fight proper lvl quests.
if (AI_VALUE(bool, "can fight equal"))
{
// Drop gray/red quests.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6);
// Drop gray/red quests with progress.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6, false, true);
// Drop gray/red completed quests.
DropQuestType(totalQuests, MAX_QUEST_LOG_SIZE - 6, false, true, true);
}
if (MAX_QUEST_LOG_SIZE - totalQuests > 4)
@@ -93,7 +109,19 @@ bool CleanQuestLogAction::Execute(Event event)
void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isGreen, bool hasProgress, bool isComplete)
{
std::vector<uint8> slots;
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
slots.push_back(slot);
if (wantNum < 100)
{
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(slots.begin(), slots.end(), g);
}
for (uint8 slot : slots)
{
uint32 questId = bot->GetQuestSlotQuestId(slot);
if (!questId)
@@ -103,12 +131,8 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG
if (!quest)
continue;
if (quest->GetRequiredClasses() &&
(quest->GetRewSpellCast() || quest->GetRewSpell())) // Do not drop class specific quests that learn spells.
continue;
if (quest->GetRequiredClasses() &&
(quest->GetRewSpellCast() || quest->GetRewSpell())) // Do not drop class specific quests that learn spells.
// Do not drop class quest, may be not rewarding gold but important spells
if (quest->GetRequiredClasses())
continue;
if (wantNum == 100)
@@ -128,13 +152,14 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG
continue;
}
if (HasProgress(bot, quest) && !hasProgress)
if (HasProgress(bot, quest) && !hasProgress && bot->GetQuestStatus(questId) != QUEST_STATUS_FAILED)
continue;
if (bot->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE && !isComplete)
continue;
if (numQuest <= wantNum && bot->GetQuestStatus(questId) != QUEST_STATUS_FAILED) // Always drop failed quests
// Always drop failed quests
if (numQuest <= wantNum && bot->GetQuestStatus(questId) != QUEST_STATUS_FAILED)
continue;
// Drop quest.
@@ -148,6 +173,12 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG
numQuest--;
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
const std::string text_quest = ChatHelper::FormatQuest(quest);
LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle());
bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL);
}
botAI->TellMaster("Quest removed" + chat->FormatQuest(quest));
}
}

View File

@@ -9,6 +9,8 @@
#include "MovementActions.h"
class PlayerbotAI;
class TravelDestination;
class WorldPosition;
class GoAction : public MovementAction
{

View File

@@ -9,6 +9,7 @@
#include "GuildPackets.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "BroadcastHelper.h"
Player* GuidManageAction::GetPlayer(Event event)
{
@@ -154,62 +155,141 @@ bool GuildManageNearbyAction::Execute(Event event)
if (!player || bot == player)
continue;
if (player->GetGuildId()) // Promote or demote nearby members based on chance.
if (player->isDND())
continue;
// Promote or demote nearby members based on chance.
if (player->GetGuildId() && player->GetGuildId() == bot->GetGuildId())
{
Guild::Member* member = guild->GetMember(player->GetGUID());
uint32 dCount = AI_VALUE(uint32, "death count");
if ((dCount < 2 || !urand(0, 10)) && guild->GetRankRights(botMember->GetRankId() & GR_RIGHT_PROMOTE))
if (!urand(0, 30) && dCount < 2 && guild->GetRankRights(botMember->GetRankId()) & GR_RIGHT_PROMOTE)
{
if (!urand(0, 10))
{
botAI->DoSpecificAction("guild promote", Event("guild management", guid), true);
BroadcastHelper::BroadcastGuildMemberPromotion(botAI, bot, player);
continue;
}
botAI->DoSpecificAction("guild promote", Event("guild management", guid), true);
continue;
}
if ((dCount > 3 || !urand(0, 10)) && guild->GetRankRights(botMember->GetRankId() & GR_RIGHT_DEMOTE))
if (!urand(0, 30) && dCount > 2 && guild->GetRankRights(botMember->GetRankId()) & GR_RIGHT_DEMOTE)
{
if (!urand(0, 10))
{
botAI->DoSpecificAction("guild demote", Event("guild management", guid), true);
BroadcastHelper::BroadcastGuildMemberDemotion(botAI, bot, player);
continue;
}
botAI->DoSpecificAction("guild demote", Event("guild management", guid), true);
continue;
}
continue;
}
if (!(guild->GetRankRights(botMember->GetRankId()) & GR_RIGHT_INVITE))
if (!sPlayerbotAIConfig->randomBotGuildNearby)
return false;
if (guild->GetMemberSize() > 1000)
return false;
if ( (guild->GetRankRights(botMember->GetRankId()) & GR_RIGHT_INVITE) == 0)
continue;
if (player->GetGuildIdInvited())
continue;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
PlayerbotAI* botAi = GET_PLAYERBOT_AI(player);
if (botAI)
{
if (botAI->GetGuilderType() == GuilderType::SOLO &&
!botAI->HasRealPlayerMaster()) // Do not invite solo players.
continue;
if (botAI->HasActivePlayerMaster()) // Do not invite alts of active players.
continue;
}
else
{
if (!sPlayerbotAIConfig->randomBotGroupNearby)
return false;
}
if (sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance)
if (!sPlayerbotAIConfig->randomBotInvitePlayer && botAi && botAi->IsRealPlayer())
continue;
if (botAI && botAI->DoSpecificAction("ginvite", Event("guild management", guid)))
if (botAi)
{
if (botAi->GetGuilderType() == GuilderType::SOLO && !botAi->HasRealPlayerMaster()) //Do not invite solo players.
continue;
if (botAi->HasActivePlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(player)) //Do not invite alts of active players.
continue;
}
bool sameGroup = bot->GetGroup() && bot->GetGroup()->IsMember(player->GetGUID());
if (!sameGroup && sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->spellDistance)
continue;
if (sPlayerbotAIConfig->inviteChat && (sRandomPlayerbotMgr->IsRandomBot(bot) || !botAI->HasActivePlayerMaster()))
{
/* std::map<std::string, std::string> placeholders;
placeholders["%name"] = player->GetName();
placeholders["%members"] = std::to_string(guild->GetMemberSize());
placeholders["%guildname"] = guild->GetName();
AreaTableEntry const* current_area = botAI->GetCurrentArea();
AreaTableEntry const* current_zone = botAI->GetCurrentZone();
placeholders["%area_name"] = current_area ? current_area->area_name[BroadcastHelper::GetLocale()] : BOT_TEXT1("string_unknown_area");
placeholders["%zone_name"] = current_zone ? current_zone->area_name[BroadcastHelper::GetLocale()] : BOT_TEXT1("string_unknown_area");
std::vector<std::string> lines;
//TODO - Move these hardcoded texts to sql!
switch ((urand(0, 10) * urand(0, 10)) / 10)
{
case 0:
lines.push_back(BOT_TEXT2("Hey %name do you want to join my guild?", placeholders));
break;
case 1:
lines.push_back(BOT_TEXT2("Hey man you wanna join my guild %name?", placeholders));
break;
case 2:
lines.push_back(BOT_TEXT2("I think you would be a good contribution to %guildname. Would you like to join %name?", placeholders));
break;
case 3:
lines.push_back(BOT_TEXT2("My guild %guildname has %members quality members. Would you like to make it 1 more %name?", placeholders));
break;
case 4:
lines.push_back(BOT_TEXT2("Hey %name do you want to join %guildname? We have %members members and looking to become number 1 of the server.", placeholders));
break;
case 5:
lines.push_back(BOT_TEXT2("I'm not really good at smalltalk. Do you wanna join my guild %name/r?", placeholders));
break;
case 6:
lines.push_back(BOT_TEXT2("Welcome to %zone_name.... do you want to join my guild %name?", placeholders));
break;
case 7:
lines.push_back(BOT_TEXT2("%name, you should join my guild!", placeholders));
break;
case 8:
lines.push_back(BOT_TEXT2("%name, I got this guild....", placeholders));
break;
case 9:
lines.push_back(BOT_TEXT2("You are actually going to join my guild %name?", placeholders));
lines.push_back(BOT_TEXT2("Haha.. you are the man! We are going to raid Molten...", placeholders));
break;
case 10:
lines.push_back(BOT_TEXT2("Hey Hey! do you guys wanna join my gild????", placeholders));
lines.push_back(BOT_TEXT2("We've got a bunch of high levels and we are really super friendly..", placeholders));
lines.push_back(BOT_TEXT2("..and watch your dog and do your homework...", placeholders));
lines.push_back(BOT_TEXT2("..and we raid once a week and are working on MC raids...", placeholders));
lines.push_back(BOT_TEXT2("..and we have more members than just me...", placeholders));
lines.push_back(BOT_TEXT2("..and please stop I'm lonenly and we can get a ride the whole time...", placeholders));
lines.push_back(BOT_TEXT2("..and it's really beautifull and I feel like crying...", placeholders));
lines.push_back(BOT_TEXT2("So what do you guys say are you going to join are you going to join?", placeholders));
break;
}
for (auto line : lines)
if (sameGroup)
{
WorldPacket data;
ChatHandler::BuildChatPacket(data, bot->GetGroup()->isRaidGroup() ? CHAT_MSG_RAID : CHAT_MSG_PARTY, line.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(), bot->GetName());
bot->GetGroup()->BroadcastPacket(&data, true);
}
else
bot->Say(line, (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));*/
}
if (botAI->DoSpecificAction("guild invite", Event("guild management", guid), true))
{
if (sPlayerbotAIConfig->inviteChat)
return true;
found++;
}
}
return found > 0;

View File

@@ -9,6 +9,7 @@
#include "GuildMgr.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "BroadcastHelper.h"
bool InviteToGroupAction::Execute(Event event)
{
@@ -24,9 +25,16 @@ bool InviteToGroupAction::Invite(Player* player)
if (!player || !player->IsInWorld())
return false;
if (bot == player)
return false;
if (!GET_PLAYERBOT_AI(player) && !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, true, player))
return false;
if (Group* group = player->GetGroup())
if (!group->isRaidGroup() && group->GetMembersCount() > 4)
group->ConvertToRaid();
WorldPacket p;
uint32 roles_mask = 0;
p << player->GetName();
@@ -48,6 +56,9 @@ bool InviteNearbyToGroupAction::Execute(Event event)
if (player->GetGroup())
continue;
if (player == bot)
continue;
if (botAI)
{
if (botAI->GetGrouperType() == GrouperType::SOLO &&
@@ -69,6 +80,26 @@ bool InviteNearbyToGroupAction::Execute(Event event)
if (sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance)
continue;
Group* group = bot->GetGroup();
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (!botAI->HasActivePlayerMaster())
{
if (guild && bot->GetGuildId() == player->GetGuildId())
{
BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group);
}
else
{
std::map<std::string, std::string> placeholders;
placeholders["%player"] = player->GetName();
if (group && group->isRaidGroup())
bot->Say(BOT_TEXT2("join_raid", placeholders), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
else
bot->Say(BOT_TEXT2("join_group", placeholders), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
}
}
return Invite(player);
}
@@ -118,6 +149,8 @@ std::vector<Player*> InviteGuildToGroupAction::getGuildMembers()
bool InviteGuildToGroupAction::Execute(Event event)
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
for (auto& member : getGuildMembers())
{
Player* player = member;
@@ -160,10 +193,30 @@ bool InviteGuildToGroupAction::Execute(Event event)
if (!botAI && sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance)
continue;
Group* group = bot->GetGroup();
BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group);
return Invite(player);
}
return false;
}
bool InviteGuildToGroupAction::isUseful() { return bot->GetGuildId() && InviteNearbyToGroupAction::isUseful(); };
bool InviteGuildToGroupAction::isUseful()
{
return bot->GetGuildId() && InviteNearbyToGroupAction::isUseful();
};
bool JoinGroupAction::Execute(Event event)
{
Player* master = event.getOwner();
Group* group = master->GetGroup();
if (group && (group->IsFull() || bot->GetGroup() == group))
return false;
if (bot->GetGroup())
if (!botAI->DoSpecificAction("leave", event, true))
return false;
return Invite(bot);
}

View File

@@ -21,6 +21,14 @@ public:
virtual bool Invite(Player* player);
};
class JoinGroupAction : public InviteToGroupAction
{
public:
JoinGroupAction(PlayerbotAI* ai, std::string name = "join") : InviteToGroupAction(ai, name) {}
bool Execute(Event event) override;
bool isUseful() override { return !bot->IsBeingTeleported(); }
};
class InviteNearbyToGroupAction : public InviteToGroupAction
{
public:

View File

@@ -98,13 +98,14 @@ bool LeaveGroupAction::Leave(Player* player)
return true;
}
bool LeaveFarAwayAction::Execute(Event event) { return Leave(nullptr); }
bool LeaveFarAwayAction::Execute(Event event)
{
// allow bot to leave party when they want
return Leave(botAI->GetGroupMaster());
}
bool LeaveFarAwayAction::isUseful()
{
if (!sPlayerbotAIConfig->randomBotGroupNearby)
return false;
if (bot->InBattleground())
return false;

View File

@@ -15,6 +15,8 @@
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "GuildMgr.h"
#include "BroadcastHelper.h"
bool LootAction::Execute(Event event)
{
@@ -423,31 +425,7 @@ 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());
// ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", proto->ItemId);
// LOG_ERROR("playerbots", "Bot {} is looting {} {} for usage {}.", bot->GetName().c_str(), itemcount,
// proto->Name1.c_str(), usage);
BroadcastHelper::BroadcastLootingItem(botAI, bot, proto);
}
AI_VALUE(LootObjectStack*, "available loot")->Remove(guid);

View File

@@ -12,64 +12,6 @@
bool QueryItemUsageAction::Execute(Event event)
{
if (!GetMaster() && !sPlayerbotAIConfig->randomBotSayWithoutMaster)
return false;
if (!sPlayerbotAIConfig->sayWhenCollectingItems)
return false;
WorldPacket& data = event.getPacket();
if (!data.empty())
{
data.rpos(0);
ObjectGuid guid;
data >> guid;
if (guid != bot->GetGUID())
return false;
uint32 received;
uint32 created;
uint32 isShowChatMessage;
uint32 notUsed;
uint32 itemId;
uint32 suffixFactor;
uint32 itemRandomPropertyId;
uint32 count;
uint32 invCount;
uint8 bagSlot;
data >> received; // 0=looted, 1=from npc
data >> created; // 0=received, 1=created
data >> isShowChatMessage; // IsShowChatMessage
data >> bagSlot; // item slot, but when added to stack: 0xFFFFFFFF
data >> notUsed;
data >> itemId;
data >> suffixFactor;
data >> itemRandomPropertyId;
data >> count;
// data >> invCount; // [-ZERO] count of items in inventory
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
if (!item)
return false;
bot->Say(QueryItem(item, count, GetCount(item)), LANG_UNIVERSAL);
return true;
}
std::string const text = event.getParam();
ItemIds items = chat->parseItems(text);
for (uint32 itemId : items)
{
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
if (!item)
continue;
botAI->TellMaster(QueryItem(item, 0, GetCount(item)));
}
return true;
}

View File

@@ -16,8 +16,10 @@ void QueryQuestAction::TellObjective(std::string const name, uint32 available, u
bool QueryQuestAction::Execute(Event event)
{
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
Player* bot = botAI->GetBot();
WorldPosition botPos(bot);
WorldPosition* ptr_botpos = &botPos;
std::string text = event.getParam();
bool travel = false;
@@ -30,7 +32,22 @@ bool QueryQuestAction::Execute(Event event)
PlayerbotChatHandler ch(bot);
uint32 questId = ch.extractQuestId(text);
if (!questId)
return false;
{
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 logQuest = bot->GetQuestSlotQuestId(slot);
Quest const* quest = sObjectMgr->GetQuestTemplate(logQuest);
if (!quest)
continue;
if (text.find(quest->GetTitle()) != std::string::npos)
{
questId = quest->GetQuestId();
break;
}
}
}
for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
@@ -58,12 +75,7 @@ bool QueryQuestAction::Execute(Event event)
std::vector<TravelDestination*> allDestinations =
sTravelMgr->getQuestTravelDestinations(bot, questId, true, true, -1);
std::sort(allDestinations.begin(), allDestinations.end(),
[botPos](TravelDestination* i, TravelDestination* j) {
return i->distanceTo(const_cast<WorldPosition*>(&botPos)) <
j->distanceTo(const_cast<WorldPosition*>(&botPos));
});
std::sort(allDestinations.begin(), allDestinations.end(), [ptr_botpos](TravelDestination* i, TravelDestination* j) {return i->distanceTo(ptr_botpos) < j->distanceTo(ptr_botpos); });
for (auto dest : allDestinations)
{
if (limit > 50)
@@ -74,7 +86,7 @@ bool QueryQuestAction::Execute(Event event)
uint32 tpoints = dest->getPoints(true).size();
uint32 apoints = dest->getPoints().size();
out << round(dest->distanceTo(const_cast<WorldPosition*>(&botPos)));
out << round(dest->distanceTo(&botPos));
out << " to " << dest->getTitle();
out << " " << apoints;
@@ -86,12 +98,6 @@ bool QueryQuestAction::Execute(Event event)
if (!dest->isActive(bot))
out << " not active";
if (dest->isFull(bot))
out << " crowded";
if (dest->isFull(bot))
out << " crowded";
botAI->TellMaster(out);
limit++;

View File

@@ -9,27 +9,49 @@
#include "Event.h"
#include "Playerbots.h"
#include "ReputationMgr.h"
#include "ServerFacade.h"
#include "BroadcastHelper.h"
bool QuestAction::Execute(Event event)
{
ObjectGuid guid = event.getObject();
Player* master = GetMaster();
if (!master)
{
if (!guid)
guid = bot->GetTarget();
}
else
{
if (!guid)
guid = master->GetTarget();
}
if (!guid)
return false;
{
if (!master)
{
guid = bot->GetTarget();
}
else
{
guid = master->GetTarget();
}
}
return ProcessQuests(guid);
if (guid)
{
return ProcessQuests(guid);
}
bool result = false;
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (const auto npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (unit && bot->GetDistance(unit) <= INTERACTION_DISTANCE)
result |= ProcessQuests(unit);
}
std::list<ObjectGuid> gos = AI_VALUE(std::list<ObjectGuid>, "nearest game objects");
for (const auto go : gos)
{
GameObject* gameobj = botAI->GetGameObject(go);
if (gameobj && bot->GetDistance(gameobj) <= INTERACTION_DISTANCE)
result |= ProcessQuests(gameobj);
}
return result;
}
bool QuestAction::CompleteQuest(Player* player, uint32 entry)
@@ -69,7 +91,15 @@ bool QuestAction::CompleteQuest(Player* player, uint32 entry)
int32 creature = pQuest->RequiredNpcOrGo[i];
uint32 creaturecount = pQuest->RequiredNpcOrGoCount[i];
if (creature > 0)
// TODO check if we need a REQSPELL condition, this methods and sql entry dosent seem implemented ?
/*if (uint32 spell_id = pQuest->GetReqSpell[i])
{
for (uint16 z = 0; z < creaturecount; ++z)
{
player->CastedCreatureOrGO(creature, ObjectGuid(), spell_id);
}
}*/
/*else*/ if (creature > 0)
{
if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(creature))
for (uint16 z = 0; z < creaturecount; ++z)
@@ -105,6 +135,14 @@ bool QuestAction::CompleteQuest(Player* player, uint32 entry)
player->ModifyMoney(-ReqOrRewMoney);
}
const std::string text_quest = ChatHelper::FormatQuest(pQuest);
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), pQuest->GetTitle());
bot->Say("Quest [ " + text_quest + " ] completed", LANG_UNIVERSAL);
}
botAI->TellMasterNoFacing("Quest completed " + text_quest);
player->CompleteQuest(entry);
return true;
@@ -129,6 +167,8 @@ bool QuestAction::ProcessQuests(WorldObject* questGiver)
if (bot->GetDistance(questGiver) > INTERACTION_DISTANCE && !sPlayerbotAIConfig->syncQuestWithPlayer)
{
//if (botAI->HasStrategy("debug", BotState::BOT_STATE_COMBAT) || botAI->HasStrategy("debug", BotState::BOT_STATE_NON_COMBAT))
botAI->TellError("Cannot talk to quest giver");
return false;
}
@@ -190,6 +230,7 @@ bool QuestAction::AcceptQuest(Quest const* quest, ObjectGuid questGiver)
if (bot->GetQuestStatus(questId) != QUEST_STATUS_NONE && bot->GetQuestStatus(questId) != QUEST_STATUS_REWARDED)
{
BroadcastHelper::BroadcastQuestAccepted(botAI, bot, quest);
out << "Accepted " << chat->FormatQuest(quest);
botAI->TellMaster(out);
return true;
@@ -199,29 +240,154 @@ bool QuestAction::AcceptQuest(Quest const* quest, ObjectGuid questGiver)
out << " " << chat->FormatQuest(quest);
botAI->TellMaster(out);
return false;
}
bool QuestObjectiveCompletedAction::Execute(Event event)
bool QuestUpdateCompleteAction::Execute(Event event)
{
WorldPacket p(event.getPacket());
p.rpos(0);
uint32 questId = 0;
p >> questId;
p.print_storage();
LOG_INFO("playerbots", "Packet: empty{} questId{}", p.empty(), questId);
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
if (qInfo)
{
std::map<std::string, std::string> placeholders;
const auto format = ChatHelper::FormatQuest(qInfo);
placeholders["%quest_link"] = format;
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), qInfo->GetTitle());
bot->Say("Quest [ " + format + " ] completed", LANG_UNIVERSAL);
}
botAI->TellMasterNoFacing("Quest completed " + format);
BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, qInfo);
}
return true;
}
/*
* For creature or gameobject
*/
bool QuestUpdateAddKillAction::Execute(Event event)
{
WorldPacket p(event.getPacket());
p.rpos(0);
uint32 entry, questId, available, required;
ObjectGuid guid;
p >> questId >> entry >> available >> required >> guid;
p >> questId >> entry >> available >> required;
if (entry & 0x80000000)
const Quest* qInfo = sObjectMgr->GetQuestTemplate(questId);
if (qInfo && (entry & 0x80000000))
{
entry &= 0x7FFFFFFF;
if (GameObjectTemplate const* info = sObjectMgr->GetGameObjectTemplate(entry))
botAI->TellMaster(chat->FormatQuestObjective(info->name, available, required));
const GameObjectTemplate* info = sObjectMgr->GetGameObjectTemplate(entry);
if (info)
BroadcastHelper::BroadcastQuestUpdateAddKill(botAI, bot, qInfo, available, required, info->name);
}
else if (qInfo)
{
CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(entry);
if (info)
{
BroadcastHelper::BroadcastQuestUpdateAddKill(botAI, bot, qInfo, available, required, info->Name);
}
}
else
{
if (CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(entry))
botAI->TellMaster(chat->FormatQuestObjective(info->Name, available, required));
std::map<std::string, std::string> placeholders;
placeholders["%quest_id"] = questId;
placeholders["%available"] = available;
placeholders["%required"] = required;
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_COMBAT) || botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT))
{
LOG_INFO("playerbots", "{} => {}", bot->GetName(), BOT_TEXT2("%available/%required for questId: %quest_id", placeholders));
botAI->Say(BOT_TEXT2("%available/%required for questId: %quest_id", placeholders));
}
botAI->TellMasterNoFacing(BOT_TEXT2("%available/%required for questId: %quest_id", placeholders));
}
return false;
}
bool QuestUpdateAddItemAction::Execute(Event event)
{
WorldPacket p(event.getPacket());
p.rpos(0);
uint32 itemId, count;
p >> itemId >> count;
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
auto const* itemPrototype = sObjectMgr->GetItemTemplate(itemId);
if (itemPrototype)
{
std::map<std::string, std::string> placeholders;
placeholders["%item_link"] = botAI->GetChatHelper()->FormatItem(itemPrototype);
uint32 availableItemsCount = botAI->GetInventoryItemsCountWithId(itemId);
placeholders["%quest_obj_available"] = std::to_string(availableItemsCount);
for (const auto& pair : botAI->GetCurrentQuestsRequiringItemId(itemId))
{
placeholders["%quest_link"] = chat->FormatQuest(pair.first);
uint32 requiredItemsCount = pair.second;
placeholders["%quest_obj_required"] = std::to_string(requiredItemsCount);
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_COMBAT) || botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT))
{
const auto text = BOT_TEXT2("%quest_link - %item_link %quest_obj_available/%quest_obj_required", placeholders);
botAI->Say(text);
LOG_INFO("playerbots", "{} => {}", bot->GetName(), text);
}
BroadcastHelper::BroadcastQuestUpdateAddItem(botAI, bot, pair.first, availableItemsCount, requiredItemsCount, itemPrototype);
}
}
return false;
}
bool QuestUpdateFailedAction::Execute(Event event)
{
//opcode SMSG_QUESTUPDATE_FAILED is never sent...(yet?)
return false;
}
bool QuestUpdateFailedTimerAction::Execute(Event event)
{
WorldPacket p(event.getPacket());
p.rpos(0);
uint32 questId;
p >> questId;
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
if (qInfo)
{
std::map<std::string, std::string> placeholders;
placeholders["%quest_link"] = botAI->GetChatHelper()->FormatQuest(qInfo);
botAI->TellMaster(BOT_TEXT2("Failed timer for %quest_link, abandoning", placeholders));
BroadcastHelper::BroadcastQuestUpdateFailedTimer(botAI, bot, qInfo);
}
else
{
botAI->TellMaster("Failed timer for " + std::to_string(questId));
}
return true;
//drop quest
bot->AbandonQuest(questId);
return false;
}

View File

@@ -8,6 +8,7 @@
#include "Action.h"
#include "Object.h"
#include "QuestDef.h"
class ObjectGuid;
class Quest;
@@ -19,23 +20,49 @@ class Object;
class QuestAction : public Action
{
public:
QuestAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) {}
QuestAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) { }
bool Execute(Event event) override;
protected:
bool CompleteQuest(Player* player, uint32 entry);
virtual void ProcessQuest(Quest const* quest, Object* questGiver) = 0;
virtual bool ProcessQuest(Quest const* quest, Object* questGiver) = 0;
bool AcceptQuest(Quest const* quest, ObjectGuid questGiver);
bool ProcessQuests(ObjectGuid questGiver);
bool ProcessQuests(WorldObject* questGiver);
};
class QuestObjectiveCompletedAction : public Action
class QuestUpdateCompleteAction : public Action
{
public:
QuestObjectiveCompletedAction(PlayerbotAI* botAI) : Action(botAI, "quest objective completed") {}
QuestUpdateCompleteAction(PlayerbotAI* ai) : Action(ai, "quest update complete") {}
bool Execute(Event event) override;
};
class QuestUpdateAddKillAction : public Action
{
public:
QuestUpdateAddKillAction(PlayerbotAI* ai) : Action(ai, "quest update add kill") {}
bool Execute(Event event) override;
};
class QuestUpdateAddItemAction : public Action
{
public:
QuestUpdateAddItemAction(PlayerbotAI* ai) : Action(ai, "quest update add item") {}
bool Execute(Event event) override;;
};
class QuestUpdateFailedAction : public Action
{
public:
QuestUpdateFailedAction(PlayerbotAI* ai) : Action(ai, "quest update failed") {}
bool Execute(Event event) override;
};
class QuestUpdateFailedTimerAction : public Action
{
public:
QuestUpdateFailedTimerAction(PlayerbotAI* ai) : Action(ai, "quest update failed timer") {}
bool Execute(Event event) override;
};

View File

@@ -14,6 +14,7 @@
#include "Formations.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "RpgSubActions.h"
bool RpgAction::Execute(Event event)
{
@@ -44,65 +45,109 @@ bool RpgAction::isUseful() { return AI_VALUE(GuidPosition, "rpg target"); }
bool RpgAction::SetNextRpgAction()
{
Strategy* rpgStrategy = botAI->GetAiObjectContext()->GetStrategy("rpg");
Strategy* rpgStrategy;
std::vector<Action*> actions;
std::vector<uint32> relevances;
std::vector<TriggerNode*> triggerNodes;
rpgStrategy->InitTriggers(triggerNodes);
for (auto& triggerNode : triggerNodes)
for (auto& strategy : botAI->GetAiObjectContext()->GetSupportedStrategies())
{
Trigger* trigger = context->GetTrigger(triggerNode->getName());
if (trigger)
if (strategy.find("rpg") == std::string::npos)
continue;
rpgStrategy = botAI->GetAiObjectContext()->GetStrategy(strategy);
rpgStrategy->InitTriggers(triggerNodes);
for (auto& triggerNode : triggerNodes)
{
triggerNode->setTrigger(trigger);
Trigger* trigger = context->GetTrigger(triggerNode->getName());
NextAction** nextActions = triggerNode->getHandlers();
trigger = triggerNode->getTrigger();
bool isChecked = false;
for (int32 i = 0; i < NextAction::size(nextActions); i++)
if (trigger)
{
NextAction* nextAction = nextActions[i];
if (nextAction->getRelevance() > 2.0f)
continue;
triggerNode->setTrigger(trigger);
if (!isChecked && !trigger->IsActive())
break;
NextAction** nextActions = triggerNode->getHandlers();
isChecked = true;
Trigger* trigger = triggerNode->getTrigger();
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
bool isChecked = false;
if (!action->isPossible() || !action->isUseful())
continue;
for (int32 i = 0; i < NextAction::size(nextActions); i++)
{
NextAction* nextAction = nextActions[i];
actions.push_back(action);
relevances.push_back((nextAction->getRelevance() - 1) * 1000);
if (nextAction->getRelevance() > 2.0f)
continue;
if (!isChecked && !trigger->IsActive())
break;
isChecked = true;
Action* action = botAI->GetAiObjectContext()->GetAction(nextAction->getName());
if (!dynamic_cast<RpgEnabled*>(action) || !action->isPossible() || !action->isUseful())
continue;
actions.push_back(action);
relevances.push_back((nextAction->getRelevance() - 1) * 1000);
}
NextAction::destroy(nextActions);
}
NextAction::destroy(nextActions);
}
for (const auto i : triggerNodes)
{
delete i;
}
triggerNodes.clear();
}
if (actions.empty())
return false;
if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_NON_COMBAT))
{
std::vector<std::pair<Action*, uint32>> sortedActions;
for (int i = 0; i < actions.size(); i++)
sortedActions.push_back(std::make_pair(actions[i], relevances[i]));
std::sort(sortedActions.begin(), sortedActions.end(), [](std::pair<Action*, uint32>i, std::pair<Action*, uint32> j) {return i.second > j.second; });
std::stringstream ss;
ss << "------" << chat->FormatWorldobject(AI_VALUE(GuidPosition, "rpg target").GetWorldObject()) << "------";
bot->Say(ss.str(), LANG_UNIVERSAL);
botAI->TellMasterNoFacing(ss.str());
for (auto action : sortedActions)
{
std::ostringstream out;
out << " " << action.first->getName() << " " << action.second;
botAI->TellMasterNoFacing(out);
}
}
std::mt19937 gen(time(0));
sTravelMgr->weighted_shuffle(actions.begin(), actions.end(), relevances.begin(), relevances.end(), gen);
Action* action = actions.front();
for (std::vector<TriggerNode*>::iterator i = triggerNodes.begin(); i != triggerNodes.end(); i++)
if ((botAI->HasStrategy("debug", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_NON_COMBAT)))
{
TriggerNode* trigger = *i;
delete trigger;
}
std::ostringstream out;
out << "do: ";
out << chat->FormatWorldobject(AI_VALUE(GuidPosition, "rpg target").GetWorldObject());
triggerNodes.clear();
out << " " << action->getName();
botAI->TellMasterNoFacing(out);
}
SET_AI_VALUE(std::string, "next rpg action", action->getName());

View File

@@ -15,16 +15,13 @@ class Unit;
class RpgAction : public MovementAction
{
public:
RpgAction(PlayerbotAI* botAI, std::string const name = "rpg") : MovementAction(botAI, name) {}
RpgAction(PlayerbotAI* botAI, std::string const name = "rpg") : MovementAction(botAI, name) { }
bool Execute(Event event) override;
bool isUseful() override;
protected:
virtual bool SetNextRpgAction();
typedef void (RpgAction::*RpgElement)(ObjectGuid guid);
bool AddIgnore(ObjectGuid guid);
bool RemIgnore(ObjectGuid guid);
bool HasIgnore(ObjectGuid guid);
@@ -33,8 +30,7 @@ protected:
class CRpgAction : public RpgAction
{
public:
CRpgAction(PlayerbotAI* botAI) : RpgAction(botAI, "crpg") {}
CRpgAction(PlayerbotAI* botAI) : RpgAction(botAI, "crpg") { }
bool isUseful() override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -7,10 +7,10 @@
#define _PLAYERBOT_SAYACTION_H
#include "Action.h"
#include "PlayerbotAI.h"
#include "NamedObjectContext.h"
class PlayerbotAI;
class SayAction : public Action, public Qualified
{
public:
@@ -31,7 +31,13 @@ public:
ChatReplyAction(PlayerbotAI* ai) : Action(ai, "chat message") {}
virtual bool Execute(Event event) { return true; }
bool isUseful() { return true; }
static void ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 guid2, std::string msg, std::string chanName,
std::string name);
static void ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& guid2, std::string& msg, std::string& chanName, std::string& name);
static bool HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name);
static bool HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name);
static bool HandleWTBItemsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name);
static bool HandleLFGQuestsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name);
static bool SendGeneralResponse(Player* bot, ChatChannelSource chatChannelSource, std::string& responseMessage, std::string& name);
static std::string GenerateReplyMessage(Player* bot, std::string& incomingMessage, uint32& guid1, std::string& name);
};
#endif

View File

@@ -39,3 +39,74 @@ bool ShareQuestAction::Execute(Event event)
return false;
}
bool AutoShareQuestAction::Execute(Event event)
{
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
bool shared = false;
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 logQuest = bot->GetQuestSlotQuestId(slot);
Quest const* quest = sObjectMgr->GetQuestTemplate(logQuest);
if (!quest)
continue;
bool partyNeedsQuest = false;
for (GroupReference* itr = bot->GetGroup()->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* player = itr->GetSource();
if (!player || player == bot || !player->IsInWorld() || !botAI->IsSafe(player)) // skip self
continue;
if (bot->GetDistance(player) > 10)
continue;
if (!player->SatisfyQuestStatus(quest, false))
continue;
if (player->GetQuestStatus(logQuest) == QUEST_STATUS_COMPLETE)
continue;
if (!player->CanTakeQuest(quest, false))
continue;
if (!player->SatisfyQuestLog(false))
continue;
if (player->GetDivider())
continue;
if (auto ai = GET_PLAYERBOT_AI(player))
{
if (PAI_VALUE(uint8, "free quest log slots") < 15 || !urand(0,5))
{
WorldPacket packet(CMSG_PUSHQUESTTOPARTY, 20);
packet << logQuest;
ai->HandleMasterIncomingPacket(packet);
}
}
else
partyNeedsQuest = true;
}
if (!partyNeedsQuest)
continue;
WorldPacket p;
p << logQuest;
bot->GetSession()->HandlePushQuestToParty(p);
botAI->TellMaster("Quest shared");
shared = true;
}
return shared;
}
bool AutoShareQuestAction::isUseful()
{
return bot->GetGroup() && !botAI->HasActivePlayerMaster();
}

View File

@@ -13,9 +13,17 @@ class PlayerbotAI;
class ShareQuestAction : public Action
{
public:
ShareQuestAction(PlayerbotAI* botAI) : Action(botAI, "share quest") {}
ShareQuestAction(PlayerbotAI* botAI, std::string name = "share quest") : Action(botAI, name) { }
bool Execute(Event event) override;
};
class AutoShareQuestAction : public ShareQuestAction
{
public:
AutoShareQuestAction(PlayerbotAI* ai) : ShareQuestAction(botAI, "auto share quest") {}
bool Execute(Event event) override;
bool isUseful() override;
};
#endif

View File

@@ -3,10 +3,19 @@
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "SuggestWhatToDoAction.h"
#include <functional>
#include "SuggestWhatToDoAction.h"
#include "ServerFacade.h"
#include "ChannelMgr.h"
#include "Event.h"
#include "ItemVisitors.h"
#include "AiFactory.h"
#include "ChatHelper.h"
#include "Playerbots.h"
#include "PlayerbotTextMgr.h"
#include "Config.h"
#include "BroadcastHelper.h"
#include "AiFactory.h"
#include "ChannelMgr.h"
#include "ChatHelper.h"
@@ -39,11 +48,13 @@ SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string con
suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindReputation, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::something, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindMaterials, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::somethingToxic, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::toxicLinks, this));
}
bool SuggestWhatToDoAction::isUseful()
{
if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId())
if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId() || bot->GetBattleground())
return false;
std::string qualifier = "suggest what to do";
@@ -88,15 +99,7 @@ void SuggestWhatToDoAction::specificQuest()
if (quests.empty())
return;
uint32 index = rand() % quests.size();
Quest const* quest = sObjectMgr->GetQuestTemplate(quests[index]);
std::map<std::string, std::string> placeholders;
placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%quest"] = chat->FormatQuest(quest);
spam(BOT_TEXT2("suggest_quest", placeholders), urand(0, 1) ? eTalkType::General : 0, !urand(0, 2), !urand(0, 3));
BroadcastHelper::BroadcastSuggestQuest(botAI, quests, bot);
}
void SuggestWhatToDoAction::grindMaterials()
@@ -190,135 +193,34 @@ void SuggestWhatToDoAction::grindReputation()
levels.push_back("exalted");
std::vector<std::string> allowedFactions;
for (const auto& i : factions)
for (auto it : factions)
{
if (bot->GetLevel() >= i.second)
allowedFactions.push_back(i.first);
if ((int)bot->GetLevel() >= it.second) allowedFactions.push_back(it.first);
}
if (allowedFactions.empty())
return;
std::map<std::string, std::string> placeholders;
placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%level"] = levels[urand(0, 2)];
if (allowedFactions.empty()) return;
std::ostringstream rnd;
rnd << urand(1, 5) << "K";
placeholders["%rndK"] = rnd.str();
std::ostringstream itemout;
// itemout << "|c004040b0" << allowedFactions[urand(0, allowedFactions.size() - 1)] << "|r";
itemout << allowedFactions[urand(0, allowedFactions.size() - 1)];
placeholders["%faction"] = itemout.str();
spam(BOT_TEXT2("suggest_faction", placeholders), eTalkType::General, true);
BroadcastHelper::BroadcastSuggestGrindReputation(botAI, levels, allowedFactions, bot);
}
void SuggestWhatToDoAction::something()
{
std::map<std::string, std::string> placeholders;
placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
AreaTableEntry const* entry = sAreaTableStore.LookupEntry(bot->GetAreaId());
if (!entry)
return;
std::ostringstream out;
// out << "|cffb04040" << entry->area_name[0] << "|r";
out << entry->area_name[_dbc_locale];
placeholders["%zone"] = out.str();
spam(BOT_TEXT2("suggest_something", placeholders), urand(0, 1) ? eTalkType::General : 0, !urand(0, 2),
!urand(0, 3));
BroadcastHelper::BroadcastSuggestSomething(botAI, bot);
}
void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, bool guild)
void SuggestWhatToDoAction::somethingToxic()
{
if (msg.empty())
return;
BroadcastHelper::BroadcastSuggestSomethingToxic(botAI, bot);
}
std::vector<std::string> channelNames;
ChannelMgr* cMgr = ChannelMgr::forTeam(bot->GetTeamId());
if (!cMgr)
return;
void SuggestWhatToDoAction::toxicLinks()
{
BroadcastHelper::BroadcastSuggestToxicLinks(botAI, bot);
}
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;
// combine full channel name
char channelName[100];
Channel* chn = nullptr;
if (channel->ChannelID == 24 || (channel->flags & CHANNEL_DBC_FLAG_LFG) != 0)
{
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;
if (flags != 0 && chn->GetFlags() != flags)
continue;
// skip local defense and universal defense
if (chn->GetChannelId() == 22 || chn->GetChannelId() == 23)
continue;
// 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)
{
if (Channel* worldChannel = cMgr->GetChannel("World", bot))
worldChannel->Say(bot->GetGUID(), 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);
}
void SuggestWhatToDoAction::thunderfury()
{
BroadcastHelper::BroadcastSuggestThunderfury(botAI, bot);
}
class FindTradeItemsVisitor : public IterateItemsVisitor
@@ -409,24 +311,14 @@ bool SuggestDungeonAction::Execute(Event event)
}
std::vector<std::string> allowedInstances;
for (const auto& instance : instances)
for (auto it : instances)
{
if (bot->GetLevel() >= instance.second)
allowedInstances.push_back(instance.first);
if (bot->GetLevel() >= it.second) allowedInstances.push_back(it.first);
}
if (allowedInstances.empty())
return false;
std::map<std::string, std::string> placeholders;
placeholders["%role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
if (allowedInstances.empty()) return false;
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));
BroadcastHelper::BroadcastSuggestInstance(botAI, allowedInstances, bot);
return true;
}
@@ -484,10 +376,6 @@ bool SuggestTradeAction::Execute(Event event)
if (!price)
return false;
std::map<std::string, std::string> placeholders;
placeholders["%item"] = chat->FormatItem(proto, count);
placeholders["%gold"] = chat->formatMoney(price);
spam(BOT_TEXT2("suggest_sell", placeholders), urand(0, 1) ? eTalkType::Trade : 0, !urand(0, 2), urand(0, 5));
BroadcastHelper::BroadcastSuggestSell(botAI, proto, count, price, bot);
return true;
}

View File

@@ -25,8 +25,10 @@ protected:
void grindReputation();
void grindMaterials();
void something();
void spam(std::string msg, uint8 flags = 0, bool worldChat = false, bool guild = false);
void toxicLinks();
void somethingToxic();
void thunderfury();
std::vector<uint32> GetIncompletedQuests();
private:
@@ -38,9 +40,7 @@ class SuggestTradeAction : public SuggestWhatToDoAction
{
public:
SuggestTradeAction(PlayerbotAI* botAI);
bool Execute(Event event) override;
bool isUseful() override { return true; }
};
class SuggestDungeonAction : public SuggestWhatToDoAction
@@ -49,8 +49,6 @@ public:
SuggestDungeonAction(PlayerbotAI* botAI);
bool Execute(Event event) override;
bool isUseful() override { return true; }
private:
static std::map<std::string, uint8> instances;
};

View File

@@ -12,9 +12,11 @@
#include "Playerbots.h"
#include "QuestDef.h"
#include "WorldPacket.h"
#include "BroadcastHelper.h"
void TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver)
bool TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver)
{
bool isCompleted = false;
std::ostringstream out;
out << "Quest ";
@@ -28,7 +30,7 @@ void TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver
{
QuestStatus masterStatus = master->GetQuestStatus(quest->GetQuestId());
if (masterStatus == QUEST_STATUS_INCOMPLETE || masterStatus == QUEST_STATUS_FAILED)
CompleteQuest(master, quest->GetQuestId());
isCompleted |= CompleteQuest(master, quest->GetQuestId());
}
}
@@ -37,39 +39,42 @@ void TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver
if (master && master->GetQuestStatus(quest->GetQuestId()) == QUEST_STATUS_COMPLETE &&
(status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_FAILED))
{
CompleteQuest(bot, quest->GetQuestId());
isCompleted |= CompleteQuest(bot, quest->GetQuestId());
status = bot->GetQuestStatus(quest->GetQuestId());
}
}
switch (status)
{
case QUEST_STATUS_COMPLETE:
TurnInQuest(quest, questGiver, out);
break;
case QUEST_STATUS_INCOMPLETE:
out << "|cffff0000Incompleted|r";
break;
case QUEST_STATUS_NONE:
out << "|cff00ff00Available|r";
break;
case QUEST_STATUS_FAILED:
out << "|cffff0000Failed|r";
break;
default:
break;
case QUEST_STATUS_COMPLETE:
isCompleted |= TurnInQuest(quest, questGiver, out);
break;
case QUEST_STATUS_INCOMPLETE:
out << "|cffff0000Incompleted|r";
break;
case QUEST_STATUS_NONE:
AcceptQuest(quest, questGiver->GetGUID());
out << "|cff00ff00Available|r";
break;
case QUEST_STATUS_FAILED:
out << "|cffff0000Failed|r";
break;
default:
break;
}
out << ": " << chat->FormatQuest(quest);
botAI->TellMaster(out);
return isCompleted;
}
void TalkToQuestGiverAction::TurnInQuest(Quest const* quest, Object* questGiver, std::ostringstream& out)
bool TalkToQuestGiverAction::TurnInQuest(Quest const* quest, Object* questGiver, std::ostringstream& out)
{
uint32 questID = quest->GetQuestId();
if (bot->GetQuestRewardStatus(questID))
return;
return false;
bot->PlayDistanceSound(621);
@@ -81,18 +86,33 @@ void TalkToQuestGiverAction::TurnInQuest(Quest const* quest, Object* questGiver,
{
RewardMultipleItem(quest, questGiver, out);
}
if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT))
{
const Quest* pQuest = sObjectMgr->GetQuestTemplate(questID);
const std::string text_quest = ChatHelper::FormatQuest(pQuest);
LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), pQuest->GetTitle());
bot->Say("Quest [ " + text_quest + " ] completed", LANG_UNIVERSAL);
}
return true;
}
void TalkToQuestGiverAction::RewardNoItem(Quest const* quest, Object* questGiver, std::ostringstream& out)
{
std::map<std::string, std::string> args;
args["%quest"] = chat->FormatQuest(quest);
if (bot->CanRewardQuest(quest, false))
{
out << BOT_TEXT2("quest_status_completed", args);
BroadcastHelper::BroadcastQuestTurnedIn(botAI, bot, quest);
bot->RewardQuest(quest, 0, questGiver, false);
out << "Completed";
}
else
{
out << "|cffff0000Unable to turn in|r";
out << BOT_TEXT2("quest_status_unable_to_complete", args);
}
}
@@ -100,15 +120,19 @@ void TalkToQuestGiverAction::RewardSingleItem(Quest const* quest, Object* questG
{
int index = 0;
ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[index]);
std::map<std::string, std::string> args;
args["%quest"] = chat->FormatQuest(quest);
args["%item"] = chat->FormatItem(item);
if (bot->CanRewardQuest(quest, index, false))
{
out << BOT_TEXT2("quest_status_complete_single_reward", args);
BroadcastHelper::BroadcastQuestTurnedIn(botAI, bot, quest);
bot->RewardQuest(quest, index, questGiver, true);
out << "Rewarded " << chat->FormatItem(item);
}
else
{
out << "|cffff0000Unable to turn in:|r, reward: " << chat->FormatItem(item);
out << BOT_TEXT2("quest_status_unable_to_complete", args);
}
}
@@ -247,26 +271,27 @@ bool TurnInQueryQuestAction::Execute(Event event)
out << "Quest ";
switch (status)
{
case QUEST_STATUS_COMPLETE:
TurnInQuest(quest, object, out);
break;
case QUEST_STATUS_INCOMPLETE:
out << "|cffff0000Incompleted|r";
break;
case QUEST_STATUS_NONE:
out << "|cff00ff00Available|r";
break;
case QUEST_STATUS_FAILED:
out << "|cffff0000Failed|r";
break;
case QUEST_STATUS_REWARDED:
out << "|cffff0000Rewarded|r";
break;
default:
break;
case QUEST_STATUS_COMPLETE:
TurnInQuest(quest, object, out);
break;
case QUEST_STATUS_INCOMPLETE:
out << "|cffff0000Incompleted|r";
break;
case QUEST_STATUS_NONE:
AcceptQuest(quest, object->GetGUID());
out << "|cff00ff00Available|r";
break;
case QUEST_STATUS_FAILED:
out << "|cffff0000Failed|r";
break;
case QUEST_STATUS_REWARDED:
out << "|cffff0000Rewarded|r";
break;
default:
break;
}
out << ": " << chat->FormatQuest(quest);
botAI->TellMaster(out);
return true;
}
}

View File

@@ -15,11 +15,11 @@ class WorldObject;
class TalkToQuestGiverAction : public QuestAction
{
public:
TalkToQuestGiverAction(PlayerbotAI* botAI, std::string name = "talk to quest giver") : QuestAction(botAI, name) {}
TalkToQuestGiverAction(PlayerbotAI* botAI, std::string name = "talk to quest giver") : QuestAction(botAI, name) { }
protected:
void ProcessQuest(Quest const* quest, Object* questGiver) override;
void TurnInQuest(Quest const* quest, Object* questGiver, std::ostringstream& out);
bool ProcessQuest(Quest const* quest, Object* questGiver) override;
bool TurnInQuest(Quest const* quest, Object* questGiver, std::ostringstream& out);
private:
void RewardNoItem(Quest const* quest, Object* questGiver, std::ostringstream& out);

View File

@@ -438,13 +438,10 @@ bool UseRandomQuestItem::Execute(Event event)
ObjectGuid goTarget;
std::vector<Item*> questItems = AI_VALUE2(std::vector<Item*>, "inventory items", "quest");
Item* item = nullptr;
uint32 delay = 0;
if (questItems.empty())
return false;
Item* item = nullptr;
for (uint8 i = 0; i < 5; i++)
{
auto itr = questItems.begin();
@@ -452,7 +449,6 @@ bool UseRandomQuestItem::Execute(Event event)
Item* questItem = *itr;
ItemTemplate const* proto = questItem->GetTemplate();
if (proto->StartQuest)
{
Quest const* qInfo = sObjectMgr->GetQuestTemplate(proto->StartQuest);
@@ -463,61 +459,14 @@ bool UseRandomQuestItem::Execute(Event event)
}
}
uint32 spellId = proto->Spells[0].SpellId;
if (spellId)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
GuidVector npcs = AI_VALUE(GuidVector, ("nearest npcs"));
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (botAI->CanCastSpell(spellId, unit, false))
{
item = questItem;
unitTarget = unit;
break;
}
}
GuidVector gos = AI_VALUE(GuidVector, ("nearest game objects"));
for (auto& go : gos)
{
GameObject* gameObject = botAI->GetGameObject(go);
GameObjectTemplate const* goInfo = gameObject->GetGOInfo();
if (!goInfo->GetLockId())
continue;
LockEntry const* lock = sLockStore.LookupEntry(goInfo->GetLockId());
for (uint8 i = 0; i < MAX_LOCK_CASE; ++i)
{
if (!lock->Type[i])
continue;
if (lock->Type[i] != LOCK_KEY_ITEM)
continue;
if (lock->Index[i] == proto->ItemId)
{
item = questItem;
goTarget = go;
unitTarget = nullptr;
break;
}
}
}
}
}
if (!item)
return false;
if (!goTarget && !unitTarget)
return false;
bool used = UseItem(item, goTarget, nullptr, unitTarget);
if (used)
botAI->SetNextCheckDelay(delay);
botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
return used;
}

View File

@@ -38,6 +38,7 @@
#include "TellMasterAction.h"
#include "TradeStatusAction.h"
#include "UseMeetingStoneAction.h"
#include "NamedObjectContext.h"
class PlayerbotAI;
@@ -52,10 +53,6 @@ public:
creators["tell not enough money"] = &WorldPacketActionContext::tell_not_enough_money;
creators["tell not enough reputation"] = &WorldPacketActionContext::tell_not_enough_reputation;
creators["tell cannot equip"] = &WorldPacketActionContext::tell_cannot_equip;
creators["talk to quest giver"] = &WorldPacketActionContext::turn_in_quest;
creators["accept quest"] = &WorldPacketActionContext::accept_quest;
creators["accept all quests"] = &WorldPacketActionContext::accept_all_quests;
creators["accept quest share"] = &WorldPacketActionContext::accept_quest_share;
creators["loot roll"] = &WorldPacketActionContext::loot_roll;
creators["master loot roll"] = &WorldPacketActionContext::master_loot_roll;
creators["revive from corpse"] = &WorldPacketActionContext::revive_from_corpse;
@@ -69,7 +66,20 @@ public:
creators["remember taxi"] = &WorldPacketActionContext::remember_taxi;
creators["accept trade"] = &WorldPacketActionContext::accept_trade;
creators["store loot"] = &WorldPacketActionContext::store_loot;
creators["quest objective completed"] = &WorldPacketActionContext::quest_objective_completed;
// quest
creators["talk to quest giver"] = &WorldPacketActionContext::turn_in_quest;
creators["accept quest"] = &WorldPacketActionContext::accept_quest;
creators["confirm quest"] = &WorldPacketActionContext::confirm_quest;
creators["accept all quests"] = &WorldPacketActionContext::accept_all_quests;
creators["accept quest share"] = &WorldPacketActionContext::accept_quest_share;
creators["quest update add kill"] = &WorldPacketActionContext::quest_update_add_kill;
creators["quest update add item"] = &WorldPacketActionContext::quest_update_add_item;
creators["quest update failed"] = &WorldPacketActionContext::quest_update_failed;
creators["quest update failed timer"] = &WorldPacketActionContext::quest_update_failed_timer;
creators["quest update complete"] = &WorldPacketActionContext::quest_update_complete;
creators["turn in query quest"] = &WorldPacketActionContext::turn_in_query_quest;
creators["party command"] = &WorldPacketActionContext::party_command;
creators["tell cast failed"] = &WorldPacketActionContext::tell_cast_failed;
creators["accept duel"] = &WorldPacketActionContext::accept_duel;
@@ -93,8 +103,6 @@ public:
creators["lfg teleport"] = &WorldPacketActionContext::lfg_teleport;
creators["see spell"] = &WorldPacketActionContext::see_spell;
creators["arena team accept"] = &WorldPacketActionContext::arena_team_accept;
creators["turn in query quest"] = &WorldPacketActionContext::turn_in_query_quest;
creators["quest confirm accept"] = &WorldPacketActionContext::quest_confirm_accept;
}
private:
@@ -107,7 +115,6 @@ private:
static Action* accept_duel(PlayerbotAI* botAI) { return new AcceptDuelAction(botAI); }
static Action* tell_cast_failed(PlayerbotAI* botAI) { return new TellCastFailedAction(botAI); }
static Action* party_command(PlayerbotAI* botAI) { return new PartyCommandAction(botAI); }
static Action* quest_objective_completed(PlayerbotAI* botAI) { return new QuestObjectiveCompletedAction(botAI); }
static Action* store_loot(PlayerbotAI* botAI) { return new StoreLootAction(botAI); }
static Action* accept_trade(PlayerbotAI* botAI) { return new TradeStatusAction(botAI); }
static Action* remember_taxi(PlayerbotAI* botAI) { return new RememberTaxiAction(botAI); }
@@ -120,21 +127,27 @@ private:
static Action* auto_release(PlayerbotAI* botAI) { return new AutoReleaseSpiritAction(botAI); }
static Action* revive_from_corpse(PlayerbotAI* botAI) { return new ReviveFromCorpseAction(botAI); }
static Action* accept_invitation(PlayerbotAI* botAI) { return new AcceptInvitationAction(botAI); }
static Action* give_leader_in_dungeon(PlayerbotAI* botAI)
{
return new GiveLeaderAction(botAI, "I don't know this dungeon, lead the way!");
}
static Action* give_leader_in_dungeon(PlayerbotAI* botAI) { return new GiveLeaderAction(botAI, "I don't know this dungeon, lead the way!"); }
static Action* pass_leadership_to_master(PlayerbotAI* botAI) { return new PassLeadershipToMasterAction(botAI); }
static Action* tell_not_enough_money(PlayerbotAI* botAI) { return new TellMasterAction(botAI, "Not enough money"); }
static Action* tell_not_enough_reputation(PlayerbotAI* botAI)
{
return new TellMasterAction(botAI, "Not enough reputation");
}
static Action* tell_not_enough_reputation(PlayerbotAI* botAI) { return new TellMasterAction(botAI, "Not enough reputation"); }
static Action* tell_cannot_equip(PlayerbotAI* botAI) { return new InventoryChangeFailureAction(botAI); }
// quest
static Action* quest_update_add_kill(PlayerbotAI* ai) { return new QuestUpdateAddKillAction(ai); }
static Action* quest_update_add_item(PlayerbotAI* ai) { return new QuestUpdateAddItemAction(ai); }
static Action* quest_update_failed(PlayerbotAI* ai) { return new QuestUpdateFailedAction(ai); }
static Action* quest_update_failed_timer(PlayerbotAI* ai) { return new QuestUpdateFailedTimerAction(ai); }
static Action* quest_update_complete(PlayerbotAI* botAI) { return new QuestUpdateCompleteAction(botAI); }
static Action* turn_in_quest(PlayerbotAI* botAI) { return new TalkToQuestGiverAction(botAI); }
static Action* accept_quest(PlayerbotAI* botAI) { return new AcceptQuestAction(botAI); }
static Action* confirm_quest(PlayerbotAI* ai) { return new ConfirmQuestAction(ai); }
static Action* accept_all_quests(PlayerbotAI* botAI) { return new AcceptAllQuestsAction(botAI); }
static Action* accept_quest_share(PlayerbotAI* botAI) { return new AcceptQuestShareAction(botAI); }
static Action* turn_in_query_quest(PlayerbotAI* botAI) { return new TurnInQueryQuestAction(botAI); }
//static Action* quest_confirm_accept(PlayerbotAI* botAI) { return new QuestConfirmAcceptAction(botAI); }
static Action* loot_roll(PlayerbotAI* botAI) { return new LootRollAction(botAI); }
static Action* master_loot_roll(PlayerbotAI* botAI) { return new MasterLootRollAction(botAI); }
static Action* bg_join(PlayerbotAI* botAI) { return new BGJoinAction(botAI); }
@@ -151,8 +164,6 @@ private:
static Action* lfg_join(PlayerbotAI* botAI) { return new LfgJoinAction(botAI); }
static Action* see_spell(PlayerbotAI* botAI) { return new SeeSpellAction(botAI); }
static Action* arena_team_accept(PlayerbotAI* botAI) { return new ArenaTeamAcceptAction(botAI); }
static Action* turn_in_query_quest(PlayerbotAI* botAI) { return new TurnInQueryQuestAction(botAI); }
static Action* quest_confirm_accept(PlayerbotAI* botAI) { return new QuestConfirmAcceptAction(botAI); }
};
#endif

View File

@@ -9,6 +9,7 @@
#include "GuildMgr.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "BroadcastHelper.h"
bool XpGainAction::Execute(Event event)
{
@@ -35,25 +36,10 @@ 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->GetMap()->IsDungeon())
{
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);
}
}
BroadcastHelper::BroadcastKill(botAI, bot, creature);
}
Unit* victim = nullptr;

View File

@@ -44,4 +44,13 @@ public:
std::string const getName() override { return "debug spell"; }
};
class DebugQuestStrategy : public Strategy
{
public:
DebugQuestStrategy(PlayerbotAI* botAI) : Strategy(botAI) { }
uint32 GetType() const override { return STRATEGY_TYPE_NONCOMBAT | STRATEGY_TYPE_COMBAT; }
std::string const getName() override { return "debug quest"; }
};
#endif

View File

@@ -11,19 +11,13 @@ NextAction** MaintenanceStrategy::getDefaultActions() { return nullptr; }
void MaintenanceStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode("random", NextAction::array(0, new NextAction("clean quest log", 6.0f), nullptr)));
triggers.push_back(
new TriggerNode("random", NextAction::array(0, new NextAction("use random recipe", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("often", NextAction::array(0, new NextAction("use random quest item", 10.0f), nullptr)));
triggers.push_back(
new TriggerNode("random", NextAction::array(0, new NextAction("disenchant random item", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("random", NextAction::array(0, new NextAction("enchant random item", 1.0f), nullptr)));
triggers.push_back(
new TriggerNode("random", NextAction::array(0, new NextAction("smart destroy item", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("clean quest log", 6.0f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("use random recipe", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("disenchant random item", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("enchant random item", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("smart destroy item", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("move stuck", NextAction::array(0, new NextAction("reset", 1.0f), nullptr)));
// triggers.push_back(new TriggerNode("move long stuck", NextAction::array(0, new NextAction("hearthstone", 0.9f),
// new NextAction("repop", 0.8f), nullptr)));
// triggers.push_back(new TriggerNode("move long stuck", NextAction::array(0, new NextAction("hearthstone", 0.9f), new NextAction("repop", 0.8f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("use random quest item", 0.9f), nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("auto share quest", 0.9f), nullptr)));
}

View File

@@ -9,16 +9,15 @@
void NonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("check mount state", 1.0f),
// new NextAction("check values", 1.0f),
nullptr)));
// triggers.push_back(new TriggerNode("near dark portal", NextAction::array(0, new NextAction("move to dark
// portal", 1.0f), nullptr))); triggers.push_back(new TriggerNode("at dark portal azeroth", NextAction::array(0, new
// NextAction("use dark portal azeroth", 1.0f), nullptr))); triggers.push_back(new TriggerNode("at dark portal
// outland", NextAction::array(0, new NextAction("move from dark portal", 1.0f), nullptr))); triggers.push_back(new
// TriggerNode("need world buff", NextAction::array(0, new NextAction("world buff", 1.0f), nullptr)));
// triggers.push_back(new TriggerNode("vehicle near", NextAction::array(0, new NextAction("enter vehicle", 10.0f),
// nullptr)));
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("clean quest log", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("timer", NextAction::array(0, new NextAction("check mount state", 1.0f),
// new NextAction("check values", 1.0f),
nullptr)));
// triggers.push_back(new TriggerNode("near dark portal", NextAction::array(0, new NextAction("move to dark portal", 1.0f), nullptr)));
// triggers.push_back(new TriggerNode("at dark portal azeroth", NextAction::array(0, new NextAction("use dark portal azeroth", 1.0f), nullptr)));
// triggers.push_back(new TriggerNode("at dark portal outland", NextAction::array(0, new NextAction("move from dark portal", 1.0f), nullptr)));
// triggers.push_back(new TriggerNode("need world buff", NextAction::array(0, new NextAction("world buff", 1.0f), nullptr)));
// triggers.push_back(new TriggerNode("vehicle near", NextAction::array(0, new NextAction("enter vehicle", 10.0f), nullptr)));
}
void CollisionStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -34,66 +34,44 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
triggers.push_back(new TriggerNode("activate taxi", NextAction::array(0, new NextAction("remember taxi", relevance),
new NextAction("taxi", relevance), nullptr)));
triggers.push_back(new TriggerNode("taxi done", NextAction::array(0, new NextAction("taxi", relevance), nullptr)));
triggers.push_back(
new TriggerNode("trade status", NextAction::array(0, new NextAction("accept trade", relevance),
new NextAction("equip upgrades", relevance), nullptr)));
triggers.push_back(new TriggerNode("area trigger",
NextAction::array(0, new NextAction("reach area trigger", relevance), nullptr)));
triggers.push_back(new TriggerNode("within area trigger",
NextAction::array(0, new NextAction("area trigger", relevance), nullptr)));
triggers.push_back(
new TriggerNode("loot response", NextAction::array(0, new NextAction("store loot", relevance), nullptr)));
triggers.push_back(
new TriggerNode("item push result", NextAction::array(0, new NextAction("query item usage", relevance),
new NextAction("equip upgrades", relevance), nullptr)));
triggers.push_back(new TriggerNode("ready check finished",
NextAction::array(0, new NextAction("finish ready check", relevance), nullptr)));
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("security check", relevance), new
// NextAction("check mail", relevance), nullptr)));
triggers.push_back(
new TriggerNode("guild invite", NextAction::array(0, new NextAction("guild accept", relevance), nullptr)));
triggers.push_back(
new TriggerNode("petition offer", NextAction::array(0, new NextAction("petition sign", relevance), nullptr)));
triggers.push_back(
new TriggerNode("lfg proposal", NextAction::array(0, new NextAction("lfg accept", relevance), nullptr)));
triggers.push_back(
new TriggerNode("lfg proposal active", NextAction::array(0, new NextAction("lfg accept", relevance), nullptr)));
triggers.push_back(new TriggerNode("arena team invite",
NextAction::array(0, new NextAction("arena team accept", relevance), nullptr)));
triggers.push_back(new TriggerNode(
"quest confirm accept", NextAction::array(0, new NextAction("quest confirm accept", relevance), nullptr)));
// triggers.push_back(new TriggerNode("no non bot players around", NextAction::array(0, new NextAction("delay",
// relevance), nullptr)));
triggers.push_back(
new TriggerNode("bg status", NextAction::array(0, new NextAction("bg status", relevance), nullptr)));
triggers.push_back(new TriggerNode("trade status", NextAction::array(0, new NextAction("accept trade", relevance), new NextAction("equip upgrades", relevance), nullptr)));
triggers.push_back(new TriggerNode("area trigger", NextAction::array(0, new NextAction("reach area trigger", relevance), nullptr)));
triggers.push_back(new TriggerNode("within area trigger", NextAction::array(0, new NextAction("area trigger", relevance), nullptr)));
triggers.push_back(new TriggerNode("loot response", NextAction::array(0, new NextAction("store loot", relevance), nullptr)));
triggers.push_back(new TriggerNode("item push result", NextAction::array(0, new NextAction("query item usage", relevance), new NextAction("equip upgrades", relevance), nullptr)));
triggers.push_back(new TriggerNode("ready check finished", NextAction::array(0, new NextAction("finish ready check", relevance), nullptr)));
// triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("security check", relevance), new NextAction("check mail", relevance), nullptr)));
triggers.push_back(new TriggerNode("guild invite", NextAction::array(0, new NextAction("guild accept", relevance), nullptr)));
triggers.push_back(new TriggerNode("petition offer", NextAction::array(0, new NextAction("petition sign", relevance), nullptr)));
triggers.push_back(new TriggerNode("lfg proposal", NextAction::array(0, new NextAction("lfg accept", relevance), nullptr)));
triggers.push_back(new TriggerNode("lfg proposal active", NextAction::array(0, new NextAction("lfg accept", relevance), nullptr)));
triggers.push_back(new TriggerNode("arena team invite", NextAction::array(0, new NextAction("arena team accept", relevance), nullptr)));
//triggers.push_back(new TriggerNode("no non bot players around", NextAction::array(0, new NextAction("delay", relevance), nullptr)));
triggers.push_back(new TriggerNode("bg status", NextAction::array(0, new NextAction("bg status", relevance), nullptr)));
triggers.push_back(new TriggerNode("xpgain", NextAction::array(0, new NextAction("xp gain", relevance), nullptr)));
triggers.push_back(
new TriggerNode("levelup", NextAction::array(0, new NextAction("auto teleport for level", relevance + 3),
new NextAction("auto talents", relevance + 2),
new NextAction("auto learn spell", relevance + 1),
new NextAction("auto upgrade equip", relevance), nullptr)));
// triggers.push_back(new TriggerNode("group destroyed", NextAction::array(0, new NextAction("reset botAI",
// relevance), nullptr)));
triggers.push_back(new TriggerNode(
"questgiver quest details", NextAction::array(0, new NextAction("turn in query quest", relevance), nullptr)));
triggers.push_back(new TriggerNode("levelup", NextAction::array(0,
new NextAction("auto teleport for level", relevance + 3),
new NextAction("auto talents", relevance + 2),
new NextAction("auto learn spell", relevance + 1),
new NextAction("auto upgrade equip", relevance),
nullptr)));
// triggers.push_back(new TriggerNode("group destroyed", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr)));
triggers.push_back(
new TriggerNode("group list", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr)));
triggers.push_back(
new TriggerNode("see spell", NextAction::array(0, new NextAction("see spell", relevance), nullptr)));
triggers.push_back(
new TriggerNode("release spirit", NextAction::array(0, new NextAction("release", relevance), nullptr)));
triggers.push_back(new TriggerNode("revive from corpse",
NextAction::array(0, new NextAction("revive from corpse", relevance), nullptr)));
triggers.push_back(new TriggerNode("master loot roll",
NextAction::array(0, new NextAction("master loot roll", relevance), nullptr)));
triggers.push_back(new TriggerNode("group list", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr)));
triggers.push_back(new TriggerNode("see spell", NextAction::array(0, new NextAction("see spell", relevance), nullptr)));
triggers.push_back(new TriggerNode("release spirit", NextAction::array(0, new NextAction("release", relevance), nullptr)));
triggers.push_back(new TriggerNode("revive from corpse", NextAction::array(0, new NextAction("revive from corpse", relevance), nullptr)));
triggers.push_back(new TriggerNode("master loot roll", NextAction::array(0, new NextAction("master loot roll", relevance), nullptr)));
// quest ?
//triggers.push_back(new TriggerNode("quest confirm", NextAction::array(0, new NextAction("quest confirm", relevance), nullptr)));
triggers.push_back(new TriggerNode("questgiver quest details", NextAction::array(0, new NextAction("turn in query quest", relevance), nullptr)));
}
WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
{
supported.push_back("loot roll");
supported.push_back("check mount state");
supported.push_back("quest objective completed");
supported.push_back("party command");
supported.push_back("ready check");
supported.push_back("uninvite");
@@ -102,6 +80,14 @@ WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : Pas
supported.push_back("random bot update");
supported.push_back("inventory change failure");
supported.push_back("bg status");
// quests
supported.push_back("quest update add kill");
supported.push_back("quest update add item");
supported.push_back("quest update failed");
supported.push_back("quest update failed timer");
supported.push_back("quest update complete");
supported.push_back("confirm quest");
}
void ReadyCheckStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)

View File

@@ -22,7 +22,7 @@ public:
class ReadyCheckStrategy : public PassTroughStrategy
{
public:
ReadyCheckStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {}
ReadyCheckStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) { }
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
std::string const getName() override { return "ready check"; }

View File

@@ -107,6 +107,12 @@ bool RpgEndQuestTrigger::IsActive()
if (AI_VALUE2(bool, "can turn in quest npc", guidP.GetEntry()))
return true;
if (!AI_VALUE2(bool, "can accept quest low level npc", guidP.GetEntry()))
return false;
if (guidP.GetEntry() == AI_VALUE(TravelTarget*, "travel target")->getEntry())
return true;
return false;
}
@@ -292,6 +298,10 @@ bool RpgQueueBGTrigger::IsActive()
if (!guidP.IsCreature())
return false;
// if bot is not leader disallow tag bg
if (bot->GetGroup() && !bot->GetGroup()->IsLeader(bot->GetGUID()))
return false;
if (AI_VALUE(BattlegroundTypeId, "rpg bg type") == BATTLEGROUND_TYPE_NONE)
return false;

View File

@@ -22,9 +22,6 @@ public:
creators["not enough reputation"] = &WorldPacketTriggerContext::no_reputation;
creators["cannot equip"] = &WorldPacketTriggerContext::cannot_equip;
creators["use game object"] = &WorldPacketTriggerContext::use_game_object;
creators["complete quest"] = &WorldPacketTriggerContext::complete_quest;
creators["accept quest"] = &WorldPacketTriggerContext::accept_quest;
creators["quest share"] = &WorldPacketTriggerContext::quest_share;
creators["loot roll"] = &WorldPacketTriggerContext::loot_roll;
creators["resurrect request"] = &WorldPacketTriggerContext::resurrect_request;
creators["area trigger"] = &WorldPacketTriggerContext::area_trigger;
@@ -34,7 +31,20 @@ public:
creators["trade status"] = &WorldPacketTriggerContext::trade_status;
creators["loot response"] = &WorldPacketTriggerContext::loot_response;
creators["out of react range"] = &WorldPacketTriggerContext::out_of_react_range;
creators["quest objective completed"] = &WorldPacketTriggerContext::quest_objective_completed;
// quest
creators["complete quest"] = &WorldPacketTriggerContext::complete_quest;
creators["accept quest"] = &WorldPacketTriggerContext::accept_quest;
creators["confirm quest"] = &WorldPacketTriggerContext::quest_confirm_accept;
creators["quest share"] = &WorldPacketTriggerContext::quest_share;
creators["quest update add kill"] = &WorldPacketTriggerContext::quest_update_add_kill;
creators["quest update add item"] = &WorldPacketTriggerContext::quest_update_add_item;
creators["quest update failed"] = &WorldPacketTriggerContext::quest_update_failed;
creators["quest update failed timer"] = &WorldPacketTriggerContext::quest_update_failed_timer;
creators["quest update complete"] = &WorldPacketTriggerContext::quest_update_complete;
creators["questgiver quest details"] = &WorldPacketTriggerContext::questgiver_quest_details;
creators["item push result"] = &WorldPacketTriggerContext::item_push_result;
creators["party command"] = &WorldPacketTriggerContext::party_command;
creators["taxi done"] = &WorldPacketTriggerContext::taxi_done;
@@ -61,17 +71,12 @@ public:
creators["receive emote"] = &WorldPacketTriggerContext::receive_emote;
creators["receive text emote"] = &WorldPacketTriggerContext::receive_text_emote;
creators["arena team invite"] = &WorldPacketTriggerContext::arena_team_invite;
creators["quest confirm accept"] = &WorldPacketTriggerContext::quest_confirm_accept;
creators["group destroyed"] = &WorldPacketTriggerContext::group_destroyed;
creators["group list"] = &WorldPacketTriggerContext::group_list;
creators["questgiver quest details"] = &WorldPacketTriggerContext::questgiver_quest_details;
}
private:
static Trigger* inventory_change_failure(PlayerbotAI* botAI)
{
return new WorldPacketTrigger(botAI, "inventory change failure");
}
static Trigger* inventory_change_failure(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "inventory change failure"); }
static Trigger* guild_invite(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "guild invite"); }
static Trigger* lfg_teleport(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "lfg teleport"); }
static Trigger* lfg_leave(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "lfg leave"); }
@@ -80,20 +85,26 @@ private:
static Trigger* lfg_update(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "lfg join"); }
static Trigger* uninvite(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "uninvite"); }
static Trigger* uninvite_guid(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "uninvite guid"); }
static Trigger* ready_check_finished(PlayerbotAI* botAI)
{
return new WorldPacketTrigger(botAI, "ready check finished");
}
static Trigger* ready_check_finished(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "ready check finished"); }
static Trigger* ready_check(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "ready check"); }
static Trigger* duel_requested(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "duel requested"); }
static Trigger* cast_failed(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "cast failed"); }
static Trigger* taxi_done(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "taxi done"); }
static Trigger* party_command(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "party command"); }
static Trigger* item_push_result(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "item push result"); }
static Trigger* quest_objective_completed(PlayerbotAI* botAI)
{
return new WorldPacketTrigger(botAI, "quest objective completed");
}
// quest
static Trigger* quest_update_add_kill(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update add kill"); }
static Trigger* quest_update_add_item(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update add item"); }
static Trigger* quest_update_failed(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update failed"); }
static Trigger* quest_update_failed_timer(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update failed timer"); }
static Trigger* quest_update_complete(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update complete"); }
static Trigger* complete_quest(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "complete quest"); }
static Trigger* accept_quest(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "accept quest"); }
static Trigger* quest_confirm_accept(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "confirm quest"); }
static Trigger* quest_share(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest share"); }
static Trigger* questgiver_quest_details(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "questgiver quest details"); }
static Trigger* out_of_react_range(PlayerbotAI* botAI) { return new OutOfReactRangeTrigger(botAI); }
static Trigger* loot_response(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "loot response"); }
static Trigger* trade_status(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "trade status"); }
@@ -108,9 +119,6 @@ private:
static Trigger* no_money(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "not enough money"); }
static Trigger* no_reputation(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "not enough reputation"); }
static Trigger* use_game_object(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "use game object"); }
static Trigger* complete_quest(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "complete quest"); }
static Trigger* accept_quest(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "accept quest"); }
static Trigger* quest_share(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "quest share"); }
static Trigger* loot_roll(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "loot roll"); }
static Trigger* taxi(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "activate taxi"); }
static Trigger* bg_status(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "bg status"); }
@@ -119,26 +127,12 @@ private:
static Trigger* petition_offer(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "petition offer"); }
static Trigger* seespell(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "see spell"); }
static Trigger* release_spirit(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "release spirit"); }
static Trigger* revive_from_corpse(PlayerbotAI* botAI)
{
return new WorldPacketTrigger(botAI, "revive from corpse");
}
static Trigger* revive_from_corpse(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "revive from corpse"); }
static Trigger* receive_emote(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "receive emote"); }
static Trigger* receive_text_emote(PlayerbotAI* botAI)
{
return new WorldPacketTrigger(botAI, "receive text emote");
}
static Trigger* receive_text_emote(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "receive text emote"); }
static Trigger* arena_team_invite(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "arena team invite"); }
static Trigger* quest_confirm_accept(PlayerbotAI* botAI)
{
return new WorldPacketTrigger(botAI, "quest confirm accept");
}
static Trigger* group_destroyed(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "group destroyed"); }
static Trigger* group_list(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "group list"); }
static Trigger* questgiver_quest_details(PlayerbotAI* botAI)
{
return new WorldPacketTrigger(botAI, "questgiver quest details");
}
};
#endif

View File

@@ -8,6 +8,7 @@
#include "Playerbots.h"
#include "ReputationMgr.h"
#include "SharedDefines.h"
#include "ServerFacade.h"
Unit* GrindTargetValue::Calculate()
{
@@ -32,6 +33,9 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
Group* group = bot->GetGroup();
Player* master = GetMaster();
if (master && (master == bot || master->GetMapId() != bot->GetMapId() || master->IsBeingTeleported() || !GET_PLAYERBOT_AI(master)))
master = nullptr;
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
for (ObjectGuid const guid : attackers)
{
@@ -48,8 +52,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
float distance = 0;
Unit* result = nullptr;
// std::unordered_map<uint32, bool> needForQuestMap;
std::unordered_map<uint32, bool> needForQuestMap;
for (ObjectGuid const guid : targets)
{
@@ -84,19 +87,30 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
// if (!bot->InBattleground() && master && master->GetDistance(unit) >= sPlayerbotAIConfig->grindDistance &&
// !sRandomPlayerbotMgr->IsRandomBot(bot)) continue;
if (!bot->InBattleground() && (int)unit->GetLevel() - (int)bot->GetLevel() > 4 && !unit->GetGUID().IsPlayer())
// Bots in bot-groups no have a more limited range to look for grind target
if (!bot->InBattleground() && master && botAI->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT)
&& sServerFacade->GetDistance2d(master, unit) > sPlayerbotAIConfig->lootDistance)
{
if (botAI->HasStrategy("debug grind", BotState::BOT_STATE_NON_COMBAT))
botAI->TellMaster(chat->FormatWorldobject(unit) + " ignored (far from master).");
continue;
}
// if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
// needForQuestMap[unit->GetEntry()] = needForQuest(unit);
if (!bot->InBattleground() && (int)unit->GetLevel() - (int)bot->GetLevel() > 4 && !unit->GetGUID().IsPlayer())
continue;
// if (!needForQuestMap[unit->GetEntry()])
// if ((urand(0, 100) < 75 || (context->GetValue<TravelTarget*>("travel target")->Get()->isWorking() &&
// context->GetValue<TravelTarget*>("travel target")->Get()->getDestination()->getName() !=
// "GrindTravelDestination"))) continue;
if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
needForQuestMap[unit->GetEntry()] = needForQuest(unit);
// if (bot->InBattleground() && bot->GetDistance(unit) > 40.0f)
// continue;
if (!needForQuestMap[unit->GetEntry()])
{
Creature* creature = dynamic_cast<Creature*>(unit);
if ((urand(0, 100) < 60 || (context->GetValue<TravelTarget*>("travel target")->Get()->isWorking() &&
context->GetValue<TravelTarget*>("travel target")->Get()->getDestination()->getName() != "GrindTravelDestination")))
{
continue;
}
}
if (Creature* creature = unit->ToCreature())
if (CreatureTemplate const* CreatureTemplate = creature->GetCreatureTemplate())