From 274101c000ce48a5803164bc150a075bfe370c33 Mon Sep 17 00:00:00 2001 From: antony Date: Wed, 31 Jul 2024 12:26:48 +0200 Subject: [PATCH] Fix crash: PlayerbotAI::FindOilFor was making the server randomly crashing ChooseTravelTargetAction::getNewTarget: when active bot groupping was making the server crash as looking for unexisting params Several bug fixes and tweak to Quest and Group New fucntionnality: Bots will now share quests randomly to their party Bots will try to accomplish group member quest before moving on to new target Bots will try to sells items only after few levels ( 5 ) when in group When dropping a quest bots will try to select a new one they are on instead of idling for few time Bots will no longuer try to invite themselfs to group or if group is full Bots are now allowed to leave party by themself Bots in groupe if not leader are forbbiden to tag in bgs Bots in bot-groups no have a more limited range to look for grind target Polish logs --- src/PlayerbotAI.cpp | 28 +++++--- src/PlayerbotAI.h | 5 ++ src/TravelMgr.cpp | 24 ++----- src/strategy/actions/AcceptQuestAction.cpp | 2 +- src/strategy/actions/ActionContext.h | 3 + src/strategy/actions/ChatActionContext.h | 3 +- .../actions/ChooseRpgTargetAction.cpp | 24 +++++-- .../actions/ChooseTravelTargetAction.cpp | 12 ++-- src/strategy/actions/DropQuestAction.cpp | 20 +++++- src/strategy/actions/InviteToGroupAction.cpp | 25 +++++++ src/strategy/actions/InviteToGroupAction.h | 8 +++ src/strategy/actions/LeaveGroupAction.cpp | 6 +- src/strategy/actions/QuestAction.cpp | 7 +- src/strategy/actions/ShareQuestAction.cpp | 71 +++++++++++++++++++ src/strategy/actions/ShareQuestAction.h | 11 ++- .../actions/TalkToQuestGiverAction.cpp | 2 +- src/strategy/generic/MaintenanceStrategy.cpp | 1 + src/strategy/triggers/RpgTriggers.cpp | 4 ++ src/strategy/values/GrindTargetValue.cpp | 10 +++ 19 files changed, 214 insertions(+), 52 deletions(-) diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index ec0e9bf0..84fd4a48 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -4220,12 +4220,12 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const return stone; } -static const uint32 uPriorizedWizardOilIds[5] = +static const std::vector uPriorizedWizardOilIds = { MINOR_WIZARD_OIL, LESSER_WIZARD_OIL, BRILLIANT_WIZARD_OIL, WIZARD_OIL, SUPERIOR_WIZARD_OIL }; -static const uint32 uPriorizedManaOilIds[4] = +static const std::vector uPriorizedManaOilIds = { MINOR_MANA_OIL, LESSER_MANA_OIL, BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, }; @@ -4236,26 +4236,26 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const ItemTemplate const* pProto = weapon->GetTemplate(); if (pProto && (pProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || pProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF || pProto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER)) { - for (uint8 i = 0; i < std::size(uPriorizedWizardOilIds); ++i) + for (const auto& id : uPriorizedWizardOilIds) { - oil = FindConsumable(uPriorizedWizardOilIds[i]); + oil = FindConsumable(uPriorizedWizardOilIds[id]); if (!oil) - oil = FindConsumable(uPriorizedManaOilIds[i]); + oil = FindConsumable(uPriorizedManaOilIds[id]); if (oil) - return oil; + return oil; } } else if (pProto && (pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2)) { - for (uint8 i = 0; i < std::size(uPriorizedManaOilIds); ++i) + for (const auto& id : uPriorizedManaOilIds) { - oil = FindConsumable(uPriorizedManaOilIds[i]); + oil = FindConsumable(uPriorizedManaOilIds[id]); if (!oil) - oil = FindConsumable(uPriorizedWizardOilIds[i]); + oil = FindConsumable(uPriorizedWizardOilIds[id]); if (oil) - return oil; + return oil; } } @@ -4741,3 +4741,11 @@ uint8 PlayerbotAI::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool sw // no free position return NULL_SLOT; } +bool PlayerbotAI::IsSafe(Player* player) +{ + return player && player->GetMapId() == bot->GetMapId() && player->GetInstanceId() == bot->GetInstanceId() && !player->IsBeingTeleported(); +} +bool PlayerbotAI::IsSafe(WorldObject* obj) +{ + return obj && obj->GetMapId() == bot->GetMapId() && obj->GetInstanceId() == bot->GetInstanceId() && (!obj->IsPlayer() || !((Player*)obj)->IsBeingTeleported()); +} diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 79eea13e..cad89fd2 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -433,6 +433,11 @@ class PlayerbotAI : public PlayerbotAIBase bool AllowActive(ActivityType activityType); bool AllowActivity(ActivityType activityType = ALL_ACTIVITY, bool checkNow = false); + //Check if player is safe to use. + bool IsSafe(Player* player); + bool IsSafe(WorldObject* obj); + + bool HasCheat(BotCheatMask mask) { return ((uint32)mask & (uint32)cheatMask) != 0 || ((uint32)mask & (uint32)sPlayerbotAIConfig->botCheatMask) != 0; } BotCheatMask GetCheat() { return cheatMask; } void SetCheat(BotCheatMask mask) { cheatMask = mask; } diff --git a/src/TravelMgr.cpp b/src/TravelMgr.cpp index 14e288e3..9e96fdde 100644 --- a/src/TravelMgr.cpp +++ b/src/TravelMgr.cpp @@ -1096,27 +1096,10 @@ bool QuestRelationTravelDestination::isActive(Player* bot) if (AI_VALUE(uint8, "free quest log slots") < 5) return false; - if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest npc::" + std::to_string(entry))) //Noone has yellow exclamation mark. + //None has yellow exclamation mark. + if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest npc::" + std::to_string(entry))) if (!AI_VALUE2(bool, "group or", "following party,near leader,can accept quest low level npc::" + std::to_string(entry) + "need quest objective::" + std::to_string(questId))) //Noone can do this quest for a usefull reward. return false; - - // higher chance bot will do rewarding quest (better gear etc) - if (dialogStatus != DIALOG_STATUS_AVAILABLE) - { - if (dialogStatus != DIALOG_STATUS_LOW_LEVEL_AVAILABLE) - { - bool hasGoodReward = false; - for (uint8 i = 0; i < questTemplate->GetRewChoiceItemsCount(); ++i) - { - ItemUsage usage = AI_VALUE2_LAZY(ItemUsage, "item usage", questTemplate->RewardChoiceItemId[i]); - if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE) - { - hasGoodReward = true; - break; - } - } - } - } } else { @@ -1275,7 +1258,8 @@ std::string const RpgTravelDestination::getTitle() { std::ostringstream out; - out << "rpg npc "; + if(entry > 0) + out << "rpg npc "; out << " " << ChatHelper::FormatWorldEntry(entry); diff --git a/src/strategy/actions/AcceptQuestAction.cpp b/src/strategy/actions/AcceptQuestAction.cpp index 5f3c0171..dd00ccad 100644 --- a/src/strategy/actions/AcceptQuestAction.cpp +++ b/src/strategy/actions/AcceptQuestAction.cpp @@ -15,7 +15,7 @@ bool AcceptAllQuestsAction::ProcessQuest(Quest const* quest, Object* questGiver) if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - LOG_INFO("playerbots", "Quest [ {} ] accepted", quest->GetTitle()); + LOG_INFO("playerbots", "{} => Quest [ {} ] accepted", bot->GetName(), quest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] accepted", LANG_UNIVERSAL); } diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index e7caf4e2..795c7aab 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -8,6 +8,7 @@ #include "AddLootAction.h" #include "AttackAction.h" #include "AutoLearnSpellAction.h" +#include "ShareQuestAction.h" #include "BattleGroundTactics.h" #include "BattleGroundJoinAction.h" #include "BuyAction.h" @@ -152,6 +153,7 @@ class ActionContext : public NamedObjectContext creators["war stomp"] = &ActionContext::war_stomp; creators["auto talents"] = &ActionContext::auto_talents; creators["auto learn spell"] = &ActionContext::auto_learn_spell; + creators["auto share quest"] = &ActionContext::auto_share_quest; creators["auto teleport for level"] = &ActionContext::auto_teleport_for_level; creators["auto upgrade equip"] = &ActionContext::auto_upgrade_equip; creators["xp gain"] = &ActionContext::xp_gain; @@ -321,6 +323,7 @@ class ActionContext : public NamedObjectContext static Action* war_stomp(PlayerbotAI* botAI) { return new CastWarStompAction(botAI); } static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); } static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); } + static Action* auto_share_quest(PlayerbotAI* ai) { return new AutoShareQuestAction(ai); } static Action* auto_teleport_for_level(PlayerbotAI* botAI) { return new AutoTeleportForLevelAction(botAI); } static Action* auto_upgrade_equip(PlayerbotAI* botAI) { return new AutoUpgradeEquipAction(botAI); } static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); } diff --git a/src/strategy/actions/ChatActionContext.h b/src/strategy/actions/ChatActionContext.h index 8fb48c64..8476389d 100644 --- a/src/strategy/actions/ChatActionContext.h +++ b/src/strategy/actions/ChatActionContext.h @@ -171,7 +171,7 @@ class ChatActionContext : public NamedObjectContext creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut; creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut; creators["tell expected dps"] = &ChatActionContext::tell_expected_dps; - + creators["join"] = &ChatActionContext::join; } private: @@ -268,6 +268,7 @@ class ChatActionContext : public NamedObjectContext static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); } static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); } static Action* tell_expected_dps(PlayerbotAI* ai) { return new TellExpectedDpsAction(ai); } + static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); } }; #endif diff --git a/src/strategy/actions/ChooseRpgTargetAction.cpp b/src/strategy/actions/ChooseRpgTargetAction.cpp index 8054dd9e..08f4f3b5 100644 --- a/src/strategy/actions/ChooseRpgTargetAction.cpp +++ b/src/strategy/actions/ChooseRpgTargetAction.cpp @@ -129,11 +129,18 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP) bool ChooseRpgTargetAction::Execute(Event event) { TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target"); - - uint32 num = 0; + Player* master = botAI->GetMaster(); + GuidPosition masterRpgTarget; + if (master && master != bot && GET_PLAYERBOT_AI(master) && master->GetMapId() == bot->GetMapId() && !master->IsBeingTeleported()) + { + Player* player = botAI->GetMaster(); + GuidPosition masterRpgTarget = PAI_VALUE(GuidPosition, "rpg target"); + } + else + master = nullptr; std::unordered_map targets; - + uint32 num = 0; GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets"); GuidVector possibleObjects = AI_VALUE(GuidVector, "nearest game objects no los"); GuidVector possiblePlayers = AI_VALUE(GuidVector, "nearest friendly players"); @@ -216,10 +223,15 @@ bool ChooseRpgTargetAction::Execute(Event event) for (auto it = begin(targets); it != end(targets);) { - if (it->second == 0 || (hasGoodRelevance && it->second <= 1.0)) - { + //Remove empty targets. + if (it->second == 0) + it = targets.erase(it); + //Remove useless targets if there's any good ones + else if (hasGoodRelevance && it->second <= 1.0) + it = targets.erase(it); + //Remove useless targets if it's not masters target. + else if (!hasGoodRelevance && master && (!masterRpgTarget || it->first != masterRpgTarget)) it = targets.erase(it); - } else ++it; } diff --git a/src/strategy/actions/ChooseTravelTargetAction.cpp b/src/strategy/actions/ChooseTravelTargetAction.cpp index e27b9d62..54f5b05c 100644 --- a/src/strategy/actions/ChooseTravelTargetAction.cpp +++ b/src/strategy/actions/ChooseTravelTargetAction.cpp @@ -44,11 +44,11 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge foundTarget = SetGroupTarget(newTarget); //Join groups members //Empty bags/repair - if (!foundTarget && urand(1, 100) > 10) //90% chance + if (!foundTarget && urand(1, 100) > 10 && bot->GetLevel() > 5) //90% chance { - if (AI_VALUE2(bool, "group or", "should sell,can sell,following party,near leader") || - AI_VALUE2(bool, "group or", "should repair,can repair,following party,near leader") || - (AI_VALUE2(bool, "group or", "should sell,can ah sell,following party,near leader") && bot->GetLevel() > 5)) + if (AI_VALUE2(bool, "group or", "should sell,can sell,following party,near leader") || + AI_VALUE2(bool, "group or", "should repair,can repair,following party,near leader") + ) { foundTarget = SetRpgTarget(newTarget); //Go to town to sell items or repair } @@ -132,11 +132,13 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge //Dungeon in group. if (!foundTarget && urand(1, 100) > 50) //50% chance + { if (AI_VALUE(bool, "can fight boss")) { foundTarget = SetBossTarget( newTarget); //Go fight a (dungeon boss) } - + } + //Do quests (start, do, end) if (!foundTarget && urand(1, 100) > 5) //95% chance { diff --git a/src/strategy/actions/DropQuestAction.cpp b/src/strategy/actions/DropQuestAction.cpp index c659fec6..d595cccb 100644 --- a/src/strategy/actions/DropQuestAction.cpp +++ b/src/strategy/actions/DropQuestAction.cpp @@ -48,7 +48,7 @@ bool DropQuestAction::Execute(Event event) { const Quest* pQuest = sObjectMgr->GetQuestTemplate(entry); const std::string text_quest = ChatHelper::FormatQuest(pQuest); - LOG_INFO("playerbots", "Quest [ {} ] removed", pQuest->GetTitle()); + LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), pQuest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL); } @@ -103,7 +103,19 @@ bool CleanQuestLogAction::Execute(Event event) void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isGreen, bool hasProgress, bool isComplete) { + std::vector slots; for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) + slots.push_back(slot); + + if (wantNum < 100) + { + std::random_device rd; + std::mt19937 g(rd()); + + std::shuffle(slots.begin(), slots.end(), g); + } + + for (uint8 slot : slots) { uint32 questId = bot->GetQuestSlotQuestId(slot); if (!questId) @@ -153,6 +165,12 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG numQuest--; + if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) + { + const std::string text_quest = ChatHelper::FormatQuest(quest); + LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle()); + bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL); + } botAI->TellMaster("Quest removed" + chat->FormatQuest(quest)); } } diff --git a/src/strategy/actions/InviteToGroupAction.cpp b/src/strategy/actions/InviteToGroupAction.cpp index 58094fc6..0967a823 100644 --- a/src/strategy/actions/InviteToGroupAction.cpp +++ b/src/strategy/actions/InviteToGroupAction.cpp @@ -22,9 +22,16 @@ bool InviteToGroupAction::Invite(Player* player) if (!player || !player->IsInWorld()) return false; + if (bot == player) + return false; + if (!GET_PLAYERBOT_AI(player) && !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, true, player)) return false; + if (Group* group = player->GetGroup()) + if (!group->isRaidGroup() && group->GetMembersCount() > 4) + group->ConvertToRaid(); + WorldPacket p; uint32 roles_mask = 0; p << player->GetName(); @@ -46,6 +53,9 @@ bool InviteNearbyToGroupAction::Execute(Event event) if (player->GetGroup()) continue; + if (player == bot) + continue; + if (botAI) { if (botAI->GetGrouperType() == GrouperType::SOLO && !botAI->HasRealPlayerMaster()) // Do not invite solo players. @@ -165,3 +175,18 @@ bool InviteGuildToGroupAction::isUseful() { return bot->GetGuildId() && InviteNearbyToGroupAction::isUseful(); }; + +bool JoinGroupAction::Execute(Event event) +{ + Player* master = event.getOwner(); + Group* group = master->GetGroup(); + + if (group && (group->IsFull() || bot->GetGroup() == group)) + return false; + + if (bot->GetGroup()) + if (!botAI->DoSpecificAction("leave", event, true)) + return false; + + return Invite(bot); +} \ No newline at end of file diff --git a/src/strategy/actions/InviteToGroupAction.h b/src/strategy/actions/InviteToGroupAction.h index 8b8fa7f7..5bc138ed 100644 --- a/src/strategy/actions/InviteToGroupAction.h +++ b/src/strategy/actions/InviteToGroupAction.h @@ -20,6 +20,14 @@ class InviteToGroupAction : public Action virtual bool Invite(Player* player); }; +class JoinGroupAction : public InviteToGroupAction +{ +public: + JoinGroupAction(PlayerbotAI* ai, std::string name = "join") : InviteToGroupAction(ai, name) {} + bool Execute(Event event) override; + bool isUseful() override { return !bot->IsBeingTeleported(); } +}; + class InviteNearbyToGroupAction : public InviteToGroupAction { public: diff --git a/src/strategy/actions/LeaveGroupAction.cpp b/src/strategy/actions/LeaveGroupAction.cpp index 433d45e7..33f85b51 100644 --- a/src/strategy/actions/LeaveGroupAction.cpp +++ b/src/strategy/actions/LeaveGroupAction.cpp @@ -97,14 +97,12 @@ bool LeaveGroupAction::Leave(Player* player) bool LeaveFarAwayAction::Execute(Event event) { - return Leave(nullptr); + // allow bot to leave party when they want + return Leave(botAI->GetGroupMaster()); } bool LeaveFarAwayAction::isUseful() { - if (!sPlayerbotAIConfig->randomBotGroupNearby) - return false; - if (bot->InBattleground()) return false; diff --git a/src/strategy/actions/QuestAction.cpp b/src/strategy/actions/QuestAction.cpp index 978654e6..cd28687b 100644 --- a/src/strategy/actions/QuestAction.cpp +++ b/src/strategy/actions/QuestAction.cpp @@ -135,7 +135,7 @@ bool QuestAction::CompleteQuest(Player* player, uint32 entry) const std::string text_quest = ChatHelper::FormatQuest(pQuest); if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - LOG_INFO("playerbots", "Quest [ {} ] completed", pQuest->GetTitle()); + LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), pQuest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] completed", LANG_UNIVERSAL); } botAI->TellMasterNoFacing("Quest completed " + text_quest); @@ -244,6 +244,9 @@ bool QuestUpdateCompleteAction::Execute(Event event) WorldPacket p(event.getPacket()); p.rpos(0); + if (p.empty()) + return false; + uint32 entry, questId, available, required; ObjectGuid guid; p >> questId >> entry >> available >> required >> guid; @@ -254,7 +257,7 @@ bool QuestUpdateCompleteAction::Execute(Event event) const std::string text_quest = ChatHelper::FormatQuest(qInfo); if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - LOG_INFO("playerbots", "Quest [ {} ] completed", qInfo->GetTitle()); + LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), qInfo->GetTitle()); bot->Say("Quest [ " + text_quest + " ] completed", LANG_UNIVERSAL); } botAI->TellMasterNoFacing("Quest completed " + text_quest); diff --git a/src/strategy/actions/ShareQuestAction.cpp b/src/strategy/actions/ShareQuestAction.cpp index 8ab7c8ba..22e68c1a 100644 --- a/src/strategy/actions/ShareQuestAction.cpp +++ b/src/strategy/actions/ShareQuestAction.cpp @@ -37,3 +37,74 @@ bool ShareQuestAction::Execute(Event event) return false; } + +bool AutoShareQuestAction::Execute(Event event) +{ + Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); + bool shared = false; + + for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) + { + uint32 logQuest = bot->GetQuestSlotQuestId(slot); + Quest const* quest = sObjectMgr->GetQuestTemplate(logQuest); + + if (!quest) + continue; + + bool partyNeedsQuest = false; + + for (GroupReference* itr = bot->GetGroup()->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* player = itr->GetSource(); + + if (!player || player == bot || !player->IsInWorld() || !botAI->IsSafe(player)) // skip self + continue; + + if (bot->GetDistance(player) > 10) + continue; + + if (!player->SatisfyQuestStatus(quest, false)) + continue; + + if (player->GetQuestStatus(logQuest) == QUEST_STATUS_COMPLETE) + continue; + + if (!player->CanTakeQuest(quest, false)) + continue; + + if (!player->SatisfyQuestLog(false)) + continue; + + if (player->GetDivider()) + continue; + + if (auto ai = GET_PLAYERBOT_AI(player)) + { + if (PAI_VALUE(uint8, "free quest log slots") < 15 || !urand(0,5)) + { + WorldPacket packet(CMSG_PUSHQUESTTOPARTY, 20); + packet << logQuest; + ai->HandleMasterIncomingPacket(packet); + } + } + else + partyNeedsQuest = true; + } + + if (!partyNeedsQuest) + continue; + + WorldPacket p; + p << logQuest; + bot->GetSession()->HandlePushQuestToParty(p); + botAI->TellMaster("Quest shared"); + shared = true; + } + + return shared; +} + +bool AutoShareQuestAction::isUseful() +{ + return bot->GetGroup() && !botAI->HasActivePlayerMaster(); +} \ No newline at end of file diff --git a/src/strategy/actions/ShareQuestAction.h b/src/strategy/actions/ShareQuestAction.h index dc646787..86c185b1 100644 --- a/src/strategy/actions/ShareQuestAction.h +++ b/src/strategy/actions/ShareQuestAction.h @@ -12,9 +12,18 @@ class PlayerbotAI; class ShareQuestAction : public Action { public: - ShareQuestAction(PlayerbotAI* botAI) : Action(botAI, "share quest") { } + ShareQuestAction(PlayerbotAI* botAI, std::string name = "share quest") : Action(botAI, name) { } bool Execute(Event event) override; }; +class AutoShareQuestAction : public ShareQuestAction +{ +public: + AutoShareQuestAction(PlayerbotAI* ai) : ShareQuestAction(botAI, "auto share quest") {} + bool Execute(Event event) override; + + bool isUseful() override; +}; + #endif diff --git a/src/strategy/actions/TalkToQuestGiverAction.cpp b/src/strategy/actions/TalkToQuestGiverAction.cpp index 402161b0..329c0f9b 100644 --- a/src/strategy/actions/TalkToQuestGiverAction.cpp +++ b/src/strategy/actions/TalkToQuestGiverAction.cpp @@ -87,7 +87,7 @@ bool TalkToQuestGiverAction::TurnInQuest(Quest const* quest, Object* questGiver, { const Quest* pQuest = sObjectMgr->GetQuestTemplate(questID); const std::string text_quest = ChatHelper::FormatQuest(pQuest); - LOG_INFO("playerbots", "Quest [ {} ] completed", pQuest->GetTitle()); + LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), pQuest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] completed", LANG_UNIVERSAL); } diff --git a/src/strategy/generic/MaintenanceStrategy.cpp b/src/strategy/generic/MaintenanceStrategy.cpp index db1a9e13..01ad78ef 100644 --- a/src/strategy/generic/MaintenanceStrategy.cpp +++ b/src/strategy/generic/MaintenanceStrategy.cpp @@ -20,5 +20,6 @@ void MaintenanceStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("move stuck", NextAction::array(0, new NextAction("reset", 1.0f), nullptr))); // triggers.push_back(new TriggerNode("move long stuck", NextAction::array(0, new NextAction("hearthstone", 0.9f), new NextAction("repop", 0.8f), nullptr))); triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("use random quest item", 0.9f), nullptr))); + triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("auto share quest", 0.9f), NULL))); } diff --git a/src/strategy/triggers/RpgTriggers.cpp b/src/strategy/triggers/RpgTriggers.cpp index eee38718..2f00affd 100644 --- a/src/strategy/triggers/RpgTriggers.cpp +++ b/src/strategy/triggers/RpgTriggers.cpp @@ -305,6 +305,10 @@ bool RpgQueueBGTrigger::IsActive() if (!guidP.IsCreature()) return false; + + // if bot is not leader disallow tag bg + if (bot->GetGroup() && !bot->GetGroup()->IsLeader(bot->GetGUID())) + return false; if (AI_VALUE(BattlegroundTypeId, "rpg bg type") == BATTLEGROUND_TYPE_NONE) return false; diff --git a/src/strategy/values/GrindTargetValue.cpp b/src/strategy/values/GrindTargetValue.cpp index 55051d4d..313f7cf0 100644 --- a/src/strategy/values/GrindTargetValue.cpp +++ b/src/strategy/values/GrindTargetValue.cpp @@ -6,6 +6,7 @@ #include "Playerbots.h" #include "ReputationMgr.h" #include "SharedDefines.h" +#include "ServerFacade.h" Unit* GrindTargetValue::Calculate() { @@ -80,6 +81,15 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) //if (!bot->InBattleground() && master && master->GetDistance(unit) >= sPlayerbotAIConfig->grindDistance && !sRandomPlayerbotMgr->IsRandomBot(bot)) //continue; + // Bots in bot-groups no have a more limited range to look for grind target + if (!bot->InBattleground() && master && botAI->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT) + && sServerFacade->GetDistance2d(master, unit) > sPlayerbotAIConfig->lootDistance) + { + if (botAI->HasStrategy("debug grind", BotState::BOT_STATE_NON_COMBAT)) + botAI->TellMaster(chat->FormatWorldobject(unit) + " ignored (far from master)."); + continue; + } + if (!bot->InBattleground() && (int)unit->GetLevel() - (int)bot->GetLevel() > 4 && !unit->GetGUID().IsPlayer()) continue;