mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-19 03:35:43 +00:00
Directory reorganization
This commit is contained in:
960
src/helper/BroadcastHelper.cpp
Normal file
960
src/helper/BroadcastHelper.cpp
Normal file
@@ -0,0 +1,960 @@
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "BroadcastHelper.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "Channel.h"
|
||||
#include "AiFactory.h"
|
||||
|
||||
BroadcastHelper::BroadcastHelper() {}
|
||||
|
||||
uint8 BroadcastHelper::GetLocale()
|
||||
{
|
||||
uint8 locale = sWorld->GetDefaultDbcLocale();
|
||||
// -- In case we're using auto detect on config file^M
|
||||
if (locale >= MAX_LOCALES)
|
||||
locale = LocaleConstant::LOCALE_enUS;
|
||||
return locale;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastTest(PlayerbotAI* ai, Player* /* bot */)
|
||||
{
|
||||
//return something to ignore the logic
|
||||
return false;
|
||||
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%rand1"] = std::to_string(urand(0, 1));
|
||||
placeholders["%rand2"] = std::to_string(urand(0, 1));
|
||||
placeholders["%rand3"] = std::to_string(urand(0, 1));
|
||||
|
||||
int32 rand = urand(0, 1);
|
||||
|
||||
if (rand == 1 && ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE))
|
||||
return true;
|
||||
else if (ai->SayToChannel(BOT_TEXT2("Posted to GuildRecruitment, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GUILD_RECRUITMENT))
|
||||
return true;
|
||||
|
||||
return ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE);
|
||||
|
||||
//int32 rand = urand(1, 8);
|
||||
if (rand == 1 && ai->SayToGuild(BOT_TEXT2("Posted to guild, %rand1, %rand2, %rand3", placeholders)))
|
||||
return true;
|
||||
else if (rand == 2 && ai->SayToWorld(BOT_TEXT2("Posted to world, %rand1, %rand2, %rand3", placeholders)))
|
||||
return true;
|
||||
else if (rand == 3 && ai->SayToChannel(BOT_TEXT2("Posted to general, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GENERAL))
|
||||
return true;
|
||||
else if (rand == 4 && ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE))
|
||||
return true;
|
||||
else if (rand == 5 && ai->SayToChannel(BOT_TEXT2("Posted to LFG, %rand1, %rand2, %rand3", placeholders), ChatChannelId::LOOKING_FOR_GROUP))
|
||||
return true;
|
||||
else if (rand == 6 && ai->SayToChannel(BOT_TEXT2("Posted to LocalDefense, %rand1, %rand2, %rand3", placeholders), ChatChannelId::LOCAL_DEFENSE))
|
||||
return true;
|
||||
else if (rand == 7 && ai->SayToChannel(BOT_TEXT2("Posted to WorldDefense, %rand1, %rand2, %rand3", placeholders), ChatChannelId::WORLD_DEFENSE))
|
||||
return true;
|
||||
else if (rand == 8 && ai->SayToChannel(BOT_TEXT2("Posted to GuildRecruitment, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GUILD_RECRUITMENT))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@param toChannels - map of (ToChannel, chance), where chance is in range 0-100 as uint32 (unless global chance is not 100%)
|
||||
|
||||
@return true if said to the channel, false otherwise
|
||||
*/
|
||||
bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::string message, std::list<std::pair<ToChannel, uint32>> toChannels)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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 (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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 (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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 (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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 (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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 (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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 (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestInstance)
|
||||
{
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
|
||||
|
||||
std::ostringstream itemout;
|
||||
//itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r";
|
||||
itemout << allowedInstances[urand(0, allowedInstances.size() - 1)];
|
||||
placeholders["%instance_name"] = itemout.str();
|
||||
|
||||
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
|
||||
return BroadcastToChannelWithGlobalChance(
|
||||
ai,
|
||||
BOT_TEXT2("suggest_instance", placeholders),
|
||||
{ {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastSuggestQuest(PlayerbotAI* ai, std::vector<uint32>& quests, Player* bot)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestQuest)
|
||||
{
|
||||
|
||||
int index = rand() % quests.size();
|
||||
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(quests[index]);
|
||||
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
|
||||
placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest);
|
||||
placeholders["%quest_level"] = std::to_string(quest->GetQuestLevel());
|
||||
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
|
||||
return BroadcastToChannelWithGlobalChance(
|
||||
ai,
|
||||
BOT_TEXT2("suggest_quest", placeholders),
|
||||
{ {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastSuggestGrindMaterials(PlayerbotAI* ai, std::string item, Player* bot)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestGrindMaterials)
|
||||
{
|
||||
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
|
||||
placeholders["%category"] = item;
|
||||
|
||||
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
|
||||
return BroadcastToChannelWithGlobalChance(
|
||||
ai,
|
||||
BOT_TEXT2("suggest_trade", placeholders),
|
||||
{ {TO_TRADE, 50}, {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastSuggestGrindReputation(PlayerbotAI* ai, std::vector<std::string> levels, std::vector<std::string> allowedFactions, Player* bot)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestGrindReputation)
|
||||
{
|
||||
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
|
||||
placeholders["%rep_level"] = levels[urand(0, 2)];
|
||||
std::ostringstream rnd; rnd << urand(1, 5) << "K";
|
||||
placeholders["%rndK"] = rnd.str();
|
||||
|
||||
std::ostringstream itemout;
|
||||
//itemout << "|c004040b0" << allowedFactions[urand(0, allowedFactions.size() - 1)] << "|r";
|
||||
itemout << allowedFactions[urand(0, allowedFactions.size() - 1)];
|
||||
placeholders["%faction"] = itemout.str();
|
||||
|
||||
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
|
||||
return BroadcastToChannelWithGlobalChance(
|
||||
ai,
|
||||
BOT_TEXT2("suggest_faction", placeholders),
|
||||
{ {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} }
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastSuggestSell(PlayerbotAI* ai, const ItemTemplate* proto, uint32 count, uint32 price, Player* bot)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
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 (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestSomething)
|
||||
{
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
|
||||
|
||||
AreaTableEntry const* current_area = ai->GetCurrentArea();
|
||||
AreaTableEntry const* current_zone = ai->GetCurrentZone();
|
||||
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
|
||||
return BroadcastToChannelWithGlobalChance(
|
||||
ai,
|
||||
BOT_TEXT2("suggest_something", placeholders),
|
||||
{ {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} }
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastSuggestSomethingToxic(PlayerbotAI* ai, Player* bot)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestSomethingToxic)
|
||||
{
|
||||
//items
|
||||
std::vector<Item*> botItems = ai->GetInventoryAndEquippedItems();
|
||||
|
||||
std::map<std::string, std::string> placeholders;
|
||||
|
||||
placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? ai->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : BOT_TEXT1("string_empty_link");
|
||||
|
||||
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
|
||||
AreaTableEntry const* current_area = ai->GetCurrentArea();
|
||||
AreaTableEntry const* current_zone = ai->GetCurrentZone();
|
||||
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
|
||||
return BroadcastToChannelWithGlobalChance(
|
||||
ai,
|
||||
BOT_TEXT2("suggest_something_toxic", placeholders),
|
||||
{ {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} }
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastSuggestToxicLinks(PlayerbotAI* ai, Player* bot)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->enableBroadcasts)
|
||||
return false;
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestToxicLinks)
|
||||
{
|
||||
//quests
|
||||
std::vector<uint32> incompleteQuests;
|
||||
for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
|
||||
{
|
||||
uint32 questId = bot->GetQuestSlotQuestId(slot);
|
||||
if (!questId)
|
||||
continue;
|
||||
|
||||
QuestStatus status = bot->GetQuestStatus(questId);
|
||||
if (status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_NONE)
|
||||
incompleteQuests.push_back(questId);
|
||||
}
|
||||
|
||||
//items
|
||||
std::vector<Item*> botItems = ai->GetInventoryAndEquippedItems();
|
||||
|
||||
//spells
|
||||
//?
|
||||
|
||||
std::map<std::string, std::string> placeholders;
|
||||
|
||||
placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? ai->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : BOT_TEXT1("string_empty_link");
|
||||
placeholders["%prefix"] = sPlayerbotAIConfig->toxicLinksPrefix;
|
||||
|
||||
if (incompleteQuests.size() > 0)
|
||||
{
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(incompleteQuests[rand() % incompleteQuests.size()]);
|
||||
placeholders["%random_taken_quest_or_item_link"] = ai->GetChatHelper()->FormatQuest(quest);
|
||||
}
|
||||
else
|
||||
{
|
||||
placeholders["%random_taken_quest_or_item_link"] = placeholders["%random_inventory_item_link"];
|
||||
}
|
||||
|
||||
placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
|
||||
AreaTableEntry const* current_area = ai->GetCurrentArea();
|
||||
AreaTableEntry const* current_zone = ai->GetCurrentZone();
|
||||
placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area");
|
||||
placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass());
|
||||
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
|
||||
placeholders["%my_level"] = std::to_string(bot->GetLevel());
|
||||
|
||||
return BroadcastToChannelWithGlobalChance(
|
||||
ai,
|
||||
BOT_TEXT2("suggest_toxic_links", placeholders),
|
||||
{ {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} }
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BroadcastHelper::BroadcastSuggestThunderfury(PlayerbotAI* ai, Player* bot)
|
||||
{
|
||||
if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestThunderfury)
|
||||
{
|
||||
std::map<std::string, std::string> placeholders;
|
||||
ItemTemplate const* thunderfuryProto = sObjectMgr->GetItemTemplate(19019);
|
||||
placeholders["%thunderfury_link"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(thunderfuryProto);
|
||||
|
||||
return BroadcastToChannelWithGlobalChance(
|
||||
ai,
|
||||
BOT_TEXT2("thunderfury_spam", placeholders),
|
||||
{ {TO_WORLD, 70}, {TO_GENERAL, 100} }
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
148
src/helper/BroadcastHelper.h
Normal file
148
src/helper/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
|
||||
);
|
||||
};
|
||||
299
src/helper/ChatFilter.cpp
Normal file
299
src/helper/ChatFilter.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "ChatFilter.h"
|
||||
|
||||
#include "Group.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
std::string const ChatFilter::Filter(std::string& message)
|
||||
{
|
||||
if (message.find("@") == std::string::npos)
|
||||
return message;
|
||||
|
||||
return message.substr(message.find(" ") + 1);
|
||||
}
|
||||
|
||||
class StrategyChatFilter : public ChatFilter
|
||||
{
|
||||
public:
|
||||
StrategyChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
|
||||
|
||||
std::string const Filter(std::string& message) override
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
|
||||
bool tank = message.find("@tank") == 0;
|
||||
if (tank && !botAI->IsTank(bot))
|
||||
return "";
|
||||
|
||||
bool dps = message.find("@dps") == 0;
|
||||
if (dps && (botAI->IsTank(bot) || botAI->IsHeal(bot)))
|
||||
return "";
|
||||
|
||||
bool heal = message.find("@heal") == 0;
|
||||
if (heal && !botAI->IsHeal(bot))
|
||||
return "";
|
||||
|
||||
bool ranged = message.find("@ranged") == 0;
|
||||
if (ranged && !botAI->IsRanged(bot))
|
||||
return "";
|
||||
|
||||
bool melee = message.find("@melee") == 0;
|
||||
if (melee && botAI->IsRanged(bot))
|
||||
return "";
|
||||
|
||||
if (tank || dps || heal || ranged || melee)
|
||||
return ChatFilter::Filter(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
class LevelChatFilter : public ChatFilter
|
||||
{
|
||||
public:
|
||||
LevelChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
|
||||
|
||||
std::string const Filter(std::string& message) override
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
|
||||
if (message[0] != '@')
|
||||
return message;
|
||||
|
||||
if (message.find("-") != std::string::npos)
|
||||
{
|
||||
uint32 fromLevel = atoi(message.substr(message.find("@") + 1, message.find("-")).c_str());
|
||||
uint32 toLevel = atoi(message.substr(message.find("-") + 1, message.find(" ")).c_str());
|
||||
|
||||
if (bot->GetLevel() >= fromLevel && bot->GetLevel() <= toLevel)
|
||||
return ChatFilter::Filter(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
uint32 level = atoi(message.substr(message.find("@") + 1, message.find(" ")).c_str());
|
||||
if (bot->GetLevel() == level)
|
||||
return ChatFilter::Filter(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
class CombatTypeChatFilter : public ChatFilter
|
||||
{
|
||||
public:
|
||||
CombatTypeChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
|
||||
|
||||
std::string const Filter(std::string& message) override
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
|
||||
bool melee = message.find("@melee") == 0;
|
||||
bool ranged = message.find("@ranged") == 0;
|
||||
|
||||
if (!melee && !ranged)
|
||||
return message;
|
||||
|
||||
switch (bot->getClass())
|
||||
{
|
||||
case CLASS_WARRIOR:
|
||||
case CLASS_PALADIN:
|
||||
case CLASS_ROGUE:
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
if (ranged)
|
||||
return "";
|
||||
break;
|
||||
case CLASS_HUNTER:
|
||||
case CLASS_PRIEST:
|
||||
case CLASS_MAGE:
|
||||
case CLASS_WARLOCK:
|
||||
if (melee)
|
||||
return "";
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (ranged && botAI->IsTank(bot))
|
||||
return "";
|
||||
if (melee && !botAI->IsTank(bot))
|
||||
return "";
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (melee && botAI->IsHeal(bot))
|
||||
return "";
|
||||
if (ranged && !botAI->IsHeal(bot))
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
|
||||
return ChatFilter::Filter(message);
|
||||
}
|
||||
};
|
||||
|
||||
class RtiChatFilter : public ChatFilter
|
||||
{
|
||||
public:
|
||||
RtiChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
|
||||
{
|
||||
rtis.push_back("@star");
|
||||
rtis.push_back("@circle");
|
||||
rtis.push_back("@diamond");
|
||||
rtis.push_back("@triangle");
|
||||
rtis.push_back("@moon");
|
||||
rtis.push_back("@square");
|
||||
rtis.push_back("@cross");
|
||||
rtis.push_back("@skull");
|
||||
}
|
||||
|
||||
std::string const Filter(std::string& message) override
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return message;
|
||||
|
||||
bool found = false;
|
||||
//bool isRti = false; //not used, shadowed by the next declaration, line marked for removal.
|
||||
for (std::vector<std::string>::iterator i = rtis.begin(); i != rtis.end(); i++)
|
||||
{
|
||||
std::string const rti = *i;
|
||||
|
||||
bool isRti = message.find(rti) == 0;
|
||||
if (!isRti)
|
||||
continue;
|
||||
|
||||
ObjectGuid rtiTarget = group->GetTargetIcon(RtiTargetValue::GetRtiIndex(rti.substr(1)));
|
||||
if (bot->GetGUID() == rtiTarget)
|
||||
return ChatFilter::Filter(message);
|
||||
|
||||
Unit* target = *botAI->GetAiObjectContext()->GetValue<Unit*>("current target");
|
||||
if (!target)
|
||||
return "";
|
||||
|
||||
if (target->GetGUID() != rtiTarget)
|
||||
return "";
|
||||
|
||||
found |= isRti;
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
return ChatFilter::Filter(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> rtis;
|
||||
};
|
||||
|
||||
class ClassChatFilter : public ChatFilter
|
||||
{
|
||||
public:
|
||||
ClassChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
|
||||
{
|
||||
classNames["@death_knight"] = CLASS_DEATH_KNIGHT;
|
||||
classNames["@druid"] = CLASS_DRUID;
|
||||
classNames["@hunter"] = CLASS_HUNTER;
|
||||
classNames["@mage"] = CLASS_MAGE;
|
||||
classNames["@paladin"] = CLASS_PALADIN;
|
||||
classNames["@priest"] = CLASS_PRIEST;
|
||||
classNames["@rogue"] = CLASS_ROGUE;
|
||||
classNames["@shaman"] = CLASS_SHAMAN;
|
||||
classNames["@warlock"] = CLASS_WARLOCK;
|
||||
classNames["@warrior"] = CLASS_WARRIOR;
|
||||
}
|
||||
|
||||
std::string const Filter(std::string& message) override
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
|
||||
bool found = false;
|
||||
//bool isClass = false; //not used, shadowed by the next declaration, line marked for removal.
|
||||
for (std::map<std::string, uint8>::iterator i = classNames.begin(); i != classNames.end(); i++)
|
||||
{
|
||||
bool isClass = message.find(i->first) == 0;
|
||||
if (isClass && bot->getClass() != i->second)
|
||||
return "";
|
||||
|
||||
found |= isClass;
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
return ChatFilter::Filter(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, uint8> classNames;
|
||||
};
|
||||
|
||||
class SubGroupChatFilter : public ChatFilter
|
||||
{
|
||||
public:
|
||||
SubGroupChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
|
||||
|
||||
std::string const Filter(std::string& message) override
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
|
||||
if (message.find("@group") == 0)
|
||||
{
|
||||
std::string const pnum = message.substr(6, message.find(" "));
|
||||
uint32 from = atoi(pnum.c_str());
|
||||
uint32 to = from;
|
||||
if (pnum.find("-") != std::string::npos)
|
||||
{
|
||||
from = atoi(pnum.substr(pnum.find("@") + 1, pnum.find("-")).c_str());
|
||||
to = atoi(pnum.substr(pnum.find("-") + 1, pnum.find(" ")).c_str());
|
||||
}
|
||||
|
||||
if (!bot->GetGroup())
|
||||
return message;
|
||||
|
||||
uint32 sg = bot->GetSubGroup() + 1;
|
||||
if (sg >= from && sg <= to)
|
||||
return ChatFilter::Filter(message);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
CompositeChatFilter::CompositeChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
|
||||
{
|
||||
filters.push_back(new StrategyChatFilter(botAI));
|
||||
filters.push_back(new ClassChatFilter(botAI));
|
||||
filters.push_back(new RtiChatFilter(botAI));
|
||||
filters.push_back(new CombatTypeChatFilter(botAI));
|
||||
filters.push_back(new LevelChatFilter(botAI));
|
||||
filters.push_back(new SubGroupChatFilter(botAI));
|
||||
}
|
||||
|
||||
CompositeChatFilter::~CompositeChatFilter()
|
||||
{
|
||||
for (std::vector<ChatFilter*>::iterator i = filters.begin(); i != filters.end(); i++)
|
||||
delete (*i);
|
||||
}
|
||||
|
||||
std::string const CompositeChatFilter::Filter(std::string& message)
|
||||
{
|
||||
for (uint32 j = 0; j < filters.size(); ++j)
|
||||
{
|
||||
for (std::vector<ChatFilter*>::iterator i = filters.begin(); i != filters.end(); i++)
|
||||
{
|
||||
message = (*i)->Filter(message);
|
||||
if (message.empty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
37
src/helper/ChatFilter.h
Normal file
37
src/helper/ChatFilter.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_CHATFILTER_H
|
||||
#define _PLAYERBOT_CHATFILTER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
#include "PlayerbotAIAware.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class ChatFilter : public PlayerbotAIAware
|
||||
{
|
||||
public:
|
||||
ChatFilter(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) {}
|
||||
virtual ~ChatFilter() {}
|
||||
|
||||
virtual std::string const Filter(std::string& message);
|
||||
};
|
||||
|
||||
class CompositeChatFilter : public ChatFilter
|
||||
{
|
||||
public:
|
||||
CompositeChatFilter(PlayerbotAI* botAI);
|
||||
|
||||
virtual ~CompositeChatFilter();
|
||||
std::string const Filter(std::string& message) override;
|
||||
|
||||
private:
|
||||
std::vector<ChatFilter*> filters;
|
||||
};
|
||||
|
||||
#endif
|
||||
658
src/helper/ChatHelper.cpp
Normal file
658
src/helper/ChatHelper.cpp
Normal file
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "ChatHelper.h"
|
||||
|
||||
#include "AiFactory.h"
|
||||
#include "Common.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "SpellInfo.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
std::map<std::string, uint32> ChatHelper::consumableSubClasses;
|
||||
std::map<std::string, uint32> ChatHelper::tradeSubClasses;
|
||||
std::map<std::string, uint32> ChatHelper::itemQualities;
|
||||
std::map<std::string, uint32> ChatHelper::projectileSubClasses;
|
||||
std::map<std::string, uint32> ChatHelper::slots;
|
||||
std::map<std::string, uint32> ChatHelper::skills;
|
||||
std::map<std::string, ChatMsg> ChatHelper::chats;
|
||||
std::map<uint8, std::string> ChatHelper::classes;
|
||||
std::map<uint8, std::string> ChatHelper::races;
|
||||
std::map<uint8, std::map<uint8, std::string> > ChatHelper::specs;
|
||||
|
||||
template <class T>
|
||||
static bool substrContainsInMap(std::string const searchTerm, std::map<std::string, T> searchIn)
|
||||
{
|
||||
for (typename std::map<std::string, T>::iterator i = searchIn.begin(); i != searchIn.end(); ++i)
|
||||
{
|
||||
std::string const term = i->first;
|
||||
if (term.size() > 1 && searchTerm.find(term) != std::string::npos)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ChatHelper::ChatHelper(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
|
||||
{
|
||||
itemQualities["poor"] = ITEM_QUALITY_POOR;
|
||||
itemQualities["gray"] = ITEM_QUALITY_POOR;
|
||||
itemQualities["normal"] = ITEM_QUALITY_NORMAL;
|
||||
itemQualities["white"] = ITEM_QUALITY_NORMAL;
|
||||
itemQualities["uncommon"] = ITEM_QUALITY_UNCOMMON;
|
||||
itemQualities["green"] = ITEM_QUALITY_UNCOMMON;
|
||||
itemQualities["rare"] = ITEM_QUALITY_RARE;
|
||||
itemQualities["blue"] = ITEM_QUALITY_RARE;
|
||||
itemQualities["epic"] = ITEM_QUALITY_EPIC;
|
||||
itemQualities["violet"] = ITEM_QUALITY_EPIC;
|
||||
itemQualities["legendary"] = ITEM_QUALITY_LEGENDARY;
|
||||
itemQualities["yellow"] = ITEM_QUALITY_LEGENDARY;
|
||||
|
||||
consumableSubClasses["potion"] = ITEM_SUBCLASS_POTION;
|
||||
consumableSubClasses["elixir"] = ITEM_SUBCLASS_ELIXIR;
|
||||
consumableSubClasses["flask"] = ITEM_SUBCLASS_FLASK;
|
||||
consumableSubClasses["scroll"] = ITEM_SUBCLASS_SCROLL;
|
||||
consumableSubClasses["food"] = ITEM_SUBCLASS_FOOD;
|
||||
consumableSubClasses["bandage"] = ITEM_SUBCLASS_BANDAGE;
|
||||
consumableSubClasses["enchant"] = ITEM_SUBCLASS_CONSUMABLE_OTHER;
|
||||
|
||||
projectileSubClasses["arrows"] = ITEM_SUBCLASS_ARROW;
|
||||
projectileSubClasses["bullets"] = ITEM_SUBCLASS_BULLET;
|
||||
|
||||
// tradeSubClasses["cloth"] = ITEM_SUBCLASS_CLOTH;
|
||||
// tradeSubClasses["leather"] = ITEM_SUBCLASS_LEATHER;
|
||||
// tradeSubClasses["metal"] = ITEM_SUBCLASS_METAL_STONE;
|
||||
// tradeSubClasses["stone"] = ITEM_SUBCLASS_METAL_STONE;
|
||||
// tradeSubClasses["ore"] = ITEM_SUBCLASS_METAL_STONE;
|
||||
// tradeSubClasses["meat"] = ITEM_SUBCLASS_MEAT;
|
||||
// tradeSubClasses["herb"] = ITEM_SUBCLASS_HERB;
|
||||
// tradeSubClasses["elemental"] = ITEM_SUBCLASS_ELEMENTAL;
|
||||
// tradeSubClasses["disenchants"] = ITEM_SUBCLASS_ENCHANTING;
|
||||
// tradeSubClasses["enchanting"] = ITEM_SUBCLASS_ENCHANTING;
|
||||
// tradeSubClasses["gems"] = ITEM_SUBCLASS_JEWELCRAFTING;
|
||||
// tradeSubClasses["jewels"] = ITEM_SUBCLASS_JEWELCRAFTING;
|
||||
// tradeSubClasses["jewelcrafting"] = ITEM_SUBCLASS_JEWELCRAFTING;
|
||||
|
||||
slots["head"] = EQUIPMENT_SLOT_HEAD;
|
||||
slots["neck"] = EQUIPMENT_SLOT_NECK;
|
||||
slots["shoulder"] = EQUIPMENT_SLOT_SHOULDERS;
|
||||
slots["shirt"] = EQUIPMENT_SLOT_BODY;
|
||||
slots["chest"] = EQUIPMENT_SLOT_CHEST;
|
||||
slots["waist"] = EQUIPMENT_SLOT_WAIST;
|
||||
slots["legs"] = EQUIPMENT_SLOT_LEGS;
|
||||
slots["feet"] = EQUIPMENT_SLOT_FEET;
|
||||
slots["wrist"] = EQUIPMENT_SLOT_WRISTS;
|
||||
slots["hands"] = EQUIPMENT_SLOT_HANDS;
|
||||
slots["finger 1"] = EQUIPMENT_SLOT_FINGER1;
|
||||
slots["finger 2"] = EQUIPMENT_SLOT_FINGER2;
|
||||
slots["trinket 1"] = EQUIPMENT_SLOT_TRINKET1;
|
||||
slots["trinket 2"] = EQUIPMENT_SLOT_TRINKET2;
|
||||
slots["back"] = EQUIPMENT_SLOT_BACK;
|
||||
slots["main hand"] = EQUIPMENT_SLOT_MAINHAND;
|
||||
slots["off hand"] = EQUIPMENT_SLOT_OFFHAND;
|
||||
slots["ranged"] = EQUIPMENT_SLOT_RANGED;
|
||||
slots["tabard"] = EQUIPMENT_SLOT_TABARD;
|
||||
|
||||
skills["first aid"] = SKILL_FIRST_AID;
|
||||
skills["fishing"] = SKILL_FISHING;
|
||||
skills["cooking"] = SKILL_COOKING;
|
||||
skills["alchemy"] = SKILL_ALCHEMY;
|
||||
skills["enchanting"] = SKILL_ENCHANTING;
|
||||
skills["engineering"] = SKILL_ENGINEERING;
|
||||
skills["leatherworking"] = SKILL_LEATHERWORKING;
|
||||
skills["blacksmithing"] = SKILL_BLACKSMITHING;
|
||||
skills["tailoring"] = SKILL_TAILORING;
|
||||
skills["herbalism"] = SKILL_HERBALISM;
|
||||
skills["mining"] = SKILL_MINING;
|
||||
skills["skinning"] = SKILL_SKINNING;
|
||||
skills["jewelcrafting"] = SKILL_JEWELCRAFTING;
|
||||
|
||||
chats["party"] = CHAT_MSG_PARTY;
|
||||
chats["p"] = CHAT_MSG_PARTY;
|
||||
chats["guild"] = CHAT_MSG_GUILD;
|
||||
chats["g"] = CHAT_MSG_GUILD;
|
||||
chats["raid"] = CHAT_MSG_RAID;
|
||||
chats["r"] = CHAT_MSG_RAID;
|
||||
chats["whisper"] = CHAT_MSG_WHISPER;
|
||||
chats["w"] = CHAT_MSG_WHISPER;
|
||||
|
||||
classes[CLASS_DRUID] = "druid";
|
||||
specs[CLASS_DRUID][0] = "balance";
|
||||
specs[CLASS_DRUID][1] = "feral combat";
|
||||
specs[CLASS_DRUID][2] = "restoration";
|
||||
|
||||
classes[CLASS_HUNTER] = "hunter";
|
||||
specs[CLASS_HUNTER][0] = "beast mastery";
|
||||
specs[CLASS_HUNTER][1] = "marksmanship";
|
||||
specs[CLASS_HUNTER][2] = "survival";
|
||||
|
||||
classes[CLASS_MAGE] = "mage";
|
||||
specs[CLASS_MAGE][0] = "arcane";
|
||||
specs[CLASS_MAGE][1] = "fire";
|
||||
specs[CLASS_MAGE][2] = "frost";
|
||||
|
||||
classes[CLASS_PALADIN] = "paladin";
|
||||
specs[CLASS_PALADIN][0] = "holy";
|
||||
specs[CLASS_PALADIN][1] = "protection";
|
||||
specs[CLASS_PALADIN][2] = "retribution";
|
||||
|
||||
classes[CLASS_PRIEST] = "priest";
|
||||
specs[CLASS_PRIEST][0] = "discipline";
|
||||
specs[CLASS_PRIEST][1] = "holy";
|
||||
specs[CLASS_PRIEST][2] = "shadow";
|
||||
|
||||
classes[CLASS_ROGUE] = "rogue";
|
||||
specs[CLASS_ROGUE][0] = "assasination";
|
||||
specs[CLASS_ROGUE][1] = "combat";
|
||||
specs[CLASS_ROGUE][2] = "subtlety";
|
||||
|
||||
classes[CLASS_SHAMAN] = "shaman";
|
||||
specs[CLASS_SHAMAN][0] = "elemental";
|
||||
specs[CLASS_SHAMAN][1] = "enhancement";
|
||||
specs[CLASS_SHAMAN][2] = "restoration";
|
||||
|
||||
classes[CLASS_WARLOCK] = "warlock";
|
||||
specs[CLASS_WARLOCK][0] = "affliction";
|
||||
specs[CLASS_WARLOCK][1] = "demonology";
|
||||
specs[CLASS_WARLOCK][2] = "destruction";
|
||||
|
||||
classes[CLASS_WARRIOR] = "warrior";
|
||||
specs[CLASS_WARRIOR][0] = "arms";
|
||||
specs[CLASS_WARRIOR][1] = "fury";
|
||||
specs[CLASS_WARRIOR][2] = "protection";
|
||||
|
||||
classes[CLASS_DEATH_KNIGHT] = "dk";
|
||||
specs[CLASS_DEATH_KNIGHT][0] = "blood";
|
||||
specs[CLASS_DEATH_KNIGHT][1] = "frost";
|
||||
specs[CLASS_DEATH_KNIGHT][2] = "unholy";
|
||||
|
||||
races[RACE_DWARF] = "Dwarf";
|
||||
races[RACE_GNOME] = "Gnome";
|
||||
races[RACE_HUMAN] = "Human";
|
||||
races[RACE_NIGHTELF] = "Night Elf";
|
||||
races[RACE_ORC] = "Orc";
|
||||
races[RACE_TAUREN] = "Tauren";
|
||||
races[RACE_TROLL] = "Troll";
|
||||
races[RACE_UNDEAD_PLAYER] = "Undead";
|
||||
races[RACE_BLOODELF] = "Blood Elf";
|
||||
races[RACE_DRAENEI] = "Draenei";
|
||||
}
|
||||
|
||||
std::string const ChatHelper::formatMoney(uint32 copper)
|
||||
{
|
||||
std::ostringstream out;
|
||||
if (!copper)
|
||||
{
|
||||
out << "0";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
uint32 gold = uint32(copper / 10000);
|
||||
copper -= (gold * 10000);
|
||||
uint32 silver = uint32(copper / 100);
|
||||
copper -= (silver * 100);
|
||||
|
||||
bool space = false;
|
||||
if (gold > 0)
|
||||
{
|
||||
out << gold << "g";
|
||||
space = true;
|
||||
}
|
||||
|
||||
if (silver > 0 && gold < 50)
|
||||
{
|
||||
if (space)
|
||||
out << " ";
|
||||
|
||||
out << silver << "s";
|
||||
space = true;
|
||||
}
|
||||
|
||||
if (copper > 0 && gold < 10)
|
||||
{
|
||||
if (space)
|
||||
out << " ";
|
||||
|
||||
out << copper << "c";
|
||||
}
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string ChatHelper::parseValue(const std::string& type, const std::string& text)
|
||||
{
|
||||
std::string retString;
|
||||
|
||||
std::string pattern = "Hvalue:" + type + ":";
|
||||
|
||||
int pos = text.find(pattern, 0);
|
||||
if (pos == -1)
|
||||
return retString;
|
||||
|
||||
pos += pattern.size();
|
||||
|
||||
int endPos = text.find('|', pos);
|
||||
if (endPos == -1)
|
||||
return retString;
|
||||
|
||||
retString = text.substr(pos, endPos - pos);
|
||||
return retString;
|
||||
}
|
||||
|
||||
uint32 ChatHelper::parseMoney(std::string const text)
|
||||
{
|
||||
// if user specified money in ##g##s##c format
|
||||
std::string acum = "";
|
||||
uint32 copper = 0;
|
||||
for (uint8 i = 0; i < text.length(); i++)
|
||||
{
|
||||
if (text[i] == 'g')
|
||||
{
|
||||
copper += (atol(acum.c_str()) * 100 * 100);
|
||||
acum = "";
|
||||
}
|
||||
else if (text[i] == 'c')
|
||||
{
|
||||
copper += atol(acum.c_str());
|
||||
acum = "";
|
||||
}
|
||||
else if (text[i] == 's')
|
||||
{
|
||||
copper += (atol(acum.c_str()) * 100);
|
||||
acum = "";
|
||||
}
|
||||
else if (text[i] == ' ')
|
||||
break;
|
||||
else if (text[i] >= 48 && text[i] <= 57)
|
||||
acum += text[i];
|
||||
else
|
||||
{
|
||||
copper = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return copper;
|
||||
}
|
||||
|
||||
ItemIds ChatHelper::parseItems(std::string const text)
|
||||
{
|
||||
ItemIds itemIds;
|
||||
|
||||
uint8 pos = 0;
|
||||
while (true)
|
||||
{
|
||||
auto i = text.find("Hitem:", pos);
|
||||
if (i == std::string::npos)
|
||||
break;
|
||||
|
||||
pos = i + 6;
|
||||
auto endPos = text.find(':', pos);
|
||||
if (endPos == std::string::npos)
|
||||
break;
|
||||
|
||||
std::string const idC = text.substr(pos, endPos - pos);
|
||||
auto id = atol(idC.c_str());
|
||||
pos = endPos;
|
||||
if (id)
|
||||
itemIds.insert(id);
|
||||
}
|
||||
|
||||
return itemIds;
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatQuest(Quest const* quest)
|
||||
{
|
||||
if (!quest)
|
||||
{
|
||||
return "Invalid quest";
|
||||
}
|
||||
|
||||
std::ostringstream out;
|
||||
QuestLocale const* locale = sObjectMgr->GetQuestLocale(quest->GetQuestId());
|
||||
std::string questTitle;
|
||||
|
||||
if (locale && locale->Title.size() > sWorld->GetDefaultDbcLocale())
|
||||
questTitle = locale->Title[sWorld->GetDefaultDbcLocale()];
|
||||
|
||||
if (questTitle.empty())
|
||||
questTitle = quest->GetTitle();
|
||||
|
||||
out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << questTitle << "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatGameobject(GameObject* go)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "|cFFFFFF00|Hfound:" << go->GetGUID().GetRawValue() << ":" << go->GetEntry() << ":"
|
||||
<< "|h[" << go->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()) << "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatWorldobject(WorldObject* wo)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "|cFFFFFF00|Hfound:" << wo->GetGUID().GetRawValue() << ":" << wo->GetEntry() << ":"
|
||||
<< "|h[";
|
||||
out << (wo->ToGameObject() ? ((GameObject*)wo)->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale())
|
||||
: wo->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()))
|
||||
<< "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatWorldEntry(int32 entry)
|
||||
{
|
||||
CreatureTemplate const* cInfo = nullptr;
|
||||
GameObjectTemplate const* gInfo = nullptr;
|
||||
|
||||
if (entry > 0)
|
||||
cInfo = sObjectMgr->GetCreatureTemplate(entry);
|
||||
else
|
||||
gInfo = sObjectMgr->GetGameObjectTemplate(entry * -1);
|
||||
|
||||
std::ostringstream out;
|
||||
out << "|cFFFFFF00|Hentry:" << abs(entry) << ":"
|
||||
<< "|h[";
|
||||
|
||||
if (entry < 0 && gInfo)
|
||||
out << gInfo->name;
|
||||
else if (entry > 0 && cInfo)
|
||||
out << cInfo->Name;
|
||||
else
|
||||
out << "unknown";
|
||||
|
||||
out << "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
|
||||
{
|
||||
std::ostringstream out;
|
||||
std::string spellName = spellInfo->SpellName[sWorld->GetDefaultDbcLocale()] ?
|
||||
spellInfo->SpellName[sWorld->GetDefaultDbcLocale()] : spellInfo->SpellName[LOCALE_enUS];
|
||||
out << "|cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellName << "]|h|r";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count, uint32 total)
|
||||
{
|
||||
char color[32];
|
||||
sprintf(color, "%x", ItemQualityColors[proto->Quality]);
|
||||
|
||||
std::string itemName;
|
||||
const ItemLocale* locale = sObjectMgr->GetItemLocale(proto->ItemId);
|
||||
|
||||
if (locale && locale->Name.size() > sWorld->GetDefaultDbcLocale())
|
||||
itemName = locale->Name[sWorld->GetDefaultDbcLocale()];
|
||||
|
||||
if (itemName.empty())
|
||||
itemName = proto->Name1;
|
||||
|
||||
std::ostringstream out;
|
||||
out << "|c" << color << "|Hitem:" << proto->ItemId << ":0:0:0:0:0:0:0"
|
||||
<< "|h[" << itemName << "]|h|r";
|
||||
|
||||
if (count > 1)
|
||||
out << "x" << count;
|
||||
|
||||
if (total > 0)
|
||||
out << " (" << total << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatQItem(uint32 itemId)
|
||||
{
|
||||
char color[32];
|
||||
sprintf(color, "%x", ItemQualityColors[0]);
|
||||
|
||||
std::ostringstream out;
|
||||
out << "|c" << color << "|Hitem:" << itemId << ":0:0:0:0:0:0:0"
|
||||
<< "|h[item"
|
||||
<< "]|h|r";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
ChatMsg ChatHelper::parseChat(std::string const text)
|
||||
{
|
||||
if (chats.find(text) != chats.end())
|
||||
return chats[text];
|
||||
|
||||
return CHAT_MSG_SYSTEM;
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatChat(ChatMsg chat)
|
||||
{
|
||||
switch (chat)
|
||||
{
|
||||
case CHAT_MSG_GUILD:
|
||||
return "guild";
|
||||
case CHAT_MSG_PARTY:
|
||||
return "party";
|
||||
case CHAT_MSG_WHISPER:
|
||||
return "whisper";
|
||||
case CHAT_MSG_RAID:
|
||||
return "raid";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
uint32 ChatHelper::parseSpell(std::string const text)
|
||||
{
|
||||
PlayerbotChatHandler handler(botAI->GetBot());
|
||||
return handler.extractSpellId(text);
|
||||
}
|
||||
|
||||
GuidVector ChatHelper::parseGameobjects(std::string const text)
|
||||
{
|
||||
GuidVector gos;
|
||||
// Link format
|
||||
// |cFFFFFF00|Hfound:" << guid << ':' << entry << ':' << "|h[" << gInfo->name << "]|h|r";
|
||||
// |cFFFFFF00|Hfound:9582:1731|h[Copper Vein]|h|r
|
||||
|
||||
uint8 pos = 0;
|
||||
while (true)
|
||||
{
|
||||
// extract GO guid
|
||||
auto i = text.find("Hfound:", pos); // base H = 11
|
||||
if (i == std::string::npos) // break if error
|
||||
break;
|
||||
|
||||
pos = i + 7; // start of window in text 11 + 7 = 18
|
||||
auto endPos = text.find(':', pos); // end of window in text 22
|
||||
if (endPos == std::string::npos) // break if error
|
||||
break;
|
||||
|
||||
std::istringstream stream(text.substr(pos, endPos - pos));
|
||||
uint64 guid;
|
||||
stream >> guid;
|
||||
|
||||
// extract GO entry
|
||||
pos = endPos + 1;
|
||||
endPos = text.find(':', pos); // end of window in text
|
||||
if (endPos == std::string::npos) // break if error
|
||||
break;
|
||||
|
||||
std::string const entryC = text.substr(pos, endPos - pos); // get std::string const within window i.e entry
|
||||
//uint32 entry = atol(entryC.c_str()); // convert ascii to float
|
||||
|
||||
ObjectGuid lootCurrent = ObjectGuid(guid);
|
||||
|
||||
if (guid)
|
||||
gos.push_back(lootCurrent);
|
||||
}
|
||||
|
||||
return gos;
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatQuestObjective(std::string const name, uint32 available, uint32 required)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "|cFFFFFFFF" << name << (available >= required ? "|c0000FF00: " : "|c00FF0000: ") << available << "/"
|
||||
<< required << "|r";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
uint32 ChatHelper::parseItemQuality(std::string const text)
|
||||
{
|
||||
if (itemQualities.find(text) == itemQualities.end())
|
||||
return MAX_ITEM_QUALITY;
|
||||
|
||||
return itemQualities[text];
|
||||
}
|
||||
|
||||
bool ChatHelper::parseItemClass(std::string const text, uint32* itemClass, uint32* itemSubClass)
|
||||
{
|
||||
if (text == "questitem")
|
||||
{
|
||||
*itemClass = ITEM_CLASS_QUEST;
|
||||
*itemSubClass = ITEM_SUBCLASS_QUEST;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (consumableSubClasses.find(text) != consumableSubClasses.end())
|
||||
{
|
||||
*itemClass = ITEM_CLASS_CONSUMABLE;
|
||||
*itemSubClass = consumableSubClasses[text];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tradeSubClasses.find(text) != tradeSubClasses.end())
|
||||
{
|
||||
*itemClass = ITEM_CLASS_TRADE_GOODS;
|
||||
*itemSubClass = tradeSubClasses[text];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (projectileSubClasses.find(text) != projectileSubClasses.end())
|
||||
{
|
||||
*itemClass = ITEM_CLASS_PROJECTILE;
|
||||
*itemSubClass = projectileSubClasses[text];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 ChatHelper::parseSlot(std::string const text)
|
||||
{
|
||||
if (slots.find(text) != slots.end())
|
||||
return slots[text];
|
||||
|
||||
return EQUIPMENT_SLOT_END;
|
||||
}
|
||||
|
||||
bool ChatHelper::parseable(std::string const text)
|
||||
{
|
||||
return text.find("|H") != std::string::npos || text == "questitem" || text == "ammo" ||
|
||||
substrContainsInMap<uint32>(text, consumableSubClasses) ||
|
||||
substrContainsInMap<uint32>(text, tradeSubClasses) || substrContainsInMap<uint32>(text, itemQualities) ||
|
||||
substrContainsInMap<uint32>(text, slots) || substrContainsInMap<ChatMsg>(text, chats) ||
|
||||
substrContainsInMap<uint32>(text, skills) || parseMoney(text) > 0;
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatClass(Player* player, int8 spec)
|
||||
{
|
||||
uint8 cls = player->getClass();
|
||||
|
||||
std::ostringstream out;
|
||||
out << specs[cls][spec] << " (";
|
||||
|
||||
std::map<uint8, uint32> tabs = AiFactory::GetPlayerSpecTabs(player);
|
||||
uint32 c0 = tabs[0];
|
||||
uint32 c1 = tabs[1];
|
||||
uint32 c2 = tabs[2];
|
||||
|
||||
out << (c0 ? "|h|cff00ff00" : "") << c0 << "|h|cffffffff/";
|
||||
out << (c1 ? "|h|cff00ff00" : "") << c1 << "|h|cffffffff/";
|
||||
out << (c2 ? "|h|cff00ff00" : "") << c2 << "|h|cffffffff";
|
||||
|
||||
out << ")|r " << classes[cls];
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatClass(uint8 cls) { return classes[cls]; }
|
||||
|
||||
std::string const ChatHelper::FormatRace(uint8 race) { return races[race]; }
|
||||
|
||||
uint32 ChatHelper::parseSkill(std::string const text)
|
||||
{
|
||||
if (skills.find(text) != skills.end())
|
||||
return skills[text];
|
||||
|
||||
return SKILL_NONE;
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatSkill(uint32 skill)
|
||||
{
|
||||
for (std::map<std::string, uint32>::iterator i = skills.begin(); i != skills.end(); ++i)
|
||||
{
|
||||
if (i->second == skill)
|
||||
return i->first;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string const ChatHelper::FormatBoolean(bool flag) { return flag ? "|cff00ff00ON|r" : "|cffffff00OFF|r"; }
|
||||
|
||||
void ChatHelper::eraseAllSubStr(std::string& mainStr, std::string const toErase)
|
||||
{
|
||||
size_t pos = std::string::npos;
|
||||
|
||||
// Search for the substring in std::string const in a loop untill nothing is found
|
||||
while ((pos = mainStr.find(toErase)) != std::string::npos)
|
||||
{
|
||||
// If found then erase it from std::string
|
||||
mainStr.erase(pos, toErase.length());
|
||||
}
|
||||
}
|
||||
|
||||
std::set<uint32> extractGeneric(std::string_view text, std::string_view prefix)
|
||||
{
|
||||
std::set<uint32_t> ids;
|
||||
std::string_view text_view = text;
|
||||
|
||||
size_t pos = 0;
|
||||
while ((pos = text_view.find(prefix, pos)) != std::string::npos)
|
||||
{
|
||||
// skip "Hquest:/Hitem:"
|
||||
pos += prefix.size();
|
||||
|
||||
// extract everything after "Hquest:/Hitem:"
|
||||
size_t end_pos = text_view.find_first_not_of("0123456789", pos);
|
||||
std::string_view number_str = text_view.substr(pos, end_pos - pos);
|
||||
|
||||
uint32 number = 0;
|
||||
|
||||
auto [ptr, ec] = std::from_chars(number_str.data(), number_str.data() + number_str.size(), number);
|
||||
|
||||
if (ec == std::errc())
|
||||
{
|
||||
ids.insert(number);
|
||||
}
|
||||
pos = end_pos;
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
std::set<uint32> ChatHelper::ExtractAllQuestIds(const std::string& text)
|
||||
{
|
||||
return extractGeneric(text, "Hquest:");
|
||||
}
|
||||
|
||||
std::set<uint32> ChatHelper::ExtractAllItemIds(const std::string& text)
|
||||
{
|
||||
return extractGeneric(text, "Hitem:");
|
||||
}
|
||||
83
src/helper/ChatHelper.h
Normal file
83
src/helper/ChatHelper.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_CHATHELPER_H
|
||||
#define _PLAYERBOT_CHATHELPER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAIAware.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
class GameObject;
|
||||
class Quest;
|
||||
class Player;
|
||||
class PlayerbotAI;
|
||||
class SpellInfo;
|
||||
class WorldObject;
|
||||
|
||||
struct ItemTemplate;
|
||||
|
||||
typedef std::set<uint32> ItemIds;
|
||||
typedef std::set<uint32> SpellIds;
|
||||
|
||||
class ChatHelper : public PlayerbotAIAware
|
||||
{
|
||||
public:
|
||||
ChatHelper(PlayerbotAI* botAI);
|
||||
|
||||
static std::string const formatMoney(uint32 copper);
|
||||
static uint32 parseMoney(std::string const text);
|
||||
static ItemIds parseItems(std::string const text);
|
||||
uint32 parseSpell(std::string const text);
|
||||
static std::string parseValue(const std::string& type, const std::string& text);
|
||||
|
||||
static std::string const FormatQuest(Quest const* quest);
|
||||
static std::string const FormatItem(ItemTemplate const* proto, uint32 count = 0, uint32 total = 0);
|
||||
static std::string const FormatQItem(uint32 itemId);
|
||||
static std::string const FormatSpell(SpellInfo const* spellInfo);
|
||||
static std::string const FormatGameobject(GameObject* go);
|
||||
static std::string const FormatWorldobject(WorldObject* wo);
|
||||
static std::string const FormatWorldEntry(int32 entry);
|
||||
static std::string const FormatQuestObjective(std::string const name, uint32 available, uint32 required);
|
||||
static GuidVector parseGameobjects(std::string const text);
|
||||
|
||||
static ChatMsg parseChat(std::string const text);
|
||||
static std::string const FormatChat(ChatMsg chat);
|
||||
|
||||
static std::string const FormatClass(Player* player, int8 spec);
|
||||
static std::string const FormatClass(uint8 cls);
|
||||
static std::string const FormatRace(uint8 race);
|
||||
static std::string const FormatSkill(uint32 skill);
|
||||
static std::string const FormatBoolean(bool flag);
|
||||
|
||||
static uint32 parseItemQuality(std::string const text);
|
||||
static bool parseItemClass(std::string const text, uint32* itemClass, uint32* itemSubClass);
|
||||
static uint32 parseSlot(std::string const text);
|
||||
uint32 parseSkill(std::string const text);
|
||||
|
||||
static bool parseable(std::string const text);
|
||||
|
||||
void eraseAllSubStr(std::string& mainStr, std::string const toErase);
|
||||
|
||||
static std::set<uint32> ExtractAllQuestIds(const std::string& text);
|
||||
static std::set<uint32> ExtractAllItemIds(const std::string& text);
|
||||
|
||||
private:
|
||||
static std::map<std::string, uint32> consumableSubClasses;
|
||||
static std::map<std::string, uint32> tradeSubClasses;
|
||||
static std::map<std::string, uint32> itemQualities;
|
||||
static std::map<std::string, uint32> projectileSubClasses;
|
||||
static std::map<std::string, uint32> slots;
|
||||
static std::map<std::string, uint32> skills;
|
||||
static std::map<std::string, ChatMsg> chats;
|
||||
static std::map<uint8, std::string> classes;
|
||||
static std::map<uint8, std::string> races;
|
||||
static std::map<uint8, std::map<uint8, std::string>> specs;
|
||||
};
|
||||
|
||||
#endif
|
||||
67
src/helper/ExternalEventHelper.cpp
Normal file
67
src/helper/ExternalEventHelper.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "ExternalEventHelper.h"
|
||||
|
||||
#include "ChatHelper.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
bool ExternalEventHelper::ParseChatCommand(std::string const command, Player* owner)
|
||||
{
|
||||
if (HandleCommand(command, "", owner))
|
||||
return true;
|
||||
|
||||
size_t i = std::string::npos;
|
||||
while (true)
|
||||
{
|
||||
size_t found = command.rfind(" ", i);
|
||||
if (found == std::string::npos || !found)
|
||||
break;
|
||||
|
||||
std::string const name = command.substr(0, found);
|
||||
std::string const param = command.substr(found + 1);
|
||||
|
||||
i = found - 1;
|
||||
|
||||
if (HandleCommand(name, param, owner))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ChatHelper::parseable(command))
|
||||
return false;
|
||||
|
||||
HandleCommand("c", command, owner);
|
||||
HandleCommand("t", command, owner);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExternalEventHelper::HandlePacket(std::map<uint16, std::string>& handlers, WorldPacket const& packet,
|
||||
Player* owner)
|
||||
{
|
||||
uint16 opcode = packet.GetOpcode();
|
||||
std::string const name = handlers[opcode];
|
||||
if (name.empty())
|
||||
return;
|
||||
|
||||
Trigger* trigger = aiObjectContext->GetTrigger(name);
|
||||
if (!trigger)
|
||||
return;
|
||||
|
||||
WorldPacket p(packet);
|
||||
trigger->ExternalEvent(p, owner);
|
||||
}
|
||||
|
||||
bool ExternalEventHelper::HandleCommand(std::string const name, std::string const param, Player* owner)
|
||||
{
|
||||
Trigger* trigger = aiObjectContext->GetTrigger(name);
|
||||
if (!trigger)
|
||||
return false;
|
||||
|
||||
trigger->ExternalEvent(param, owner);
|
||||
|
||||
return true;
|
||||
}
|
||||
30
src/helper/ExternalEventHelper.h
Normal file
30
src/helper/ExternalEventHelper.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_EXTERNALEVENTHELPER_H
|
||||
#define _PLAYERBOT_EXTERNALEVENTHELPER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
class AiObjectContext;
|
||||
class Player;
|
||||
class WorldPacket;
|
||||
|
||||
class ExternalEventHelper
|
||||
{
|
||||
public:
|
||||
ExternalEventHelper(AiObjectContext* aiObjectContext) : aiObjectContext(aiObjectContext) {}
|
||||
|
||||
bool ParseChatCommand(std::string const command, Player* owner = nullptr);
|
||||
void HandlePacket(std::map<uint16, std::string>& handlers, WorldPacket const& packet, Player* owner = nullptr);
|
||||
bool HandleCommand(std::string const name, std::string const param, Player* owner = nullptr);
|
||||
|
||||
private:
|
||||
AiObjectContext* aiObjectContext;
|
||||
};
|
||||
|
||||
#endif
|
||||
197
src/helper/FleeManager.cpp
Normal file
197
src/helper/FleeManager.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "FleeManager.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
|
||||
FleeManager::FleeManager(Player* bot, float maxAllowedDistance, float followAngle, bool forceMaxDistance,
|
||||
WorldPosition startPosition)
|
||||
: bot(bot),
|
||||
maxAllowedDistance(maxAllowedDistance),
|
||||
followAngle(followAngle),
|
||||
forceMaxDistance(forceMaxDistance),
|
||||
startPosition(startPosition ? startPosition : WorldPosition(bot))
|
||||
{
|
||||
}
|
||||
|
||||
void FleeManager::calculateDistanceToCreatures(FleePoint* point)
|
||||
{
|
||||
point->minDistance = -1.0f;
|
||||
point->sumDistance = 0.0f;
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GuidVector units = *botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los");
|
||||
for (GuidVector::iterator i = units.begin(); i != units.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit)
|
||||
continue;
|
||||
|
||||
float d = sServerFacade->GetDistance2d(unit, point->x, point->y);
|
||||
point->sumDistance += d;
|
||||
if (point->minDistance < 0 || point->minDistance > d)
|
||||
point->minDistance = d;
|
||||
}
|
||||
}
|
||||
|
||||
bool intersectsOri(float angle, std::vector<float>& angles, float angleIncrement)
|
||||
{
|
||||
for (std::vector<float>::iterator i = angles.begin(); i != angles.end(); ++i)
|
||||
{
|
||||
float ori = *i;
|
||||
if (abs(angle - ori) < angleIncrement)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FleeManager::calculatePossibleDestinations(std::vector<FleePoint*>& points)
|
||||
{
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Unit* target = *botAI->GetAiObjectContext()->GetValue<Unit*>("current target");
|
||||
|
||||
float botPosX = startPosition.getX();
|
||||
float botPosY = startPosition.getY();
|
||||
float botPosZ = startPosition.getZ();
|
||||
|
||||
FleePoint start(botAI, botPosX, botPosY, botPosZ);
|
||||
calculateDistanceToCreatures(&start);
|
||||
|
||||
std::vector<float> enemyOri;
|
||||
GuidVector units = *botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los");
|
||||
for (GuidVector::iterator i = units.begin(); i != units.end(); ++i)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(*i);
|
||||
if (!unit)
|
||||
continue;
|
||||
|
||||
float ori = bot->GetAngle(unit);
|
||||
enemyOri.push_back(ori);
|
||||
}
|
||||
|
||||
float distIncrement = std::max(sPlayerbotAIConfig->followDistance,
|
||||
(maxAllowedDistance - sPlayerbotAIConfig->tooCloseDistance) / 10.0f);
|
||||
for (float dist = maxAllowedDistance; dist >= sPlayerbotAIConfig->tooCloseDistance; dist -= distIncrement)
|
||||
{
|
||||
float angleIncrement = std::max(M_PI / 20, M_PI / 4 / (1.0 + dist - sPlayerbotAIConfig->tooCloseDistance));
|
||||
for (float add = 0.0f; add < M_PI / 4 + angleIncrement; add += angleIncrement)
|
||||
{
|
||||
for (float angle = add; angle < add + 2 * static_cast<float>(M_PI) + angleIncrement;
|
||||
angle += static_cast<float>(M_PI) / 4)
|
||||
{
|
||||
if (intersectsOri(angle, enemyOri, angleIncrement))
|
||||
continue;
|
||||
|
||||
float x = botPosX + cos(angle) * maxAllowedDistance, y = botPosY + sin(angle) * maxAllowedDistance,
|
||||
z = botPosZ + CONTACT_DISTANCE;
|
||||
if (forceMaxDistance &&
|
||||
sServerFacade->IsDistanceLessThan(sServerFacade->GetDistance2d(bot, x, y),
|
||||
maxAllowedDistance - sPlayerbotAIConfig->tooCloseDistance))
|
||||
continue;
|
||||
|
||||
bot->UpdateAllowedPositionZ(x, y, z);
|
||||
|
||||
Map* map = startPosition.getMap();
|
||||
if (map && map->IsInWater(bot->GetPhaseMask(), x, y, z, bot->GetCollisionHeight()))
|
||||
continue;
|
||||
|
||||
if (!bot->IsWithinLOS(x, y, z) || (target && !target->IsWithinLOS(x, y, z)))
|
||||
continue;
|
||||
|
||||
FleePoint* point = new FleePoint(botAI, x, y, z);
|
||||
calculateDistanceToCreatures(point);
|
||||
|
||||
if (sServerFacade->IsDistanceGreaterOrEqualThan(point->minDistance - start.minDistance,
|
||||
sPlayerbotAIConfig->followDistance))
|
||||
points.push_back(point);
|
||||
else
|
||||
delete point;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FleeManager::cleanup(std::vector<FleePoint*>& points)
|
||||
{
|
||||
for (std::vector<FleePoint*>::iterator i = points.begin(); i != points.end(); i++)
|
||||
{
|
||||
delete *i;
|
||||
}
|
||||
|
||||
points.clear();
|
||||
}
|
||||
|
||||
bool FleeManager::isBetterThan(FleePoint* point, FleePoint* other)
|
||||
{
|
||||
return point->sumDistance - other->sumDistance > 0;
|
||||
}
|
||||
|
||||
FleePoint* FleeManager::selectOptimalDestination(std::vector<FleePoint*>& points)
|
||||
{
|
||||
FleePoint* best = nullptr;
|
||||
for (std::vector<FleePoint*>::iterator i = points.begin(); i != points.end(); i++)
|
||||
{
|
||||
FleePoint* point = *i;
|
||||
if (!best || isBetterThan(point, best))
|
||||
best = point;
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
bool FleeManager::CalculateDestination(float* rx, float* ry, float* rz)
|
||||
{
|
||||
std::vector<FleePoint*> points;
|
||||
calculatePossibleDestinations(points);
|
||||
|
||||
FleePoint* point = selectOptimalDestination(points);
|
||||
if (!point)
|
||||
{
|
||||
cleanup(points);
|
||||
return false;
|
||||
}
|
||||
|
||||
*rx = point->x;
|
||||
*ry = point->y;
|
||||
*rz = point->z;
|
||||
|
||||
cleanup(points);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FleeManager::isUseful()
|
||||
{
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
GuidVector units = *botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los");
|
||||
for (GuidVector::iterator i = units.begin(); i != units.end(); ++i)
|
||||
{
|
||||
Creature* creature = botAI->GetCreature(*i);
|
||||
if (!creature)
|
||||
continue;
|
||||
|
||||
if (startPosition.sqDistance(WorldPosition(creature)) <
|
||||
creature->GetAttackDistance(bot) * creature->GetAttackDistance(bot))
|
||||
return true;
|
||||
|
||||
// float d = sServerFacade->GetDistance2d(unit, bot);
|
||||
// if (sServerFacade->IsDistanceLessThan(d, sPlayerbotAIConfig->aggroDistance)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
59
src/helper/FleeManager.h
Normal file
59
src/helper/FleeManager.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_FLEEMANAGER_H
|
||||
#define _PLAYERBOT_FLEEMANAGER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
#include "TravelMgr.h"
|
||||
|
||||
class Player;
|
||||
class PlayerbotAI;
|
||||
|
||||
class FleePoint
|
||||
{
|
||||
public:
|
||||
FleePoint(PlayerbotAI* botAI, float x, float y, float z)
|
||||
: x(x), y(y), z(z), sumDistance(0.0f), minDistance(0.0f), botAI(botAI)
|
||||
{
|
||||
}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
|
||||
float sumDistance;
|
||||
float minDistance;
|
||||
|
||||
private:
|
||||
PlayerbotAI* botAI;
|
||||
};
|
||||
|
||||
class FleeManager
|
||||
{
|
||||
public:
|
||||
FleeManager(Player* bot, float maxAllowedDistance, float followAngle, bool forceMaxDistance = false,
|
||||
WorldPosition startPosition = WorldPosition());
|
||||
|
||||
bool CalculateDestination(float* rx, float* ry, float* rz);
|
||||
bool isUseful();
|
||||
|
||||
private:
|
||||
void calculatePossibleDestinations(std::vector<FleePoint*>& points);
|
||||
void calculateDistanceToCreatures(FleePoint* point);
|
||||
void cleanup(std::vector<FleePoint*>& points);
|
||||
FleePoint* selectOptimalDestination(std::vector<FleePoint*>& points);
|
||||
bool isBetterThan(FleePoint* point, FleePoint* other);
|
||||
|
||||
Player* bot;
|
||||
float maxAllowedDistance;
|
||||
[[maybe_unused]] float followAngle; // unused - whipowill
|
||||
bool forceMaxDistance;
|
||||
WorldPosition startPosition;
|
||||
};
|
||||
|
||||
#endif
|
||||
1243
src/helper/GuildTaskMgr.cpp
Normal file
1243
src/helper/GuildTaskMgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
62
src/helper/GuildTaskMgr.h
Normal file
62
src/helper/GuildTaskMgr.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_GUILDTASKMGR_H
|
||||
#define _PLAYERBOT_GUILDTASKMGR_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Transaction.h"
|
||||
|
||||
class ChatHandler;
|
||||
class Player;
|
||||
class Unit;
|
||||
|
||||
class GuildTaskMgr
|
||||
{
|
||||
public:
|
||||
GuildTaskMgr(){};
|
||||
virtual ~GuildTaskMgr(){};
|
||||
|
||||
static GuildTaskMgr* instance()
|
||||
{
|
||||
static GuildTaskMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void Update(Player* owner, Player* guildMaster);
|
||||
|
||||
static bool HandleConsoleCommand(ChatHandler* handler, char const* args);
|
||||
bool IsGuildTaskItem(uint32 itemId, uint32 guildId);
|
||||
bool CheckItemTask(uint32 itemId, uint32 obtained, Player* owner, Player* bot, bool byMail = false);
|
||||
void CheckKillTask(Player* owner, Unit* victim);
|
||||
void CheckKillTaskInternal(Player* owner, Unit* victim);
|
||||
bool CheckTaskTransfer(std::string const text, Player* owner, Player* bot);
|
||||
|
||||
private:
|
||||
std::map<uint32, uint32> GetTaskValues(uint32 owner, std::string const type, uint32* validIn = nullptr);
|
||||
uint32 GetTaskValue(uint32 owner, uint32 guildId, std::string const type, uint32* validIn = nullptr);
|
||||
uint32 SetTaskValue(uint32 owner, uint32 guildId, std::string const type, uint32 value, uint32 validIn);
|
||||
uint32 CreateTask(Player* owner, uint32 guildId);
|
||||
bool SendAdvertisement(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId);
|
||||
bool SendItemAdvertisement(CharacterDatabaseTransaction& trans, uint32 itemId, uint32 owner, uint32 guildId,
|
||||
uint32 validIn);
|
||||
bool SendKillAdvertisement(CharacterDatabaseTransaction& trans, uint32 creatureId, uint32 owner, uint32 guildId,
|
||||
uint32 validIn);
|
||||
bool SendThanks(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId, uint32 payment);
|
||||
bool Reward(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId);
|
||||
bool CreateItemTask(Player* owner, uint32 guildId);
|
||||
bool CreateKillTask(Player* owner, uint32 guildId);
|
||||
uint32 GetMaxItemTaskCount(uint32 itemId);
|
||||
void CleanupAdverts();
|
||||
void RemoveDuplicatedAdverts();
|
||||
void DeleteMail(std::vector<uint32> buffer);
|
||||
void SendCompletionMessage(Player* player, std::string const verb);
|
||||
};
|
||||
|
||||
#define sGuildTaskMgr GuildTaskMgr::instance()
|
||||
|
||||
#endif
|
||||
50
src/helper/Helpers.cpp
Normal file
50
src/helper/Helpers.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "Helpers.h"
|
||||
|
||||
char* strstri(char const* haystack, char const* needle)
|
||||
{
|
||||
if (!*needle)
|
||||
{
|
||||
return (char*)haystack;
|
||||
}
|
||||
|
||||
for (; *haystack; ++haystack)
|
||||
{
|
||||
if (tolower(*haystack) == tolower(*needle))
|
||||
{
|
||||
char const *h = haystack, *n = needle;
|
||||
for (; *h && *n; ++h, ++n)
|
||||
{
|
||||
if (tolower(*h) != tolower(*n))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*n)
|
||||
{
|
||||
return (char*)haystack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string& ltrim(std::string& s)
|
||||
{
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) { return !std::isspace(c); }));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string& rtrim(std::string& s)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) { return !std::isspace(c); }).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string& trim(std::string& s) { return ltrim(rtrim(s)); }
|
||||
55
src/helper/Helpers.h
Normal file
55
src/helper/Helpers.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_HELPERS_H
|
||||
#define _PLAYERBOT_HELPERS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <functional>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
void split(std::vector<std::string>& dest, std::string const str, char const* delim)
|
||||
{
|
||||
char* pTempStr = strdup(str.c_str());
|
||||
char* pWord = strtok(pTempStr, delim);
|
||||
|
||||
while (pWord != nullptr)
|
||||
{
|
||||
dest.push_back(pWord);
|
||||
pWord = strtok(nullptr, delim);
|
||||
}
|
||||
|
||||
free(pTempStr);
|
||||
}
|
||||
|
||||
std::vector<std::string>& split(std::string const s, char delim, std::vector<std::string>& elems)
|
||||
{
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
|
||||
while (getline(ss, item, delim))
|
||||
{
|
||||
elems.push_back(item);
|
||||
}
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(std::string const s, char delim)
|
||||
{
|
||||
std::vector<std::string> elems;
|
||||
return split(s, delim, elems);
|
||||
}
|
||||
|
||||
#endif
|
||||
95
src/helper/ItemVisitors.cpp
Normal file
95
src/helper/ItemVisitors.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "ItemVisitors.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool FindUsableItemVisitor::Visit(Item* item)
|
||||
{
|
||||
if (bot->CanUseItem(item->GetTemplate()) == EQUIP_ERR_OK)
|
||||
return FindItemVisitor::Visit(item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FindPotionVisitor::Accept(ItemTemplate const* proto)
|
||||
{
|
||||
if (proto->Class == ITEM_CLASS_CONSUMABLE &&
|
||||
(proto->SubClass == ITEM_SUBCLASS_POTION || proto->SubClass == ITEM_SUBCLASS_FLASK))
|
||||
{
|
||||
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
for (uint8 i = 0; i < 3; i++)
|
||||
{
|
||||
if (spellInfo->Effects[i].Effect == effectId)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindMountVisitor::Accept(ItemTemplate const* proto)
|
||||
{
|
||||
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
for (uint8 i = 0; i < 3; i++)
|
||||
{
|
||||
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOUNTED)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindPetVisitor::Accept(ItemTemplate const* proto)
|
||||
{
|
||||
if (proto->Class == ITEM_CLASS_MISC)
|
||||
{
|
||||
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
for (uint8 i = 0; i < 3; i++)
|
||||
{
|
||||
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SUMMON_PET)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FindItemUsageVisitor::FindItemUsageVisitor(Player* bot, ItemUsage usage) : FindUsableItemVisitor(bot), usage(usage)
|
||||
{
|
||||
context = GET_PLAYERBOT_AI(bot)->GetAiObjectContext();
|
||||
};
|
||||
|
||||
bool FindItemUsageVisitor::Accept(ItemTemplate const* proto)
|
||||
{
|
||||
if (AI_VALUE2(ItemUsage, "item usage", proto->ItemId) == usage)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindUsableNamedItemVisitor::Accept(ItemTemplate const* proto)
|
||||
{
|
||||
return proto && !proto->Name1.empty() && strstri(proto->Name1.c_str(), name.c_str());
|
||||
}
|
||||
418
src/helper/ItemVisitors.h
Normal file
418
src/helper/ItemVisitors.h
Normal file
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ITEMVISITORS_H
|
||||
#define _PLAYERBOT_ITEMVISITORS_H
|
||||
|
||||
#include "ChatHelper.h"
|
||||
#include "Common.h"
|
||||
#include "Item.h"
|
||||
#include "ItemUsageValue.h"
|
||||
|
||||
class AiObjectContext;
|
||||
class Player;
|
||||
|
||||
char* strstri(char const* str1, char const* str2);
|
||||
|
||||
enum IterateItemsMask : uint32
|
||||
{
|
||||
ITERATE_ITEMS_IN_BAGS = 1,
|
||||
ITERATE_ITEMS_IN_EQUIP = 2,
|
||||
ITERATE_ITEMS_IN_BANK = 4,
|
||||
ITERATE_ALL_ITEMS = 255
|
||||
};
|
||||
|
||||
class IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
IterateItemsVisitor() {}
|
||||
|
||||
virtual bool Visit(Item* item) = 0;
|
||||
};
|
||||
|
||||
class FindItemVisitor : public IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
FindItemVisitor() : IterateItemsVisitor(), result() {}
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
if (!Accept(item->GetTemplate()))
|
||||
return true;
|
||||
|
||||
result.push_back(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Item*>& GetResult() { return result; }
|
||||
|
||||
protected:
|
||||
virtual bool Accept(ItemTemplate const* proto) = 0;
|
||||
|
||||
private:
|
||||
std::vector<Item*> result;
|
||||
};
|
||||
|
||||
class FindUsableItemVisitor : public FindItemVisitor
|
||||
{
|
||||
public:
|
||||
FindUsableItemVisitor(Player* bot) : FindItemVisitor(), bot(bot) {}
|
||||
|
||||
bool Visit(Item* item) override;
|
||||
|
||||
private:
|
||||
Player* bot;
|
||||
};
|
||||
|
||||
class FindItemsByQualityVisitor : public IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
FindItemsByQualityVisitor(uint32 quality, uint32 count) : IterateItemsVisitor(), quality(quality), count(count) {}
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
if (item->GetTemplate()->Quality != quality)
|
||||
return true;
|
||||
|
||||
if (result.size() >= (size_t)count)
|
||||
return false;
|
||||
|
||||
result.push_back(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Item*>& GetResult() { return result; }
|
||||
|
||||
private:
|
||||
uint32 quality;
|
||||
uint32 count;
|
||||
std::vector<Item*> result;
|
||||
};
|
||||
|
||||
class FindItemsToTradeByQualityVisitor : public FindItemsByQualityVisitor
|
||||
{
|
||||
public:
|
||||
FindItemsToTradeByQualityVisitor(uint32 quality, uint32 count) : FindItemsByQualityVisitor(quality, count) {}
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
if (item->IsSoulBound())
|
||||
return true;
|
||||
|
||||
return FindItemsByQualityVisitor::Visit(item);
|
||||
}
|
||||
};
|
||||
|
||||
class FindItemsToTradeByClassVisitor : public IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
FindItemsToTradeByClassVisitor(uint32 itemClass, uint32 itemSubClass, uint32 count)
|
||||
: IterateItemsVisitor(), itemClass(itemClass), itemSubClass(itemSubClass), count(count)
|
||||
{
|
||||
} // reorder args - whipowill
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
if (item->IsSoulBound())
|
||||
return true;
|
||||
|
||||
if (item->GetTemplate()->Class != itemClass || item->GetTemplate()->SubClass != itemSubClass)
|
||||
return true;
|
||||
|
||||
if (result.size() >= (size_t)count)
|
||||
return false;
|
||||
|
||||
result.push_back(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Item*>& GetResult() { return result; }
|
||||
|
||||
private:
|
||||
uint32 itemClass;
|
||||
uint32 itemSubClass;
|
||||
uint32 count;
|
||||
std::vector<Item*> result;
|
||||
};
|
||||
|
||||
class QueryItemCountVisitor : public IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
QueryItemCountVisitor(uint32 itemId) : count(0), itemId(itemId) {}
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
if (item->GetTemplate()->ItemId == itemId)
|
||||
count += item->GetCount();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 GetCount() { return count; }
|
||||
|
||||
protected:
|
||||
uint32 count;
|
||||
uint32 itemId;
|
||||
};
|
||||
|
||||
class QueryNamedItemCountVisitor : public QueryItemCountVisitor
|
||||
{
|
||||
public:
|
||||
QueryNamedItemCountVisitor(std::string const name) : QueryItemCountVisitor(0), name(name) {}
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
ItemTemplate const* proto = item->GetTemplate();
|
||||
if (proto && proto->Name1.c_str() && strstri(proto->Name1.c_str(), name.c_str()))
|
||||
count += item->GetCount();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string const name;
|
||||
};
|
||||
|
||||
class FindNamedItemVisitor : public FindItemVisitor
|
||||
{
|
||||
public:
|
||||
FindNamedItemVisitor([[maybe_unused]] Player* bot, std::string const name) : FindItemVisitor(), name(name) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override
|
||||
{
|
||||
return proto && proto->Name1.c_str() && strstri(proto->Name1.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
std::string const name;
|
||||
};
|
||||
|
||||
class FindItemByIdVisitor : public FindItemVisitor
|
||||
{
|
||||
public:
|
||||
FindItemByIdVisitor(uint32 id) : FindItemVisitor(), id(id) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override { return proto->ItemId == id; }
|
||||
|
||||
private:
|
||||
uint32 id;
|
||||
};
|
||||
|
||||
class FindItemByIdsVisitor : public FindItemVisitor
|
||||
{
|
||||
public:
|
||||
FindItemByIdsVisitor(ItemIds ids) : FindItemVisitor(), ids(ids) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override { return ids.find(proto->ItemId) != ids.end(); }
|
||||
|
||||
private:
|
||||
ItemIds ids;
|
||||
};
|
||||
|
||||
class ListItemsVisitor : public IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
ListItemsVisitor() : IterateItemsVisitor() {}
|
||||
|
||||
std::map<uint32, uint32> items;
|
||||
std::map<uint32, bool> soulbound;
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
uint32 id = item->GetTemplate()->ItemId;
|
||||
|
||||
if (items.find(id) == items.end())
|
||||
items[id] = 0;
|
||||
|
||||
items[id] += item->GetCount();
|
||||
soulbound[id] = item->IsSoulBound();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ItemCountByQuality : public IterateItemsVisitor
|
||||
{
|
||||
public:
|
||||
ItemCountByQuality() : IterateItemsVisitor()
|
||||
{
|
||||
for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i)
|
||||
count[i] = 0;
|
||||
}
|
||||
|
||||
bool Visit(Item* item) override
|
||||
{
|
||||
++count[item->GetTemplate()->Quality];
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
std::map<uint32, uint32> count;
|
||||
};
|
||||
|
||||
class FindPotionVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindPotionVisitor(Player* bot, uint32 effectId) : FindUsableItemVisitor(bot), effectId(effectId) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override;
|
||||
|
||||
private:
|
||||
uint32 effectId;
|
||||
};
|
||||
|
||||
class FindFoodVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindFoodVisitor(Player* bot, uint32 spellCategory, bool conjured = false)
|
||||
: FindUsableItemVisitor(bot), spellCategory(spellCategory), conjured(conjured)
|
||||
{
|
||||
}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override
|
||||
{
|
||||
return proto->Class == ITEM_CLASS_CONSUMABLE &&
|
||||
(proto->SubClass == ITEM_SUBCLASS_CONSUMABLE || proto->SubClass == ITEM_SUBCLASS_FOOD) &&
|
||||
proto->Spells[0].SpellCategory == spellCategory && (!conjured || proto->IsConjuredConsumable());
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 spellCategory;
|
||||
bool conjured;
|
||||
};
|
||||
|
||||
class FindMountVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindMountVisitor(Player* bot) : FindUsableItemVisitor(bot) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override;
|
||||
|
||||
private:
|
||||
uint32 effectId;
|
||||
};
|
||||
|
||||
class FindPetVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindPetVisitor(Player* bot) : FindUsableItemVisitor(bot) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override;
|
||||
};
|
||||
|
||||
class FindAmmoVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindAmmoVisitor(Player* bot, uint32 weaponType) : FindUsableItemVisitor(bot), weaponType(weaponType) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override
|
||||
{
|
||||
if (proto->Class == ITEM_CLASS_PROJECTILE)
|
||||
{
|
||||
uint32 subClass = 0;
|
||||
switch (weaponType)
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||
subClass = ITEM_SUBCLASS_BULLET;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||
subClass = ITEM_SUBCLASS_ARROW;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!subClass)
|
||||
return false;
|
||||
|
||||
if (proto->SubClass == subClass)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 weaponType;
|
||||
};
|
||||
|
||||
class FindQuestItemVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindQuestItemVisitor(Player* bot) : FindUsableItemVisitor(bot) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override
|
||||
{
|
||||
if (proto->Class == ITEM_CLASS_QUEST)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class FindRecipeVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindRecipeVisitor(Player* bot, SkillType skill = SKILL_NONE) : FindUsableItemVisitor(bot), skill(skill){};
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override
|
||||
{
|
||||
if (proto->Class == ITEM_CLASS_RECIPE)
|
||||
{
|
||||
if (skill == SKILL_NONE)
|
||||
return true;
|
||||
|
||||
switch (proto->SubClass)
|
||||
{
|
||||
case ITEM_SUBCLASS_LEATHERWORKING_PATTERN:
|
||||
return skill == SKILL_LEATHERWORKING;
|
||||
case ITEM_SUBCLASS_TAILORING_PATTERN:
|
||||
return skill == SKILL_TAILORING;
|
||||
case ITEM_SUBCLASS_ENGINEERING_SCHEMATIC:
|
||||
return skill == SKILL_ENGINEERING;
|
||||
case ITEM_SUBCLASS_BLACKSMITHING:
|
||||
return skill == SKILL_BLACKSMITHING;
|
||||
case ITEM_SUBCLASS_COOKING_RECIPE:
|
||||
return skill == SKILL_COOKING;
|
||||
case ITEM_SUBCLASS_ALCHEMY_RECIPE:
|
||||
return skill == SKILL_ALCHEMY;
|
||||
case ITEM_SUBCLASS_FIRST_AID_MANUAL:
|
||||
return skill == SKILL_FIRST_AID;
|
||||
case ITEM_SUBCLASS_ENCHANTING_FORMULA:
|
||||
return skill == SKILL_ENCHANTING;
|
||||
case ITEM_SUBCLASS_FISHING_MANUAL:
|
||||
return skill == SKILL_FISHING;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
SkillType skill;
|
||||
};
|
||||
|
||||
class FindItemUsageVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindItemUsageVisitor(Player* bot, ItemUsage usage = ITEM_USAGE_NONE);
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override;
|
||||
|
||||
private:
|
||||
AiObjectContext* context;
|
||||
ItemUsage usage;
|
||||
};
|
||||
|
||||
class FindUsableNamedItemVisitor : public FindUsableItemVisitor
|
||||
{
|
||||
public:
|
||||
FindUsableNamedItemVisitor(Player* bot, std::string name) : FindUsableItemVisitor(bot), name(name) {}
|
||||
|
||||
bool Accept(ItemTemplate const* proto) override;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
};
|
||||
#endif
|
||||
412
src/helper/LootObjectStack.cpp
Normal file
412
src/helper/LootObjectStack.cpp
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "LootObjectStack.h"
|
||||
|
||||
#include "LootMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Unit.h"
|
||||
|
||||
#define MAX_LOOT_OBJECT_COUNT 200
|
||||
|
||||
LootTarget::LootTarget(ObjectGuid guid) : guid(guid), asOfTime(time(nullptr)) {}
|
||||
|
||||
LootTarget::LootTarget(LootTarget const& other)
|
||||
{
|
||||
guid = other.guid;
|
||||
asOfTime = other.asOfTime;
|
||||
}
|
||||
|
||||
LootTarget& LootTarget::operator=(LootTarget const& other)
|
||||
{
|
||||
if ((void*)this == (void*)&other)
|
||||
return *this;
|
||||
|
||||
guid = other.guid;
|
||||
asOfTime = other.asOfTime;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool LootTarget::operator<(LootTarget const& other) const { return guid < other.guid; }
|
||||
|
||||
void LootTargetList::shrink(time_t fromTime)
|
||||
{
|
||||
for (std::set<LootTarget>::iterator i = begin(); i != end();)
|
||||
{
|
||||
if (i->asOfTime <= fromTime)
|
||||
erase(i++);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
LootObject::LootObject(Player* bot, ObjectGuid guid) : guid(), skillId(SKILL_NONE), reqSkillValue(0), reqItem(0)
|
||||
{
|
||||
Refresh(bot, guid);
|
||||
}
|
||||
|
||||
void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
||||
{
|
||||
skillId = SKILL_NONE;
|
||||
reqSkillValue = 0;
|
||||
reqItem = 0;
|
||||
guid.Clear();
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Creature* creature = botAI->GetCreature(lootGUID);
|
||||
if (creature && creature->getDeathState() == DeathState::Corpse)
|
||||
{
|
||||
if (creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE))
|
||||
guid = lootGUID;
|
||||
|
||||
if (creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE))
|
||||
{
|
||||
skillId = creature->GetCreatureTemplate()->GetRequiredLootSkill();
|
||||
uint32 targetLevel = creature->GetLevel();
|
||||
reqSkillValue = targetLevel < 10 ? 1 : targetLevel < 20 ? (targetLevel - 10) * 10 : targetLevel * 5;
|
||||
if (botAI->HasSkill((SkillType)skillId) && bot->GetSkillValue(skillId) >= reqSkillValue)
|
||||
guid = lootGUID;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject* go = botAI->GetGameObject(lootGUID);
|
||||
if (go && go->isSpawned() && go->GetGoState() == GO_STATE_READY)
|
||||
{
|
||||
bool onlyHasQuestItems = true;
|
||||
bool hasAnyQuestItems = false;
|
||||
|
||||
GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(go->GetEntry());
|
||||
for (int i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
|
||||
{
|
||||
if (!items || i >= items->size())
|
||||
break;
|
||||
|
||||
uint32 itemId = uint32((*items)[i]);
|
||||
if (!itemId)
|
||||
continue;
|
||||
|
||||
hasAnyQuestItems = true;
|
||||
|
||||
if (IsNeededForQuest(bot, itemId))
|
||||
{
|
||||
this->guid = lootGUID;
|
||||
return;
|
||||
}
|
||||
|
||||
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
continue;
|
||||
|
||||
if (proto->Class != ITEM_CLASS_QUEST)
|
||||
{
|
||||
onlyHasQuestItems = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the correct loot table entry
|
||||
uint32 lootEntry = go->GetGOInfo()->GetLootId();
|
||||
if (lootEntry == 0)
|
||||
return;
|
||||
|
||||
// Check the main loot template
|
||||
if (const LootTemplate* lootTemplate = LootTemplates_Gameobject.GetLootFor(lootEntry))
|
||||
{
|
||||
Loot loot;
|
||||
lootTemplate->Process(loot, LootTemplates_Gameobject, 1, bot);
|
||||
|
||||
for (const LootItem& item : loot.items)
|
||||
{
|
||||
uint32 itemId = item.itemid;
|
||||
if (!itemId)
|
||||
continue;
|
||||
|
||||
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
continue;
|
||||
|
||||
if (proto->Class != ITEM_CLASS_QUEST)
|
||||
{
|
||||
onlyHasQuestItems = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// If this item references another loot table, process it
|
||||
if (const LootTemplate* refLootTemplate = LootTemplates_Reference.GetLootFor(itemId))
|
||||
{
|
||||
Loot refLoot;
|
||||
refLootTemplate->Process(refLoot, LootTemplates_Reference, 1, bot);
|
||||
|
||||
for (const LootItem& refItem : refLoot.items)
|
||||
{
|
||||
uint32 refItemId = refItem.itemid;
|
||||
if (!refItemId)
|
||||
continue;
|
||||
|
||||
const ItemTemplate* refProto = sObjectMgr->GetItemTemplate(refItemId);
|
||||
if (!refProto)
|
||||
continue;
|
||||
|
||||
if (refProto->Class != ITEM_CLASS_QUEST)
|
||||
{
|
||||
onlyHasQuestItems = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If gameobject has only quest items that bot doesn’t need, skip it.
|
||||
if (hasAnyQuestItems && onlyHasQuestItems)
|
||||
return;
|
||||
|
||||
// Otherwise, loot it.
|
||||
guid = lootGUID;
|
||||
|
||||
uint32 goId = go->GetEntry();
|
||||
uint32 lockId = go->GetGOInfo()->GetLockId();
|
||||
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
|
||||
if (!lockInfo)
|
||||
return;
|
||||
|
||||
for (uint8 i = 0; i < 8; ++i)
|
||||
{
|
||||
switch (lockInfo->Type[i])
|
||||
{
|
||||
case LOCK_KEY_ITEM:
|
||||
if (lockInfo->Index[i] > 0)
|
||||
{
|
||||
reqItem = lockInfo->Index[i];
|
||||
guid = lootGUID;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCK_KEY_SKILL:
|
||||
if (goId == 13891 || goId == 19535) // Serpentbloom
|
||||
{
|
||||
this->guid = lootGUID;
|
||||
}
|
||||
else if (SkillByLockType(LockType(lockInfo->Index[i])) > 0)
|
||||
{
|
||||
skillId = SkillByLockType(LockType(lockInfo->Index[i]));
|
||||
reqSkillValue = std::max((uint32)1, lockInfo->Skill[i]);
|
||||
guid = lootGUID;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCK_KEY_NONE:
|
||||
guid = lootGUID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LootObject::IsNeededForQuest(Player* bot, uint32 itemId)
|
||||
{
|
||||
for (int qs = 0; qs < MAX_QUEST_LOG_SIZE; ++qs)
|
||||
{
|
||||
uint32 questId = bot->GetQuestSlotQuestId(qs);
|
||||
if (questId == 0)
|
||||
continue;
|
||||
|
||||
QuestStatusData& qData = bot->getQuestStatusMap()[questId];
|
||||
if (qData.Status != QUEST_STATUS_INCOMPLETE)
|
||||
continue;
|
||||
|
||||
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
|
||||
if (!qInfo)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
|
||||
{
|
||||
if (!qInfo->RequiredItemCount[i] || (qInfo->RequiredItemCount[i] - qData.ItemCount[i]) <= 0)
|
||||
continue;
|
||||
|
||||
if (qInfo->RequiredItemId[i] != itemId)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldObject* LootObject::GetWorldObject(Player* bot)
|
||||
{
|
||||
Refresh(bot, guid);
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
Creature* creature = botAI->GetCreature(guid);
|
||||
if (creature && creature->getDeathState() == DeathState::Corpse && creature->IsInWorld())
|
||||
return creature;
|
||||
|
||||
GameObject* go = botAI->GetGameObject(guid);
|
||||
if (go && go->isSpawned() && go->IsInWorld())
|
||||
return go;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LootObject::LootObject(LootObject const& other)
|
||||
{
|
||||
guid = other.guid;
|
||||
skillId = other.skillId;
|
||||
reqSkillValue = other.reqSkillValue;
|
||||
reqItem = other.reqItem;
|
||||
}
|
||||
|
||||
bool LootObject::IsLootPossible(Player* bot)
|
||||
{
|
||||
if (IsEmpty() || !bot)
|
||||
return false;
|
||||
|
||||
WorldObject* worldObj = GetWorldObject(bot); // Store result to avoid multiple calls
|
||||
if (!worldObj)
|
||||
return false;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
if (!botAI)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (reqItem && !bot->HasItemCount(reqItem, 1))
|
||||
return false;
|
||||
|
||||
if (abs(worldObj->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE -2.0f)
|
||||
return false;
|
||||
|
||||
Creature* creature = botAI->GetCreature(guid);
|
||||
if (creature && creature->getDeathState() == DeathState::Corpse)
|
||||
{
|
||||
if (!bot->isAllowedToLoot(creature) && skillId != SKILL_SKINNING)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (skillId == SKILL_NONE)
|
||||
return true;
|
||||
|
||||
if (skillId == SKILL_FISHING)
|
||||
return false;
|
||||
|
||||
if (!botAI->HasSkill((SkillType)skillId))
|
||||
return false;
|
||||
|
||||
if (!reqSkillValue)
|
||||
return true;
|
||||
|
||||
uint32 skillValue = uint32(bot->GetSkillValue(skillId));
|
||||
if (reqSkillValue > skillValue)
|
||||
return false;
|
||||
|
||||
if (skillId == SKILL_MINING &&
|
||||
!bot->HasItemCount(756, 1) &&
|
||||
!bot->HasItemCount(778, 1) &&
|
||||
!bot->HasItemCount(1819, 1) &&
|
||||
!bot->HasItemCount(1893, 1) &&
|
||||
!bot->HasItemCount(1959, 1) &&
|
||||
!bot->HasItemCount(2901, 1) &&
|
||||
!bot->HasItemCount(9465, 1) &&
|
||||
!bot->HasItemCount(20723, 1) &&
|
||||
!bot->HasItemCount(40772, 1) &&
|
||||
!bot->HasItemCount(40892, 1) &&
|
||||
!bot->HasItemCount(40893, 1))
|
||||
{
|
||||
return false; // Bot is missing a mining pick
|
||||
}
|
||||
|
||||
if (skillId == SKILL_SKINNING &&
|
||||
!bot->HasItemCount(7005, 1) &&
|
||||
!bot->HasItemCount(40772, 1) &&
|
||||
!bot->HasItemCount(40893, 1) &&
|
||||
!bot->HasItemCount(12709, 1) &&
|
||||
!bot->HasItemCount(19901, 1))
|
||||
{
|
||||
return false; // Bot is missing a skinning knife
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LootObjectStack::Add(ObjectGuid guid)
|
||||
{
|
||||
if (availableLoot.size() >= MAX_LOOT_OBJECT_COUNT)
|
||||
{
|
||||
availableLoot.shrink(time(nullptr) - 30);
|
||||
}
|
||||
|
||||
if (availableLoot.size() >= MAX_LOOT_OBJECT_COUNT)
|
||||
{
|
||||
availableLoot.clear();
|
||||
}
|
||||
|
||||
if (!availableLoot.insert(guid).second)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LootObjectStack::Remove(ObjectGuid guid)
|
||||
{
|
||||
LootTargetList::iterator i = availableLoot.find(guid);
|
||||
if (i != availableLoot.end())
|
||||
availableLoot.erase(i);
|
||||
}
|
||||
|
||||
void LootObjectStack::Clear() { availableLoot.clear(); }
|
||||
|
||||
bool LootObjectStack::CanLoot(float maxDistance)
|
||||
{
|
||||
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
|
||||
return !ordered.empty();
|
||||
}
|
||||
|
||||
LootObject LootObjectStack::GetLoot(float maxDistance)
|
||||
{
|
||||
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
|
||||
return ordered.empty() ? LootObject() : *ordered.begin();
|
||||
}
|
||||
std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
|
||||
{
|
||||
availableLoot.shrink(time(nullptr) - 30);
|
||||
|
||||
std::map<float, LootObject> sortedMap;
|
||||
LootTargetList safeCopy(availableLoot);
|
||||
for (LootTargetList::iterator i = safeCopy.begin(); i != safeCopy.end(); i++)
|
||||
{
|
||||
ObjectGuid guid = i->guid;
|
||||
LootObject lootObject(bot, guid);
|
||||
if (!lootObject.IsLootPossible(bot)) // Ensure loot object is valid
|
||||
continue;
|
||||
|
||||
WorldObject* worldObj = lootObject.GetWorldObject(bot);
|
||||
if (!worldObj) // Prevent null pointer dereference
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float distance = bot->GetDistance(worldObj);
|
||||
if (!maxDistance || distance <= maxDistance)
|
||||
sortedMap[distance] = lootObject;
|
||||
}
|
||||
|
||||
std::vector<LootObject> result;
|
||||
for (auto& [_, lootObject] : sortedMap)
|
||||
result.push_back(lootObject);
|
||||
|
||||
return result;
|
||||
}
|
||||
86
src/helper/LootObjectStack.h
Normal file
86
src/helper/LootObjectStack.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LOOTOBJECTSTACK_H
|
||||
#define _PLAYERBOT_LOOTOBJECTSTACK_H
|
||||
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
class AiObjectContext;
|
||||
class Player;
|
||||
class WorldObject;
|
||||
|
||||
struct ItemTemplate;
|
||||
|
||||
class LootStrategy
|
||||
{
|
||||
public:
|
||||
LootStrategy() {}
|
||||
virtual ~LootStrategy(){};
|
||||
virtual bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) = 0;
|
||||
virtual std::string const GetName() = 0;
|
||||
};
|
||||
|
||||
class LootObject
|
||||
{
|
||||
public:
|
||||
LootObject() : skillId(0), reqSkillValue(0), reqItem(0) {}
|
||||
LootObject(Player* bot, ObjectGuid guid);
|
||||
LootObject(LootObject const& other);
|
||||
|
||||
bool IsEmpty() { return !guid; }
|
||||
bool IsLootPossible(Player* bot);
|
||||
void Refresh(Player* bot, ObjectGuid guid);
|
||||
WorldObject* GetWorldObject(Player* bot);
|
||||
ObjectGuid guid;
|
||||
|
||||
uint32 skillId;
|
||||
uint32 reqSkillValue;
|
||||
uint32 reqItem;
|
||||
|
||||
private:
|
||||
static bool IsNeededForQuest(Player* bot, uint32 itemId);
|
||||
};
|
||||
|
||||
class LootTarget
|
||||
{
|
||||
public:
|
||||
LootTarget(ObjectGuid guid);
|
||||
LootTarget(LootTarget const& other);
|
||||
|
||||
public:
|
||||
LootTarget& operator=(LootTarget const& other);
|
||||
bool operator<(LootTarget const& other) const;
|
||||
|
||||
public:
|
||||
ObjectGuid guid;
|
||||
time_t asOfTime;
|
||||
};
|
||||
|
||||
class LootTargetList : public std::set<LootTarget>
|
||||
{
|
||||
public:
|
||||
void shrink(time_t fromTime);
|
||||
};
|
||||
|
||||
class LootObjectStack
|
||||
{
|
||||
public:
|
||||
LootObjectStack(Player* bot) : bot(bot) {}
|
||||
|
||||
bool Add(ObjectGuid guid);
|
||||
void Remove(ObjectGuid guid);
|
||||
void Clear();
|
||||
bool CanLoot(float maxDistance);
|
||||
LootObject GetLoot(float maxDistance = 0);
|
||||
|
||||
private:
|
||||
std::vector<LootObject> OrderByDistance(float maxDistance = 0);
|
||||
|
||||
Player* bot;
|
||||
LootTargetList availableLoot;
|
||||
};
|
||||
|
||||
#endif
|
||||
303
src/helper/PerformanceMonitor.cpp
Normal file
303
src/helper/PerformanceMonitor.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "PerformanceMonitor.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
PerformanceMonitorOperation* PerformanceMonitor::start(PerformanceMetric metric, std::string const name,
|
||||
PerformanceStack* stack)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->perfMonEnabled)
|
||||
return nullptr;
|
||||
|
||||
std::string stackName = name;
|
||||
|
||||
if (stack)
|
||||
{
|
||||
if (!stack->empty())
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << stackName << " [";
|
||||
|
||||
for (std::vector<std::string>::reverse_iterator i = stack->rbegin(); i != stack->rend(); ++i)
|
||||
out << *i << (std::next(i) == stack->rend() ? "" : "|");
|
||||
|
||||
out << "]";
|
||||
|
||||
stackName = out.str().c_str();
|
||||
}
|
||||
|
||||
stack->push_back(name);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(lock);
|
||||
PerformanceData* pd = data[metric][stackName];
|
||||
if (!pd)
|
||||
{
|
||||
pd = new PerformanceData();
|
||||
pd->minTime = 0;
|
||||
pd->maxTime = 0;
|
||||
pd->totalTime = 0;
|
||||
pd->count = 0;
|
||||
data[metric][stackName] = pd;
|
||||
}
|
||||
|
||||
return new PerformanceMonitorOperation(pd, name, stack);
|
||||
}
|
||||
|
||||
void PerformanceMonitor::PrintStats(bool perTick, bool fullStack)
|
||||
{
|
||||
if (data.empty())
|
||||
return;
|
||||
|
||||
if (!perTick)
|
||||
{
|
||||
float updateAITotalTime = 0;
|
||||
for (auto& map : data[PERF_MON_TOTAL])
|
||||
if (map.first.find("PlayerbotAI::UpdateAIInternal") != std::string::npos)
|
||||
updateAITotalTime += map.second->totalTime;
|
||||
|
||||
LOG_INFO(
|
||||
"playerbots",
|
||||
"--------------------------------------[TOTAL BOT]------------------------------------------------------");
|
||||
LOG_INFO("playerbots",
|
||||
"percentage time | min .. max ( avg of count) - type : name");
|
||||
LOG_INFO(
|
||||
"playerbots",
|
||||
"-------------------------------------------------------------------------------------------------------");
|
||||
|
||||
for (std::map<PerformanceMetric, std::map<std::string, PerformanceData*>>::iterator i = data.begin();
|
||||
i != data.end(); ++i)
|
||||
{
|
||||
std::map<std::string, PerformanceData*> pdMap = i->second;
|
||||
|
||||
std::string key;
|
||||
switch (i->first)
|
||||
{
|
||||
case PERF_MON_TRIGGER:
|
||||
key = "Trigger";
|
||||
break;
|
||||
case PERF_MON_VALUE:
|
||||
key = "Value";
|
||||
break;
|
||||
case PERF_MON_ACTION:
|
||||
key = "Action";
|
||||
break;
|
||||
case PERF_MON_RNDBOT:
|
||||
key = "RndBot";
|
||||
break;
|
||||
case PERF_MON_TOTAL:
|
||||
key = "Total";
|
||||
break;
|
||||
default:
|
||||
key = "?";
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<std::string> names;
|
||||
|
||||
for (std::map<std::string, PerformanceData*>::iterator j = pdMap.begin(); j != pdMap.end(); ++j)
|
||||
{
|
||||
if (key == "Total" && j->first.find("PlayerbotAI::UpdateAIInternal") == std::string::npos)
|
||||
continue;
|
||||
|
||||
names.push_back(j->first);
|
||||
}
|
||||
|
||||
std::sort(names.begin(), names.end(),
|
||||
[pdMap](std::string const i, std::string const j)
|
||||
{ return pdMap.at(i)->totalTime < pdMap.at(j)->totalTime; });
|
||||
|
||||
uint64 typeTotalTime = 0;
|
||||
uint64 typeMinTime = 0xffffffffu;
|
||||
uint64 typeMaxTime = 0;
|
||||
uint32 typeCount = 0;
|
||||
for (auto& name : names)
|
||||
{
|
||||
PerformanceData* pd = pdMap[name];
|
||||
typeTotalTime += pd->totalTime;
|
||||
typeCount += pd->count;
|
||||
if (typeMinTime > pd->minTime)
|
||||
typeMinTime = pd->minTime;
|
||||
if (typeMaxTime < pd->maxTime)
|
||||
typeMaxTime = pd->maxTime;
|
||||
float perc = (float)pd->totalTime / updateAITotalTime * 100.0f;
|
||||
float time = (float)pd->totalTime / 1000000.0f;
|
||||
float minTime = (float)pd->minTime / 1000.0f;
|
||||
float maxTime = (float)pd->maxTime / 1000.0f;
|
||||
float avg = (float)pd->totalTime / (float)pd->count / 1000.0f;
|
||||
std::string disName = name;
|
||||
if (!fullStack && disName.find("|") != std::string::npos)
|
||||
disName = disName.substr(0, disName.find("|")) + "]";
|
||||
|
||||
if (perc >= 0.1f || avg >= 0.25f || pd->maxTime > 1000)
|
||||
{
|
||||
LOG_INFO("playerbots",
|
||||
"{:7.3f}% {:10.3f}s | {:7.1f} .. {:7.1f} ({:10.3f} of {:10d}) - {:6} : {}", perc, time,
|
||||
minTime, maxTime, avg, pd->count, key.c_str(), disName.c_str());
|
||||
}
|
||||
}
|
||||
float tPerc = (float)typeTotalTime / (float)updateAITotalTime * 100.0f;
|
||||
float tTime = (float)typeTotalTime / 1000000.0f;
|
||||
float tMinTime = (float)typeMinTime / 1000.0f;
|
||||
float tMaxTime = (float)typeMaxTime / 1000.0f;
|
||||
float tAvg = (float)typeTotalTime / (float)typeCount / 1000.0f;
|
||||
LOG_INFO("playerbots", "{:7.3f}% {:10.3f}s | {:7.1f} .. {:7.1f} ({:10.3f} of {:10d}) - {:6} : {}", tPerc,
|
||||
tTime, tMinTime, tMaxTime, tAvg, typeCount, key.c_str(), "Total");
|
||||
LOG_INFO("playerbots", " ");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float fullTickCount = data[PERF_MON_TOTAL]["PlayerbotAIBase::FullTick"]->count;
|
||||
float fullTickTotalTime = data[PERF_MON_TOTAL]["PlayerbotAIBase::FullTick"]->totalTime;
|
||||
|
||||
LOG_INFO(
|
||||
"playerbots",
|
||||
"---------------------------------------[PER TICK]------------------------------------------------------");
|
||||
LOG_INFO("playerbots",
|
||||
"percentage time | min .. max ( avg of count) - type : name");
|
||||
LOG_INFO(
|
||||
"playerbots",
|
||||
"-------------------------------------------------------------------------------------------------------");
|
||||
|
||||
for (std::map<PerformanceMetric, std::map<std::string, PerformanceData*>>::iterator i = data.begin();
|
||||
i != data.end(); ++i)
|
||||
{
|
||||
std::map<std::string, PerformanceData*> pdMap = i->second;
|
||||
|
||||
std::string key;
|
||||
switch (i->first)
|
||||
{
|
||||
case PERF_MON_TRIGGER:
|
||||
key = "Trigger";
|
||||
break;
|
||||
case PERF_MON_VALUE:
|
||||
key = "Value";
|
||||
break;
|
||||
case PERF_MON_ACTION:
|
||||
key = "Action";
|
||||
break;
|
||||
case PERF_MON_RNDBOT:
|
||||
key = "RndBot";
|
||||
break;
|
||||
case PERF_MON_TOTAL:
|
||||
key = "Total";
|
||||
break;
|
||||
default:
|
||||
key = "?";
|
||||
}
|
||||
|
||||
std::vector<std::string> names;
|
||||
|
||||
for (std::map<std::string, PerformanceData*>::iterator j = pdMap.begin(); j != pdMap.end(); ++j)
|
||||
{
|
||||
names.push_back(j->first);
|
||||
}
|
||||
|
||||
std::sort(names.begin(), names.end(),
|
||||
[pdMap](std::string const i, std::string const j)
|
||||
{ return pdMap.at(i)->totalTime < pdMap.at(j)->totalTime; });
|
||||
|
||||
uint64 typeTotalTime = 0;
|
||||
uint64 typeMinTime = 0xffffffffu;
|
||||
uint64 typeMaxTime = 0;
|
||||
uint32 typeCount = 0;
|
||||
for (auto& name : names)
|
||||
{
|
||||
PerformanceData* pd = pdMap[name];
|
||||
typeTotalTime += pd->totalTime;
|
||||
typeCount += pd->count;
|
||||
if (typeMinTime > pd->minTime)
|
||||
typeMinTime = pd->minTime;
|
||||
if (typeMaxTime < pd->maxTime)
|
||||
typeMaxTime = pd->maxTime;
|
||||
float perc = (float)pd->totalTime / fullTickTotalTime * 100.0f;
|
||||
float time = (float)pd->totalTime / fullTickCount / 1000.0f;
|
||||
float minTime = (float)pd->minTime / 1000.0f;
|
||||
float maxTime = (float)pd->maxTime / 1000.0f;
|
||||
float avg = (float)pd->totalTime / (float)pd->count / 1000.0f;
|
||||
float amount = (float)pd->count / fullTickCount;
|
||||
std::string disName = name;
|
||||
if (!fullStack && disName.find("|") != std::string::npos)
|
||||
disName = disName.substr(0, disName.find("|")) + "]";
|
||||
if (perc >= 0.1f || avg >= 0.25f || pd->maxTime > 1000)
|
||||
{
|
||||
LOG_INFO("playerbots",
|
||||
"{:7.3f}% {:9.3f}ms | {:7.1f} .. {:7.1f} ({:10.3f} of {:10.2f}) - {:6} : {}", perc,
|
||||
time, minTime, maxTime, avg, amount, key.c_str(), disName.c_str());
|
||||
}
|
||||
}
|
||||
if (i->first != PERF_MON_TOTAL)
|
||||
{
|
||||
float tPerc = (float)typeTotalTime / (float)fullTickTotalTime * 100.0f;
|
||||
float tTime = (float)typeTotalTime / fullTickCount / 1000.0f;
|
||||
float tMinTime = (float)typeMinTime / 1000.0f;
|
||||
float tMaxTime = (float)typeMaxTime / 1000.0f;
|
||||
float tAvg = (float)typeTotalTime / (float)typeCount / 1000.0f;
|
||||
float tAmount = (float)typeCount / fullTickCount;
|
||||
LOG_INFO("playerbots", "{:7.3f}% {:9.3f}ms | {:7.1f} .. {:7.1f} ({:10.3f} of {:10.2f}) - {:6} : {}",
|
||||
tPerc, tTime, tMinTime, tMaxTime, tAvg, tAmount, key.c_str(), "Total");
|
||||
}
|
||||
LOG_INFO("playerbots", " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PerformanceMonitor::Reset()
|
||||
{
|
||||
for (std::map<PerformanceMetric, std::map<std::string, PerformanceData*>>::iterator i = data.begin();
|
||||
i != data.end(); ++i)
|
||||
{
|
||||
std::map<std::string, PerformanceData*> pdMap = i->second;
|
||||
for (std::map<std::string, PerformanceData*>::iterator j = pdMap.begin(); j != pdMap.end(); ++j)
|
||||
{
|
||||
PerformanceData* pd = j->second;
|
||||
std::lock_guard<std::mutex> guard(pd->lock);
|
||||
pd->minTime = 0;
|
||||
pd->maxTime = 0;
|
||||
pd->totalTime = 0;
|
||||
pd->count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PerformanceMonitorOperation::PerformanceMonitorOperation(PerformanceData* data, std::string const name,
|
||||
PerformanceStack* stack)
|
||||
: data(data), name(name), stack(stack)
|
||||
{
|
||||
started = (std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()))
|
||||
.time_since_epoch();
|
||||
}
|
||||
|
||||
void PerformanceMonitorOperation::finish()
|
||||
{
|
||||
std::chrono::microseconds finished =
|
||||
(std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()))
|
||||
.time_since_epoch();
|
||||
uint64 elapsed = (finished - started).count();
|
||||
|
||||
std::lock_guard<std::mutex> guard(data->lock);
|
||||
if (elapsed > 0)
|
||||
{
|
||||
if (!data->minTime || data->minTime > elapsed)
|
||||
data->minTime = elapsed;
|
||||
|
||||
if (!data->maxTime || data->maxTime < elapsed)
|
||||
data->maxTime = elapsed;
|
||||
|
||||
data->totalTime += elapsed;
|
||||
}
|
||||
|
||||
++data->count;
|
||||
|
||||
if (stack)
|
||||
{
|
||||
stack->erase(std::remove(stack->begin(), stack->end(), name), stack->end());
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
74
src/helper/PerformanceMonitor.h
Normal file
74
src/helper/PerformanceMonitor.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_PERFORMANCEMONITOR_H
|
||||
#define _PLAYERBOT_PERFORMANCEMONITOR_H
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
typedef std::vector<std::string> PerformanceStack;
|
||||
|
||||
struct PerformanceData
|
||||
{
|
||||
uint64 minTime;
|
||||
uint64 maxTime;
|
||||
uint64 totalTime;
|
||||
uint32 count;
|
||||
std::mutex lock;
|
||||
};
|
||||
|
||||
enum PerformanceMetric
|
||||
{
|
||||
PERF_MON_TRIGGER,
|
||||
PERF_MON_VALUE,
|
||||
PERF_MON_ACTION,
|
||||
PERF_MON_RNDBOT,
|
||||
PERF_MON_TOTAL
|
||||
};
|
||||
|
||||
class PerformanceMonitorOperation
|
||||
{
|
||||
public:
|
||||
PerformanceMonitorOperation(PerformanceData* data, std::string const name, PerformanceStack* stack);
|
||||
void finish();
|
||||
|
||||
private:
|
||||
PerformanceData* data;
|
||||
std::string const name;
|
||||
PerformanceStack* stack;
|
||||
std::chrono::microseconds started;
|
||||
};
|
||||
|
||||
class PerformanceMonitor
|
||||
{
|
||||
public:
|
||||
PerformanceMonitor(){};
|
||||
virtual ~PerformanceMonitor(){};
|
||||
static PerformanceMonitor* instance()
|
||||
{
|
||||
static PerformanceMonitor instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
public:
|
||||
PerformanceMonitorOperation* start(PerformanceMetric metric, std::string const name,
|
||||
PerformanceStack* stack = nullptr);
|
||||
void PrintStats(bool perTick = false, bool fullStack = false);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
std::map<PerformanceMetric, std::map<std::string, PerformanceData*> > data;
|
||||
std::mutex lock;
|
||||
};
|
||||
|
||||
#define sPerformanceMonitor PerformanceMonitor::instance()
|
||||
|
||||
#endif
|
||||
97
src/helper/PlaceholderHelper.cpp
Normal file
97
src/helper/PlaceholderHelper.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "PlaceholderHelper.h"
|
||||
|
||||
#include "AiFactory.h"
|
||||
#include "PlayerbotTextMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Util.h"
|
||||
|
||||
void PlaceholderHelper::MapDungeon(PlaceholderMap& placeholders, DungeonSuggestion const* dungeonSuggestion,
|
||||
Player* bot)
|
||||
{
|
||||
std::ostringstream out;
|
||||
Insertion insertion = {out, dungeonSuggestion};
|
||||
InsertDungeonName(insertion);
|
||||
InsertDungeonStrategy(insertion);
|
||||
InsertDifficulty(insertion, bot);
|
||||
|
||||
placeholders["%dungeon"] = out.str();
|
||||
}
|
||||
|
||||
void PlaceholderHelper::MapRole(PlaceholderMap& placeholders, Player* bot)
|
||||
{
|
||||
BotRoles const role = AiFactory::GetPlayerRoles(bot);
|
||||
std::string roleText;
|
||||
switch (role)
|
||||
{
|
||||
case BOT_ROLE_TANK:
|
||||
roleText = "Tank";
|
||||
break;
|
||||
case BOT_ROLE_HEALER:
|
||||
roleText = "Healer";
|
||||
break;
|
||||
case BOT_ROLE_DPS:
|
||||
roleText = "DPS";
|
||||
break;
|
||||
case BOT_ROLE_NONE:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
bool const hasRole = !roleText.empty();
|
||||
if (hasRole)
|
||||
{
|
||||
placeholders["%role"] = roleText;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaceholderHelper::InsertDungeonName(Insertion& insertion)
|
||||
{
|
||||
std::string name = insertion.dungeonSuggestion->name;
|
||||
bool const hasAbbrevation = !insertion.dungeonSuggestion->abbrevation.empty();
|
||||
if (hasAbbrevation)
|
||||
{
|
||||
name = insertion.dungeonSuggestion->abbrevation;
|
||||
}
|
||||
|
||||
insertion.out << "|c00b000b0" << name << "|r";
|
||||
}
|
||||
|
||||
void PlaceholderHelper::InsertDungeonStrategy(Insertion& insertion)
|
||||
{
|
||||
bool const hasStrategy = !insertion.dungeonSuggestion->strategy.empty();
|
||||
bool const isRandomlyUsingStrategy = urand(0, 1);
|
||||
if (hasStrategy && isRandomlyUsingStrategy)
|
||||
{
|
||||
std::string strategy = insertion.dungeonSuggestion->strategy;
|
||||
insertion.out << " " + strategy;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaceholderHelper::InsertDifficulty(Insertion& insertion, [[maybe_unused]] Player* bot)
|
||||
{
|
||||
bool const hasHeroic = insertion.dungeonSuggestion->difficulty == DUNGEON_DIFFICULTY_HEROIC;
|
||||
std::string difficultyText;
|
||||
if (hasHeroic)
|
||||
{
|
||||
bool const isRandomlyNormal = urand(0, 1);
|
||||
bool const isRandomlyHeroic = urand(0, 1);
|
||||
std::vector<std::string> normalAbbrevations = {"Normal", "N"};
|
||||
std::vector<std::string> heroicAbbrevations = {"Heroic", "HC", "H"};
|
||||
uint32 const randomAbbrevationIndex = urand(0, 1);
|
||||
if (isRandomlyNormal)
|
||||
{
|
||||
difficultyText = normalAbbrevations[randomAbbrevationIndex];
|
||||
}
|
||||
else if (isRandomlyHeroic)
|
||||
{
|
||||
difficultyText = heroicAbbrevations[randomAbbrevationIndex];
|
||||
}
|
||||
|
||||
insertion.out << " " << difficultyText;
|
||||
}
|
||||
}
|
||||
35
src/helper/PlaceholderHelper.h
Normal file
35
src/helper/PlaceholderHelper.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_PLACEHOLDERHELPER_H
|
||||
#define _PLAYERBOT_PLACEHOLDERHELPER_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotDungeonSuggestionMgr.h"
|
||||
|
||||
typedef std::map<std::string, std::string> PlaceholderMap;
|
||||
|
||||
class PlaceholderHelper
|
||||
{
|
||||
public:
|
||||
static void MapRole(PlaceholderMap& placeholders, Player* bot);
|
||||
static void MapDungeon(PlaceholderMap& placeholders, DungeonSuggestion const* dungeonSuggestion, Player* bot);
|
||||
|
||||
private:
|
||||
struct Insertion
|
||||
{
|
||||
std::ostringstream& out;
|
||||
DungeonSuggestion const* dungeonSuggestion;
|
||||
};
|
||||
|
||||
static void InsertDungeonName(Insertion& insertion);
|
||||
static void InsertDungeonStrategy(Insertion& insertion);
|
||||
static void InsertDifficulty(Insertion& insertion, Player* bot);
|
||||
};
|
||||
|
||||
#endif
|
||||
48
src/helper/PlayerbotDungeonSuggestionMgr.cpp
Normal file
48
src/helper/PlayerbotDungeonSuggestionMgr.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "PlayerbotDungeonSuggestionMgr.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::vector<DungeonSuggestion> const PlayerbotDungeonSuggestionMgr::GetDungeonSuggestions()
|
||||
{
|
||||
return m_dungeonSuggestions;
|
||||
}
|
||||
|
||||
void PlayerbotDungeonSuggestionMgr::LoadDungeonSuggestions()
|
||||
{
|
||||
LOG_INFO("server.loading", "Loading playerbots dungeon suggestions...");
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
uint32 count = 0;
|
||||
auto statement = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_DUNGEON_SUGGESTION);
|
||||
uint8 const expansion = sWorld->getIntConfig(CONFIG_EXPANSION);
|
||||
statement->SetData(0, expansion);
|
||||
|
||||
PreparedQueryResult result = PlayerbotsDatabase.Query(statement);
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
std::string const name = fields[0].Get<std::string>();
|
||||
uint8 const difficulty = fields[1].Get<uint8>();
|
||||
uint8 const min_level = fields[2].Get<uint8>();
|
||||
uint8 const max_level = fields[3].Get<uint8>();
|
||||
std::string const abbrevation = fields[4].Get<std::string>();
|
||||
std::string const strategy = fields[5].Get<std::string>();
|
||||
|
||||
DungeonSuggestion const row = {
|
||||
name, static_cast<Difficulty>(difficulty), min_level, max_level, abbrevation, strategy};
|
||||
|
||||
m_dungeonSuggestions.push_back(row);
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", "{} playerbots dungeon suggestions loaded in {} ms", count,
|
||||
GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
45
src/helper/PlayerbotDungeonSuggestionMgr.h
Normal file
45
src/helper/PlayerbotDungeonSuggestionMgr.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H
|
||||
#define _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
#include "DBCEnums.h"
|
||||
|
||||
struct DungeonSuggestion
|
||||
{
|
||||
std::string name;
|
||||
Difficulty difficulty;
|
||||
uint8 min_level;
|
||||
uint8 max_level;
|
||||
std::string abbrevation;
|
||||
std::string strategy;
|
||||
};
|
||||
|
||||
class PlayerbotDungeonSuggestionMgr
|
||||
{
|
||||
public:
|
||||
PlayerbotDungeonSuggestionMgr(){};
|
||||
~PlayerbotDungeonSuggestionMgr(){};
|
||||
static PlayerbotDungeonSuggestionMgr* instance()
|
||||
{
|
||||
static PlayerbotDungeonSuggestionMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void LoadDungeonSuggestions();
|
||||
std::vector<DungeonSuggestion> const GetDungeonSuggestions();
|
||||
|
||||
private:
|
||||
std::vector<DungeonSuggestion> m_dungeonSuggestions;
|
||||
};
|
||||
|
||||
#define sPlayerbotDungeonSuggestionMgr PlayerbotDungeonSuggestionMgr::instance()
|
||||
|
||||
#endif
|
||||
209
src/helper/PlayerbotTextMgr.cpp
Normal file
209
src/helper/PlayerbotTextMgr.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "PlayerbotTextMgr.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
|
||||
void PlayerbotTextMgr::replaceAll(std::string& str, const std::string& from, const std::string& to)
|
||||
{
|
||||
if (from.empty())
|
||||
return;
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
|
||||
{
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotTextMgr::LoadBotTexts()
|
||||
{
|
||||
LOG_INFO("playerbots", "Loading playerbots texts...");
|
||||
|
||||
uint32 count = 0;
|
||||
if (PreparedQueryResult result =
|
||||
PlayerbotsDatabase.Query(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_TEXT)))
|
||||
{
|
||||
do
|
||||
{
|
||||
std::map<uint32, std::string> text;
|
||||
Field* fields = result->Fetch();
|
||||
std::string name = fields[0].Get<std::string>();
|
||||
text[0] = fields[1].Get<std::string>();
|
||||
uint8 sayType = fields[2].Get<uint8>();
|
||||
uint8 replyType = fields[3].Get<uint8>();
|
||||
for (uint8 i = 1; i < MAX_LOCALES; ++i)
|
||||
{
|
||||
text[i] = fields[i + 3].Get<std::string>();
|
||||
}
|
||||
|
||||
botTexts[name].push_back(BotTextEntry(name, text, sayType, replyType));
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
LOG_INFO("playerbots", "{} playerbots texts loaded", count);
|
||||
}
|
||||
|
||||
void PlayerbotTextMgr::LoadBotTextChance()
|
||||
{
|
||||
if (botTextChance.empty())
|
||||
{
|
||||
QueryResult results = PlayerbotsDatabase.Query("SELECT name, probability FROM ai_playerbot_texts_chance");
|
||||
if (results)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
std::string name = fields[0].Get<std::string>();
|
||||
uint32 probability = fields[1].Get<uint32>();
|
||||
|
||||
botTextChance[name] = probability;
|
||||
} while (results->NextRow());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// general texts
|
||||
|
||||
std::string PlayerbotTextMgr::GetBotText(std::string name)
|
||||
{
|
||||
if (botTexts.empty())
|
||||
{
|
||||
LOG_ERROR("playerbots", "Can't get bot text {}! No bots texts loaded!", name);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (botTexts[name].empty())
|
||||
{
|
||||
LOG_ERROR("playerbots", "Can't get bot text {}! No bots texts for this name!", name);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<BotTextEntry>& list = botTexts[name];
|
||||
BotTextEntry textEntry = list[urand(0, list.size() - 1)];
|
||||
return !textEntry.m_text[GetLocalePriority()].empty() ? textEntry.m_text[GetLocalePriority()] : textEntry.m_text[0];
|
||||
}
|
||||
|
||||
std::string PlayerbotTextMgr::GetBotText(std::string name, std::map<std::string, std::string> placeholders)
|
||||
{
|
||||
std::string botText = GetBotText(name);
|
||||
if (botText.empty())
|
||||
return "";
|
||||
|
||||
for (std::map<std::string, std::string>::iterator i = placeholders.begin(); i != placeholders.end(); ++i)
|
||||
replaceAll(botText, i->first, i->second);
|
||||
|
||||
return botText;
|
||||
}
|
||||
|
||||
// chat replies
|
||||
|
||||
std::string PlayerbotTextMgr::GetBotText(ChatReplyType replyType, std::map<std::string, std::string> placeholders)
|
||||
{
|
||||
if (botTexts.empty())
|
||||
{
|
||||
LOG_ERROR("playerbots", "Can't get bot text reply {}! No bots texts loaded!", replyType);
|
||||
return "";
|
||||
}
|
||||
if (botTexts["reply"].empty())
|
||||
{
|
||||
LOG_ERROR("playerbots", "Can't get bot text reply {}! No bots texts replies!", replyType);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<BotTextEntry>& list = botTexts["reply"];
|
||||
std::vector<BotTextEntry> proper_list;
|
||||
for (auto text : list)
|
||||
{
|
||||
if (text.m_replyType == replyType)
|
||||
proper_list.push_back(text);
|
||||
}
|
||||
|
||||
if (proper_list.empty())
|
||||
return "";
|
||||
|
||||
BotTextEntry textEntry = proper_list[urand(0, proper_list.size() - 1)];
|
||||
std::string botText =
|
||||
!textEntry.m_text[GetLocalePriority()].empty() ? textEntry.m_text[GetLocalePriority()] : textEntry.m_text[0];
|
||||
for (auto& placeholder : placeholders)
|
||||
replaceAll(botText, placeholder.first, placeholder.second);
|
||||
|
||||
return botText;
|
||||
}
|
||||
|
||||
std::string PlayerbotTextMgr::GetBotText(ChatReplyType replyType, std::string name)
|
||||
{
|
||||
std::map<std::string, std::string> placeholders;
|
||||
placeholders["%s"] = name;
|
||||
|
||||
return GetBotText(replyType, placeholders);
|
||||
}
|
||||
|
||||
// probabilities
|
||||
|
||||
bool PlayerbotTextMgr::rollTextChance(std::string name)
|
||||
{
|
||||
if (!botTextChance[name])
|
||||
return true;
|
||||
|
||||
return urand(0, 100) < botTextChance[name];
|
||||
}
|
||||
|
||||
bool PlayerbotTextMgr::GetBotText(std::string name, std::string& text)
|
||||
{
|
||||
if (!rollTextChance(name))
|
||||
return false;
|
||||
|
||||
text = GetBotText(name);
|
||||
return !text.empty();
|
||||
}
|
||||
|
||||
bool PlayerbotTextMgr::GetBotText(std::string name, std::string& text, std::map<std::string, std::string> placeholders)
|
||||
{
|
||||
if (!rollTextChance(name))
|
||||
return false;
|
||||
|
||||
text = GetBotText(name, placeholders);
|
||||
return !text.empty();
|
||||
}
|
||||
|
||||
void PlayerbotTextMgr::AddLocalePriority(uint32 locale)
|
||||
{
|
||||
if (!locale)
|
||||
return;
|
||||
|
||||
botTextLocalePriority[locale]++;
|
||||
}
|
||||
|
||||
uint32 PlayerbotTextMgr::GetLocalePriority()
|
||||
{
|
||||
uint32 topLocale = 0;
|
||||
|
||||
// if no real players online, reset top locale
|
||||
if (!sWorldSessionMgr->GetActiveSessionCount())
|
||||
{
|
||||
ResetLocalePriority();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < MAX_LOCALES; ++i)
|
||||
{
|
||||
if (botTextLocalePriority[i] > topLocale)
|
||||
topLocale = i;
|
||||
}
|
||||
|
||||
return topLocale;
|
||||
}
|
||||
|
||||
void PlayerbotTextMgr::ResetLocalePriority()
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_LOCALES; ++i)
|
||||
{
|
||||
botTextLocalePriority[i] = 0;
|
||||
}
|
||||
}
|
||||
103
src/helper/PlayerbotTextMgr.h
Normal file
103
src/helper/PlayerbotTextMgr.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_PLAYERBOTTEXTMGR_H
|
||||
#define _PLAYERBOT_PLAYERBOTTEXTMGR_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#define BOT_TEXT1(name) sPlayerbotTextMgr->GetBotText(name)
|
||||
#define BOT_TEXT2(name, replace) sPlayerbotTextMgr->GetBotText(name, replace)
|
||||
|
||||
struct BotTextEntry
|
||||
{
|
||||
BotTextEntry(std::string name, std::map<uint32, std::string> text, uint32 say_type, uint32 reply_type)
|
||||
: m_name(name), m_text(text), m_sayType(say_type), m_replyType(reply_type)
|
||||
{
|
||||
}
|
||||
std::string m_name;
|
||||
std::map<uint32, std::string> m_text;
|
||||
uint32 m_sayType;
|
||||
uint32 m_replyType;
|
||||
};
|
||||
|
||||
struct ChatReplyData
|
||||
{
|
||||
ChatReplyData(uint32 guid, uint32 type, std::string chat) : m_type(type), m_guid(guid), m_chat(chat) {}
|
||||
uint32 m_type, m_guid = 0;
|
||||
std::string m_chat = "";
|
||||
};
|
||||
|
||||
struct ChatQueuedReply
|
||||
{
|
||||
ChatQueuedReply(uint32 type, uint32 guid1, uint32 guid2, std::string msg, std::string chanName, std::string name,
|
||||
time_t time)
|
||||
: m_type(type), m_guid1(guid1), m_guid2(guid2), m_msg(msg), m_chanName(chanName), m_name(name), m_time(time)
|
||||
{
|
||||
}
|
||||
uint32 m_type;
|
||||
uint32 m_guid1;
|
||||
uint32 m_guid2;
|
||||
std::string m_msg;
|
||||
std::string m_chanName;
|
||||
std::string m_name;
|
||||
time_t m_time;
|
||||
};
|
||||
|
||||
enum ChatReplyType
|
||||
{
|
||||
REPLY_NOT_UNDERSTAND,
|
||||
REPLY_GRUDGE,
|
||||
REPLY_VICTIM,
|
||||
REPLY_ATTACKER,
|
||||
REPLY_HELLO,
|
||||
REPLY_NAME,
|
||||
REPLY_ADMIN_ABUSE
|
||||
};
|
||||
|
||||
class PlayerbotTextMgr
|
||||
{
|
||||
public:
|
||||
PlayerbotTextMgr()
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_LOCALES; ++i)
|
||||
{
|
||||
botTextLocalePriority[i] = 0;
|
||||
}
|
||||
};
|
||||
virtual ~PlayerbotTextMgr(){};
|
||||
static PlayerbotTextMgr* instance()
|
||||
{
|
||||
static PlayerbotTextMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
std::string GetBotText(std::string name, std::map<std::string, std::string> placeholders);
|
||||
std::string GetBotText(std::string name);
|
||||
std::string GetBotText(ChatReplyType replyType, std::map<std::string, std::string> placeholders);
|
||||
std::string GetBotText(ChatReplyType replyType, std::string name);
|
||||
bool GetBotText(std::string name, std::string& text);
|
||||
bool GetBotText(std::string name, std::string& text, std::map<std::string, std::string> placeholders);
|
||||
void LoadBotTexts();
|
||||
void LoadBotTextChance();
|
||||
static void replaceAll(std::string& str, const std::string& from, const std::string& to);
|
||||
bool rollTextChance(std::string text);
|
||||
|
||||
uint32 GetLocalePriority();
|
||||
void AddLocalePriority(uint32 locale);
|
||||
void ResetLocalePriority();
|
||||
|
||||
private:
|
||||
std::map<std::string, std::vector<BotTextEntry>> botTexts;
|
||||
std::map<std::string, uint32> botTextChance;
|
||||
uint32 botTextLocalePriority[MAX_LOCALES];
|
||||
};
|
||||
|
||||
#define sPlayerbotTextMgr PlayerbotTextMgr::instance()
|
||||
|
||||
#endif
|
||||
75
src/helper/ServerFacade.cpp
Normal file
75
src/helper/ServerFacade.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "ServerFacade.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "TargetedMovementGenerator.h"
|
||||
|
||||
float ServerFacade::GetDistance2d(Unit* unit, WorldObject* wo)
|
||||
{
|
||||
ASSERT_NOTNULL(unit);
|
||||
ASSERT_NOTNULL(wo);
|
||||
|
||||
float dist = unit->GetDistance2d(wo);
|
||||
return std::round(dist * 10.0f) / 10.0f;
|
||||
}
|
||||
|
||||
float ServerFacade::GetDistance2d(Unit* unit, float x, float y)
|
||||
{
|
||||
float dist = unit->GetDistance2d(x, y);
|
||||
return std::round(dist * 10.0f) / 10.0f;
|
||||
}
|
||||
|
||||
bool ServerFacade::IsDistanceLessThan(float dist1, float dist2)
|
||||
{
|
||||
// return dist1 - dist2 < sPlayerbotAIConfig->targetPosRecalcDistance;
|
||||
return dist1 < dist2;
|
||||
}
|
||||
|
||||
bool ServerFacade::IsDistanceGreaterThan(float dist1, float dist2)
|
||||
{
|
||||
// return dist1 - dist2 > sPlayerbotAIConfig->targetPosRecalcDistance;
|
||||
return dist1 > dist2;
|
||||
}
|
||||
|
||||
bool ServerFacade::IsDistanceGreaterOrEqualThan(float dist1, float dist2) { return !IsDistanceLessThan(dist1, dist2); }
|
||||
|
||||
bool ServerFacade::IsDistanceLessOrEqualThan(float dist1, float dist2) { return !IsDistanceGreaterThan(dist1, dist2); }
|
||||
|
||||
void ServerFacade::SetFacingTo(Player* bot, WorldObject* wo, bool force)
|
||||
{
|
||||
float angle = bot->GetAngle(wo);
|
||||
// if (!force && bot->isMoving())
|
||||
// bot->SetFacingTo(bot->GetAngle(wo));
|
||||
// else
|
||||
// {
|
||||
bot->SetOrientation(angle);
|
||||
bot->SendMovementFlagUpdate();
|
||||
// }
|
||||
}
|
||||
|
||||
Unit* ServerFacade::GetChaseTarget(Unit* target)
|
||||
{
|
||||
MovementGenerator* movementGen = target->GetMotionMaster()->top();
|
||||
if (movementGen && movementGen->GetMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
{
|
||||
if (target->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
return static_cast<ChaseMovementGenerator<Player> const*>(movementGen)->GetTarget();
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<ChaseMovementGenerator<Creature> const*>(movementGen)->GetTarget();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ServerFacade::SendPacket(Player *player, WorldPacket *packet)
|
||||
{
|
||||
return player->GetSession()->SendPacket(packet);
|
||||
}
|
||||
43
src/helper/ServerFacade.h
Normal file
43
src/helper/ServerFacade.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_SERVERFACADE_H
|
||||
#define _PLAYERBOT_SERVERFACADE_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
class Player;
|
||||
class Unit;
|
||||
class WorldObject;
|
||||
class WorldPacket;
|
||||
|
||||
class ServerFacade
|
||||
{
|
||||
public:
|
||||
ServerFacade(){};
|
||||
virtual ~ServerFacade(){};
|
||||
static ServerFacade* instance()
|
||||
{
|
||||
static ServerFacade instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
public:
|
||||
float GetDistance2d(Unit* unit, WorldObject* wo);
|
||||
float GetDistance2d(Unit* unit, float x, float y);
|
||||
bool IsDistanceLessThan(float dist1, float dist2);
|
||||
bool IsDistanceGreaterThan(float dist1, float dist2);
|
||||
bool IsDistanceGreaterOrEqualThan(float dist1, float dist2);
|
||||
bool IsDistanceLessOrEqualThan(float dist1, float dist2);
|
||||
|
||||
void SetFacingTo(Player* bot, WorldObject* wo, bool force = false);
|
||||
Unit* GetChaseTarget(Unit* target);
|
||||
|
||||
void SendPacket(Player *player, WorldPacket* packet);
|
||||
};
|
||||
|
||||
#define sServerFacade ServerFacade::instance()
|
||||
|
||||
#endif
|
||||
505
src/helper/Talentspec.cpp
Normal file
505
src/helper/Talentspec.cpp
Normal file
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "Talentspec.h"
|
||||
|
||||
#include "Event.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
uint32 TalentSpec::TalentListEntry::tabPage() const
|
||||
{
|
||||
return talentTabInfo->TalentTabID == 41 ? 1 : talentTabInfo->tabpage;
|
||||
}
|
||||
|
||||
// Checks a talent link on basic validity.
|
||||
bool TalentSpec::CheckTalentLink(std::string const link, std::ostringstream* out)
|
||||
{
|
||||
std::string const validChar = "-";
|
||||
std::string const validNums = "012345";
|
||||
uint32 nums = 0;
|
||||
|
||||
for (char const& c : link)
|
||||
{
|
||||
if (validChar.find(c) == std::string::npos && validNums.find(c) == std::string::npos)
|
||||
{
|
||||
*out << "talent link is invalid. Must be in format 0-0-0";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (validNums.find(c) != std::string::npos)
|
||||
nums++;
|
||||
}
|
||||
|
||||
if (nums == 0)
|
||||
{
|
||||
*out << "talent link is invalid. Needs atleast one number.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 TalentSpec::LeveltoPoints(uint32 level) const
|
||||
{
|
||||
uint32 talentPointsForLevel = level < 10 ? 0 : level - 9;
|
||||
return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT));
|
||||
}
|
||||
|
||||
uint32 TalentSpec::PointstoLevel(uint32 points) const
|
||||
{
|
||||
return uint32(ceil(points / sWorld->getRate(RATE_TALENT))) + 9;
|
||||
}
|
||||
|
||||
TalentSpec::TalentSpec(uint32 classMask) { GetTalents(classMask); }
|
||||
|
||||
TalentSpec::TalentSpec(TalentSpec* base, std::string const link)
|
||||
{
|
||||
talents = base->talents;
|
||||
ReadTalents(link);
|
||||
}
|
||||
|
||||
TalentSpec::TalentSpec(Player* bot)
|
||||
{
|
||||
GetTalents(bot->getClassMask());
|
||||
ReadTalents(bot);
|
||||
}
|
||||
|
||||
TalentSpec::TalentSpec(Player* bot, std::string const link)
|
||||
{
|
||||
GetTalents(bot->getClassMask());
|
||||
ReadTalents(link);
|
||||
}
|
||||
|
||||
// Check the talentspec for errors.
|
||||
bool TalentSpec::CheckTalents(uint32 level, std::ostringstream* out)
|
||||
{
|
||||
for (auto& entry : talents)
|
||||
{
|
||||
if (entry.rank > entry.maxRank)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry.talentInfo->RankID[0]);
|
||||
*out << "spec is not for this class. " << spellInfo->SpellName[0] << " has " << (entry.rank - entry.maxRank)
|
||||
<< " points above max rank.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.rank > 0 && entry.talentInfo->DependsOn)
|
||||
{
|
||||
if (sTalentStore.LookupEntry(entry.talentInfo->DependsOn))
|
||||
{
|
||||
bool found = false;
|
||||
SpellInfo const* spellInfodep = nullptr;
|
||||
|
||||
for (auto& dep : talents)
|
||||
if (dep.talentInfo->TalentID == entry.talentInfo->DependsOn)
|
||||
{
|
||||
spellInfodep = sSpellMgr->GetSpellInfo(dep.talentInfo->RankID[0]);
|
||||
if (dep.rank >= entry.talentInfo->DependsOnRank)
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry.talentInfo->RankID[0]);
|
||||
*out << "spec is invalid. Talent:" << spellInfo->SpellName[0]
|
||||
<< " needs: " << spellInfodep->SpellName[0] << " at rank: " << entry.talentInfo->DependsOnRank;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < 3; i++)
|
||||
{
|
||||
std::vector<TalentListEntry> talentTree = GetTalentTree(i);
|
||||
uint32 points = 0;
|
||||
|
||||
for (auto& entry : talentTree)
|
||||
{
|
||||
if (entry.rank > 0 && entry.talentInfo->Row * 5 > points)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry.talentInfo->RankID[0]);
|
||||
*out << "spec is is invalid. Talent " << spellInfo->SpellName[0] << " is selected with only " << points
|
||||
<< " in row below it.";
|
||||
return false;
|
||||
}
|
||||
|
||||
points += entry.rank;
|
||||
}
|
||||
}
|
||||
|
||||
if (points > LeveltoPoints(level))
|
||||
{
|
||||
*out << "spec is for a higher level. (" << PointstoLevel(points) << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the talents for the bots to the current spec.
|
||||
void TalentSpec::ApplyTalents(Player* bot, std::ostringstream* out)
|
||||
{
|
||||
for (auto& entry : talents)
|
||||
{
|
||||
if (entry.rank == 0)
|
||||
continue;
|
||||
bot->LearnTalent(entry.talentInfo->TalentID, entry.rank - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a base talentlist for a class.
|
||||
void TalentSpec::GetTalents(uint32 classMask)
|
||||
{
|
||||
TalentListEntry entry;
|
||||
|
||||
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
|
||||
{
|
||||
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
|
||||
if (!talentInfo)
|
||||
continue;
|
||||
|
||||
TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
|
||||
if (!talentTabInfo)
|
||||
continue;
|
||||
|
||||
if ((classMask & talentTabInfo->ClassMask) == 0)
|
||||
continue;
|
||||
|
||||
entry.entry = i;
|
||||
entry.rank = 0;
|
||||
entry.talentInfo = talentInfo;
|
||||
entry.talentTabInfo = talentTabInfo;
|
||||
|
||||
for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
|
||||
{
|
||||
uint32 spellId = talentInfo->RankID[rank];
|
||||
if (!spellId)
|
||||
continue;
|
||||
|
||||
entry.maxRank = rank + 1;
|
||||
}
|
||||
|
||||
talents.push_back(entry);
|
||||
}
|
||||
|
||||
SortTalents(talents, SORT_BY_DEFAULT);
|
||||
}
|
||||
|
||||
// Sorts a talent list by page, row, column.
|
||||
bool sortTalentMap(TalentSpec::TalentListEntry i, TalentSpec::TalentListEntry j, uint32* tabSort)
|
||||
{
|
||||
uint32 itab = i.tabPage();
|
||||
uint32 jtab = j.tabPage();
|
||||
if (tabSort[itab] < tabSort[jtab])
|
||||
return true;
|
||||
|
||||
if (tabSort[itab] > tabSort[jtab])
|
||||
return false;
|
||||
|
||||
if (i.talentInfo->Row < j.talentInfo->Row)
|
||||
return true;
|
||||
|
||||
if (i.talentInfo->Row > j.talentInfo->Row)
|
||||
return false;
|
||||
|
||||
if (i.talentInfo->Col < j.talentInfo->Col)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TalentSpec::SortTalents(uint32 sortBy) { SortTalents(talents, sortBy); }
|
||||
|
||||
// Sort the talents.
|
||||
void TalentSpec::SortTalents(std::vector<TalentListEntry>& talents, uint32 sortBy)
|
||||
{
|
||||
switch (sortBy)
|
||||
{
|
||||
case SORT_BY_DEFAULT:
|
||||
{
|
||||
uint32 tabSort[] = {0, 1, 2};
|
||||
std::sort(talents.begin(), talents.end(),
|
||||
[&tabSort](TalentSpec::TalentListEntry i, TalentSpec::TalentListEntry j)
|
||||
{ return sortTalentMap(i, j, tabSort); });
|
||||
break;
|
||||
}
|
||||
case SORT_BY_POINTS_TREE:
|
||||
{
|
||||
uint32 tabSort[] = {GetTalentPoints(talents, 0) * 100 - urand(0, 99),
|
||||
GetTalentPoints(talents, 1) * 100 - urand(0, 99),
|
||||
GetTalentPoints(talents, 2) * 100 - urand(0, 99)};
|
||||
std::sort(talents.begin(), talents.end(),
|
||||
[&tabSort](TalentSpec::TalentListEntry i, TalentSpec::TalentListEntry j)
|
||||
{ return sortTalentMap(i, j, tabSort); });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the talent ranks to the current rank of the player.
|
||||
void TalentSpec::ReadTalents(Player* bot)
|
||||
{
|
||||
for (auto& entry : talents)
|
||||
for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
|
||||
{
|
||||
uint32 spellId = entry.talentInfo->RankID[rank];
|
||||
if (!spellId)
|
||||
continue;
|
||||
|
||||
if (bot->HasSpell(spellId))
|
||||
{
|
||||
entry.rank = rank + 1;
|
||||
points += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the talent ranks to the ranks of the link.
|
||||
void TalentSpec::ReadTalents(std::string const link)
|
||||
{
|
||||
//uint32 rank = 0; //not used, line marked for removal.
|
||||
uint32 pos = 0;
|
||||
uint32 tab = 0;
|
||||
std::string chr;
|
||||
|
||||
if (link.substr(pos, 1) == "-")
|
||||
{
|
||||
pos++;
|
||||
tab++;
|
||||
}
|
||||
|
||||
if (link.substr(pos, 1) == "-")
|
||||
{
|
||||
pos++;
|
||||
tab++;
|
||||
}
|
||||
|
||||
for (auto& entry : talents)
|
||||
{
|
||||
if (entry.tabPage() == tab)
|
||||
{
|
||||
chr = link.substr(pos, 1);
|
||||
|
||||
if (chr == " " || chr == "#")
|
||||
break;
|
||||
|
||||
entry.rank = stoi(chr);
|
||||
points += entry.rank;
|
||||
|
||||
pos++;
|
||||
if (pos <= link.size() && link.substr(pos, 1) == "-")
|
||||
{
|
||||
pos++;
|
||||
tab++;
|
||||
}
|
||||
|
||||
if (pos <= link.size() && link.substr(pos, 1) == "-")
|
||||
{
|
||||
pos++;
|
||||
tab++;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos > link.size() - 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns only a specific tree from a talent list.
|
||||
std::vector<TalentSpec::TalentListEntry> TalentSpec::GetTalentTree(uint32 tabpage)
|
||||
{
|
||||
std::vector<TalentListEntry> retList;
|
||||
|
||||
for (auto& entry : talents)
|
||||
if (entry.tabPage() == tabpage)
|
||||
retList.push_back(entry);
|
||||
|
||||
return std::move(retList);
|
||||
}
|
||||
|
||||
uint32 TalentSpec::GetTalentPoints(int32 tabpage) { return GetTalentPoints(talents, tabpage); };
|
||||
|
||||
// Counts the point in a talent list.
|
||||
uint32 TalentSpec::GetTalentPoints(std::vector<TalentListEntry>& talents, int32 tabpage)
|
||||
{
|
||||
if (tabpage == -1)
|
||||
return points;
|
||||
|
||||
uint32 tPoints = 0;
|
||||
for (auto& entry : talents)
|
||||
if (entry.tabPage() == tabpage)
|
||||
tPoints = tPoints + entry.rank;
|
||||
|
||||
return tPoints;
|
||||
}
|
||||
|
||||
// Generates a wow-head link from a talent list.
|
||||
std::string const TalentSpec::GetTalentLink()
|
||||
{
|
||||
std::string link = "";
|
||||
std::string treeLink[3];
|
||||
uint32 points[3];
|
||||
uint32 curPoints = 0;
|
||||
|
||||
for (uint8 i = 0; i < 3; i++)
|
||||
{
|
||||
points[i] = GetTalentPoints(i);
|
||||
|
||||
for (auto& entry : GetTalentTree(i))
|
||||
{
|
||||
curPoints += entry.rank;
|
||||
treeLink[i] += std::to_string(entry.rank);
|
||||
if (curPoints >= points[i])
|
||||
{
|
||||
curPoints = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
link = treeLink[0];
|
||||
|
||||
if (treeLink[1] != "0" || treeLink[2] != "0")
|
||||
link = link + "-" + treeLink[1];
|
||||
|
||||
if (treeLink[2] != "0")
|
||||
link = link + "-" + treeLink[2];
|
||||
|
||||
return std::move(link);
|
||||
}
|
||||
|
||||
uint32 TalentSpec::highestTree()
|
||||
{
|
||||
uint32 p1 = GetTalentPoints(0);
|
||||
uint32 p2 = GetTalentPoints(1);
|
||||
uint32 p3 = GetTalentPoints(2);
|
||||
|
||||
if (p1 > p2 && p1 > p3)
|
||||
return 0;
|
||||
|
||||
if (p2 > p1 && p2 > p3)
|
||||
return 1;
|
||||
|
||||
if (p3 > p1 && p3 > p2)
|
||||
return 2;
|
||||
|
||||
if (p1 > p2 || p1 > p3)
|
||||
return 0;
|
||||
|
||||
if (p2 > p3 || p2 > p1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string const TalentSpec::FormatSpec(Player* bot)
|
||||
{
|
||||
// uint8 cls = bot->getClass(); //not used, (used in lined 403), line marked for removal.
|
||||
|
||||
std::ostringstream out;
|
||||
// out << chathelper:: specs[cls][highestTree()] << " (";
|
||||
|
||||
uint32 c0 = GetTalentPoints(0);
|
||||
uint32 c1 = GetTalentPoints(1);
|
||||
uint32 c2 = GetTalentPoints(2);
|
||||
|
||||
out << (c0 ? "|h|cff00ff00" : "") << c0 << "|h|cffffffff/";
|
||||
out << (c1 ? "|h|cff00ff00" : "") << c1 << "|h|cffffffff/";
|
||||
out << (c2 ? "|h|cff00ff00" : "") << c2 << "|h|cffffffff";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// Removes talentpoints to match the level
|
||||
void TalentSpec::CropTalents(uint32 level)
|
||||
{
|
||||
if (points <= LeveltoPoints(level))
|
||||
return;
|
||||
|
||||
SortTalents(talents, SORT_BY_POINTS_TREE);
|
||||
|
||||
uint32 points = 0;
|
||||
for (auto& entry : talents)
|
||||
{
|
||||
if (points + entry.rank > LeveltoPoints(level))
|
||||
entry.rank = std::max(0u, LeveltoPoints(level) - points);
|
||||
|
||||
points += entry.rank;
|
||||
}
|
||||
|
||||
SortTalents(talents, SORT_BY_DEFAULT);
|
||||
}
|
||||
|
||||
// Substracts ranks. Follows the sorting of the newList.
|
||||
std::vector<TalentSpec::TalentListEntry> TalentSpec::SubTalentList(std::vector<TalentListEntry>& oldList,
|
||||
std::vector<TalentListEntry>& newList,
|
||||
uint32 reverse = SUBSTRACT_OLD_NEW)
|
||||
{
|
||||
std::vector<TalentSpec::TalentListEntry> deltaList = newList;
|
||||
|
||||
for (auto& newentry : deltaList)
|
||||
for (auto& oldentry : oldList)
|
||||
if (oldentry.entry == newentry.entry)
|
||||
{
|
||||
if (reverse == ABSOLUTE_DIST)
|
||||
newentry.rank = std::abs(int32(newentry.rank - oldentry.rank));
|
||||
else if (reverse == ADDED_POINTS || reverse == REMOVED_POINTS)
|
||||
newentry.rank = std::max(0u, (newentry.rank - oldentry.rank) * (reverse / 2));
|
||||
else
|
||||
newentry.rank = (newentry.rank - oldentry.rank) * reverse;
|
||||
}
|
||||
|
||||
return deltaList;
|
||||
}
|
||||
|
||||
bool TalentSpec::isEarlierVersionOf(TalentSpec& newSpec)
|
||||
{
|
||||
for (auto& newentry : newSpec.talents)
|
||||
for (auto& oldentry : talents)
|
||||
if (oldentry.entry == newentry.entry)
|
||||
if (oldentry.rank > newentry.rank)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Modifies current talents towards new talents up to a maxium of points.
|
||||
void TalentSpec::ShiftTalents(TalentSpec* currentSpec, uint32 level)
|
||||
{
|
||||
//uint32 currentPoints = currentSpec->GetTalentPoints(); //not used, line marked for removal.
|
||||
if (points >= LeveltoPoints(level)) // We have no more points to spend. Better reset and crop
|
||||
{
|
||||
CropTalents(level);
|
||||
return;
|
||||
}
|
||||
|
||||
SortTalents(SORT_BY_POINTS_TREE); // Apply points first to the largest new tree.
|
||||
|
||||
std::vector<TalentSpec::TalentListEntry> deltaList = SubTalentList(currentSpec->talents, talents);
|
||||
|
||||
for (auto& entry : deltaList)
|
||||
{
|
||||
if (entry.rank < 0) // We have to remove talents. Might as well reset and crop the new list.
|
||||
{
|
||||
CropTalents(level);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Start from the current spec.
|
||||
talents = currentSpec->talents;
|
||||
|
||||
for (auto& entry : deltaList)
|
||||
{
|
||||
if (entry.rank + points > LeveltoPoints(level)) // Running out of points. Only apply what we have left.
|
||||
entry.rank = std::max(0, std::abs(int32(LeveltoPoints(level) - points)));
|
||||
|
||||
for (auto& subentry : talents)
|
||||
if (entry.entry == subentry.entry)
|
||||
subentry.rank = subentry.rank + entry.rank;
|
||||
|
||||
points = points + entry.rank;
|
||||
}
|
||||
}
|
||||
107
src/helper/Talentspec.h
Normal file
107
src/helper/Talentspec.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_TALENTSPEC_H
|
||||
#define _PLAYERBOT_TALENTSPEC_H
|
||||
|
||||
#include "Action.h"
|
||||
|
||||
struct TalentEntry;
|
||||
struct TalentTabEntry;
|
||||
|
||||
#define SORT_BY_DEFAULT 0
|
||||
#define SORT_BY_POINTS_TREE 1
|
||||
#define ABSOLUTE_DIST 0
|
||||
#define SUBSTRACT_OLD_NEW 1
|
||||
#define SUBSTRACT_NEW_OLD -1
|
||||
#define ADDED_POINTS 2
|
||||
#define REMOVED_POINTS -2
|
||||
|
||||
// unused currently
|
||||
class TalentSpec
|
||||
{
|
||||
public:
|
||||
struct TalentListEntry
|
||||
{
|
||||
uint32 entry;
|
||||
uint32 rank;
|
||||
uint32 maxRank;
|
||||
TalentEntry const* talentInfo;
|
||||
TalentTabEntry const* talentTabInfo;
|
||||
uint32 tabPage() const;
|
||||
};
|
||||
|
||||
TalentSpec(){};
|
||||
virtual ~TalentSpec() {}
|
||||
TalentSpec(uint32 classMask);
|
||||
TalentSpec(TalentSpec* base, std::string const link);
|
||||
TalentSpec(Player* bot);
|
||||
TalentSpec(Player* bot, std::string const link);
|
||||
|
||||
uint32 points = 0;
|
||||
std::vector<TalentListEntry> talents;
|
||||
|
||||
bool CheckTalentLink(std::string const link, std::ostringstream* out);
|
||||
virtual bool CheckTalents(uint32 maxPoints, std::ostringstream* out);
|
||||
void CropTalents(uint32 level);
|
||||
void ShiftTalents(TalentSpec* oldTalents, uint32 level);
|
||||
void ApplyTalents(Player* bot, std::ostringstream* out);
|
||||
|
||||
uint32 GetTalentPoints(std::vector<TalentListEntry>& talents, int32 tabpage = -1);
|
||||
uint32 GetTalentPoints(int32 tabpage = -1);
|
||||
bool isEarlierVersionOf(TalentSpec& newSpec);
|
||||
|
||||
std::string const GetTalentLink();
|
||||
uint32 highestTree();
|
||||
std::string const FormatSpec(Player* bot);
|
||||
|
||||
protected:
|
||||
uint32 LeveltoPoints(uint32 level) const;
|
||||
uint32 PointstoLevel(uint32 points) const;
|
||||
void GetTalents(uint32 classMask);
|
||||
void SortTalents(std::vector<TalentListEntry>& talents, uint32 sortBy);
|
||||
void SortTalents(uint32 sortBy);
|
||||
|
||||
void ReadTalents(Player* bot);
|
||||
void ReadTalents(std::string const link);
|
||||
|
||||
std::vector<TalentListEntry> GetTalentTree(uint32 tabpage);
|
||||
std::vector<TalentListEntry> SubTalentList(std::vector<TalentListEntry>& oldList,
|
||||
std::vector<TalentListEntry>& newList, uint32 reverse);
|
||||
};
|
||||
|
||||
class TalentPath
|
||||
{
|
||||
public:
|
||||
TalentPath(uint32 pathId, std::string const pathName, uint32 pathProbability)
|
||||
{
|
||||
id = pathId;
|
||||
name = pathName;
|
||||
probability = pathProbability;
|
||||
};
|
||||
|
||||
uint32 id = 0;
|
||||
std::string name = "";
|
||||
uint32 probability = 100;
|
||||
std::vector<TalentSpec> talentSpec;
|
||||
};
|
||||
|
||||
class ClassSpecs
|
||||
{
|
||||
public:
|
||||
ClassSpecs(){};
|
||||
ClassSpecs(uint32 specsClassMask)
|
||||
{
|
||||
classMask = specsClassMask;
|
||||
baseSpec = TalentSpec(specsClassMask);
|
||||
}
|
||||
|
||||
uint32 classMask = 0;
|
||||
TalentSpec baseSpec;
|
||||
|
||||
std::vector<TalentPath> talentPath;
|
||||
};
|
||||
|
||||
#endif
|
||||
4298
src/helper/TravelMgr.cpp
Normal file
4298
src/helper/TravelMgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
945
src/helper/TravelMgr.h
Normal file
945
src/helper/TravelMgr.h
Normal file
@@ -0,0 +1,945 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_TRAVELMGR_H
|
||||
#define _PLAYERBOT_TRAVELMGR_H
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <random>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "Corpse.h"
|
||||
#include "CreatureData.h"
|
||||
#include "GameObject.h"
|
||||
#include "GridDefines.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
|
||||
class GuidPosition;
|
||||
class ObjectGuid;
|
||||
class Quest;
|
||||
class Player;
|
||||
class PlayerbotAI;
|
||||
|
||||
struct QuestStatusData;
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
class Vector2;
|
||||
class Vector3;
|
||||
class Vector4;
|
||||
} // namespace G3D
|
||||
|
||||
// Constructor types for WorldPosition
|
||||
enum WorldPositionConst
|
||||
{
|
||||
WP_RANDOM = 0,
|
||||
WP_CENTROID = 1,
|
||||
WP_MEAN_CENTROID = 2,
|
||||
WP_CLOSEST = 3
|
||||
};
|
||||
|
||||
enum TravelState
|
||||
{
|
||||
TRAVEL_STATE_IDLE = 0,
|
||||
TRAVEL_STATE_TRAVEL_PICK_UP_QUEST = 1,
|
||||
TRAVEL_STATE_WORK_PICK_UP_QUEST = 2,
|
||||
TRAVEL_STATE_TRAVEL_DO_QUEST = 3,
|
||||
TRAVEL_STATE_WORK_DO_QUEST = 4,
|
||||
TRAVEL_STATE_TRAVEL_HAND_IN_QUEST = 5,
|
||||
TRAVEL_STATE_WORK_HAND_IN_QUEST = 6,
|
||||
TRAVEL_STATE_TRAVEL_RPG = 7,
|
||||
TRAVEL_STATE_TRAVEL_EXPLORE = 8,
|
||||
MAX_TRAVEL_STATE
|
||||
};
|
||||
|
||||
enum TravelStatus
|
||||
{
|
||||
TRAVEL_STATUS_NONE = 0,
|
||||
TRAVEL_STATUS_PREPARE = 1,
|
||||
TRAVEL_STATUS_TRAVEL = 2,
|
||||
TRAVEL_STATUS_WORK = 3,
|
||||
TRAVEL_STATUS_COOLDOWN = 4,
|
||||
TRAVEL_STATUS_EXPIRED = 5,
|
||||
MAX_TRAVEL_STATUS
|
||||
};
|
||||
|
||||
class QuestTravelDestination;
|
||||
|
||||
// A quest destination container for quick lookup of all destinations related to a quest.
|
||||
struct QuestContainer
|
||||
{
|
||||
std::vector<QuestTravelDestination*> questGivers;
|
||||
std::vector<QuestTravelDestination*> questTakers;
|
||||
std::vector<QuestTravelDestination*> questObjectives;
|
||||
};
|
||||
|
||||
typedef std::pair<int32, int32> mGridCoord;
|
||||
|
||||
// Extension of WorldLocation with distance functions.
|
||||
class WorldPosition : public WorldLocation
|
||||
{
|
||||
public:
|
||||
// Constructors
|
||||
WorldPosition() : WorldLocation(){};
|
||||
WorldPosition(WorldLocation const& loc) : WorldLocation(loc) {}
|
||||
WorldPosition(WorldPosition const& pos) : WorldLocation(pos), visitors(pos.visitors) {}
|
||||
WorldPosition(std::string const str);
|
||||
WorldPosition(uint32 mapid, float x, float y, float z = 0.f, float orientation = 0.f)
|
||||
: WorldLocation(mapid, x, y, z, orientation)
|
||||
{
|
||||
}
|
||||
WorldPosition(uint32 mapId, const Position& pos);
|
||||
WorldPosition(WorldObject const* wo);
|
||||
WorldPosition(std::vector<WorldPosition*> list, WorldPositionConst conType);
|
||||
WorldPosition(std::vector<WorldPosition> list, WorldPositionConst conType);
|
||||
WorldPosition(uint32 mapid, GridCoord grid);
|
||||
WorldPosition(uint32 mapid, CellCoord cell);
|
||||
WorldPosition(uint32 mapid, mGridCoord grid);
|
||||
|
||||
//Setters
|
||||
void set(const WorldLocation& pos);
|
||||
void set(const WorldObject* wo);
|
||||
void set(const WorldPosition& pos);
|
||||
void setMapId(uint32 id);
|
||||
void setX(float x);
|
||||
void setY(float y);
|
||||
void setZ(float z);
|
||||
void setO(float o);
|
||||
|
||||
void addVisitor() { ++visitors; }
|
||||
|
||||
void remVisitor() { --visitors; }
|
||||
|
||||
// Getters
|
||||
operator bool() const;
|
||||
friend bool operator==(WorldPosition const& p1, const WorldPosition& p2);
|
||||
friend bool operator!=(WorldPosition const& p1, const WorldPosition& p2);
|
||||
|
||||
WorldPosition& operator=(WorldPosition const&) = default;
|
||||
WorldPosition& operator+=(WorldPosition const& p1);
|
||||
WorldPosition& operator-=(WorldPosition const& p1);
|
||||
|
||||
uint32 getMapId();
|
||||
float getX();
|
||||
float getY();
|
||||
float getZ();
|
||||
float getO();
|
||||
|
||||
G3D::Vector3 getVector3();
|
||||
std::string const print();
|
||||
|
||||
std::string const to_string();
|
||||
std::vector<std::string> split(const std::string& s, char delimiter);
|
||||
|
||||
void printWKT(std::vector<WorldPosition> points, std::ostringstream& out, uint32 dim = 0, bool loop = false);
|
||||
void printWKT(std::ostringstream& out) { printWKT({*this}, out); }
|
||||
|
||||
uint32 getVisitors() { return visitors; }
|
||||
|
||||
bool isOverworld();
|
||||
bool isInWater();
|
||||
bool isUnderWater();
|
||||
|
||||
WorldPosition relPoint(WorldPosition* center);
|
||||
WorldPosition offset(WorldPosition* center);
|
||||
float size();
|
||||
|
||||
// Slow distance function using possible map transfers.
|
||||
float distance(WorldPosition* center);
|
||||
float distance(WorldPosition center) { return distance(¢er); }
|
||||
|
||||
float fDist(WorldPosition* center);
|
||||
float fDist(WorldPosition center) { return fDist(¢er); }
|
||||
|
||||
template <class T>
|
||||
std::pair<T, WorldPosition> closest(std::vector<std::pair<T, WorldPosition>> list)
|
||||
{
|
||||
return *std::min_element(list.begin(), list.end(),
|
||||
[this](std::pair<T, WorldPosition> i, std::pair<T, WorldPosition> j)
|
||||
{ return this->distance(i.second) < this->distance(j.second); });
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::pair<T, WorldPosition> closest(std::vector<T> list)
|
||||
{
|
||||
return closest(GetPosList(list));
|
||||
}
|
||||
|
||||
// Returns the closest point from the list.
|
||||
WorldPosition* closest(std::vector<WorldPosition*> list)
|
||||
{
|
||||
return *std::min_element(list.begin(), list.end(),
|
||||
[this](WorldPosition* i, WorldPosition* j)
|
||||
{ return this->distance(i) < this->distance(j); });
|
||||
}
|
||||
|
||||
WorldPosition closest(std::vector<WorldPosition> list)
|
||||
{
|
||||
return *std::min_element(list.begin(), list.end(),
|
||||
[this](WorldPosition i, WorldPosition j)
|
||||
{ return this->distance(i) < this->distance(j); });
|
||||
}
|
||||
|
||||
// Quick square distance in 2d plane.
|
||||
float sqDistance2d(WorldPosition center)
|
||||
{
|
||||
return (getX() - center.getX()) * (getX() - center.getX()) +
|
||||
(getY() - center.getY()) * (getY() - center.getY());
|
||||
}
|
||||
|
||||
// Quick square distance calculation without map check. Used for getting the minimum distant points.
|
||||
float sqDistance(WorldPosition center)
|
||||
{
|
||||
return (getX() - center.getX()) * (getX() - center.getX()) +
|
||||
(getY() - center.getY()) * (getY() - center.getY()) +
|
||||
(getZ() - center.getZ()) * (getZ() - center.getZ());
|
||||
}
|
||||
|
||||
float sqDistance2d(WorldPosition* center)
|
||||
{
|
||||
return (getX() - center->getX()) * (getX() - center->getX()) +
|
||||
(getY() - center->getY()) * (getY() - center->getY());
|
||||
}
|
||||
|
||||
float sqDistance(WorldPosition* center)
|
||||
{
|
||||
return (getX() - center->getX()) * (getX() - center->getX()) +
|
||||
(getY() - center->getY()) * (getY() - center->getY()) +
|
||||
(getZ() - center->getZ()) * (getZ() - center->getZ());
|
||||
}
|
||||
|
||||
// Returns the closest point of the list. Fast but only works for the same map.
|
||||
WorldPosition* closestSq(std::vector<WorldPosition*> list)
|
||||
{
|
||||
return *std::min_element(list.begin(), list.end(),
|
||||
[this](WorldPosition* i, WorldPosition* j)
|
||||
{ return this->sqDistance(i) < this->sqDistance(j); });
|
||||
}
|
||||
|
||||
WorldPosition closestSq(std::vector<WorldPosition> list)
|
||||
{
|
||||
return *std::min_element(list.begin(), list.end(),
|
||||
[this](WorldPosition i, WorldPosition j)
|
||||
{ return this->sqDistance(i) < this->sqDistance(j); });
|
||||
}
|
||||
|
||||
float getAngleTo(WorldPosition endPos)
|
||||
{
|
||||
float ang = atan2(endPos.getY() - getY(), endPos.getX() - getX());
|
||||
return (ang >= 0) ? ang : 2 * static_cast<float>(M_PI) + ang;
|
||||
}
|
||||
|
||||
float getAngleBetween(WorldPosition dir1, WorldPosition dir2) { return abs(getAngleTo(dir1) - getAngleTo(dir2)); }
|
||||
|
||||
WorldPosition lastInRange(std::vector<WorldPosition> list, float minDist = -1.f, float maxDist = -1.f);
|
||||
WorldPosition firstOutRange(std::vector<WorldPosition> list, float minDist = -1.f, float maxDist = -1.f);
|
||||
|
||||
float mSign(WorldPosition* p1, WorldPosition* p2)
|
||||
{
|
||||
return (getX() - p2->getX()) * (p1->getY() - p2->getY()) - (p1->getX() - p2->getX()) * (getY() - p2->getY());
|
||||
}
|
||||
|
||||
bool isInside(WorldPosition* p1, WorldPosition* p2, WorldPosition* p3);
|
||||
|
||||
// Map functions. Player independent.
|
||||
MapEntry const* getMapEntry();
|
||||
uint32 getInstanceId();
|
||||
Map* getMap();
|
||||
float getHeight(); // remove const - whipowill
|
||||
|
||||
std::set<Transport*> getTransports(uint32 entry = 0);
|
||||
|
||||
GridCoord getGridCoord() { return Acore::ComputeGridCoord(getX(), getY()); };
|
||||
std::vector<GridCoord> getGridCoord(WorldPosition secondPos);
|
||||
std::vector<WorldPosition> fromGridCoord(GridCoord GridCoord);
|
||||
|
||||
CellCoord getCellCoord() { return Acore::ComputeCellCoord(getX(), getY()); }
|
||||
std::vector<WorldPosition> fromCellCoord(CellCoord cellCoord);
|
||||
std::vector<WorldPosition> gridFromCellCoord(CellCoord cellCoord);
|
||||
|
||||
mGridCoord getmGridCoord()
|
||||
{
|
||||
return std::make_pair((int32)(CENTER_GRID_ID - getX() / SIZE_OF_GRIDS),
|
||||
(int32)(CENTER_GRID_ID - getY() / SIZE_OF_GRIDS));
|
||||
}
|
||||
|
||||
std::vector<mGridCoord> getmGridCoords(WorldPosition secondPos);
|
||||
std::vector<WorldPosition> frommGridCoord(mGridCoord GridCoord);
|
||||
|
||||
void loadMapAndVMap(uint32 mapId, uint8 x, uint8 y);
|
||||
|
||||
void loadMapAndVMap() { loadMapAndVMap(getMapId(), getmGridCoord().first, getmGridCoord().second); }
|
||||
|
||||
void loadMapAndVMaps(WorldPosition secondPos);
|
||||
|
||||
// Display functions
|
||||
WorldPosition getDisplayLocation();
|
||||
float getDisplayX() { return getDisplayLocation().getY() * -1.0; }
|
||||
|
||||
float getDisplayY() { return getDisplayLocation().getX(); }
|
||||
|
||||
uint16 getAreaId();
|
||||
AreaTableEntry const* getArea();
|
||||
std::string const getAreaName(bool fullName = true, bool zoneName = false);
|
||||
|
||||
std::vector<WorldPosition> fromPointsArray(std::vector<G3D::Vector3> path);
|
||||
|
||||
// Pathfinding
|
||||
std::vector<WorldPosition> getPathStepFrom(WorldPosition startPos, Unit* bot);
|
||||
std::vector<WorldPosition> getPathFromPath(std::vector<WorldPosition> startPath, Unit* bot, uint8 maxAttempt = 40);
|
||||
|
||||
std::vector<WorldPosition> getPathFrom(WorldPosition startPos, Unit* bot)
|
||||
{
|
||||
return getPathFromPath({startPos}, bot);
|
||||
}
|
||||
|
||||
std::vector<WorldPosition> getPathTo(WorldPosition endPos, Unit* bot) { return endPos.getPathFrom(*this, bot); }
|
||||
|
||||
bool isPathTo(std::vector<WorldPosition> path, float maxDistance = sPlayerbotAIConfig->targetPosRecalcDistance)
|
||||
{
|
||||
return !path.empty() && distance(path.back()) < maxDistance;
|
||||
};
|
||||
bool cropPathTo(std::vector<WorldPosition>& path, float maxDistance = sPlayerbotAIConfig->targetPosRecalcDistance);
|
||||
bool canPathTo(WorldPosition endPos, Unit* bot) { return endPos.isPathTo(getPathTo(endPos, bot)); }
|
||||
|
||||
float getPathLength(std::vector<WorldPosition> points)
|
||||
{
|
||||
float dist = 0.f;
|
||||
for (auto& p : points)
|
||||
{
|
||||
if (&p == &points.front())
|
||||
dist = 0.f;
|
||||
else
|
||||
dist += std::prev(&p, 1)->distance(p);
|
||||
}
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
bool GetReachableRandomPointOnGround(Player* bot, float radius, bool randomRange = true);
|
||||
|
||||
uint32 getUnitsAggro(GuidVector& units, Player* bot);
|
||||
|
||||
// Creatures
|
||||
std::vector<CreatureData const*> getCreaturesNear(float radius = 0, uint32 entry = 0);
|
||||
|
||||
// GameObjects
|
||||
std::vector<GameObjectData const*> getGameObjectsNear(float radius = 0, uint32 entry = 0);
|
||||
|
||||
private:
|
||||
uint32 visitors = 0;
|
||||
};
|
||||
|
||||
inline ByteBuffer& operator<<(ByteBuffer& b, WorldPosition& guidP)
|
||||
{
|
||||
b << guidP.getMapId();
|
||||
b << guidP.getX();
|
||||
b << guidP.getY();
|
||||
b << guidP.getZ();
|
||||
b << guidP.getO();
|
||||
b << guidP.getVisitors();
|
||||
return b;
|
||||
}
|
||||
|
||||
inline ByteBuffer& operator>>(ByteBuffer& b, [[maybe_unused]] WorldPosition& g)
|
||||
{
|
||||
uint32 mapid;
|
||||
float coord_x;
|
||||
float coord_y;
|
||||
float coord_z;
|
||||
float orientation;
|
||||
uint32 visitors = 0;
|
||||
b >> mapid;
|
||||
b >> coord_x;
|
||||
b >> coord_y;
|
||||
b >> coord_z;
|
||||
b >> orientation;
|
||||
b >> visitors;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
// Generic creature finder
|
||||
class FindPointCreatureData
|
||||
{
|
||||
public:
|
||||
FindPointCreatureData(WorldPosition point1 = WorldPosition(), float radius1 = 0, uint32 entry1 = 0)
|
||||
{
|
||||
point = point1;
|
||||
radius = radius1;
|
||||
entry = entry1;
|
||||
}
|
||||
|
||||
void operator()(CreatureData const& dataPair);
|
||||
std::vector<CreatureData const*> GetResult() const { return data; };
|
||||
|
||||
private:
|
||||
WorldPosition point;
|
||||
float radius;
|
||||
uint32 entry;
|
||||
|
||||
std::vector<CreatureData const*> data;
|
||||
};
|
||||
|
||||
// Generic gameObject finder
|
||||
class FindPointGameObjectData
|
||||
{
|
||||
public:
|
||||
FindPointGameObjectData(WorldPosition point1 = WorldPosition(), float radius1 = 0, uint32 entry1 = 0)
|
||||
{
|
||||
point = point1;
|
||||
radius = radius1;
|
||||
entry = entry1;
|
||||
}
|
||||
|
||||
void operator()(GameObjectData const& dataPair);
|
||||
std::vector<GameObjectData const*> GetResult() const { return data; };
|
||||
|
||||
private:
|
||||
WorldPosition point;
|
||||
float radius;
|
||||
uint32 entry;
|
||||
|
||||
std::vector<GameObjectData const*> data;
|
||||
};
|
||||
|
||||
class GuidPosition : public ObjectGuid, public WorldPosition
|
||||
{
|
||||
public:
|
||||
GuidPosition() : ObjectGuid(), WorldPosition(), loadedFromDB(false) { }
|
||||
GuidPosition(WorldObject* wo);
|
||||
GuidPosition(CreatureData const& creData);
|
||||
GuidPosition(GameObjectData const& goData);
|
||||
CreatureTemplate const* GetCreatureTemplate();
|
||||
GameObjectTemplate const* GetGameObjectTemplate();
|
||||
|
||||
WorldObject* GetWorldObject();
|
||||
Creature* GetCreature();
|
||||
Unit* GetUnit();
|
||||
GameObject* GetGameObject();
|
||||
Player* GetPlayer();
|
||||
|
||||
bool HasNpcFlag(NPCFlags flag);
|
||||
|
||||
bool isDead(); // For loaded grids check if the unit/object is unloaded/dead.
|
||||
|
||||
operator bool() const { return !IsEmpty(); }
|
||||
bool operator==(ObjectGuid const& guid) const { return GetRawValue() == guid.GetRawValue(); }
|
||||
bool operator!=(ObjectGuid const& guid) const { return GetRawValue() != guid.GetRawValue(); }
|
||||
bool operator<(ObjectGuid const& guid) const { return GetRawValue() < guid.GetRawValue(); }
|
||||
|
||||
private:
|
||||
bool loadedFromDB;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::vector<std::pair<T, WorldPosition>> GetPosList(std::vector<T> oList)
|
||||
{
|
||||
std::vector<std::pair<T, WorldPosition>> retList;
|
||||
for (auto& obj : oList)
|
||||
retList.push_back(std::make_pair(obj, WorldPosition(obj)));
|
||||
|
||||
return std::move(retList);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::vector<std::pair<T, WorldPosition>> GetPosVector(std::vector<T> oList)
|
||||
{
|
||||
std::vector<std::pair<T, WorldPosition>> retList;
|
||||
for (auto& obj : oList)
|
||||
retList.push_back(make_pair(obj, WorldPosition(obj)));
|
||||
|
||||
return std::move(retList);
|
||||
};
|
||||
|
||||
class mapTransfer
|
||||
{
|
||||
public:
|
||||
mapTransfer(WorldPosition pointFrom1, WorldPosition pointTo1, float portalLength1 = 0.1f)
|
||||
: pointFrom(pointFrom1), pointTo(pointTo1), portalLength(portalLength1)
|
||||
{
|
||||
}
|
||||
|
||||
bool isFrom(WorldPosition point) { return point.getMapId() == pointFrom.getMapId(); }
|
||||
|
||||
bool isTo(WorldPosition point) { return point.getMapId() == pointTo.getMapId(); }
|
||||
|
||||
WorldPosition* getPointFrom() { return &pointFrom; }
|
||||
|
||||
WorldPosition* getPointTo() { return &pointTo; }
|
||||
|
||||
bool isUseful(WorldPosition point) { return isFrom(point) || isTo(point); }
|
||||
|
||||
float distance(WorldPosition point)
|
||||
{
|
||||
return isUseful(point) ? (isFrom(point) ? point.distance(pointFrom) : point.distance(pointTo)) : 200000;
|
||||
}
|
||||
|
||||
bool isUseful(WorldPosition start, WorldPosition end) { return isFrom(start) && isTo(end); }
|
||||
|
||||
float distance(WorldPosition start, WorldPosition end)
|
||||
{
|
||||
return (isUseful(start, end) ? (start.distance(pointFrom) + portalLength + pointTo.distance(end)) : 200000);
|
||||
}
|
||||
|
||||
float fDist(WorldPosition start, WorldPosition end);
|
||||
|
||||
private:
|
||||
WorldPosition pointFrom;
|
||||
WorldPosition pointTo;
|
||||
float portalLength = 0.1f;
|
||||
};
|
||||
|
||||
// A destination for a bot to travel to and do something.
|
||||
class TravelDestination
|
||||
{
|
||||
public:
|
||||
TravelDestination() {}
|
||||
TravelDestination(float radiusMin1, float radiusMax1)
|
||||
{
|
||||
radiusMin = radiusMin1;
|
||||
radiusMax = radiusMax1;
|
||||
}
|
||||
TravelDestination(std::vector<WorldPosition*> points1, float radiusMin1, float radiusMax1)
|
||||
{
|
||||
points = points1;
|
||||
radiusMin = radiusMin1;
|
||||
radiusMax = radiusMax1;
|
||||
}
|
||||
virtual ~TravelDestination() = default;
|
||||
|
||||
void addPoint(WorldPosition* pos) { points.push_back(pos); }
|
||||
|
||||
void setExpireDelay(uint32 delay) { expireDelay = delay; }
|
||||
|
||||
void setCooldownDelay(uint32 delay) { cooldownDelay = delay; }
|
||||
|
||||
void setMaxVisitors(uint32 maxVisitors1 = 0, uint32 maxVisitorsPerPoint1 = 0)
|
||||
{
|
||||
maxVisitors = maxVisitors1;
|
||||
maxVisitorsPerPoint = maxVisitorsPerPoint1;
|
||||
}
|
||||
|
||||
std::vector<WorldPosition*> getPoints(bool ignoreFull = false);
|
||||
uint32 getExpireDelay() { return expireDelay; }
|
||||
uint32 getCooldownDelay() { return cooldownDelay; }
|
||||
|
||||
void addVisitor() { ++visitors; }
|
||||
|
||||
void remVisitor() { --visitors; }
|
||||
|
||||
uint32 getVisitors() { return visitors; }
|
||||
|
||||
virtual Quest const* GetQuestTemplate() { return nullptr; }
|
||||
virtual bool isActive([[maybe_unused]] Player* bot) { return false; }
|
||||
|
||||
bool isFull(bool ignoreFull = false);
|
||||
|
||||
virtual std::string const getName() { return "TravelDestination"; }
|
||||
virtual int32 getEntry() { return 0; }
|
||||
virtual std::string const getTitle() { return "generic travel destination"; }
|
||||
|
||||
WorldPosition* nearestPoint(WorldPosition* pos);
|
||||
|
||||
float distanceTo(WorldPosition* pos) { return nearestPoint(pos)->distance(pos); }
|
||||
bool onMap(WorldPosition* pos) { return nearestPoint(pos)->getMapId() == pos->getMapId(); }
|
||||
virtual bool isIn(WorldPosition* pos, float radius = 0.f)
|
||||
{
|
||||
return onMap(pos) && distanceTo(pos) <= (radius ? radius : radiusMin);
|
||||
}
|
||||
virtual bool isOut(WorldPosition* pos, float radius = 0.f)
|
||||
{
|
||||
return !onMap(pos) || distanceTo(pos) > (radius ? radius : radiusMax);
|
||||
}
|
||||
float getRadiusMin() { return radiusMin; }
|
||||
|
||||
std::vector<WorldPosition*> touchingPoints(WorldPosition* pos);
|
||||
std::vector<WorldPosition*> sortedPoints(WorldPosition* pos);
|
||||
std::vector<WorldPosition*> nextPoint(WorldPosition* pos, bool ignoreFull = true);
|
||||
|
||||
protected:
|
||||
std::vector<WorldPosition*> points;
|
||||
float radiusMin = 0;
|
||||
float radiusMax = 0;
|
||||
|
||||
uint32 visitors = 0;
|
||||
uint32 maxVisitors = 0;
|
||||
uint32 maxVisitorsPerPoint = 0;
|
||||
uint32 expireDelay = 5 * 1000;
|
||||
uint32 cooldownDelay = 60 * 1000;
|
||||
};
|
||||
|
||||
// A travel target that is always inactive and jumps to cooldown.
|
||||
class NullTravelDestination : public TravelDestination
|
||||
{
|
||||
public:
|
||||
NullTravelDestination(uint32 cooldownDelay1 = 5 * 60 * 1000) : TravelDestination()
|
||||
{
|
||||
cooldownDelay = cooldownDelay1;
|
||||
};
|
||||
|
||||
Quest const* GetQuestTemplate() override { return nullptr; }
|
||||
|
||||
bool isActive([[maybe_unused]] Player* bot) override { return false; }
|
||||
|
||||
std::string const getName() override { return "NullTravelDestination"; }
|
||||
std::string const getTitle() override { return "no destination"; }
|
||||
|
||||
bool isIn([[maybe_unused]] WorldPosition* pos, [[maybe_unused]] float radius = 0.f) override { return true; }
|
||||
bool isOut([[maybe_unused]] WorldPosition* pos, [[maybe_unused]] float radius = 0.f) override { return false; }
|
||||
};
|
||||
|
||||
// A travel target specifically related to a quest.
|
||||
class QuestTravelDestination : public TravelDestination
|
||||
{
|
||||
public:
|
||||
QuestTravelDestination(uint32 questId1, float radiusMin1, float radiusMax1);
|
||||
|
||||
Quest const* GetQuestTemplate() override { return questTemplate; }
|
||||
|
||||
bool isActive(Player* bot) override;
|
||||
|
||||
std::string const getName() override { return "QuestTravelDestination"; }
|
||||
int32 getEntry() override { return 0; }
|
||||
std::string const getTitle() override;
|
||||
|
||||
protected:
|
||||
uint32 questId;
|
||||
Quest const* questTemplate;
|
||||
};
|
||||
|
||||
// A quest giver or taker.
|
||||
class QuestRelationTravelDestination : public QuestTravelDestination
|
||||
{
|
||||
public:
|
||||
QuestRelationTravelDestination(uint32 quest_id1, uint32 entry1, uint32 relation1, float radiusMin1,
|
||||
float radiusMax1)
|
||||
: QuestTravelDestination(quest_id1, radiusMin1, radiusMax1)
|
||||
{
|
||||
entry = entry1;
|
||||
relation = relation1;
|
||||
}
|
||||
|
||||
bool isActive(Player* bot) override;
|
||||
std::string const getName() override { return "QuestRelationTravelDestination"; }
|
||||
int32 getEntry() override { return entry; }
|
||||
std::string const getTitle() override;
|
||||
virtual uint32 getRelation() { return relation; }
|
||||
|
||||
private:
|
||||
uint32 relation;
|
||||
int32 entry;
|
||||
};
|
||||
|
||||
// A quest objective (creature/gameobject to grind/loot)
|
||||
class QuestObjectiveTravelDestination : public QuestTravelDestination
|
||||
{
|
||||
public:
|
||||
QuestObjectiveTravelDestination(uint32 quest_id1, uint32 entry1, uint32 objective1, float radiusMin1,
|
||||
float radiusMax1, uint32 itemId1 = 0)
|
||||
: QuestTravelDestination(quest_id1, radiusMin1, radiusMax1)
|
||||
{
|
||||
objective = objective1;
|
||||
entry = entry1;
|
||||
itemId = itemId1;
|
||||
}
|
||||
|
||||
bool isCreature();
|
||||
uint32 ReqCreature();
|
||||
uint32 ReqGOId();
|
||||
uint32 ReqCount();
|
||||
|
||||
bool isActive(Player* bot) override;
|
||||
std::string const getName() override { return "QuestObjectiveTravelDestination"; }
|
||||
int32 getEntry() override { return entry; }
|
||||
std::string const getTitle() override;
|
||||
|
||||
private:
|
||||
uint32 objective;
|
||||
uint32 entry;
|
||||
uint32 itemId = 0;
|
||||
};
|
||||
|
||||
// A location with rpg target(s) based on race and level
|
||||
class RpgTravelDestination : public TravelDestination
|
||||
{
|
||||
public:
|
||||
RpgTravelDestination(uint32 entry1, float radiusMin1, float radiusMax1) : TravelDestination(radiusMin1, radiusMax1)
|
||||
{
|
||||
entry = entry1;
|
||||
}
|
||||
|
||||
bool isActive(Player* bot) override;
|
||||
virtual CreatureTemplate const* GetCreatureTemplate();
|
||||
std::string const getName() override { return "RpgTravelDestination"; }
|
||||
int32 getEntry() override { return 0; }
|
||||
std::string const getTitle() override;
|
||||
|
||||
protected:
|
||||
uint32 entry;
|
||||
};
|
||||
|
||||
// A location with zone exploration target(s)
|
||||
class ExploreTravelDestination : public TravelDestination
|
||||
{
|
||||
public:
|
||||
ExploreTravelDestination(uint32 areaId1, float radiusMin1, float radiusMax1)
|
||||
: TravelDestination(radiusMin1, radiusMax1)
|
||||
{
|
||||
areaId = areaId1;
|
||||
}
|
||||
|
||||
bool isActive(Player* bot) override;
|
||||
std::string const getName() override { return "ExploreTravelDestination"; }
|
||||
int32 getEntry() override { return 0; }
|
||||
std::string const getTitle() override { return title; };
|
||||
virtual void setTitle(std::string newTitle) { title = newTitle; }
|
||||
virtual uint32 getAreaId() { return areaId; }
|
||||
|
||||
protected:
|
||||
uint32 areaId;
|
||||
std::string title = "";
|
||||
};
|
||||
|
||||
// A location with zone exploration target(s)
|
||||
class GrindTravelDestination : public TravelDestination
|
||||
{
|
||||
public:
|
||||
GrindTravelDestination(int32 entry1, float radiusMin1, float radiusMax1) : TravelDestination(radiusMin1, radiusMax1)
|
||||
{
|
||||
entry = entry1;
|
||||
}
|
||||
|
||||
bool isActive(Player* bot) override;
|
||||
virtual CreatureTemplate const* GetCreatureTemplate();
|
||||
std::string const getName() override { return "GrindTravelDestination"; }
|
||||
int32 getEntry() override { return entry; }
|
||||
std::string const getTitle() override;
|
||||
|
||||
protected:
|
||||
int32 entry;
|
||||
};
|
||||
|
||||
// A location with a boss
|
||||
class BossTravelDestination : public TravelDestination
|
||||
{
|
||||
public:
|
||||
BossTravelDestination(int32 entry1, float radiusMin1, float radiusMax1) : TravelDestination(radiusMin1, radiusMax1)
|
||||
{
|
||||
entry = entry1;
|
||||
cooldownDelay = 1000;
|
||||
}
|
||||
|
||||
bool isActive(Player* bot) override;
|
||||
CreatureTemplate const* getCreatureTemplate();
|
||||
std::string const getName() override { return "BossTravelDestination"; }
|
||||
int32 getEntry() override { return entry; }
|
||||
std::string const getTitle() override;
|
||||
|
||||
protected:
|
||||
int32 entry;
|
||||
};
|
||||
|
||||
// Current target and location for the bot to travel to.
|
||||
// The flow is as follows:
|
||||
// PREPARE (wait until no loot is near)
|
||||
// TRAVEL (move towards target until close enough) (rpg and grind is disabled)
|
||||
// WORK (grind/rpg until the target is no longer active) (rpg and grind is enabled on quest mobs)
|
||||
// COOLDOWN (wait some time free to do what the bot wants)
|
||||
// EXPIRE (if any of the above actions take too long pick a new target)
|
||||
class TravelTarget : AiObject
|
||||
{
|
||||
public:
|
||||
TravelTarget(PlayerbotAI* botAI) : AiObject(botAI), m_status(TRAVEL_STATUS_NONE), startTime(getMSTime()){};
|
||||
TravelTarget(PlayerbotAI* botAI, TravelDestination* tDestination1, WorldPosition* wPosition1)
|
||||
: AiObject(botAI), m_status(TRAVEL_STATUS_NONE), startTime(getMSTime())
|
||||
{
|
||||
setTarget(tDestination1, wPosition1);
|
||||
}
|
||||
|
||||
~TravelTarget();
|
||||
|
||||
void setTarget(TravelDestination* tDestination1, WorldPosition* wPosition1, bool groupCopy1 = false);
|
||||
void setStatus(TravelStatus status);
|
||||
void setExpireIn(uint32 expireMs) { statusTime = getExpiredTime() + expireMs; }
|
||||
|
||||
void incRetry(bool isMove)
|
||||
{
|
||||
if (isMove)
|
||||
++moveRetryCount;
|
||||
else
|
||||
++extendRetryCount;
|
||||
}
|
||||
|
||||
void setRetry(bool isMove, uint32 newCount = 0)
|
||||
{
|
||||
if (isMove)
|
||||
moveRetryCount = newCount;
|
||||
else
|
||||
extendRetryCount = newCount;
|
||||
}
|
||||
|
||||
void setForced(bool forced1) { forced = forced1; }
|
||||
|
||||
void setRadius(float radius1) { radius = radius1; }
|
||||
|
||||
void copyTarget(TravelTarget* target);
|
||||
void addVisitors();
|
||||
void releaseVisitors();
|
||||
|
||||
float distance(Player* bot);
|
||||
|
||||
WorldPosition* getPosition();
|
||||
TravelDestination* getDestination();
|
||||
|
||||
uint32 getEntry()
|
||||
{
|
||||
if (!tDestination)
|
||||
return 0;
|
||||
|
||||
return tDestination->getEntry();
|
||||
}
|
||||
|
||||
PlayerbotAI* getAi() { return botAI; }
|
||||
|
||||
uint32 getExpiredTime() { return getMSTime() - startTime; }
|
||||
|
||||
uint32 getTimeLeft() { return statusTime - getExpiredTime(); }
|
||||
|
||||
uint32 getMaxTravelTime();
|
||||
uint32 getRetryCount(bool isMove) { return isMove ? moveRetryCount : extendRetryCount; }
|
||||
|
||||
bool isTraveling();
|
||||
bool isActive();
|
||||
bool isWorking();
|
||||
bool isPreparing();
|
||||
bool isMaxRetry(bool isMove) { return isMove ? (moveRetryCount > 5) : (extendRetryCount > 5); }
|
||||
|
||||
TravelStatus getStatus() { return m_status; }
|
||||
|
||||
TravelState getTravelState();
|
||||
|
||||
bool isGroupCopy() { return groupCopy; }
|
||||
|
||||
bool isForced() { return forced; }
|
||||
|
||||
protected:
|
||||
TravelStatus m_status;
|
||||
|
||||
uint32 startTime;
|
||||
uint32 statusTime = 0;
|
||||
|
||||
bool forced = false;
|
||||
float radius = 0.f;
|
||||
bool groupCopy = false;
|
||||
bool visitor = true;
|
||||
|
||||
uint32 extendRetryCount = 0;
|
||||
uint32 moveRetryCount = 0;
|
||||
|
||||
TravelDestination* tDestination = nullptr;
|
||||
WorldPosition* wPosition = nullptr;
|
||||
};
|
||||
|
||||
// General container for all travel destinations.
|
||||
class TravelMgr
|
||||
{
|
||||
public:
|
||||
TravelMgr(){};
|
||||
|
||||
static TravelMgr* instance()
|
||||
{
|
||||
static TravelMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void Clear();
|
||||
void LoadQuestTravelTable();
|
||||
|
||||
template <class D, class W, class URBG>
|
||||
void weighted_shuffle(D first, D last, W first_weight, W last_weight, URBG&& g)
|
||||
{
|
||||
while (first != last && first_weight != last_weight)
|
||||
{
|
||||
std::discrete_distribution<int> dd(first_weight, last_weight);
|
||||
auto i = dd(g);
|
||||
|
||||
if (i)
|
||||
{
|
||||
std::swap(*first, *std::next(first, i));
|
||||
std::swap(*first_weight, *std::next(first_weight, i));
|
||||
}
|
||||
++first;
|
||||
++first_weight;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<WorldPosition*> getNextPoint(WorldPosition* center, std::vector<WorldPosition*> points,
|
||||
uint32 amount = 1);
|
||||
std::vector<WorldPosition> getNextPoint(WorldPosition center, std::vector<WorldPosition> points, uint32 amount = 1);
|
||||
QuestStatusData* getQuestStatus(Player* bot, uint32 questId);
|
||||
bool getObjectiveStatus(Player* bot, Quest const* pQuest, uint32 objective);
|
||||
uint32 getDialogStatus(Player* pPlayer, int32 questgiver, Quest const* pQuest);
|
||||
std::vector<TravelDestination*> getQuestTravelDestinations(Player* bot, int32 questId = -1, bool ignoreFull = false,
|
||||
bool ignoreInactive = false, float maxDistance = 5000,
|
||||
bool ignoreObjectives = false);
|
||||
std::vector<TravelDestination*> getRpgTravelDestinations(Player* bot, bool ignoreFull = false,
|
||||
bool ignoreInactive = false, float maxDistance = 5000);
|
||||
std::vector<TravelDestination*> getExploreTravelDestinations(Player* bot, bool ignoreFull = false,
|
||||
bool ignoreInactive = false);
|
||||
std::vector<TravelDestination*> getGrindTravelDestinations(Player* bot, bool ignoreFull = false,
|
||||
bool ignoreInactive = false, float maxDistance = 5000);
|
||||
std::vector<TravelDestination*> getBossTravelDestinations(Player* bot, bool ignoreFull = false,
|
||||
bool ignoreInactive = false, float maxDistance = 25000);
|
||||
|
||||
void setNullTravelTarget(Player* player);
|
||||
|
||||
void addMapTransfer(WorldPosition start, WorldPosition end, float portalDistance = 0.1f, bool makeShortcuts = true);
|
||||
void loadMapTransfers();
|
||||
float mapTransDistance(WorldPosition start, WorldPosition end);
|
||||
float fastMapTransDistance(WorldPosition start, WorldPosition end);
|
||||
|
||||
NullTravelDestination* nullTravelDestination = new NullTravelDestination();
|
||||
WorldPosition* nullWorldPosition = new WorldPosition();
|
||||
|
||||
void addBadVmap(uint32 mapId, uint8 x, uint8 y) { badVmap.push_back(std::make_tuple(mapId, x, y)); }
|
||||
|
||||
void addBadMmap(uint32 mapId, uint8 x, uint8 y) { badMmap.push_back(std::make_tuple(mapId, x, y)); }
|
||||
|
||||
bool isBadVmap(uint32 mapId, uint8 x, uint8 y)
|
||||
{
|
||||
return std::find(badVmap.begin(), badVmap.end(), std::make_tuple(mapId, x, y)) != badVmap.end();
|
||||
}
|
||||
|
||||
bool isBadMmap(uint32 mapId, uint8 x, uint8 y)
|
||||
{
|
||||
return std::find(badMmap.begin(), badMmap.end(), std::make_tuple(mapId, x, y)) != badMmap.end();
|
||||
}
|
||||
|
||||
void printGrid(uint32 mapId, int x, int y, std::string const type);
|
||||
void printObj(WorldObject* obj, std::string const type);
|
||||
|
||||
// protected:
|
||||
void logQuestError(uint32 errorNr, Quest* quest, uint32 objective = 0, uint32 unitId = 0, uint32 itemId = 0);
|
||||
|
||||
std::vector<uint32> avoidLoaded;
|
||||
|
||||
std::vector<QuestTravelDestination*> questGivers;
|
||||
std::vector<RpgTravelDestination*> rpgNpcs;
|
||||
std::vector<GrindTravelDestination*> grindMobs;
|
||||
std::vector<BossTravelDestination*> bossMobs;
|
||||
|
||||
std::unordered_map<uint32, ExploreTravelDestination*> exploreLocs;
|
||||
std::unordered_map<uint32, QuestContainer*> quests;
|
||||
|
||||
std::vector<std::tuple<uint32, uint8, uint8>> badVmap, badMmap;
|
||||
|
||||
std::unordered_map<std::pair<uint32, uint32>, std::vector<mapTransfer>, boost::hash<std::pair<uint32, uint32>>>
|
||||
mapTransfersMap;
|
||||
};
|
||||
|
||||
#define sTravelMgr TravelMgr::instance()
|
||||
|
||||
#endif
|
||||
2586
src/helper/TravelNode.cpp
Normal file
2586
src/helper/TravelNode.cpp
Normal file
File diff suppressed because it is too large
Load Diff
600
src/helper/TravelNode.h
Normal file
600
src/helper/TravelNode.h
Normal file
@@ -0,0 +1,600 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_TRAVELNODE_H
|
||||
#define _PLAYERBOT_TRAVELNODE_H
|
||||
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "TravelMgr.h"
|
||||
|
||||
// THEORY
|
||||
//
|
||||
// Pathfinding in (c)mangos is based on detour recast an opensource nashmesh creation and pathfinding codebase.
|
||||
// This system is used for mob and npc pathfinding and in this codebase also for bots.
|
||||
// Because mobs and npc movement is based on following a player or a set path the PathGenerator is limited to 296y.
|
||||
// This means that when trying to find a path from A to B distances beyond 296y will be a best guess often moving in a
|
||||
// straight path. Bots would get stuck moving from Northshire to Stormwind because there is no 296y path that doesn't
|
||||
// go (initially) the wrong direction.
|
||||
//
|
||||
// To remedy this limitation without altering the PathGenerator limits too much this node system was introduced.
|
||||
//
|
||||
// <S> ---> [N1] ---> [N2] ---> [N3] ---> <E>
|
||||
//
|
||||
// Bot at <S> wants to move to <E>
|
||||
// [N1],[N2],[N3] are predefined nodes for wich we know we can move from [N1] to [N2] and from [N2] to [N3] but not
|
||||
// from [N1] to [N3] If we can move fom [S] to [N1] and from [N3] to [E] we have a complete route to travel.
|
||||
//
|
||||
// Termonology:
|
||||
// Node: a location on a map for which we know bots are likely to want to travel to or need to travel past to reach
|
||||
// other nodes. Link: the connection between two nodes. A link signifies that the bot can travel from one node to
|
||||
// another. A link is one-directional. Path: the waypointpath returned by the standard PathGenerator to move from one
|
||||
// node (or position) to another. A path can be imcomplete or empty which means there is no link. Route: the list of
|
||||
// nodes that give the shortest route from a node to a distant node. Routes are calculated using a standard A* search
|
||||
// based on links.
|
||||
//
|
||||
// On server start saved nodes and links are loaded. Paths and routes are calculated on the fly but saved for future
|
||||
// use. Nodes can be added and removed realtime however because bots access the nodes from different threads this
|
||||
// requires a locking mechanism.
|
||||
//
|
||||
// Initially the current nodes have been made:
|
||||
// Flightmasters and Inns (Bots can use these to fast-travel so eventually they will be included in the route
|
||||
// calculation) WorldBosses and Unique bosses in instances (These are a logical places bots might want to go in
|
||||
// instances) Player start spawns (Obviously all lvl1 bots will spawn and move from here) Area triggers locations with
|
||||
// teleport and their teleport destinations (These used to travel in or between maps) Transports including elevators
|
||||
// (Again used to travel in and in maps) (sub)Zone means (These are the center most point for each sub-zone which is
|
||||
// good for global coverage)
|
||||
//
|
||||
// To increase coverage/linking extra nodes can be automatically be created.
|
||||
// Current implentation places nodes on paths (including complete) at sub-zone transitions or randomly.
|
||||
// After calculating possible links the node is removed if it does not create local coverage.
|
||||
//
|
||||
|
||||
enum class TravelNodePathType : uint8
|
||||
{
|
||||
none = 0,
|
||||
walk = 1,
|
||||
portal = 2,
|
||||
transport = 3,
|
||||
flightPath = 4,
|
||||
teleportSpell = 5
|
||||
};
|
||||
|
||||
// A connection between two nodes.
|
||||
class TravelNodePath
|
||||
{
|
||||
public:
|
||||
// Legacy Constructor for travelnodestore
|
||||
// TravelNodePath(float distance1, float extraCost1, bool portal1 = false, uint32 portalId1 = 0, bool transport1 =
|
||||
// false, bool calculated = false, uint8 maxLevelMob1 = 0, uint8 maxLevelAlliance1 = 0, uint8 maxLevelHorde1 = 0,
|
||||
// float swimDistance1 = 0, bool flightPath1 = false);
|
||||
|
||||
// Constructor
|
||||
TravelNodePath(float distance = 0.1f, float extraCost = 0, uint8 pathType = (uint8)TravelNodePathType::walk,
|
||||
uint32 pathObject = 0, bool calculated = false, std::vector<uint8> maxLevelCreature = {0, 0, 0},
|
||||
float swimDistance = 0)
|
||||
: extraCost(extraCost),
|
||||
calculated(calculated),
|
||||
distance(distance),
|
||||
maxLevelCreature(maxLevelCreature),
|
||||
swimDistance(swimDistance),
|
||||
pathType(TravelNodePathType(pathType)),
|
||||
pathObject(pathObject) // reorder args - whipowill
|
||||
{
|
||||
if (pathType != (uint8)TravelNodePathType::walk)
|
||||
complete = true;
|
||||
};
|
||||
|
||||
TravelNodePath(TravelNodePath* basePath)
|
||||
{
|
||||
complete = basePath->complete;
|
||||
path = basePath->path;
|
||||
extraCost = basePath->extraCost;
|
||||
calculated = basePath->calculated;
|
||||
distance = basePath->distance;
|
||||
maxLevelCreature = basePath->maxLevelCreature;
|
||||
swimDistance = basePath->swimDistance;
|
||||
pathType = basePath->pathType;
|
||||
pathObject = basePath->pathObject;
|
||||
};
|
||||
|
||||
// Getters
|
||||
bool getComplete() { return complete || pathType != TravelNodePathType::walk; }
|
||||
std::vector<WorldPosition> getPath() { return path; }
|
||||
|
||||
TravelNodePathType getPathType() { return pathType; }
|
||||
uint32 getPathObject() { return pathObject; }
|
||||
|
||||
float getDistance() { return distance; }
|
||||
float getSwimDistance() { return swimDistance; }
|
||||
float getExtraCost() { return extraCost; }
|
||||
std::vector<uint8> getMaxLevelCreature() { return maxLevelCreature; }
|
||||
|
||||
void setCalculated(bool calculated1 = true) { calculated = calculated1; }
|
||||
|
||||
bool getCalculated() { return calculated; }
|
||||
|
||||
std::string const print();
|
||||
|
||||
// Setters
|
||||
void setComplete(bool complete1) { complete = complete1; }
|
||||
|
||||
void setPath(std::vector<WorldPosition> path1) { path = path1; }
|
||||
|
||||
void setPathAndCost(std::vector<WorldPosition> path1, float speed)
|
||||
{
|
||||
setPath(path1);
|
||||
calculateCost(true);
|
||||
extraCost = distance / speed;
|
||||
}
|
||||
|
||||
// void setPortal(bool portal1, uint32 portalId1 = 0) { portal = portal1; portalId = portalId1; }
|
||||
// void setTransport(bool transport1) { transport = transport1; }
|
||||
|
||||
void setPathType(TravelNodePathType pathType1) { pathType = pathType1; }
|
||||
|
||||
void setPathObject(uint32 pathObject1) { pathObject = pathObject1; }
|
||||
|
||||
void calculateCost(bool distanceOnly = false);
|
||||
|
||||
float getCost(Player* bot = nullptr, uint32 cGold = 0);
|
||||
uint32 getPrice();
|
||||
|
||||
private:
|
||||
// Does the path have all the points to get to the destination?
|
||||
bool complete = false;
|
||||
|
||||
// List of WorldPositions to get to the destination.
|
||||
std::vector<WorldPosition> path = {};
|
||||
|
||||
// The extra (loading/transport) time it takes to take this path.
|
||||
float extraCost = 0;
|
||||
|
||||
bool calculated = false;
|
||||
|
||||
// Derived distance in yards
|
||||
float distance = 0.1f;
|
||||
|
||||
// Calculated mobs level along the way.
|
||||
std::vector<uint8> maxLevelCreature = {0, 0, 0}; // mobs, horde, alliance
|
||||
|
||||
// Calculated swiming distances along the way.
|
||||
float swimDistance = 0;
|
||||
|
||||
TravelNodePathType pathType = TravelNodePathType::walk;
|
||||
uint32 pathObject = 0;
|
||||
|
||||
/*
|
||||
//Is the path a portal/teleport to the destination?
|
||||
bool portal = false;
|
||||
//Area trigger Id
|
||||
uint32 portalId = 0;
|
||||
|
||||
//Is the path transport based?
|
||||
bool transport = false;
|
||||
|
||||
// Is the path a flightpath?
|
||||
bool flightPath = false;
|
||||
*/
|
||||
};
|
||||
|
||||
// A waypoint to travel from or to.
|
||||
// Each node knows which other nodes can be reached without help.
|
||||
class TravelNode
|
||||
{
|
||||
public:
|
||||
// Constructors
|
||||
TravelNode(){};
|
||||
|
||||
TravelNode(WorldPosition point1, std::string const nodeName1 = "Travel Node", bool important1 = false)
|
||||
{
|
||||
nodeName = nodeName1;
|
||||
point = point1;
|
||||
important = important1;
|
||||
}
|
||||
|
||||
TravelNode(TravelNode* baseNode)
|
||||
{
|
||||
nodeName = baseNode->nodeName;
|
||||
point = baseNode->point;
|
||||
important = baseNode->important;
|
||||
}
|
||||
|
||||
// Setters
|
||||
void setLinked(bool linked1) { linked = linked1; }
|
||||
void setPoint(WorldPosition point1) { point = point1; }
|
||||
|
||||
// Getters
|
||||
std::string const getName() { return nodeName; };
|
||||
WorldPosition* getPosition() { return &point; };
|
||||
std::unordered_map<TravelNode*, TravelNodePath>* getPaths() { return &paths; }
|
||||
std::unordered_map<TravelNode*, TravelNodePath*>* getLinks() { return &links; }
|
||||
bool isImportant() { return important; };
|
||||
bool isLinked() { return linked; }
|
||||
|
||||
bool isTransport()
|
||||
{
|
||||
for (auto const& link : *getLinks())
|
||||
if (link.second->getPathType() == TravelNodePathType::transport)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 getTransportId()
|
||||
{
|
||||
for (auto const& link : *getLinks())
|
||||
if (link.second->getPathType() == TravelNodePathType::transport)
|
||||
return link.second->getPathObject();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isPortal()
|
||||
{
|
||||
for (auto const& link : *getLinks())
|
||||
if (link.second->getPathType() == TravelNodePathType::portal)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isWalking()
|
||||
{
|
||||
for (auto link : *getLinks())
|
||||
if (link.second->getPathType() == TravelNodePathType::walk)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// WorldLocation shortcuts
|
||||
uint32 getMapId() { return point.getMapId(); }
|
||||
float getX() { return point.getX(); }
|
||||
float getY() { return point.getY(); }
|
||||
float getZ() { return point.getZ(); }
|
||||
float getO() { return point.getO(); }
|
||||
float getDistance(WorldPosition pos) { return point.distance(pos); }
|
||||
float getDistance(TravelNode* node) { return point.distance(node->getPosition()); }
|
||||
float fDist(TravelNode* node) { return point.fDist(node->getPosition()); }
|
||||
float fDist(WorldPosition pos) { return point.fDist(pos); }
|
||||
|
||||
TravelNodePath* setPathTo(TravelNode* node, TravelNodePath path = TravelNodePath(), bool isLink = true)
|
||||
{
|
||||
if (this != node)
|
||||
{
|
||||
paths[node] = path;
|
||||
if (isLink)
|
||||
links[node] = &paths[node];
|
||||
|
||||
return &paths[node];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool hasPathTo(TravelNode* node) { return paths.find(node) != paths.end(); }
|
||||
TravelNodePath* getPathTo(TravelNode* node) { return &paths[node]; }
|
||||
bool hasCompletePathTo(TravelNode* node) { return hasPathTo(node) && getPathTo(node)->getComplete(); }
|
||||
TravelNodePath* buildPath(TravelNode* endNode, Unit* bot, bool postProcess = false);
|
||||
|
||||
void setLinkTo(TravelNode* node, float distance = 0.1f)
|
||||
{
|
||||
if (this != node)
|
||||
{
|
||||
if (!hasPathTo(node))
|
||||
setPathTo(node, TravelNodePath(distance));
|
||||
else
|
||||
links[node] = &paths[node];
|
||||
}
|
||||
}
|
||||
|
||||
bool hasLinkTo(TravelNode* node) { return links.find(node) != links.end(); }
|
||||
float linkCostTo(TravelNode* node) { return paths.find(node)->second.getDistance(); }
|
||||
float linkDistanceTo(TravelNode* node) { return paths.find(node)->second.getDistance(); }
|
||||
void removeLinkTo(TravelNode* node, bool removePaths = false);
|
||||
|
||||
bool isEqual(TravelNode* compareNode);
|
||||
|
||||
// Removes links to other nodes that can also be reached by passing another node.
|
||||
bool isUselessLink(TravelNode* farNode);
|
||||
void cropUselessLink(TravelNode* farNode);
|
||||
bool cropUselessLinks();
|
||||
|
||||
// Returns all nodes that can be reached from this node.
|
||||
std::vector<TravelNode*> getNodeMap(bool importantOnly = false, std::vector<TravelNode*> ignoreNodes = {});
|
||||
|
||||
// Checks if it is even possible to route to this node.
|
||||
bool hasRouteTo(TravelNode* node)
|
||||
{
|
||||
if (routes.empty())
|
||||
for (auto mNode : getNodeMap())
|
||||
routes[mNode] = true;
|
||||
|
||||
return routes.find(node) != routes.end();
|
||||
};
|
||||
|
||||
void print(bool printFailed = true);
|
||||
|
||||
protected:
|
||||
// Logical name of the node
|
||||
std::string nodeName;
|
||||
// WorldPosition of the node.
|
||||
WorldPosition point;
|
||||
|
||||
// List of paths to other nodes.
|
||||
std::unordered_map<TravelNode*, TravelNodePath> paths;
|
||||
// List of links to other nodes.
|
||||
std::unordered_map<TravelNode*, TravelNodePath*> links;
|
||||
|
||||
// List of nodes and if there is 'any' route possible
|
||||
std::unordered_map<TravelNode*, bool> routes;
|
||||
|
||||
// This node should not be removed
|
||||
bool important = false;
|
||||
|
||||
// This node has been checked for nearby links
|
||||
bool linked = false;
|
||||
|
||||
// This node is a (moving) transport.
|
||||
// bool transport = false;
|
||||
// Entry of transport.
|
||||
// uint32 transportId = 0;
|
||||
};
|
||||
|
||||
class PortalNode : public TravelNode
|
||||
{
|
||||
public:
|
||||
PortalNode(TravelNode* baseNode) : TravelNode(baseNode){};
|
||||
|
||||
void SetPortal(TravelNode* baseNode, TravelNode* endNode, uint32 portalSpell)
|
||||
{
|
||||
nodeName = baseNode->getName();
|
||||
point = *baseNode->getPosition();
|
||||
paths.clear();
|
||||
links.clear();
|
||||
TravelNodePath path(0.1f, 0.1f, (uint8)TravelNodePathType::teleportSpell, portalSpell, true);
|
||||
setPathTo(endNode, path);
|
||||
};
|
||||
};
|
||||
|
||||
// Route step type
|
||||
enum PathNodeType
|
||||
{
|
||||
NODE_PREPATH = 0,
|
||||
NODE_PATH = 1,
|
||||
NODE_NODE = 2,
|
||||
NODE_PORTAL = 3,
|
||||
NODE_TRANSPORT = 4,
|
||||
NODE_FLIGHTPATH = 5,
|
||||
NODE_TELEPORT = 6
|
||||
};
|
||||
|
||||
struct PathNodePoint
|
||||
{
|
||||
WorldPosition point;
|
||||
PathNodeType type = NODE_PATH;
|
||||
uint32 entry = 0;
|
||||
};
|
||||
|
||||
// A complete list of points the bots has to walk to or teleport to.
|
||||
class TravelPath
|
||||
{
|
||||
public:
|
||||
TravelPath(){};
|
||||
TravelPath(std::vector<PathNodePoint> fullPath1) { fullPath = fullPath1; }
|
||||
TravelPath(std::vector<WorldPosition> path, PathNodeType type = NODE_PATH, uint32 entry = 0)
|
||||
{
|
||||
addPath(path, type, entry);
|
||||
}
|
||||
|
||||
void addPoint(PathNodePoint point) { fullPath.push_back(point); }
|
||||
void addPoint(WorldPosition point, PathNodeType type = NODE_PATH, uint32 entry = 0)
|
||||
{
|
||||
fullPath.push_back(PathNodePoint{point, type, entry});
|
||||
}
|
||||
void addPath(std::vector<WorldPosition> path, PathNodeType type = NODE_PATH, uint32 entry = 0)
|
||||
{
|
||||
for (auto& p : path)
|
||||
{
|
||||
fullPath.push_back(PathNodePoint{p, type, entry});
|
||||
};
|
||||
}
|
||||
void addPath(std::vector<PathNodePoint> newPath)
|
||||
{
|
||||
fullPath.insert(fullPath.end(), newPath.begin(), newPath.end());
|
||||
}
|
||||
void clear() { fullPath.clear(); }
|
||||
|
||||
bool empty() { return fullPath.empty(); }
|
||||
std::vector<PathNodePoint> getPath() { return fullPath; }
|
||||
WorldPosition getFront() { return fullPath.front().point; }
|
||||
WorldPosition getBack() { return fullPath.back().point; }
|
||||
|
||||
std::vector<WorldPosition> getPointPath()
|
||||
{
|
||||
std::vector<WorldPosition> retVec;
|
||||
for (auto const& p : fullPath)
|
||||
retVec.push_back(p.point);
|
||||
return retVec;
|
||||
};
|
||||
|
||||
bool makeShortCut(WorldPosition startPos, float maxDist);
|
||||
bool shouldMoveToNextPoint(WorldPosition startPos, std::vector<PathNodePoint>::iterator beg,
|
||||
std::vector<PathNodePoint>::iterator ed, std::vector<PathNodePoint>::iterator p,
|
||||
float& moveDist, float maxDist);
|
||||
WorldPosition getNextPoint(WorldPosition startPos, float maxDist, TravelNodePathType& pathType, uint32& entry);
|
||||
|
||||
std::ostringstream const print();
|
||||
|
||||
private:
|
||||
std::vector<PathNodePoint> fullPath;
|
||||
};
|
||||
|
||||
// An stored A* search that gives a complete route from one node to another.
|
||||
class TravelNodeRoute
|
||||
{
|
||||
public:
|
||||
TravelNodeRoute() {}
|
||||
TravelNodeRoute(std::vector<TravelNode*> nodes1) { nodes = nodes1; /*currentNode = route.begin();*/ }
|
||||
|
||||
bool isEmpty() { return nodes.empty(); }
|
||||
|
||||
bool hasNode(TravelNode* node) { return findNode(node) != nodes.end(); }
|
||||
float getTotalDistance();
|
||||
|
||||
std::vector<TravelNode*> getNodes() { return nodes; }
|
||||
|
||||
TravelPath buildPath(std::vector<WorldPosition> pathToStart = {}, std::vector<WorldPosition> pathToEnd = {},
|
||||
Unit* bot = nullptr);
|
||||
|
||||
std::ostringstream const print();
|
||||
|
||||
private:
|
||||
std::vector<TravelNode*>::iterator findNode(TravelNode* node)
|
||||
{
|
||||
return std::find(nodes.begin(), nodes.end(), node);
|
||||
}
|
||||
std::vector<TravelNode*> nodes;
|
||||
};
|
||||
|
||||
// A node container to aid A* calculations with nodes.
|
||||
class TravelNodeStub
|
||||
{
|
||||
public:
|
||||
TravelNodeStub(TravelNode* dataNode1) { dataNode = dataNode1; }
|
||||
|
||||
TravelNode* dataNode;
|
||||
float m_f = 0.0, m_g = 0.0, m_h = 0.0;
|
||||
bool open = false, close = false;
|
||||
TravelNodeStub* parent = nullptr;
|
||||
uint32 currentGold = 0;
|
||||
};
|
||||
|
||||
// The container of all nodes.
|
||||
class TravelNodeMap
|
||||
{
|
||||
public:
|
||||
TravelNodeMap(){};
|
||||
TravelNodeMap(TravelNodeMap* baseMap);
|
||||
|
||||
static TravelNodeMap* instance()
|
||||
{
|
||||
static TravelNodeMap instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
TravelNode* addNode(WorldPosition pos, std::string const preferedName = "Travel Node", bool isImportant = false,
|
||||
bool checkDuplicate = true, bool transport = false, uint32 transportId = 0);
|
||||
void removeNode(TravelNode* node);
|
||||
bool removeNodes()
|
||||
{
|
||||
if (m_nMapMtx.try_lock_for(std::chrono::seconds(10)))
|
||||
{
|
||||
for (auto& node : m_nodes)
|
||||
removeNode(node);
|
||||
|
||||
m_nMapMtx.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
void fullLinkNode(TravelNode* startNode, Unit* bot);
|
||||
|
||||
// Get all nodes
|
||||
std::vector<TravelNode*> getNodes() { return m_nodes; }
|
||||
std::vector<TravelNode*> getNodes(WorldPosition pos, float range = -1);
|
||||
|
||||
// Find nearest node.
|
||||
TravelNode* getNode(TravelNode* sameNode)
|
||||
{
|
||||
for (auto& node : m_nodes)
|
||||
{
|
||||
if (node->getName() == sameNode->getName() && node->getPosition() == sameNode->getPosition())
|
||||
return node;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TravelNode* getNode(WorldPosition pos, std::vector<WorldPosition>& ppath, Unit* bot = nullptr, float range = -1);
|
||||
TravelNode* getNode(WorldPosition pos, Unit* bot = nullptr, float range = -1)
|
||||
{
|
||||
std::vector<WorldPosition> ppath;
|
||||
return getNode(pos, ppath, bot, range);
|
||||
}
|
||||
|
||||
// Get Random Node
|
||||
TravelNode* getRandomNode(WorldPosition pos)
|
||||
{
|
||||
std::vector<TravelNode*> rNodes = getNodes(pos);
|
||||
if (rNodes.empty())
|
||||
return nullptr;
|
||||
|
||||
return rNodes[urand(0, rNodes.size() - 1)];
|
||||
}
|
||||
|
||||
// Finds the best nodePath between two nodes
|
||||
TravelNodeRoute getRoute(TravelNode* start, TravelNode* goal, Player* bot = nullptr);
|
||||
|
||||
// Find the best node between two positions
|
||||
TravelNodeRoute getRoute(WorldPosition startPos, WorldPosition endPos, std::vector<WorldPosition>& startPath,
|
||||
Player* bot = nullptr);
|
||||
|
||||
// Find the full path between those locations
|
||||
static TravelPath getFullPath(WorldPosition startPos, WorldPosition endPos, Player* bot = nullptr);
|
||||
|
||||
// Manage/update nodes
|
||||
void manageNodes(Unit* bot, bool mapFull = false);
|
||||
|
||||
void setHasToGen() { hasToGen = true; }
|
||||
|
||||
void generateNpcNodes();
|
||||
void generateStartNodes();
|
||||
void generateAreaTriggerNodes();
|
||||
void generateNodes();
|
||||
void generateTransportNodes();
|
||||
void generateZoneMeanNodes();
|
||||
|
||||
void generateWalkPaths();
|
||||
void removeLowNodes();
|
||||
void removeUselessPaths();
|
||||
void calculatePathCosts();
|
||||
void generateTaxiPaths();
|
||||
void generatePaths();
|
||||
|
||||
void generateAll();
|
||||
|
||||
void printMap();
|
||||
|
||||
void printNodeStore();
|
||||
void saveNodeStore();
|
||||
void loadNodeStore();
|
||||
|
||||
bool cropUselessNode(TravelNode* startNode);
|
||||
TravelNode* addZoneLinkNode(TravelNode* startNode);
|
||||
TravelNode* addRandomExtNode(TravelNode* startNode);
|
||||
|
||||
void calcMapOffset();
|
||||
WorldPosition getMapOffset(uint32 mapId);
|
||||
|
||||
std::shared_timed_mutex m_nMapMtx;
|
||||
std::unordered_map<ObjectGuid, std::unordered_map<uint32, TravelNode*>> teleportNodes;
|
||||
|
||||
private:
|
||||
std::vector<TravelNode*> m_nodes;
|
||||
|
||||
std::vector<std::pair<uint32, WorldPosition>> mapOffsets;
|
||||
|
||||
bool hasToSave = false;
|
||||
bool hasToGen = false;
|
||||
bool hasToFullGen = false;
|
||||
};
|
||||
|
||||
#define sTravelNodeMap TravelNodeMap::instance()
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user