From b172e88010ef0e931d461b521fa3ea3a7a109828 Mon Sep 17 00:00:00 2001 From: dillyns <49765217+dillyns@users.noreply.github.com> Date: Fri, 27 Mar 2026 13:38:39 -0400 Subject: [PATCH] Resto druids should always be in tree form (#2192) # Pull Request Resto druids would not go into tree of life form when in combat until certain triggers were hit. They should be in tree of life form all the time. Move tree form actions from the various triggers to default actions instead, with highest priority ## Feature Evaluation Please answer the following: - Describe the **minimum logic** required to achieve the intended behavior?| When healer druids enter combat their first priority should be entering tree form. - Describe the **cheapest implementation** that produces an acceptable result? Add tree form action to default healer druid actions instead of triggers. - Describe the **runtime cost** when this logic executes across many bots? nil --- ## How to Test the Changes - Have a resto druid bot with tree of life form talented. - Enter combat - The druid should immediately enter tree of life form ## Complexity & Impact Does this change add new decision branches? - - [x] No - - [ ] Yes (**explain below**) Does this change increase per-bot or per-tick processing? - - [x] No - - [ ] Yes (**describe and justify impact**) Could this logic scale poorly under load? - - [x] No - - [ ] Yes (**explain why**) --- ## Defaults & Configuration Does this change modify default bot behavior? - - [x] No - - [ ] Yes (**explain why**) If this introduces more advanced or AI-heavy logic: - - [x] Lightweight mode remains the default - - [ ] More complex behavior is optional and thereby configurable --- ## AI Assistance Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - - [x] No - - [ ] Yes (**explain below**) If yes, please specify: - AI tool or model used (e.g. ChatGPT, GPT-4, Claude, etc.) - Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation) - Which parts of the change were influenced or generated - Whether the result was manually reviewed and adapted AI assistance is allowed, but all submitted code must be fully understood, reviewed, and owned by the contributor. Any AI-influenced changes must be verified against existing CORE and PB logic. We expect contributors to be honest about what they do and do not understand. --- ## Final Checklist - - [x] Stability is not compromised - - [x] Performance impact is understood, tested, and acceptable - - [x] Added logic complexity is justified and explained - - [x] Documentation updated if needed --- ## Notes for Reviewers Anything that significantly improves realism at the cost of stability or performance should be carefully discussed before merging. --- src/Ai/Class/Druid/DruidAiObjectContext.cpp | 2 ++ src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp | 8 ++++---- src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp | 4 ++++ src/Ai/Class/Druid/Trigger/DruidTriggers.h | 11 +++++++++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Ai/Class/Druid/DruidAiObjectContext.cpp b/src/Ai/Class/Druid/DruidAiObjectContext.cpp index cc12f009f..3a638eedc 100644 --- a/src/Ai/Class/Druid/DruidAiObjectContext.cpp +++ b/src/Ai/Class/Druid/DruidAiObjectContext.cpp @@ -112,6 +112,7 @@ public: creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat; creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time; creators["hurricane channel check"] = &DruidTriggerFactoryInternal::hurricane_channel_check; + creators["no healer dps strategy"] = &DruidTriggerFactoryInternal::no_healer_dps_strategy; } private: @@ -149,6 +150,7 @@ private: static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); } static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); } static Trigger* hurricane_channel_check(PlayerbotAI* ai) { return new HurricaneChannelCheckTrigger(ai); } + static Trigger* no_healer_dps_strategy(PlayerbotAI* ai) { return new NoHealerDpsStrategyTrigger(ai); } }; class DruidAiObjectContextInternal : public NamedObjectContext diff --git a/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp index d5ac8fcea..36b90a146 100644 --- a/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp @@ -149,10 +149,10 @@ void DruidHealerDpsStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("healer should attack", { - NextAction("cancel tree form", ACTION_DEFAULT + 0.3f), - NextAction("moonfire", ACTION_DEFAULT + 0.2f), - NextAction("wrath", ACTION_DEFAULT + 0.1f), - NextAction("starfire", ACTION_DEFAULT), + NextAction("cancel tree form", ACTION_DEFAULT + 0.4f), + NextAction("moonfire", ACTION_DEFAULT + 0.3f), + NextAction("wrath", ACTION_DEFAULT + 0.2f), + NextAction("starfire", ACTION_DEFAULT + 0.1f), })); } diff --git a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp index 5d2c4ce34..7529cbb8e 100644 --- a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp @@ -33,6 +33,10 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) { GenericDruidStrategy::InitTriggers(triggers); + // no healer dps strategy + triggers.push_back(new TriggerNode("no healer dps strategy", + { NextAction("tree form", ACTION_DEFAULT) })); + triggers.push_back(new TriggerNode( "party member to heal out of spell range", { NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) })); diff --git a/src/Ai/Class/Druid/Trigger/DruidTriggers.h b/src/Ai/Class/Druid/Trigger/DruidTriggers.h index 5541cc10e..adad0d2a3 100644 --- a/src/Ai/Class/Druid/Trigger/DruidTriggers.h +++ b/src/Ai/Class/Druid/Trigger/DruidTriggers.h @@ -280,4 +280,15 @@ protected: static const std::set HURRICANE_SPELL_IDS; }; +class NoHealerDpsStrategyTrigger : public Trigger +{ +public: + NoHealerDpsStrategyTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no healer dps strategy") {} + + bool IsActive() override + { + return !botAI->HasStrategy("healer dps", BOT_STATE_COMBAT); + } +}; + #endif