Merge branch 'master' into eots-fixes

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

View File

@@ -172,6 +172,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal)
if (!action)
{
//LOG_ERROR("playerbots", "Action: {} - is UNKNOWN - c:{} l:{}", actionNode->getName().c_str(), botAI->GetBot()->getClass(), botAI->GetBot()->GetLevel());
LogAction("A:%s - UNKNOWN", actionNode->getName().c_str());
}
else if (action->isUseful())

View File

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

View File

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

View File

@@ -6,6 +6,7 @@
#include "Event.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "GuildMgr.h"
bool AutoLearnSpellAction::Execute(Event event)
{
@@ -27,7 +28,6 @@ bool AutoLearnSpellAction::Execute(Event event)
return true;
}
void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
{
if (sPlayerbotAIConfig->autoLearnTrainerSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
@@ -35,6 +35,22 @@ void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
if (sPlayerbotAIConfig->autoLearnQuestSpells && sRandomPlayerbotMgr->IsRandomBot(bot))// || (!botAI->GetMaster() && sRandomPlayerbotMgr->IsRandomBot(bot)))
LearnQuestSpells(out);
if (sPlayerbotAIConfig->randomBotGuildTalk)
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
{
std::string toSay = "";
if (urand(0, 3))
toSay = "Ding !";
else
toSay = "Yay level " + std::to_string(bot->GetLevel()) + " !";
guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL);
}
}
}
void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out)

View File

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

View File

@@ -447,7 +447,7 @@ bool EmoteActionBase::ReceiveEmote(Player* source, uint32 emote, bool verbal)
case TEXT_EMOTE_RASP:
emoteId = EMOTE_ONESHOT_RUDE;
textEmote = TEXT_EMOTE_RASP;
emoteText = "Right back at you, bub!", LANG_UNIVERSAL;
emoteText = "Right back at you, bub!"; // , LANG_UNIVERSAL;
break;
case TEXT_EMOTE_ROAR:
case TEXT_EMOTE_THREATEN:
@@ -671,9 +671,9 @@ bool EmoteAction::Execute(Event event)
if (pSource && (pSource->GetGUID() != bot->GetGUID()) && ((urand(0, 1) && bot->HasInArc(static_cast<float>(M_PI), pSource, 10.0f)) ||
(namlen > 1 && strstri(bot->GetName().c_str(), nam.c_str()))))
{
LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_TEXT_EMOTE {} from player {} <{}>",
/*LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_TEXT_EMOTE {} from player {} <{}>",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
bot->GetName(), text_emote, pSource->GetGUID().ToString().c_str(), pSource->GetName());
bot->GetName(), text_emote, pSource->GetGUID().ToString().c_str(), pSource->GetName());*/
emote = text_emote;
}
@@ -693,9 +693,9 @@ bool EmoteAction::Execute(Event event)
if ((pSource->GetGUID() != bot->GetGUID()) && (pSource->GetTarget() == bot->GetGUID() ||
(urand(0, 1) && bot->HasInArc(static_cast<float>(M_PI), pSource, 10.0f))))
{
LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_EMOTE {} from player {} <{}>",
/*LOG_INFO("playerbots", "Bot {} {}:{} <{}> received SMSG_EMOTE {} from player {} <{}>",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName(),
emoteId, pSource->GetGUID().ToString().c_str(), pSource->GetName());
emoteId, pSource->GetGUID().ToString().c_str(), pSource->GetName());*/
std::vector<uint32> types;
for (int32 i = sEmotesTextStore.GetNumRows(); i >= 0; --i)

View File

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

View File

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

View File

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

View File

@@ -12,6 +12,7 @@
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "GuildMgr.h"
bool LootAction::Execute(Event event)
{
@@ -416,6 +417,23 @@ bool StoreLootAction::Execute(Event event)
if (proto->Quality >= ITEM_QUALITY_RARE && !urand(0, 1) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT))
botAI->PlayEmote(TEXT_EMOTE_CHEER);
if (sPlayerbotAIConfig->randomBotGuildTalk && bot->GetGuildId() && urand(0, 10) && proto->Quality >= ITEM_QUALITY_RARE)
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
{
std::string toSay = "";
if (urand(0, 3))
toSay = "Yay I looted " + chat->FormatItem(proto) + " !";
else
toSay = "Guess who got a " + chat->FormatItem(proto) + " ? Me !";
guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL);
}
}
// std::ostringstream out;
// out << "Looting " << chat->FormatItem(proto);
// botAI->TellMasterNoFacing(out.str());

View File

@@ -859,6 +859,21 @@ void MovementAction::UpdateMovementState()
if (bot->IsFlying())
bot->UpdateSpeed(MOVE_FLIGHT, true);
Transport* newTransport = bot->GetMap()->GetTransportForPos(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot);
if (newTransport != bot->GetTransport())
{
LOG_DEBUG("playerbots", "Bot {} is on a transport", bot->GetName());
if (bot->GetTransport())
bot->GetTransport()->RemovePassenger(bot, true);
if (newTransport)
newTransport->AddPassenger(bot, true);
bot->StopMovingOnCurrentPos();
}
// Temporary speed increase in group
//if (botAI->HasRealPlayerMaster())
//bot->SetSpeedRate(MOVE_RUN, 1.1f);
@@ -1565,7 +1580,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj()
return false;
}
std::ostringstream name;
name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (aura)";
name << spellInfo->SpellName[LOCALE_enUS]; // << "] (aura)";
if (FleePosition(dynOwner->GetPosition(), radius)) {
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
lastTellTimer = time(NULL);
@@ -1605,7 +1620,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage()
continue;
}
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (spellInfo->IsPositive()) {
if (!spellInfo || spellInfo->IsPositive()) {
continue;
}
float radius = (float)goInfo->trap.diameter / 2;
@@ -1623,7 +1638,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage()
continue;
}
std::ostringstream name;
name << spellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; // << "] (object)";
name << spellInfo->SpellName[LOCALE_enUS]; // << "] (object)";
if (FleePosition(go->GetPosition(), radius)) {
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
lastTellTimer = time(NULL);
@@ -1672,7 +1687,7 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
break;
}
std::ostringstream name;
name << triggerSpellInfo->SpellName[sWorld->GetDefaultDbcLocale()]; //<< "] (unit)";
name << triggerSpellInfo->SpellName[LOCALE_enUS]; //<< "] (unit)";
if (FleePosition(unit->GetPosition(), radius)) {
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
lastTellTimer = time(NULL);

View File

@@ -10,6 +10,16 @@
#include "GuildMgr.h"
#include <regex>
static const std::unordered_set<std::string> noReplyMsgs = {
"join", "leave", "follow", "attack", "pull", "flee", "reset", "reset ai",
"all ?", "talents", "talents list", "talents auto", "talk", "stay", "stats",
"who", "items", "leave", "join", "repair", "summon", "nc ?", "co ?", "de ?",
"dead ?", "follow", "los", "guard", "do accept invitation", "stats", "react ?",
"reset strats", "home",
};
static const std::unordered_set<std::string> noReplyMsgParts = { "+", "-","@" , "follow target", "focus heal", "cast ", "accept [", "e [", "destroy [", "go zone" };
static const std::unordered_set<std::string> noReplyMsgStarts = { "e ", "accept ", "cast ", "destroy " };
SayAction::SayAction(PlayerbotAI* botAI) : Action(botAI, "say"), Qualified()
{
}
@@ -105,6 +115,9 @@ bool SayAction::isUseful()
if (!botAI->AllowActivity())
return false;
if (botAI->HasStrategy("silent", BotState::BOT_STATE_NON_COMBAT))
return false;
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
return (time(nullptr) - lastSaid) > 30;
}
@@ -114,6 +127,35 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
ChatReplyType replyType = REPLY_NOT_UNDERSTAND; // default not understand
std::string respondsText = "";
// if we're just commanding bots around, don't respond...
// first one is for exact word matches
if (noReplyMsgs.find(msg) != noReplyMsgs.end()) {
/*std::ostringstream out;
out << "DEBUG ChatReplyDo decided to ignore exact blocklist match" << msg;
bot->Say(out.str(), LANG_UNIVERSAL);*/
return;
}
// second one is for partial matches like + or - where we change strats
if (std::any_of(noReplyMsgParts.begin(), noReplyMsgParts.end(), [&msg](const std::string& part) { return msg.find(part) != std::string::npos; })) {
/*std::ostringstream out;
out << "DEBUG ChatReplyDo decided to ignore partial blocklist match" << msg;
bot->Say(out.str(), LANG_UNIVERSAL);*/
return;
}
if (std::any_of(noReplyMsgStarts.begin(), noReplyMsgStarts.end(), [&msg](const std::string& start) {
return msg.find(start) == 0; // Check if the start matches the beginning of msg
})) {
/*std::ostringstream out;
out << "DEBUG ChatReplyDo decided to ignore start blocklist match" << msg;
bot->Say(out.str(), LANG_UNIVERSAL);*/
return;
}
ObjectGuid receiver = sCharacterCache->GetCharacterGuidByName(name);
Player* plr = ObjectAccessor::FindPlayer(receiver);
// Chat Logic
int32 verb_pos = -1;
int32 verb_type = -1;
@@ -149,18 +191,16 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
// Responds
for (uint32 i = 0; i < 8; i++)
{
// // blame gm with chat tag
// if (Player* plr = sObjectMgr->GetPlayer(ObjectGuid(HIGHGUID_PLAYER, guid1)))
// {
// if (plr->isGMChat())
// {
// replyType = REPLY_ADMIN_ABUSE;
// found = true;
// break;
// }
// }
//
if (word[i] == "hi" || word[i] == "hey" || word[i] == "hello" || word[i] == "wazzup")
// blame gm with chat tag
if (plr && plr->isGMChat())
{
replyType = REPLY_ADMIN_ABUSE;
found = true;
break;
}
if (word[i] == "hi" || word[i] == "hey" || word[i] == "hello" || word[i] == "wazzup"
|| word[i] == "salut" || word[i] == "plop" || word[i] == "yo")
{
replyType = REPLY_HELLO;
found = true;
@@ -169,22 +209,25 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
if (verb_type < 4)
{
if (word[i] == "am" || word[i] == "are" || word[i] == "is")
if (word[i] == "am" || word[i] == "are" || word[i] == "is" || word[i] == "suis" || word[i] == "as" || word[i] == "est"
|| word[i] == "dois" || word[i] == "doit")
{
verb_pos = i;
verb_type = 2; // present
if (verb_pos == 0)
is_quest = 1;
}
else if (word[i] == "will")
else if (word[i] == "will" || word[i] == "vais" || word[i] == "sera")
{
verb_pos = i;
verb_type = 3; // future
}
else if (word[i] == "was" || word[i] == "were")
else if (word[i] == "was" || word[i] == "were" || word[i] == "été" || word[i] == "ai" || word[i] == "eu" || word[i] == "étions" || word[i] == "etion" )
{
verb_pos = i;
verb_type = 1; // past
}
else if (word[i] == "shut" || word[i] == "noob")
else if (word[i] == "shut" || word[i] == "noob" || word[i] == "tg")
{
if (msg.find(bot->GetName()) == std::string::npos)
{
@@ -600,22 +643,20 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
{
if (type == CHAT_MSG_WHISPER)
{
ObjectGuid receiver = sCharacterCache->GetCharacterGuidByName(name);
if (!receiver || !receiver.IsPlayer() || !ObjectAccessor::FindPlayer(receiver))
if (plr)
{
return;
}
if (bot->GetTeamId() == TEAM_ALLIANCE)
{
bot->Whisper(c, LANG_COMMON, ObjectAccessor::FindPlayer(receiver));
}
else
{
bot->Whisper(c, LANG_ORCISH, ObjectAccessor::FindPlayer(receiver));
if (bot->GetTeamId() == TEAM_ALLIANCE)
{
bot->Whisper(c, LANG_COMMON, plr);
}
else
{
bot->Whisper(c, LANG_ORCISH, plr);
}
}
}
if (type == CHAT_MSG_SAY)
else if (type == CHAT_MSG_SAY)
{
if (bot->GetTeamId() == TEAM_ALLIANCE)
bot->Say(respondsText, LANG_COMMON);
@@ -623,7 +664,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
bot->Say(respondsText, LANG_ORCISH);
}
if (type == CHAT_MSG_YELL)
else if (type == CHAT_MSG_YELL)
{
if (bot->GetTeamId() == TEAM_ALLIANCE)
bot->Yell(respondsText, LANG_COMMON);
@@ -631,9 +672,9 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
bot->Yell(respondsText, LANG_ORCISH);
}
if (type == CHAT_MSG_GUILD)
else if (type == CHAT_MSG_GUILD)
{
if (!bot->GetGuildId())
if (!bot->GetGuildId() || !sPlayerbotAIConfig->randomBotGuildTalk)
return;
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
@@ -645,4 +686,4 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32 type, uint32 guid1, uint32
}
GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<time_t>("last said", "chat")->Set(time(nullptr) + urand(5, 25));
}
}
}

View File

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

View File

@@ -3,6 +3,7 @@
*/
#include "SuggestWhatToDoAction.h"
#include "ServerFacade.h"
#include "ChannelMgr.h"
#include "Event.h"
#include "ItemVisitors.h"
@@ -11,24 +12,47 @@
#include "Playerbots.h"
#include "PlayerbotTextMgr.h"
#include "GuildMgr.h"
#include "Config.h"
std::map<std::string, uint8> SuggestWhatToDoAction::instances;
#include <functional>
enum eTalkType
{
/*0x18*/ General = ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
/*0x3C*/ Trade = ChannelFlags::CHANNEL_FLAG_CITY | ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG | ChannelFlags::CHANNEL_FLAG_TRADE,
/*0x18*/ LocalDefence = ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
/*x038*/ GuildRecruitment = ChannelFlags::CHANNEL_FLAG_CITY | ChannelFlags::CHANNEL_FLAG_GENERAL | ChannelFlags::CHANNEL_FLAG_NOT_LFG,
/*0x50*/ LookingForGroup = ChannelFlags::CHANNEL_FLAG_LFG | ChannelFlags::CHANNEL_FLAG_GENERAL
};
std::map<std::string, uint8> SuggestDungeonAction::instances;
std::map<std::string, uint8> SuggestWhatToDoAction::factions;
SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string const name) : InventoryAction(botAI, name)
SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string const name)
: InventoryAction{ botAI, name }
, _dbc_locale{ sWorld->GetDefaultDbcLocale() }
{
suggestions.push_back(&SuggestWhatToDoAction::specificQuest);
suggestions.push_back(&SuggestWhatToDoAction::grindReputation);
suggestions.push_back(&SuggestWhatToDoAction::something);
suggestions.push_back(std::bind(&SuggestWhatToDoAction::specificQuest, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindReputation, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::something, this));
suggestions.push_back(std::bind(&SuggestWhatToDoAction::grindMaterials, this));
}
bool SuggestWhatToDoAction::Execute(Event event)
bool SuggestWhatToDoAction::isUseful()
{
if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId())
return false;
std::string qualifier = "suggest what to do";
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
return (time(0) - lastSaid) > 30;
}
bool SuggestWhatToDoAction::Execute(Event event)
{
uint32 index = rand() % suggestions.size();
(this->*suggestions[index])();
auto fnct_ptr = suggestions[index];
fnct_ptr();
std::string const qualifier = "suggest what to do";
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
@@ -37,73 +61,6 @@ bool SuggestWhatToDoAction::Execute(Event event)
return true;
}
void SuggestWhatToDoAction::instance()
{
if (instances.empty())
{
instances["Ragefire Chasm"] = 15;
instances["Deadmines"] = 18;
instances["Wailing Caverns"] = 18;
instances["Shadowfang Keep"] = 25;
instances["Blackfathom Deeps"] = 20;
instances["Stockade"] = 20;
instances["Gnomeregan"] = 35;
instances["Razorfen Kraul"] = 35;
instances["Maraudon"] = 50;
instances["Scarlet Monestery"] = 40;
instances["Uldaman"] = 45;
instances["Dire Maul"] = 58;
instances["Scholomance"] = 59;
instances["Razorfen Downs"] = 40;
instances["Strathholme"] = 59;
instances["Zul'Farrak"] = 45;
instances["Blackrock Depths"] = 55;
instances["Temple of Atal'Hakkar"] = 55;
instances["Lower Blackrock Spire"] = 57;
instances["Hellfire Citidel"] = 65;
instances["Coilfang Reservoir"] = 65;
instances["Auchindoun"] = 65;
instances["Cavens of Time"] = 68;
instances["Tempest Keep"] = 69;
instances["Magister's Terrace"] = 70;
instances["Utgarde Keep"] = 75;
instances["The Nexus"] = 75;
instances["Ahn'kahet: The Old Kingdom"] = 75;
instances["Azjol-Nerub"] = 75;
instances["Drak'Tharon Keep"] = 75;
instances["Violet Hold"] = 80;
instances["Gundrak"] = 77;
instances["Halls of Stone"] = 77;
instances["Halls of Lightning"] = 77;
instances["Oculus"] = 77;
instances["Utgarde Pinnacle"] = 77;
instances["Trial of the Champion"] = 80;
instances["Forge of Souls"] = 80;
instances["Pit of Saron"] = 80;
instances["Halls of Reflection"] = 80;
}
std::vector<std::string> allowedInstances;
for (auto & instance : instances)
{
if (bot->GetLevel() >= instance.second) allowedInstances.push_back(instance.first);
}
if (allowedInstances.empty()) return;
std::map<std::string, std::string> placeholders;
placeholders["%role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
std::ostringstream itemout;
//itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r";
itemout << allowedInstances[urand(0, allowedInstances.size() - 1)];
placeholders["%instance"] = itemout.str();
spam(BOT_TEXT2("suggest_instance", placeholders), urand(0, 1) ? 0x50 : 0, urand(0, 2), urand(0, 2));
}
std::vector<uint32> SuggestWhatToDoAction::GetIncompletedQuests()
{
std::vector<uint32> result;
@@ -136,7 +93,52 @@ void SuggestWhatToDoAction::specificQuest()
placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%quest"] = chat->FormatQuest(quest);
spam(BOT_TEXT2("suggest_quest", placeholders), urand(0, 1) ? 0x18 : 0, urand(0, 2), urand(0, 2));
spam(BOT_TEXT2("suggest_quest", placeholders), urand(0, 1) ? eTalkType::General : 0, !urand(0, 2), !urand(0, 3));
}
void SuggestWhatToDoAction::grindMaterials()
{
/*if (bot->GetLevel() <= 5)
return;
auto result = CharacterDatabase.Query("SELECT distinct category, multiplier FROM ahbot_category where category not in ('other', 'quest', 'trade', 'reagent') and multiplier > 3 order by multiplier desc limit 10");
if (!result)
return;
std::map<std::string, double> categories;
do
{
Field* fields = result->Fetch();
categories[fields[0].Get<std::string>()] = fields[1].Get<float>();
} while (result->NextRow());
for (std::map<std::string, double>::iterator i = categories.begin(); i != categories.end(); ++i)
{
if (urand(0, 10) < 3) {
std::string name = i->first;
double multiplier = i->second;
for (int j = 0; j < ahbot::CategoryList::instance.size(); j++)
{
ahbot::Category* category = ahbot::CategoryList::instance[j];
if (name == category->GetName())
{
std::string item = category->GetLabel();
transform(item.begin(), item.end(), item.begin(), ::tolower);
std::ostringstream itemout;
itemout << "|c0000b000" << item << "|r";
item = itemout.str();
std::map<std::string, std::string> placeholders;
placeholders["%role"] = chat->formatClass(bot, AiFactory::GetPlayerSpecTab(bot));
placeholders["%category"] = item;
spam(BOT_TEXT2("suggest_trade", placeholders), urand(0, 1) ? 0x3C : 0x18, !urand(0, 2), !urand(0, 3));
return;
}
}
}
}*/
}
void SuggestWhatToDoAction::grindReputation()
@@ -185,12 +187,11 @@ void SuggestWhatToDoAction::grindReputation()
levels.push_back("exalted");
std::vector<std::string> allowedFactions;
for (std::map<std::string, uint8>::iterator i = factions.begin(); i != factions.end(); ++i)
for (const auto& i : factions)
{
if (bot->GetLevel() >= i->second)
allowedFactions.push_back(i->first);
if (bot->GetLevel() >= i.second)
allowedFactions.push_back(i.first);
}
if (allowedFactions.empty())
return;
@@ -207,7 +208,7 @@ void SuggestWhatToDoAction::grindReputation()
itemout << allowedFactions[urand(0, allowedFactions.size() - 1)];
placeholders["%faction"] = itemout.str();
spam(BOT_TEXT2("suggest_faction", placeholders), 0x18, true);
spam(BOT_TEXT2("suggest_faction", placeholders), eTalkType::General, true);
}
void SuggestWhatToDoAction::something()
@@ -221,10 +222,10 @@ void SuggestWhatToDoAction::something()
std::ostringstream out;
// out << "|cffb04040" << entry->area_name[0] << "|r";
out << entry->area_name[0];
out << entry->area_name[_dbc_locale];
placeholders["%zone"] = out.str();
spam(BOT_TEXT2("suggest_something", placeholders), urand(0, 1) ? 0x18 : 0, urand(0, 2), urand(0, 2));
spam(BOT_TEXT2("suggest_something", placeholders), urand(0, 1) ? eTalkType::General : 0, !urand(0, 2), !urand(0, 3));
}
void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, bool guild)
@@ -237,58 +238,65 @@ void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, b
if (!cMgr)
return;
AreaTableEntry const* zone = sAreaTableStore.LookupEntry(bot->GetMap()->GetZoneId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()));
if (!zone) return;
/*AreaTableEntry const* area = sAreaTableStore.LookupEntry(bot->GetMap()->GetAreaId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()));
if (!area) return;*/
for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i)
{
ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i);
if (!channel) continue;
for (AreaTableEntry const* current_zone : sAreaTableStore)
// combine full channel name
char channelName[100];
Channel* chn = nullptr;
if (channel->ChannelID == 24 || (channel->flags & CHANNEL_DBC_FLAG_LFG) != 0)
{
if (!current_zone)
continue;
std::string chanName = channel->pattern[_dbc_locale];
chn = cMgr->GetChannel(chanName, bot);
}
else
{
snprintf(channelName, 100, channel->pattern[_dbc_locale], zone->area_name[_dbc_locale]);
chn = cMgr->GetChannel(channelName, bot);
}
if (!chn)
continue;
// skip world chat here
if (chn->GetName() == "World")
continue;
// combine full channel name
char channelName[100];
Channel* chn = nullptr;
if ((channel->flags & CHANNEL_DBC_FLAG_LFG) != 0)
{
std::string chanName = channel->pattern[0];
chn = cMgr->GetChannel(chanName, bot);
}
else
{
snprintf(channelName, 100, channel->pattern[0], current_zone->area_name[0]);
chn = cMgr->GetChannel(channelName, bot);
}
if (!chn)
continue;
// skip world chat here
if (chn->GetName() == "World")
continue;
if (flags != 0 && chn->GetFlags() != flags)
continue;
if (flags != 0 && chn->GetFlags() != flags)
continue;
// skip local defense and universal defense
if (chn->GetChannelId() == 22 || chn->GetChannelId() == 23)
continue;
// skip local defense
//if (chn->GetFlags() == 0x18)
// continue;
// no filter, pick several options
if (flags == CHANNEL_FLAG_NONE)
{
channelNames.push_back(chn->GetName());
}
else
chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
// no filter, pick several options
if (flags == CHANNEL_FLAG_NONE)
{
channelNames.push_back(chn->GetName());
}
else
{
if (!bot->IsInChannel(chn))
chn->JoinChannel(bot, "");
chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
}
if (!channelNames.empty())
{
std::string randomName = channelNames[urand(0, channelNames.size() - 1)];
if (Channel* chn = cMgr->GetChannel(randomName, bot))
{
if (!bot->IsInChannel(chn))
chn->JoinChannel(bot, "");
chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
}
}
if (worldChat)
@@ -296,13 +304,13 @@ void SuggestWhatToDoAction::spam(std::string msg, uint8 flags, bool worldChat, b
if (Channel* worldChannel = cMgr->GetChannel("World", bot))
worldChannel->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL);
}
}
if (guild && bot->GetGuildId())
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
guild->BroadcastToGuild(bot->GetSession(), false, msg.c_str(), LANG_UNIVERSAL);
}
if (sPlayerbotAIConfig->randomBotGuildTalk && guild && bot->GetGuildId())
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
guild->BroadcastToGuild(bot->GetSession(), false, msg.c_str(), LANG_UNIVERSAL);
}
}
@@ -337,15 +345,88 @@ class FindTradeItemsVisitor : public IterateItemsVisitor
uint32 quality;
};
SuggestDungeonAction::SuggestDungeonAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest dungeon")
{
}
bool SuggestDungeonAction::Execute(Event event)
{
// TODO: use sPlayerbotDungeonSuggestionMgr
if (!sPlayerbotAIConfig->randomBotSuggestDungeons || bot->GetGroup()) return false;
if (instances.empty())
{
instances["Ragefire Chasm"] = 15;
instances["Deadmines"] = 18;
instances["Wailing Caverns"] = 18;
instances["Shadowfang Keep"] = 25;
instances["Blackfathom Deeps"] = 20;
instances["Stockade"] = 20;
instances["Gnomeregan"] = 35;
instances["Razorfen Kraul"] = 35;
instances["Maraudon"] = 50;
instances["Scarlet Monestery"] = 40;
instances["Uldaman"] = 45;
instances["Dire Maul"] = 58;
instances["Scholomance"] = 59;
instances["Razorfen Downs"] = 40;
instances["Strathholme"] = 59;
instances["Zul'Farrak"] = 45;
instances["Blackrock Depths"] = 55;
instances["Temple of Atal'Hakkar"] = 55;
instances["Lower Blackrock Spire"] = 57;
instances["Hellfire Citidel"] = 65;
instances["Coilfang Reservoir"] = 65;
instances["Auchindoun"] = 65;
instances["Cavens of Time"] = 68;
instances["Tempest Keep"] = 69;
instances["Magister's Terrace"] = 70;
instances["Utgarde Keep"] = 75;
instances["The Nexus"] = 75;
instances["Ahn'kahet: The Old Kingdom"] = 75;
instances["Azjol-Nerub"] = 75;
instances["Drak'Tharon Keep"] = 75;
instances["Violet Hold"] = 80;
instances["Gundrak"] = 77;
instances["Halls of Stone"] = 77;
instances["Halls of Lightning"] = 77;
instances["Oculus"] = 77;
instances["Utgarde Pinnacle"] = 77;
instances["Trial of the Champion"] = 80;
instances["Forge of Souls"] = 80;
instances["Pit of Saron"] = 80;
instances["Halls of Reflection"] = 80;
}
std::vector<std::string> allowedInstances;
for (const auto& instance : instances)
{
if (bot->GetLevel() >= instance.second)
allowedInstances.push_back(instance.first);
}
if (allowedInstances.empty()) return false;
std::map<std::string, std::string> placeholders;
placeholders["%role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot));
std::ostringstream itemout;
//itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r";
itemout << allowedInstances[urand(0, allowedInstances.size() - 1)];
placeholders["%instance"] = itemout.str();
spam(BOT_TEXT2("suggest_instance", placeholders), urand(0, 1) ? eTalkType::LookingForGroup : 0, !urand(0, 2), !urand(0, 3));
return true;
}
SuggestTradeAction::SuggestTradeAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest trade")
{
}
bool SuggestTradeAction::Execute(Event event)
{
if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId())
return false;
uint32 quality = urand(0, 100);
if (quality > 95)
quality = ITEM_QUALITY_LEGENDARY;
@@ -400,13 +481,7 @@ bool SuggestTradeAction::Execute(Event event)
placeholders["%item"] = chat->FormatItem(proto, count);
placeholders["%gold"] = chat->formatMoney(price);
spam(BOT_TEXT2("suggest_sell", placeholders), urand(0, 1) ? 0x3C : 0, urand(0, 1), urand(0, 5));
spam(BOT_TEXT2("suggest_sell", placeholders), urand(0, 1) ? eTalkType::Trade : 0, !urand(0, 2), urand(0, 5));
return true;
}
bool SuggestWhatToDoAction::isUseful()
{
std::string const qualifier = "suggest what to do";
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
return (time(nullptr) - lastSaid) > 30;
}

View File

@@ -18,19 +18,19 @@ class SuggestWhatToDoAction : public InventoryAction
bool isUseful() override;
protected:
typedef void (SuggestWhatToDoAction::*Suggestion)();
using Suggestion = std::function<void()>;
std::vector<Suggestion> suggestions;
void instance();
void specificQuest();
void grindReputation();
void grindMaterials();
void something();
void spam(std::string msg, uint8 flags = 0, bool worldChat = false, bool guild = false);
std::vector<uint32> GetIncompletedQuests();
private:
static std::map<std::string, uint8> instances;
static std::map<std::string, uint8> factions;
const int32_t _dbc_locale;
};
class SuggestTradeAction : public SuggestWhatToDoAction
@@ -42,4 +42,15 @@ class SuggestTradeAction : public SuggestWhatToDoAction
bool isUseful() override { return true; }
};
class SuggestDungeonAction : public SuggestWhatToDoAction
{
public:
SuggestDungeonAction(PlayerbotAI* botAI);
bool Execute(Event event) override;
bool isUseful() override { return true; }
private:
static std::map<std::string, uint8> instances;
};
#endif

View File

@@ -6,6 +6,7 @@
#include "Event.h"
#include "ChatHelper.h"
#include "Playerbots.h"
#include "World.h"
bool TellLosAction::Execute(Event event)
{
@@ -52,7 +53,7 @@ void TellLosAction::ListUnits(std::string const title, GuidVector units)
for (ObjectGuid const guid : units)
{
if (Unit* unit = botAI->GetUnit(guid)) {
botAI->TellMaster(unit->GetName());
botAI->TellMaster(unit->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()));
}
}
@@ -137,4 +138,4 @@ bool TellExpectedDpsAction::Execute(Event event)
float dps = AI_VALUE(float, "expected group dps");
botAI->TellMaster("Expected Group DPS: " + std::to_string(dps));
return true;
}
}

View File

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

View File

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

View File

@@ -6,6 +6,7 @@
#include "Event.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "GuildMgr.h"
bool XpGainAction::Execute(Event event)
{
@@ -32,6 +33,26 @@ bool XpGainAction::Execute(Event event)
p >> groupBonus; // 8 group bonus
}
if (sPlayerbotAIConfig->randomBotGuildTalk && bot->GetGuildId() && urand(0, 10))
{
Creature* creature = botAI->GetCreature(guid);
if (creature && (creature->isElite() || creature->isWorldBoss() || creature->GetLevel() > 61 || creature->GetLevel() > bot->GetLevel() + 4))
{
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
if (guild)
{
std::string toSay = "";
if (urand(0, 3))
toSay = "Wow I just killed " + creature->GetName() + " !";
else
toSay = "Awesome that " + creature->GetName() + " went down quickly !";
guild->BroadcastToGuild(bot->GetSession(), false, toSay, LANG_UNIVERSAL);
}
}
}
Unit* victim = nullptr;
if (guid)
victim = botAI->GetUnit(guid);

View File

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

View File

@@ -7,22 +7,24 @@
void EmoteStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("emote", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest what to do", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest trade", 1.0f), nullptr)));
if (sPlayerbotAIConfig->randomBotSuggestDungeons)
if (sPlayerbotAIConfig->randomBotEmote)
{
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("suggest dungeon", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("emote", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("receive text emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr)));
triggers.push_back(new TriggerNode("receive emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr)));
}
if (sPlayerbotAIConfig->randomBotTalk)
{
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("suggest what to do", 10.0f),
new NextAction("suggest dungeon", 3.0f),
new NextAction("suggest trade", 3.0f),
new NextAction("talk", 1.0f),
nullptr)));
}
if (sPlayerbotAIConfig->enableGreet)
{
triggers.push_back(new TriggerNode("new player nearby", NextAction::array(0, new NextAction("greet", 1.0f), nullptr)));
}
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("talk", 1.0f), nullptr)));
triggers.push_back(new TriggerNode("receive text emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr)));
triggers.push_back(new TriggerNode("receive emote", NextAction::array(0, new NextAction("emote", 10.0f), nullptr)));
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("rpg mount anim", 1.0f), nullptr)));
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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