mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Chat refactor quests and generic + suggest
This commit is contained in:
@@ -458,9 +458,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
|
||||
|
||||
@@ -1263,27 +1260,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 # Reply someone saying something.
|
||||
# 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
|
||||
#
|
||||
|
||||
919
src/BroadcastHelper.cpp
Normal file
919
src/BroadcastHelper.cpp
Normal file
@@ -0,0 +1,919 @@
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "BroadcastHelper.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "Channel.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"] = ai->GetChatHelper()->FormatClass(bot->GetSpec());
|
||||
|
||||
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"] = ai->GetChatHelper()->FormatClass(bot->GetSpec());
|
||||
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"] = ai->GetChatHelper()->FormatClass(bot->GetSpec());
|
||||
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"] = ai->GetChatHelper()->FormatClass(bot->GetSpec());
|
||||
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"] = ai->GetChatHelper()->FormatClass(bot->GetSpec());
|
||||
|
||||
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"] = ai->GetChatHelper()->FormatClass(bot->GetSpec());
|
||||
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"] = ai->GetChatHelper()->FormatClass(bot->GetSpec());
|
||||
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
148
src/BroadcastHelper.h
Normal 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
|
||||
);
|
||||
};
|
||||
@@ -344,11 +344,11 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal
|
||||
std::list<ChatQueuedReply> delayedResponses;
|
||||
while (!chatReplies.empty())
|
||||
{
|
||||
ChatQueuedReply holder = chatReplies.front();
|
||||
ChatQueuedReply& holder = chatReplies.front();
|
||||
time_t checkTime = holder.m_time;
|
||||
if (checkTime && time(0) < checkTime)
|
||||
{
|
||||
delayedResponses.push_back(holder);
|
||||
delayedResponses.push_back(std::move(holder));
|
||||
chatReplies.pop();
|
||||
continue;
|
||||
}
|
||||
@@ -420,11 +420,11 @@ void PlayerbotAI::HandleCommands()
|
||||
std::list<ChatCommandHolder> delayed;
|
||||
while (!chatCommands.empty())
|
||||
{
|
||||
ChatCommandHolder holder = chatCommands.front();
|
||||
ChatCommandHolder& holder = chatCommands.front();
|
||||
time_t checkTime = holder.GetTime();
|
||||
if (checkTime && time(0) < checkTime)
|
||||
{
|
||||
delayed.push_back(holder);
|
||||
delayed.push_back(std::move(holder));
|
||||
chatCommands.pop();
|
||||
continue;
|
||||
}
|
||||
@@ -940,6 +940,11 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
||||
p >> guid1 >> unused;
|
||||
if (guid1.IsEmpty() || p.size() > p.DEFAULT_SIZE)
|
||||
return;
|
||||
if (p.GetOpcode() == SMSG_GM_MESSAGECHAT)
|
||||
{
|
||||
p >> textLen;
|
||||
p >> name;
|
||||
}
|
||||
|
||||
switch (msgtype)
|
||||
{
|
||||
@@ -965,6 +970,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
||||
bool isPaused = time(0) < lastChat;
|
||||
bool shouldReply = false;
|
||||
bool isFromFreeBot = false;
|
||||
if (!sCharacterCache->GetCharacterNameByGuid(guid1, name)) {/*sould return or ?*/}
|
||||
uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guid1);
|
||||
isFromFreeBot = sPlayerbotAIConfig->IsInRandomAccountList(accountId);
|
||||
bool isMentioned = message.find(bot->GetName()) != std::string::npos;
|
||||
@@ -984,34 +990,43 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
||||
if (lang == LANG_ADDON)
|
||||
return;
|
||||
|
||||
// several talk config option later
|
||||
/*
|
||||
|
||||
TODO:
|
||||
toxic link
|
||||
legendary grind / thunderfury etc
|
||||
|
||||
*/
|
||||
if (isFromFreeBot && urand(0, 20))
|
||||
return;
|
||||
|
||||
//if (msgtype == CHAT_MSG_GUILD && (!sPlayerbotAIConfig->guildRepliesRate || urand(1, 100) >= sPlayerbotAIConfig->guildRepliesRate))
|
||||
//return;
|
||||
|
||||
if (!isFromFreeBot)
|
||||
if (message.starts_with(sPlayerbotAIConfig->toxicLinksPrefix)
|
||||
&& (GetChatHelper()->ExtractAllItemIds(message).size() > 0 || GetChatHelper()->ExtractAllQuestIds(message).size() > 0)
|
||||
&& sPlayerbotAIConfig->toxicLinksRepliesChance)
|
||||
{
|
||||
if (!isMentioned && urand(0, 4))
|
||||
if (urand(0, 50) > 0 || urand(1, 100) > sPlayerbotAIConfig->toxicLinksRepliesChance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((GetChatHelper()->ExtractAllItemIds(message).count(19019) && sPlayerbotAIConfig->thunderfuryRepliesChance))
|
||||
{
|
||||
if (urand(0, 60) > 0 || urand(1, 100) > sPlayerbotAIConfig->thunderfuryRepliesChance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (urand(0, 20 + 10 * isMentioned))
|
||||
if (isFromFreeBot && urand(0, 20))
|
||||
return;
|
||||
}
|
||||
|
||||
//if (msgtype == CHAT_MSG_GUILD && (!sPlayerbotAIConfig->guildRepliesRate || urand(1, 100) >= sPlayerbotAIConfig->guildRepliesRate))
|
||||
//return;
|
||||
|
||||
if (!isFromFreeBot)
|
||||
{
|
||||
if (!isMentioned && urand(0, 4))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (urand(0, 20 + 10 * isMentioned))
|
||||
return;
|
||||
}
|
||||
}
|
||||
QueueChatResponse(msgtype, guid1, ObjectGuid(), message, chanName, name);
|
||||
GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 25));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -4653,6 +4668,209 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
|
||||
return oil;
|
||||
}
|
||||
|
||||
std::vector<Item*> PlayerbotAI::GetInventoryAndEquippedItems()
|
||||
{
|
||||
std::vector<Item*> items;
|
||||
|
||||
for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
|
||||
{
|
||||
if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
||||
{
|
||||
if (Item* pItem = pBag->GetItemByPos(j))
|
||||
{
|
||||
items.push_back(pItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
items.push_back(pItem);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
items.push_back(pItem);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
{
|
||||
items.push_back(pItem);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
std::vector<Item*> PlayerbotAI::GetInventoryItems()
|
||||
{
|
||||
std::vector<Item*> items;
|
||||
|
||||
for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
|
||||
{
|
||||
if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
||||
{
|
||||
if (Item* pItem = pBag->GetItemByPos(j))
|
||||
{
|
||||
items.push_back(pItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
items.push_back(pItem);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
items.push_back(pItem);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
uint32 PlayerbotAI::GetInventoryItemsCountWithId(uint32 itemId)
|
||||
{
|
||||
uint32 count = 0;
|
||||
|
||||
for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
|
||||
{
|
||||
if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
||||
{
|
||||
if (Item* pItem = pBag->GetItemByPos(j))
|
||||
{
|
||||
if (pItem->GetTemplate()->ItemId == itemId)
|
||||
{
|
||||
count += pItem->GetCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
if (pItem->GetTemplate()->ItemId == itemId)
|
||||
{
|
||||
count += pItem->GetCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
if (pItem->GetTemplate()->ItemId == itemId)
|
||||
{
|
||||
count += pItem->GetCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool PlayerbotAI::HasItemInInventory(uint32 itemId)
|
||||
{
|
||||
|
||||
for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
|
||||
{
|
||||
if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
||||
{
|
||||
if (Item* pItem = pBag->GetItemByPos(j))
|
||||
{
|
||||
if (pItem->GetTemplate()->ItemId == itemId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
if (pItem->GetTemplate()->ItemId == itemId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
|
||||
{
|
||||
if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
{
|
||||
if (pItem->GetTemplate()->ItemId == itemId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::pair<const Quest*, uint32>> PlayerbotAI::GetCurrentQuestsRequiringItemId(uint32 itemId)
|
||||
{
|
||||
std::vector<std::pair<const Quest*, uint32>> result;
|
||||
|
||||
if (!itemId)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
|
||||
{
|
||||
uint32 questId = bot->GetQuestSlotQuestId(slot);
|
||||
if (!questId)
|
||||
continue;
|
||||
|
||||
QuestStatus status = bot->GetQuestStatus(questId);
|
||||
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||
for (uint8 i = 0; i < std::size(quest->RequiredItemId); ++i)
|
||||
{
|
||||
if (quest->RequiredItemId[i] == itemId)
|
||||
{
|
||||
result.push_back(std::pair(quest, quest->RequiredItemId[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// on self
|
||||
void PlayerbotAI::ImbueItem(Item* item)
|
||||
{
|
||||
@@ -4807,9 +5025,9 @@ bool PlayerbotAI::IsInRealGuild()
|
||||
return !(sPlayerbotAIConfig->IsInRandomAccountList(leaderAccount));
|
||||
}
|
||||
|
||||
void PlayerbotAI::QueueChatResponse(uint8 msgtype, ObjectGuid guid1, ObjectGuid guid2, std::string message, std::string chanName, std::string name)
|
||||
void PlayerbotAI::QueueChatResponse(uint8& msgtype, ObjectGuid& guid1, ObjectGuid guid2, std::string& message, std::string& chanName, std::string& name)
|
||||
{
|
||||
chatReplies.push(ChatQueuedReply(msgtype, guid1.GetCounter(), guid2.GetCounter(), message, chanName, name, time(nullptr) + urand(inCombat ? 10 : 5, inCombat ? 25 : 15)));
|
||||
chatReplies.push({ msgtype, guid1.GetCounter(), guid2.GetCounter(), message, chanName, name, time(nullptr) + urand(inCombat ? 10 : 5, inCombat ? 25 : 15) });
|
||||
}
|
||||
|
||||
bool PlayerbotAI::EqualLowercaseName(std::string s1, std::string s2)
|
||||
|
||||
@@ -364,7 +364,7 @@ class PlayerbotAI : public PlayerbotAIBase
|
||||
|
||||
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 QueueChatResponse(uint8& msgtype, ObjectGuid& guid1, ObjectGuid guid2, std::string& message, std::string& chanName, std::string& name);
|
||||
void HandleBotOutgoingPacket(WorldPacket const& packet);
|
||||
void HandleMasterIncomingPacket(WorldPacket const& packet);
|
||||
void HandleMasterOutgoingPacket(WorldPacket const& packet);
|
||||
@@ -525,6 +525,11 @@ class PlayerbotAI : public PlayerbotAIBase
|
||||
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();
|
||||
|
||||
@@ -140,11 +140,85 @@ bool PlayerbotAIConfig::Initialize()
|
||||
minRandomBotsPriceChangeInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBotsPriceChangeInterval", 2 * HOUR);
|
||||
maxRandomBotsPriceChangeInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotsPriceChangeInterval", 48 * HOUR);
|
||||
randomBotJoinLfg = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinLfg", true);
|
||||
|
||||
//////////////////////////// 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);
|
||||
@@ -337,8 +411,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
|
||||
|
||||
@@ -95,11 +95,78 @@ class PlayerbotAIConfig
|
||||
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 +248,6 @@ class PlayerbotAIConfig
|
||||
|
||||
uint32 commandServerPort;
|
||||
bool perfMonEnabled;
|
||||
|
||||
bool enableGreet;
|
||||
bool summonWhenGroup;
|
||||
bool randomBotShowHelmet;
|
||||
bool randomBotShowCloak;
|
||||
@@ -212,8 +277,6 @@ class PlayerbotAIConfig
|
||||
bool freeFood;
|
||||
bool autoLearnQuestSpells;
|
||||
bool autoTeleportForLevel;
|
||||
bool randomBotSayWithoutMaster;
|
||||
bool sayWhenCollectingItems;
|
||||
bool randomBotGroupNearby;
|
||||
uint32 tweakValue; //Debugging config
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "Event.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
bool AutoLearnSpellAction::Execute(Event event)
|
||||
{
|
||||
@@ -30,18 +30,12 @@ bool AutoLearnSpellAction::Execute(Event event)
|
||||
|
||||
void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
|
||||
{
|
||||
BroadcastHelper::BroadcastLevelup(botAI, bot);
|
||||
if (sPlayerbotAIConfig->autoLearnTrainerSpells)
|
||||
LearnTrainerSpells(out);
|
||||
|
||||
if (sPlayerbotAIConfig->autoLearnQuestSpells)
|
||||
LearnQuestSpells(out);
|
||||
|
||||
if (sPlayerbotAIConfig->randomBotGuildTalk)
|
||||
{
|
||||
std::map<std::string, std::string> args;
|
||||
args["%my_level"] = std::to_string(bot->GetLevel());
|
||||
botAI->SayToGuild(BOT_TEXT2("broadcast_levelup_generic", args));
|
||||
}
|
||||
}
|
||||
|
||||
void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "GuildPackets.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
Player* GuidManageAction::GetPlayer(Event event)
|
||||
{
|
||||
@@ -167,6 +168,7 @@ bool GuildManageNearbyAction::Execute(Event event)
|
||||
{
|
||||
if (!urand(0, 10))
|
||||
{
|
||||
BroadcastHelper::BroadcastGuildMemberPromotion(botAI, bot, player);
|
||||
botAI->DoSpecificAction("guild promote", Event("guild management", guid), true);
|
||||
|
||||
continue;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "GuildMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
bool InviteToGroupAction::Execute(Event event)
|
||||
{
|
||||
@@ -76,6 +77,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);
|
||||
}
|
||||
|
||||
@@ -125,6 +146,8 @@ std::vector<Player*> InviteGuildToGroupAction::getGuildMembers()
|
||||
|
||||
bool InviteGuildToGroupAction::Execute(Event event)
|
||||
{
|
||||
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
|
||||
|
||||
for (auto& member : getGuildMembers())
|
||||
{
|
||||
Player* player = member;
|
||||
@@ -165,6 +188,8 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
bool LootAction::Execute(Event event)
|
||||
{
|
||||
@@ -417,19 +418,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 && urand(0, 10) && proto->Quality >= ITEM_QUALITY_RARE)
|
||||
{
|
||||
std::map<std::string, std::string> args;
|
||||
args["%item"] = chat->FormatItem(proto);
|
||||
botAI->SayToGuild(BOT_TEXT2("loot_command", args));
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -10,64 +10,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Playerbots.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
bool QuestAction::Execute(Event event)
|
||||
{
|
||||
@@ -226,6 +227,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;
|
||||
@@ -253,13 +255,17 @@ bool QuestUpdateCompleteAction::Execute(Event event)
|
||||
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
|
||||
if (qInfo)
|
||||
{
|
||||
const std::string text_quest = ChatHelper::FormatQuest(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 [ " + text_quest + " ] completed", LANG_UNIVERSAL);
|
||||
bot->Say("Quest [ " + format + " ] completed", LANG_UNIVERSAL);
|
||||
}
|
||||
botAI->TellMasterNoFacing("Quest completed " + text_quest);
|
||||
botAI->TellMasterNoFacing("Quest completed " + format);
|
||||
BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, qInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -276,9 +282,38 @@ bool QuestUpdateAddKillAction::Execute(Event event)
|
||||
uint32 entry, questId, available, required;
|
||||
p >> questId >> entry >> available >> required;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Update progression kill questid {" << std::to_string(questId) << "} {" << std::to_string(available) << "} / {" << std::to_string(required) << "}";
|
||||
botAI->TellMasterNoFacing(ss.str());
|
||||
const Quest* qInfo = sObjectMgr->GetQuestTemplate(questId);
|
||||
if (qInfo && (entry & 0x80000000))
|
||||
{
|
||||
entry &= 0x7FFFFFFF;
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -292,10 +327,28 @@ bool QuestUpdateAddItemAction::Execute(Event event)
|
||||
|
||||
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);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Update progression itemid {" << std::to_string(itemId) << "} count: {" << std::to_string(count) << "}";
|
||||
botAI->TellMasterNoFacing(ss.str());
|
||||
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;
|
||||
}
|
||||
@@ -320,7 +373,10 @@ bool QuestUpdateFailedTimerAction::Execute(Event event)
|
||||
|
||||
if (qInfo)
|
||||
{
|
||||
botAI->TellMaster("Failed timer for " + botAI->GetChatHelper()->FormatQuest(qInfo) +", abandoning");
|
||||
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
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ bool SayAction::isUseful()
|
||||
return (time(nullptr) - lastSaid) > 30;
|
||||
}
|
||||
|
||||
void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32 guid2, std::string msg, std::string chanName, std::string name)
|
||||
void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& guid2, std::string& msg, std::string& chanName, std::string& name)
|
||||
{
|
||||
ChatReplyType replyType = REPLY_NOT_UNDERSTAND; // default not understand
|
||||
std::string respondsText = "";
|
||||
@@ -165,7 +165,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
|
||||
}
|
||||
|
||||
//toxic links
|
||||
if (msg.starts_with("gnomes")
|
||||
if (msg.starts_with(sPlayerbotAIConfig->toxicLinksPrefix)
|
||||
&& (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).size() > 0 || GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg).size() > 0))
|
||||
{
|
||||
HandleToxicLinksReply(bot, chatChannelSource, msg, name);
|
||||
@@ -179,10 +179,11 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
|
||||
return;
|
||||
}
|
||||
|
||||
SendGeneralResponse(bot, chatChannelSource, GenerateReplyMessage(bot, msg, guid1, name), name);
|
||||
auto messageRepy = GenerateReplyMessage(bot, msg, guid1, name);
|
||||
SendGeneralResponse(bot, chatChannelSource, messageRepy, name);
|
||||
}
|
||||
|
||||
bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource, std::string msg, std::string name)
|
||||
bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name)
|
||||
{
|
||||
std::map<std::string, std::string> placeholders;
|
||||
const auto thunderfury = sObjectMgr->GetItemTemplate(19019);
|
||||
@@ -208,17 +209,164 @@ bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chat
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource, std::string msg, std::string name)
|
||||
bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name)
|
||||
{
|
||||
//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 = GET_PLAYERBOT_AI(bot)->GetInventoryAndEquippedItems();
|
||||
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? GET_PLAYERBOT_AI(bot)->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"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatQuest(quest);
|
||||
}
|
||||
else
|
||||
{
|
||||
placeholders["%random_taken_quest_or_item_link"] = placeholders["%random_inventory_item_link"];
|
||||
}
|
||||
|
||||
placeholders["%my_role"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatClass(bot->getClass());
|
||||
AreaTableEntry const* current_area = GET_PLAYERBOT_AI(bot)->GetCurrentArea();
|
||||
AreaTableEntry const* current_zone = GET_PLAYERBOT_AI(bot)->GetCurrentZone();
|
||||
placeholders["%area_name"] = current_area ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%zone_name"] = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%my_class"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
|
||||
switch (chatChannelSource)
|
||||
{
|
||||
case ChatChannelSource::SRC_WORLD:
|
||||
{
|
||||
GET_PLAYERBOT_AI(bot)->SayToWorld(BOT_TEXT2("suggest_toxic_links", placeholders));
|
||||
break;
|
||||
}
|
||||
case ChatChannelSource::SRC_GENERAL:
|
||||
{
|
||||
GET_PLAYERBOT_AI(bot)->SayToChannel(BOT_TEXT2("suggest_toxic_links", placeholders), ChatChannelId::GENERAL);
|
||||
break;
|
||||
}
|
||||
case ChatChannelSource::SRC_GUILD:
|
||||
{
|
||||
GET_PLAYERBOT_AI(bot)->SayToGuild(BOT_TEXT2("suggest_toxic_links", placeholders));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 60));
|
||||
|
||||
return true;
|
||||
}
|
||||
bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatChannelSource, std::string msg, std::string name)
|
||||
bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name)
|
||||
{
|
||||
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 60));
|
||||
auto messageItemIds = GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg);
|
||||
|
||||
if (messageItemIds.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<uint32> matchingItemIds;
|
||||
|
||||
for (auto messageItemId : messageItemIds)
|
||||
{
|
||||
if (GET_PLAYERBOT_AI(bot)->HasItemInInventory(messageItemId))
|
||||
{
|
||||
matchingItemIds.insert(messageItemId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchingItemIds.empty())
|
||||
{
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%other_name"] = name;
|
||||
AreaTableEntry const* current_area = GET_PLAYERBOT_AI(bot)->GetCurrentArea();
|
||||
AreaTableEntry const* current_zone = GET_PLAYERBOT_AI(bot)->GetCurrentZone();
|
||||
placeholders["%area_name"] = current_area ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%zone_name"] = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%my_class"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
placeholders["%my_role"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatClass(bot->GetSpec());
|
||||
placeholders["%formatted_item_links"] = "";
|
||||
|
||||
for (auto matchingItemId : matchingItemIds)
|
||||
{
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(matchingItemId);
|
||||
placeholders["%formatted_item_links"] += GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(proto, GET_PLAYERBOT_AI(bot)->GetInventoryItemsCountWithId(matchingItemId));
|
||||
placeholders["%formatted_item_links"] += " ";
|
||||
}
|
||||
|
||||
switch (chatChannelSource)
|
||||
{
|
||||
case ChatChannelSource::SRC_WORLD:
|
||||
{
|
||||
//may reply to the same channel or whisper
|
||||
if (urand(0, 1))
|
||||
{
|
||||
std::string responseMessage = BOT_TEXT2("response_wtb_items_channel", placeholders);
|
||||
GET_PLAYERBOT_AI(bot)->SayToWorld(responseMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string responseMessage = BOT_TEXT2("response_wtb_items_whisper", placeholders);
|
||||
GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ChatChannelSource::SRC_GENERAL:
|
||||
{
|
||||
//may reply to the same channel or whisper
|
||||
if (urand(0, 1))
|
||||
{
|
||||
std::string responseMessage = BOT_TEXT2("response_wtb_items_channel", placeholders);
|
||||
GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string responseMessage = BOT_TEXT2("response_wtb_items_whisper", placeholders);
|
||||
GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ChatChannelSource::SRC_TRADE:
|
||||
{
|
||||
//may reply to the same channel or whisper
|
||||
if (urand(0, 1))
|
||||
{
|
||||
std::string responseMessage = BOT_TEXT2("response_wtb_items_channel", placeholders);
|
||||
GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::TRADE);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string responseMessage = BOT_TEXT2("response_wtb_items_whisper", placeholders);
|
||||
GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(0) + urand(5, 60));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatChannelSource, std::string msg, std::string name)
|
||||
bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name)
|
||||
{
|
||||
auto messageQuestIds = GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg);
|
||||
|
||||
@@ -303,7 +451,7 @@ bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatCh
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatReplyAction::SendGeneralResponse(Player* bot, ChatChannelSource chatChannelSource, std::string responseMessage, std::string name)
|
||||
bool ChatReplyAction::SendGeneralResponse(Player* bot, ChatChannelSource chatChannelSource, std::string& responseMessage, std::string& name)
|
||||
{
|
||||
// send responds
|
||||
switch (chatChannelSource)
|
||||
@@ -341,13 +489,11 @@ bool ChatReplyAction::SendGeneralResponse(Player* bot, ChatChannelSource chatCha
|
||||
case ChatChannelSource::SRC_LOOKING_FOR_GROUP:
|
||||
{
|
||||
//do not reply to the chat
|
||||
//may whisper
|
||||
break;
|
||||
}
|
||||
case ChatChannelSource::SRC_GUILD_RECRUITMENT:
|
||||
{
|
||||
//do not reply to the chat
|
||||
//may whisper
|
||||
break;
|
||||
}
|
||||
case ChatChannelSource::SRC_WHISPER:
|
||||
@@ -378,7 +524,7 @@ bool ChatReplyAction::SendGeneralResponse(Player* bot, ChatChannelSource chatCha
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string incomingMessage, uint32 guid1, std::string name)
|
||||
std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& incomingMessage, uint32& guid1, std::string& name)
|
||||
{
|
||||
ChatReplyType replyType = REPLY_NOT_UNDERSTAND; // default not understand
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ public:
|
||||
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 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);
|
||||
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
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
#include "ChatHelper.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotTextMgr.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "Config.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -36,6 +36,8 @@ 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()
|
||||
@@ -85,15 +87,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()
|
||||
@@ -187,123 +181,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* zone = sAreaTableStore.LookupEntry(bot->GetMap()->GetZoneId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()));
|
||||
if (!zone) return;
|
||||
|
||||
std::ostringstream out;
|
||||
// out << "|cffb04040" << entry->area_name[0] << "|r";
|
||||
out << zone->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
|
||||
{
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
botAI->SayToGuild(msg);
|
||||
}
|
||||
void SuggestWhatToDoAction::thunderfury()
|
||||
{
|
||||
BroadcastHelper::BroadcastSuggestThunderfury(botAI, bot);
|
||||
}
|
||||
|
||||
class FindTradeItemsVisitor : public IterateItemsVisitor
|
||||
@@ -394,22 +299,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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -469,11 +366,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;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ class SuggestWhatToDoAction : public InventoryAction
|
||||
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();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Playerbots.h"
|
||||
#include "QuestDef.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
bool TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver)
|
||||
{
|
||||
@@ -96,14 +97,19 @@ bool TalkToQuestGiverAction::TurnInQuest(Quest const* quest, Object* questGiver,
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,15 +117,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "Event.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
#include "GuildMgr.h"
|
||||
#include "BroadcastHelper.h"
|
||||
|
||||
bool XpGainAction::Execute(Event event)
|
||||
{
|
||||
@@ -33,19 +33,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::map<std::string, std::string> args;
|
||||
args["%victim_name"] = creature->GetName();
|
||||
botAI->SayToGuild(BOT_TEXT2("broadcast_killed_rare", args));
|
||||
}
|
||||
}
|
||||
BroadcastHelper::BroadcastKill(botAI, bot, creature);
|
||||
}
|
||||
|
||||
Unit* victim = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user