From 5d7fb9e97d93116a78808d34dea5d9cd056fae26 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Sat, 11 Apr 2026 07:24:38 +0200 Subject: [PATCH] Druid - lifebloom (#2292) ## Pull Request Description Restoration Druid now use lifebloom when get Clearcasting proc. ## How to Test the Changes 1. Invite to group restoration druid and tank 2. Attack target (for example dummy) 3. When druid heals and get clearcasting should cast lifebloom on tank ## Impact Assessment - Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots? - - [x] No, not at all - - [ ] Minimal impact (**explain below**) - - [ ] Moderate impact (**explain below**) - Does this change modify default bot behavior? - - [ ] No - - [x] Yes (**explain why**) Restoration druid by default use lifebloom on tank when got clearcasting proc. - 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? - - [ ] No - - [x] Yes (**explain below**) Copilot CLI to review. ## 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 obraz obraz --- src/Ai/Class/Druid/Action/DruidActions.cpp | 10 ++++++++++ src/Ai/Class/Druid/Action/DruidActions.h | 8 ++++++++ src/Ai/Class/Druid/DruidAiObjectContext.cpp | 4 ++++ src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp | 3 +++ src/Ai/Class/Druid/Trigger/DruidTriggers.h | 6 ++++++ 5 files changed, 31 insertions(+) diff --git a/src/Ai/Class/Druid/Action/DruidActions.cpp b/src/Ai/Class/Druid/Action/DruidActions.cpp index d6a950b17..2eb809480 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidActions.cpp @@ -23,6 +23,16 @@ std::vector CastAbolishPoisonOnPartyAction::getAlternatives() CastSpellAction::getPrerequisites()); } +bool CastLifebloomOnMainTankAction::isUseful() +{ + Unit* target = GetTarget(); + if (!target || !target->IsAlive() || !CastSpellAction::isUseful()) + return false; + + Aura* lifebloom = botAI->GetAura("lifebloom", target, true, true); + return !lifebloom || lifebloom->GetStackAmount() < 3 || lifebloom->GetDuration() < 2000; +} + Value* CastEntanglingRootsCcAction::GetTargetValue() { return context->GetValue("cc target", "entangling roots"); diff --git a/src/Ai/Class/Druid/Action/DruidActions.h b/src/Ai/Class/Druid/Action/DruidActions.h index e3e177590..016c3dfc4 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.h +++ b/src/Ai/Class/Druid/Action/DruidActions.h @@ -128,6 +128,14 @@ public: CastThornsOnMainTankAction(PlayerbotAI* botAI) : BuffOnMainTankAction(botAI, "thorns", false) {} }; +class CastLifebloomOnMainTankAction : public BuffOnMainTankAction +{ +public: + CastLifebloomOnMainTankAction(PlayerbotAI* botAI) : BuffOnMainTankAction(botAI, "lifebloom", true) {} + + bool isUseful() override; +}; + class CastOmenOfClarityAction : public CastBuffSpellAction { public: diff --git a/src/Ai/Class/Druid/DruidAiObjectContext.cpp b/src/Ai/Class/Druid/DruidAiObjectContext.cpp index 17b561d19..3d9086cff 100644 --- a/src/Ai/Class/Druid/DruidAiObjectContext.cpp +++ b/src/Ai/Class/Druid/DruidAiObjectContext.cpp @@ -79,6 +79,7 @@ public: DruidTriggerFactoryInternal() { creators["omen of clarity"] = &DruidTriggerFactoryInternal::omen_of_clarity; + creators["clearcasting"] = &DruidTriggerFactoryInternal::clearcasting; creators["thorns"] = &DruidTriggerFactoryInternal::thorns; creators["thorns on party"] = &DruidTriggerFactoryInternal::thorns_on_party; creators["thorns on main tank"] = &DruidTriggerFactoryInternal::thorns_on_main_tank; @@ -117,6 +118,7 @@ public: private: static Trigger* natures_swiftness(PlayerbotAI* botAI) { return new NaturesSwiftnessTrigger(botAI); } + static Trigger* clearcasting(PlayerbotAI* botAI) { return new ClearcastingTrigger(botAI); } static Trigger* eclipse_solar(PlayerbotAI* botAI) { return new EclipseSolarTrigger(botAI); } static Trigger* eclipse_lunar(PlayerbotAI* botAI) { return new EclipseLunarTrigger(botAI); } static Trigger* thorns(PlayerbotAI* botAI) { return new ThornsTrigger(botAI); } @@ -209,6 +211,7 @@ public: creators["thorns"] = &DruidAiObjectContextInternal::thorns; creators["thorns on party"] = &DruidAiObjectContextInternal::thorns_on_party; creators["thorns on main tank"] = &DruidAiObjectContextInternal::thorns_on_main_tank; + creators["lifebloom on main tank"] = &DruidAiObjectContextInternal::lifebloom_on_main_tank; creators["cure poison"] = &DruidAiObjectContextInternal::cure_poison; creators["cure poison on party"] = &DruidAiObjectContextInternal::cure_poison_on_party; creators["abolish poison"] = &DruidAiObjectContextInternal::abolish_poison; @@ -304,6 +307,7 @@ private: static Action* thorns(PlayerbotAI* botAI) { return new CastThornsAction(botAI); } static Action* thorns_on_party(PlayerbotAI* botAI) { return new CastThornsOnPartyAction(botAI); } static Action* thorns_on_main_tank(PlayerbotAI* botAI) { return new CastThornsOnMainTankAction(botAI); } + static Action* lifebloom_on_main_tank(PlayerbotAI* botAI) { return new CastLifebloomOnMainTankAction(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* abolish_poison(PlayerbotAI* botAI) { return new CastAbolishPoisonAction(botAI); } diff --git a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp index 7529cbb8e..0710e0fdc 100644 --- a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp @@ -56,6 +56,9 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) new TriggerNode("party member critical health", { NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4) })); + triggers.push_back(new TriggerNode("clearcasting", + { NextAction("lifebloom on main tank", ACTION_CRITICAL_HEAL - 1) })); + triggers.push_back(new TriggerNode( "group heal setting", { diff --git a/src/Ai/Class/Druid/Trigger/DruidTriggers.h b/src/Ai/Class/Druid/Trigger/DruidTriggers.h index adad0d2a3..01daaeba6 100644 --- a/src/Ai/Class/Druid/Trigger/DruidTriggers.h +++ b/src/Ai/Class/Druid/Trigger/DruidTriggers.h @@ -61,6 +61,12 @@ public: OmenOfClarityTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "omen of clarity") {} }; +class ClearcastingTrigger : public HasAuraTrigger +{ +public: + ClearcastingTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "clearcasting") {} +}; + class RakeTrigger : public DebuffTrigger { public: