From 24c6534229f3930ed83501a6e132d84e10d7fcf2 Mon Sep 17 00:00:00 2001 From: bo Date: Thu, 12 Mar 2026 18:44:27 -0500 Subject: [PATCH] Rogue combat strategy fixes --- .../strategy/rogue/DpsRogueStrategy.cpp | 10 +- .../rogue/GenericRogueNonCombatStrategy.cpp | 11 ++ .../playerbot/strategy/rogue/RogueActions.h | 117 ++++++++++++++++++ .../strategy/rogue/RogueAiObjectContext.cpp | 32 ++++- .../strategy/rogue/RogueAmbushStrategy.h | 26 ++++ .../strategy/rogue/RogueComboActions.h | 2 +- .../strategy/rogue/RogueFinishingActions.h | 31 +++-- .../strategy/rogue/RogueOpeningActions.h | 43 ++++++- .../strategy/rogue/RogueSapStrategy.h | 26 ++++ .../playerbot/strategy/rogue/RogueTriggers.h | 59 +++++++++ 10 files changed, 336 insertions(+), 21 deletions(-) create mode 100644 src/modules/Bots/playerbot/strategy/rogue/RogueAmbushStrategy.h create mode 100644 src/modules/Bots/playerbot/strategy/rogue/RogueSapStrategy.h diff --git a/src/modules/Bots/playerbot/strategy/rogue/DpsRogueStrategy.cpp b/src/modules/Bots/playerbot/strategy/rogue/DpsRogueStrategy.cpp index 68d619d8b..a9233720c 100644 --- a/src/modules/Bots/playerbot/strategy/rogue/DpsRogueStrategy.cpp +++ b/src/modules/Bots/playerbot/strategy/rogue/DpsRogueStrategy.cpp @@ -85,9 +85,17 @@ void DpsRogueStrategy::InitTriggers(std::list &triggers) MeleeCombatStrategy::InitTriggers(triggers); triggers.push_back(new TriggerNode( - "combo points available", + "slice and dice", + NextAction::array(0, new NextAction("slice and dice", ACTION_HIGH + 3), NULL))); + + triggers.push_back(new TriggerNode( + "combo points for target available", NextAction::array(0, new NextAction("rupture", ACTION_HIGH + 2), NULL))); + triggers.push_back(new TriggerNode( + "expose armor", + NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 1), NULL))); + triggers.push_back(new TriggerNode( "medium threat", NextAction::array(0, new NextAction("vanish", ACTION_HIGH), NULL))); diff --git a/src/modules/Bots/playerbot/strategy/rogue/GenericRogueNonCombatStrategy.cpp b/src/modules/Bots/playerbot/strategy/rogue/GenericRogueNonCombatStrategy.cpp index f128c4151..b80971659 100644 --- a/src/modules/Bots/playerbot/strategy/rogue/GenericRogueNonCombatStrategy.cpp +++ b/src/modules/Bots/playerbot/strategy/rogue/GenericRogueNonCombatStrategy.cpp @@ -11,4 +11,15 @@ void GenericRogueNonCombatStrategy::InitTriggers(std::list &trigge { NonCombatStrategy::InitTriggers(triggers); + triggers.push_back(new TriggerNode( + "attack", + NextAction::array(0, new NextAction("begin ambush", 101.0f), NULL))); + + triggers.push_back(new TriggerNode( + "sap", + NextAction::array(0, new NextAction("begin sap", ACTION_HIGH + 1), NULL))); + + triggers.push_back(new TriggerNode( + "stealth", + NextAction::array(0, new NextAction("stealth", ACTION_NORMAL), NULL))); } diff --git a/src/modules/Bots/playerbot/strategy/rogue/RogueActions.h b/src/modules/Bots/playerbot/strategy/rogue/RogueActions.h index dd3bb2537..e305383be 100644 --- a/src/modules/Bots/playerbot/strategy/rogue/RogueActions.h +++ b/src/modules/Bots/playerbot/strategy/rogue/RogueActions.h @@ -60,4 +60,121 @@ namespace ai public: CastKickOnEnemyHealerAction(PlayerbotAI* ai) : CastSpellOnEnemyHealerAction(ai, "kick") {} }; + + class CastStealthAction : public CastBuffSpellAction + { + public: + CastStealthAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "stealth") {} + }; + + class BeginSapAction : public Action + { + public: + BeginSapAction(PlayerbotAI* ai) : Action(ai, "begin sap") {} + + virtual bool Execute(Event event) + { + Player* master = ai->GetMaster(); + if (!master) + return false; + + ObjectGuid targetGuid = master->GetSelectionGuid(); + if (targetGuid.IsEmpty()) + return false; + + Unit* target = ai->GetUnit(targetGuid); + if (!target || !target->IsAlive()) + return false; + + bot->SetSelectionGuid(targetGuid); + context->GetValue("current target")->Set(target); + ai->ChangeStrategy("+sap", BOT_STATE_NON_COMBAT); + return true; + } + }; + + class EndSapAction : public Action + { + public: + EndSapAction(PlayerbotAI* ai) : Action(ai, "end sap") {} + + virtual bool isUseful() + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target || !target->IsAlive()) + return true; + if (ai->HasAura("sap", target)) + return true; + if (bot->IsInCombat()) + return true; + return false; + } + + virtual bool Execute(Event event) + { + Unit* target = AI_VALUE(Unit*, "current target"); + bool sapSucceeded = target && ai->HasAura("sap", target); + if (sapSucceeded) + { + target->DeleteThreatList(); + target->CombatStop(); + bot->CombatStop(); + } + bot->AttackStop(); + context->GetValue("current target")->Set(NULL); + bot->SetSelectionGuid(ObjectGuid()); + ai->ChangeStrategy("-sap", BOT_STATE_NON_COMBAT); + return true; + } + }; + + class BeginAmbushAction : public Action + { + public: + BeginAmbushAction(PlayerbotAI* ai) : Action(ai, "begin ambush") {} + + virtual bool Execute(Event event) + { + Player* master = ai->GetMaster(); + if (!master) + return false; + + ObjectGuid guid = master->GetSelectionGuid(); + if (guid.IsEmpty()) + return false; + + Unit* target = ai->GetUnit(guid); + if (!target || !target->IsAlive()) + return false; + + if (bot->IsFriendlyTo(target)) + return false; + + if (!bot->IsWithinLOSInMap(target)) + return false; + + bot->SetSelectionGuid(guid); + context->GetValue("current target")->Set(target); + ai->ChangeStrategy("+ambush", BOT_STATE_NON_COMBAT); + return true; + } + }; + + class RogueEndAmbushAction : public Action + { + public: + RogueEndAmbushAction(PlayerbotAI* ai) : Action(ai, "end ambush") {} + + virtual bool isUseful() + { + return bot->IsInCombat(); + } + + virtual bool Execute(Event event) + { + ai->ChangeStrategy("-ambush", BOT_STATE_NON_COMBAT); + ai->ChangeEngine(BOT_STATE_COMBAT); + return true; + } + }; } diff --git a/src/modules/Bots/playerbot/strategy/rogue/RogueAiObjectContext.cpp b/src/modules/Bots/playerbot/strategy/rogue/RogueAiObjectContext.cpp index 6fdea8a4f..c5a4c8763 100644 --- a/src/modules/Bots/playerbot/strategy/rogue/RogueAiObjectContext.cpp +++ b/src/modules/Bots/playerbot/strategy/rogue/RogueAiObjectContext.cpp @@ -2,10 +2,12 @@ #include "../../playerbot.h" #include "RogueActions.h" #include "RogueTriggers.h" +#include "../triggers/ChatCommandTrigger.h" #include "RogueAiObjectContext.h" #include "DpsRogueStrategy.h" #include "GenericRogueNonCombatStrategy.h" -#include "../generic/PullStrategy.h" +#include "RogueAmbushStrategy.h" +#include "RogueSapStrategy.h" #include "../NamedObjectContext.h" using namespace ai; @@ -24,13 +26,15 @@ namespace ai { creators["dps"] = &rogue::StrategyFactoryInternal::dps; creators["nc"] = &rogue::StrategyFactoryInternal::nc; - creators["pull"] = &rogue::StrategyFactoryInternal::pull; + creators["ambush"] = &rogue::StrategyFactoryInternal::ambush; + creators["sap"] = &rogue::StrategyFactoryInternal::sap_strategy; } private: static Strategy* dps(PlayerbotAI* ai) { return new DpsRogueStrategy(ai); } static Strategy* nc(PlayerbotAI* ai) { return new GenericRogueNonCombatStrategy(ai); } - static Strategy* pull(PlayerbotAI* ai) { return new PullStrategy(ai, "shoot"); } + static Strategy* ambush(PlayerbotAI* ai) { return new RogueAmbushStrategy(ai); } + static Strategy* sap_strategy(PlayerbotAI* ai) { return new RogueSapStrategy(ai); } }; }; }; @@ -51,6 +55,9 @@ namespace ai creators["slice and dice"] = &TriggerFactoryInternal::slice_and_dice; creators["expose armor"] = &TriggerFactoryInternal::expose_armor; creators["kick on enemy healer"] = &TriggerFactoryInternal::kick_on_enemy_healer; + creators["combo points for target available"] = &TriggerFactoryInternal::combo_points_for_target_available; + creators["stealth"] = &TriggerFactoryInternal::stealth; + creators["sap"] = &TriggerFactoryInternal::sap; } @@ -60,6 +67,9 @@ namespace ai static Trigger* slice_and_dice(PlayerbotAI* ai) { return new SliceAndDiceTrigger(ai); } static Trigger* expose_armor(PlayerbotAI* ai) { return new ExposeArmorTrigger(ai); } static Trigger* kick_on_enemy_healer(PlayerbotAI* ai) { return new KickInterruptEnemyHealerSpellTrigger(ai); } + static Trigger* combo_points_for_target_available(PlayerbotAI* ai) { return new ComboPointsForTargetAvailableTrigger(ai); } + static Trigger* stealth(PlayerbotAI* ai) { return new StealthTrigger(ai); } + static Trigger* sap(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "sap"); } }; }; }; @@ -90,6 +100,14 @@ namespace ai creators["backstab"] = &AiObjectContextInternal::backstab; creators["expose armor"] = &AiObjectContextInternal::expose_armor; creators["kick on enemy healer"] = &AiObjectContextInternal::kick_on_enemy_healer; + creators["sap"] = &AiObjectContextInternal::sap; + creators["begin sap"] = &AiObjectContextInternal::begin_sap; + creators["end sap"] = &AiObjectContextInternal::end_sap; + creators["garrote"] = &AiObjectContextInternal::garrote; + creators["cheap shot"] = &AiObjectContextInternal::cheap_shot; + creators["stealth"] = &AiObjectContextInternal::stealth; + creators["begin ambush"] = &AiObjectContextInternal::begin_ambush; + creators["end ambush"] = &AiObjectContextInternal::end_ambush; } private: @@ -107,6 +125,14 @@ namespace ai static Action* backstab(PlayerbotAI* ai) { return new CastBackstabAction(ai); } static Action* expose_armor(PlayerbotAI* ai) { return new CastExposeArmorAction(ai); } static Action* kick_on_enemy_healer(PlayerbotAI* ai) { return new CastKickOnEnemyHealerAction(ai); } + static Action* sap(PlayerbotAI* ai) { return new CastSapAction(ai); } + static Action* begin_sap(PlayerbotAI* ai) { return new BeginSapAction(ai); } + static Action* end_sap(PlayerbotAI* ai) { return new EndSapAction(ai); } + static Action* garrote(PlayerbotAI* ai) { return new CastGarroteAction(ai); } + static Action* cheap_shot(PlayerbotAI* ai) { return new CastCheapShotAction(ai); } + static Action* stealth(PlayerbotAI* ai) { return new CastStealthAction(ai); } + static Action* begin_ambush(PlayerbotAI* ai) { return new BeginAmbushAction(ai); } + static Action* end_ambush(PlayerbotAI* ai) { return new RogueEndAmbushAction(ai); } }; }; }; diff --git a/src/modules/Bots/playerbot/strategy/rogue/RogueAmbushStrategy.h b/src/modules/Bots/playerbot/strategy/rogue/RogueAmbushStrategy.h new file mode 100644 index 000000000..12aef527a --- /dev/null +++ b/src/modules/Bots/playerbot/strategy/rogue/RogueAmbushStrategy.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../Strategy.h" + +namespace ai +{ + class RogueAmbushStrategy : public Strategy + { + public: + RogueAmbushStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual string getName() { return "ambush"; } + + virtual NextAction** getDefaultActions() + { + // Stealth first; once stealthed, cheap shot moves to target (via reach melee + // prerequisite) and opens. End ambush fires once combat begins. + return NextAction::array(0, + new NextAction("stealth", 105.0f), + new NextAction("reach melee", 104.5f), + new NextAction("cheap shot", 104.0f), + new NextAction("end ambush", 103.0f), + new NextAction("sinister strike", 100.0f), + NULL); + } + }; +} diff --git a/src/modules/Bots/playerbot/strategy/rogue/RogueComboActions.h b/src/modules/Bots/playerbot/strategy/rogue/RogueComboActions.h index 0568847e3..ee4d39815 100644 --- a/src/modules/Bots/playerbot/strategy/rogue/RogueComboActions.h +++ b/src/modules/Bots/playerbot/strategy/rogue/RogueComboActions.h @@ -9,7 +9,7 @@ namespace ai virtual bool isUseful() { - return CastMeleeSpellAction::isUseful() && AI_VALUE2(uint8, "combo", "self target") < 5; + return CastMeleeSpellAction::isUseful() && AI_VALUE2(uint8, "combo", "current target") < 5; } }; diff --git a/src/modules/Bots/playerbot/strategy/rogue/RogueFinishingActions.h b/src/modules/Bots/playerbot/strategy/rogue/RogueFinishingActions.h index 3a50517fe..378c98246 100644 --- a/src/modules/Bots/playerbot/strategy/rogue/RogueFinishingActions.h +++ b/src/modules/Bots/playerbot/strategy/rogue/RogueFinishingActions.h @@ -2,34 +2,45 @@ namespace ai { - class CastEviscerateAction : public CastMeleeSpellAction + class CastFinishingMoveAction : public CastMeleeSpellAction { public: - CastEviscerateAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "eviscerate") {} + CastFinishingMoveAction(PlayerbotAI* ai, string name) : CastMeleeSpellAction(ai, name) {} + + virtual bool isUseful() + { + return CastMeleeSpellAction::isUseful() && AI_VALUE2(uint8, "combo", "current target") >= 1; + } + }; + + class CastEviscerateAction : public CastFinishingMoveAction + { + public: + CastEviscerateAction(PlayerbotAI* ai) : CastFinishingMoveAction(ai, "eviscerate") {} }; - class CastSliceAndDiceAction : public CastMeleeSpellAction + class CastSliceAndDiceAction : public CastFinishingMoveAction { public: - CastSliceAndDiceAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "slice and dice") {} + CastSliceAndDiceAction(PlayerbotAI* ai) : CastFinishingMoveAction(ai, "slice and dice") {} }; - class CastExposeArmorAction : public CastMeleeSpellAction + class CastExposeArmorAction : public CastFinishingMoveAction { public: - CastExposeArmorAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "expose armor") {} + CastExposeArmorAction(PlayerbotAI* ai) : CastFinishingMoveAction(ai, "expose armor") {} }; - class CastRuptureAction : public CastMeleeSpellAction + class CastRuptureAction : public CastFinishingMoveAction { public: - CastRuptureAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "rupture") {} + CastRuptureAction(PlayerbotAI* ai) : CastFinishingMoveAction(ai, "rupture") {} }; - class CastKidneyShotAction : public CastMeleeSpellAction + class CastKidneyShotAction : public CastFinishingMoveAction { public: - CastKidneyShotAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "kidney shot") {} + CastKidneyShotAction(PlayerbotAI* ai) : CastFinishingMoveAction(ai, "kidney shot") {} }; } diff --git a/src/modules/Bots/playerbot/strategy/rogue/RogueOpeningActions.h b/src/modules/Bots/playerbot/strategy/rogue/RogueOpeningActions.h index 718efebf2..773d6c80d 100644 --- a/src/modules/Bots/playerbot/strategy/rogue/RogueOpeningActions.h +++ b/src/modules/Bots/playerbot/strategy/rogue/RogueOpeningActions.h @@ -2,23 +2,54 @@ namespace ai { - class CastSapAction : public CastMeleeSpellAction + class CastStealthedOpeningAction : public CastMeleeSpellAction { public: - CastSapAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "sap") {} + CastStealthedOpeningAction(PlayerbotAI* ai, string name) : CastMeleeSpellAction(ai, name) {} + + virtual bool isUseful() + { + return CastMeleeSpellAction::isUseful() && ai->HasAura("stealth", ai->GetBot()); + } }; - class CastGarroteAction : public CastMeleeSpellAction + class CastSapAction : public CastStealthedOpeningAction { public: - CastGarroteAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "garrote") {} + CastSapAction(PlayerbotAI* ai) : CastStealthedOpeningAction(ai, "sap") {} + + virtual bool Execute(Event event) + { + bool result = CastStealthedOpeningAction::Execute(event); + if (result) + { + Unit* sapTarget = GetTarget(); + if (sapTarget) + { + sapTarget->DeleteThreatList(); + sapTarget->CombatStop(); + } + ai->GetBot()->ClearInCombat(); + } + return result; + } }; + class CastGarroteAction : public CastStealthedOpeningAction + { + public: + CastGarroteAction(PlayerbotAI* ai) : CastStealthedOpeningAction(ai, "garrote") {} + + virtual bool isUseful() + { + return CastStealthedOpeningAction::isUseful() && AI_VALUE2(bool, "behind", "current target"); + } + }; - class CastCheapShotAction : public CastMeleeSpellAction + class CastCheapShotAction : public CastStealthedOpeningAction { public: - CastCheapShotAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "cheap shot") {} + CastCheapShotAction(PlayerbotAI* ai) : CastStealthedOpeningAction(ai, "cheap shot") {} }; } diff --git a/src/modules/Bots/playerbot/strategy/rogue/RogueSapStrategy.h b/src/modules/Bots/playerbot/strategy/rogue/RogueSapStrategy.h new file mode 100644 index 000000000..815b174b9 --- /dev/null +++ b/src/modules/Bots/playerbot/strategy/rogue/RogueSapStrategy.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../Strategy.h" +#include "../NamedObjectContext.h" + +namespace ai +{ + class RogueSapStrategy : public Strategy + { + public: + RogueSapStrategy(PlayerbotAI* ai) : Strategy(ai) {} + + virtual string getName() { return "sap"; } + + virtual NextAction** getDefaultActions() + { + return NextAction::array(0, + new NextAction("stealth", ACTION_HIGH + 3), + new NextAction("reach melee", ACTION_HIGH + 2), + new NextAction("sap", ACTION_HIGH + 1), + new NextAction("end sap", ACTION_NORMAL + 1), + new NextAction("follow master", ACTION_NORMAL), + NULL); + } + }; +} diff --git a/src/modules/Bots/playerbot/strategy/rogue/RogueTriggers.h b/src/modules/Bots/playerbot/strategy/rogue/RogueTriggers.h index 8d20e12b8..27ab6bbe4 100644 --- a/src/modules/Bots/playerbot/strategy/rogue/RogueTriggers.h +++ b/src/modules/Bots/playerbot/strategy/rogue/RogueTriggers.h @@ -33,4 +33,63 @@ namespace ai public: KickInterruptEnemyHealerSpellTrigger(PlayerbotAI* ai) : InterruptEnemyHealerTrigger(ai, "kick") {} }; + + class StealthTrigger : public Trigger + { + public: + StealthTrigger(PlayerbotAI* ai) : Trigger(ai, "stealth") {} + + virtual bool IsActive() + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (target && !target->IsAlive()) + target = NULL; + if (!target && !bot->IsInCombat() && ai->HasAura("stealth", bot)) + bot->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + return target && !ai->HasAura("stealth", bot); + } + }; + + class ComboPointsForTargetAvailableTrigger : public Trigger + { + public: + ComboPointsForTargetAvailableTrigger(PlayerbotAI* ai) + : Trigger(ai, "combo points for target available"), m_threshold(2) {} + + virtual bool IsActive() + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + + ObjectGuid guid = target->GetObjectGuid(); + if (guid != m_lastTargetGuid) + { + m_lastTargetGuid = guid; + m_threshold = 2; + } + + uint8 combo = AI_VALUE2(uint8, "combo", "current target"); + if (combo >= m_threshold) + { + m_threshold = nextThreshold(target); + return true; + } + return false; + } + + private: + uint8 m_threshold; + ObjectGuid m_lastTargetGuid; + + uint8 nextThreshold(Unit* target) + { + Creature* creature = dynamic_cast(target); + if (creature && creature->GetCreatureInfo() && + creature->GetCreatureInfo()->Rank > CREATURE_ELITE_NORMAL) + return 3 + urand(0, 2); // 3, 4, or 5 + else + return 1 + urand(0, 1); // 1 or 2 + } + }; }