From fffbf6573884def9936d07cdcbf7f24ebc87cb1f Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 19 Jun 2026 17:35:22 -0500 Subject: [PATCH] Flag Carriers and Spirits Don't Eat or Drink (#2487) ## Pull Request Description I got the Save the Day achievement in WSG because the flag carrier I was chasing was way out of reach but decided to sit down right in front of the flag cap point to have a drink. Flag carriers have one job to do, and they should never stop to eat or drink. Also, Spirits of Redemption drink, and that's just weird. Stop it. ## Feature Evaluation - Describe the **minimum logic** required to achieve the intended behavior. - Describe the **processing cost** when this logic executes across many bots. I added exclusions from EatAction::isPossible and DrinkAction::isPossible for bots that have any of the WSG or EoS flag auras. I also changed the check for Druid shapeshift aura strings into a helper that checks GetShapeshiftForm, including Spirit of Redemption. FWIW, I don't understand why Bears and Cats should not be allowed to eat or drink, but I preserved those exclusions since they existed before and I don't know if there is a good reason for it. Changes to RogueActions are just to align the constant names for the flag auras with those used in AC. ## How to Test the Changes Easiest way is to enter a BG and use .damage on a flag carrier (easier to do your teammate) to put them at low health. Before, they would sit down and drink if out of combat. Now, they will not. ## Impact Assessment - Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots? - - [ ] No, not at all - - [x] Minimal impact (**explain below**) - - [ ] Moderate impact (**explain below**) These checks should cost virtually nothing, and I wouldn't be surprised if overall the performance load is very slightly lighter by using GetShapeshiftForm instead of the string-based lookups for aura names. - Does this change modify default bot behavior? - - [ ] No - - [x] Yes (**explain why**) Because default bots are idiots. - Does this change add new decision branches or increase maintenance complexity? - - [x] No - - [ ] Yes (**explain below**) ## AI Assistance Was AI assistance used while working on this change? - - [x] No - - [ ] Yes (**explain below**) ## Final Checklist - - [x] Stability is not compromised. - - [x] Performance impact is understood, tested, and acceptable. - - [x] Added logic complexity is justified and explained. - - [x] Any new bot dialogue lines are translated. - - [x] Documentation updated if needed (Conf comments, WiKi commands). ## Notes for Reviewers --- src/Ai/Base/Actions/NonCombatActions.cpp | 53 ++++++++++++++++++---- src/Ai/Class/Rogue/Action/RogueActions.cpp | 14 +++--- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/Ai/Base/Actions/NonCombatActions.cpp b/src/Ai/Base/Actions/NonCombatActions.cpp index 9de1e0bae..1f83cf0cd 100644 --- a/src/Ai/Base/Actions/NonCombatActions.cpp +++ b/src/Ai/Base/Actions/NonCombatActions.cpp @@ -8,6 +8,31 @@ #include "Event.h" #include "Playerbots.h" +namespace +{ +constexpr uint32 BG_WS_SPELL_WARSONG_FLAG = 23333; +constexpr uint32 BG_WS_SPELL_SILVERWING_FLAG = 23335; +constexpr uint32 BG_EY_NETHERSTORM_FLAG_SPELL = 34976; + +bool IsDisallowedShapeshiftForm(Player* bot) +{ + if (bot->getClass() == CLASS_DRUID) + { + ShapeshiftForm form = bot->GetShapeshiftForm(); + return form == FORM_TRAVEL || form == FORM_AQUA || + form == FORM_FLIGHT || form == FORM_FLIGHT_EPIC || + form == FORM_BEAR || form == FORM_DIREBEAR || + form == FORM_CAT; + } + else if (bot->getClass() == CLASS_PRIEST) + { + return bot->GetShapeshiftForm() == FORM_SPIRITOFREDEMPTION; + } + + return false; +} +} + bool DrinkAction::Execute(Event event) { if (botAI->HasCheat(BotCheatMask::food)) @@ -55,10 +80,16 @@ bool DrinkAction::isUseful() bool DrinkAction::isPossible() { - return !bot->IsInCombat() && !bot->IsMounted() && - !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form", - "flight form", "swift flight form", nullptr) && - (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible()); + if (bot->IsInCombat() || bot->IsMounted() || IsDisallowedShapeshiftForm(bot)) + return false; + + if (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || + bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL)) + { + return false; + } + + return botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible(); } bool EatAction::Execute(Event event) @@ -104,8 +135,14 @@ bool EatAction::isUseful() { return UseItemAction::isUseful() && AI_VALUE2(uint8 bool EatAction::isPossible() { - return !bot->IsInCombat() && !bot->IsMounted() && - !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form", - "flight form", "swift flight form", nullptr) && - (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible()); + if (bot->IsInCombat() || bot->IsMounted() || IsDisallowedShapeshiftForm(bot)) + return false; + + if (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || + bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL)) + { + return false; + } + + return botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible(); } diff --git a/src/Ai/Class/Rogue/Action/RogueActions.cpp b/src/Ai/Class/Rogue/Action/RogueActions.cpp index aae0d3eb0..653d39a34 100644 --- a/src/Ai/Class/Rogue/Action/RogueActions.cpp +++ b/src/Ai/Class/Rogue/Action/RogueActions.cpp @@ -13,9 +13,9 @@ namespace { -constexpr uint32 SPELL_WARSONG_FLAG = 23333; -constexpr uint32 SPELL_SILVERWING_FLAG = 23335; -constexpr uint32 SPELL_NETHERSTORM_FLAG = 34976; +constexpr uint32 BG_WS_SPELL_WARSONG_FLAG = 23333; +constexpr uint32 BG_WS_SPELL_SILVERWING_FLAG = 23335; +constexpr uint32 BG_EY_NETHERSTORM_FLAG_SPELL = 34976; constexpr uint32 SPELL_MASTER_POISONER_RANK_3 = 58410; } @@ -30,8 +30,8 @@ bool CastStealthAction::isUseful() bool CastStealthAction::isPossible() { // do not use with WSG flag or EYE flag - return !bot->HasAura(SPELL_WARSONG_FLAG) && !bot->HasAura(SPELL_SILVERWING_FLAG) && - !bot->HasAura(SPELL_NETHERSTORM_FLAG); + return !bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) && !bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) && + !bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL); } bool UnstealthAction::Execute(Event /*event*/) @@ -59,8 +59,8 @@ bool CheckStealthAction::Execute(Event /*event*/) bool CastVanishAction::isUseful() { // do not use with WSG flag or EYE flag - return !bot->HasAura(SPELL_WARSONG_FLAG) && !bot->HasAura(SPELL_SILVERWING_FLAG) && - !bot->HasAura(SPELL_NETHERSTORM_FLAG); + return !bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) && !bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) && + !bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL); } bool CastEnvenomAction::isUseful()