diff --git a/src/strategy/rogue/RogueTriggers.cpp b/src/strategy/rogue/RogueTriggers.cpp index d4b89443..3d013ed6 100644 --- a/src/strategy/rogue/RogueTriggers.cpp +++ b/src/strategy/rogue/RogueTriggers.cpp @@ -105,7 +105,8 @@ bool SprintTrigger::IsActive() bool ExposeArmorTrigger::IsActive() { - return DebuffTrigger::IsActive() && !botAI->HasAura("sunder armor", bot, false, false, -1, true) && + Unit* target = AI_VALUE(Unit*, "current target"); // Get the bot's current target + return DebuffTrigger::IsActive() && !botAI->HasAura("sunder armor", target, false, false, -1, true) && AI_VALUE2(uint8, "combo", "current target") <= 3; } diff --git a/src/strategy/warrior/ArmsWarriorStrategy.cpp b/src/strategy/warrior/ArmsWarriorStrategy.cpp index b6c5eed7..2049f034 100644 --- a/src/strategy/warrior/ArmsWarriorStrategy.cpp +++ b/src/strategy/warrior/ArmsWarriorStrategy.cpp @@ -17,6 +17,9 @@ public: creators["piercing howl"] = &piercing_howl; creators["mocking blow"] = &mocking_blow; creators["heroic strike"] = &heroic_strike; + creators["enraged regeneration"] = &enraged_regeneration; + creators["retaliation"] = &retaliation; + creators["shattering throw"] = &shattering_throw; } private: @@ -25,6 +28,30 @@ private: ACTION_NODE_A(piercing_howl, "piercing howl", "mocking blow"); ACTION_NODE_A(mocking_blow, "mocking blow", "hamstring"); ACTION_NODE_A(heroic_strike, "heroic strike", "melee"); + + static ActionNode* enraged_regeneration(PlayerbotAI* botAI) + { + return new ActionNode("enraged regeneration", + /*P*/ nullptr, + /*A*/ nullptr, + /*C*/ nullptr); + } + + static ActionNode* retaliation(PlayerbotAI* botAI) + { + return new ActionNode("retaliation", + /*P*/ nullptr, + /*A*/ nullptr, + /*C*/ nullptr); + } + + static ActionNode* shattering_throw(PlayerbotAI* botAI) + { + return new ActionNode("shattering throw", + /*P*/ nullptr, + /*A*/ nullptr, + /*C*/ nullptr); + } }; ArmsWarriorStrategy::ArmsWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI) @@ -93,9 +120,18 @@ void ArmsWarriorStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("death wish", NextAction::array(0, new NextAction("death wish", ACTION_HIGH + 2), nullptr))); - triggers.push_back(new TriggerNode( - "critical health", NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr))); + triggers.push_back(new TriggerNode("critical health", + NextAction::array(0, new NextAction("intimidating shout", ACTION_EMERGENCY), nullptr))); + triggers.push_back(new TriggerNode("medium health", + NextAction::array(0, new NextAction("enraged regeneration", ACTION_EMERGENCY), nullptr))); + + triggers.push_back(new TriggerNode("almost full health", + NextAction::array(0, new NextAction("retaliation", ACTION_EMERGENCY + 1), nullptr))); + + triggers.push_back(new TriggerNode("shattering throw trigger", + NextAction::array(0, new NextAction("shattering throw", ACTION_INTERRUPT + 1), nullptr))); + // triggers.push_back(new TriggerNode("medium aoe", // NextAction::array(0, new NextAction("thunder clap", ACTION_HIGH + 2), nullptr))); /* diff --git a/src/strategy/warrior/FuryWarriorStrategy.cpp b/src/strategy/warrior/FuryWarriorStrategy.cpp index 49410f2c..1b166775 100644 --- a/src/strategy/warrior/FuryWarriorStrategy.cpp +++ b/src/strategy/warrior/FuryWarriorStrategy.cpp @@ -18,6 +18,7 @@ public: creators["piercing howl"] = &piercing_howl; // creators["bloodthirst"] = &bloodthirst; creators["pummel"] = &pummel; + creators["enraged regeneration"] = &enraged_regeneration; } private: @@ -27,6 +28,14 @@ private: // ACTION_NODE_A(death_wish, "death wish", "berserker rage"); // ACTION_NODE_A(bloodthirst, "bloodthirst", "melee"); ACTION_NODE_A(pummel, "pummel", "intercept"); + + static ActionNode* enraged_regeneration(PlayerbotAI* botAI) + { + return new ActionNode("enraged regeneration", + /*P*/ nullptr, + /*A*/ nullptr, + /*C*/ nullptr); + } }; FuryWarriorStrategy::FuryWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI) @@ -85,4 +94,6 @@ void FuryWarriorStrategy::InitTriggers(std::vector& triggers) new TriggerNode("death wish", NextAction::array(0, new NextAction("death wish", ACTION_HIGH), nullptr))); triggers.push_back( new TriggerNode("recklessness", NextAction::array(0, new NextAction("recklessness", ACTION_HIGH), nullptr))); + triggers.push_back(new TriggerNode("critical health", + NextAction::array(0, new NextAction("enraged regeneration", ACTION_EMERGENCY), nullptr))); } diff --git a/src/strategy/warrior/GenericWarriorStrategy.h b/src/strategy/warrior/GenericWarriorStrategy.h index dbfe339c..af3e6acc 100644 --- a/src/strategy/warrior/GenericWarriorStrategy.h +++ b/src/strategy/warrior/GenericWarriorStrategy.h @@ -20,6 +20,8 @@ public: creators["charge"] = &charge; creators["mocking blow"] = &mocking_blow; creators["overpower"] = &overpower; + creators["retaliation"] = &retaliation; + creators["shattering throw"] = &shattering_throw; // temp creators["mortal strike"] = &mortal_strike; @@ -57,6 +59,8 @@ private: ACTION_NODE_P(intervene, "intervene", "defensive stance"); // temp ACTION_NODE_P(mortal_strike, "mortal strike", "battle stance"); + ACTION_NODE_P(retaliation, "retaliation", "battle stance"); + ACTION_NODE_P(shattering_throw, "shattering throw", "battle stance"); }; class GenericWarriorStrategy : public CombatStrategy diff --git a/src/strategy/warrior/TankWarriorStrategy.cpp b/src/strategy/warrior/TankWarriorStrategy.cpp index c1e8aa18..0f8622cb 100644 --- a/src/strategy/warrior/TankWarriorStrategy.cpp +++ b/src/strategy/warrior/TankWarriorStrategy.cpp @@ -23,6 +23,7 @@ public: creators["taunt"] = &taunt; creators["taunt spell"] = &taunt; creators["vigilance"] = &vigilance; + creators["enraged regeneration"] = &enraged_regeneration; } private: @@ -50,6 +51,14 @@ private: /*A*/ nullptr, /*C*/ nullptr); } + + static ActionNode* enraged_regeneration(PlayerbotAI* botAI) + { + return new ActionNode("enraged regeneration", + /*P*/ nullptr, + /*A*/ nullptr, + /*C*/ nullptr); + } }; TankWarriorStrategy::TankWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI) @@ -105,8 +114,10 @@ void TankWarriorStrategy::InitTriggers(std::vector& triggers) NextAction::array(0, new NextAction("heroic throw on snare target", ACTION_INTERRUPT), nullptr))); triggers.push_back(new TriggerNode( "low health", NextAction::array(0, new NextAction("shield wall", ACTION_MEDIUM_HEAL), nullptr))); - triggers.push_back(new TriggerNode( - "critical health", NextAction::array(0, new NextAction("last stand", ACTION_EMERGENCY + 3), nullptr))); + triggers.push_back(new TriggerNode("critical health", + NextAction::array(0, new NextAction("last stand", ACTION_EMERGENCY + 3), + new NextAction("enraged regeneration", ACTION_EMERGENCY + 2), + nullptr))); // triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("battle shout taunt", // ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( diff --git a/src/strategy/warrior/WarriorActions.cpp b/src/strategy/warrior/WarriorActions.cpp index 662bcc2d..a917afd2 100644 --- a/src/strategy/warrior/WarriorActions.cpp +++ b/src/strategy/warrior/WarriorActions.cpp @@ -101,3 +101,144 @@ bool CastVigilanceAction::Execute(Event event) return botAI->CastSpell("vigilance", target); } + +bool CastRetaliationAction::isUseful() +{ + // Spell cooldown check + if (!bot->HasSpell(20230)) + { + return false; + } + + // Spell cooldown check + if (bot->HasSpellCooldown(20230)) + { + return false; + } + + uint8 meleeAttackers = 0; + GuidVector attackers = AI_VALUE(GuidVector, "attackers"); + + for (ObjectGuid const& guid : attackers) + { + Unit* attacker = botAI->GetUnit(guid); + if (!attacker || !attacker->IsAlive() || attacker->GetVictim() != bot) + continue; + + // Check if the attacker is melee-based using unit_class + if (attacker->GetTypeId() == TYPEID_UNIT) + { + Creature* creature = attacker->ToCreature(); + if (creature && (creature->IsClass(CLASS_WARRIOR) + || creature->IsClass(CLASS_ROGUE) + || creature->IsClass(CLASS_PALADIN))) + { + ++meleeAttackers; + } + } + else if (attacker->GetTypeId() == TYPEID_PLAYER) + { + Player* playerAttacker = attacker->ToPlayer(); + if (playerAttacker && botAI->IsMelee(playerAttacker)) // Reuse existing Player melee check + { + ++meleeAttackers; + } + } + + // Early exit if we already have enough melee attackers + if (meleeAttackers >= 2) + break; + } + + // Only cast Retaliation if there are at least 2 melee attackers and the buff is not active + return meleeAttackers >= 2 && !botAI->HasAura("retaliation", bot); +} + +Unit* CastShatteringThrowAction::GetTarget() +{ + GuidVector enemies = AI_VALUE(GuidVector, "possible targets"); + + for (ObjectGuid const& guid : enemies) + { + Unit* enemy = botAI->GetUnit(guid); + if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot)) + continue; + + if (bot->IsWithinDistInMap(enemy, 25.0f) && + (enemy->HasAura(642) || // Divine Shield + enemy->HasAura(45438) || // Ice Block + enemy->HasAura(41450))) // Blessing of Protection + { + return enemy; + } + } + + return nullptr; // No valid target +} + +bool CastShatteringThrowAction::isUseful() +{ + + // Spell cooldown check + if (!bot->HasSpell(64382)) + { + return false; + } + + // Spell cooldown check + if (bot->HasSpellCooldown(64382)) + { + return false; + } + + GuidVector enemies = AI_VALUE(GuidVector, "possible targets"); + + for (ObjectGuid const& guid : enemies) + { + Unit* enemy = botAI->GetUnit(guid); + if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot)) + continue; + + // Check if the enemy is within 25 yards and has the specific auras + if (bot->IsWithinDistInMap(enemy, 25.0f) && + (enemy->HasAura(642) || // Divine Shield + enemy->HasAura(45438) || // Ice Block + enemy->HasAura(41450))) // Blessing of Protection + { + return true; + } + } + + return false; // No valid targets within range +} + +bool CastShatteringThrowAction::isPossible() +{ + Unit* target = GetTarget(); + if (!target) + return false; + + // Range check: Shattering Throw is 30 yards + if (!bot->IsWithinDistInMap(target, 30.0f)) + { + return false; + } + + // Check line of sight + if (!bot->IsWithinLOSInMap(target)) + { + return false; + } + + // If the minimal checks above pass, simply return true. + return true; +} + +bool CastShatteringThrowAction::Execute(Event event) +{ + Unit* target = GetTarget(); + if (!target) + return false; + + return botAI->CastSpell("shattering throw", target); +} diff --git a/src/strategy/warrior/WarriorActions.h b/src/strategy/warrior/WarriorActions.h index 38e14f89..5683589c 100644 --- a/src/strategy/warrior/WarriorActions.h +++ b/src/strategy/warrior/WarriorActions.h @@ -60,11 +60,11 @@ SNARE_ACTION(CastThunderClapSnareAction, "thunder clap"); SNARE_ACTION(CastHamstringAction, "hamstring"); MELEE_ACTION(CastOverpowerAction, "overpower"); MELEE_ACTION(CastMockingBlowAction, "mocking blow"); -BUFF_ACTION(CastRetaliationAction, "retaliation"); +// BUFF_ACTION(CastRetaliationAction, "retaliation"); // arms 3.3.5 SPELL_ACTION(CastHeroicThrowAction, "heroic throw"); SNARE_ACTION(CastHeroicThrowSnareAction, "heroic throw"); -DEBUFF_ACTION(CastShatteringThrowAction, "shattering throw"); +// DEBUFF_ACTION(CastShatteringThrowAction, "shattering throw"); // arms talents MELEE_ACTION(CastMortalStrikeAction, "mortal strike"); @@ -144,4 +144,23 @@ public: bool Execute(Event event) override; }; +class CastRetaliationAction : public CastBuffSpellAction +{ +public: + CastRetaliationAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "retaliation") {} + + bool isUseful() override; +}; + +class CastShatteringThrowAction : public CastSpellAction +{ +public: + CastShatteringThrowAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shattering throw") {} + + Unit* GetTarget() override; + bool isUseful() override; + bool isPossible() override; + bool Execute(Event event) override; +}; + #endif diff --git a/src/strategy/warrior/WarriorAiObjectContext.cpp b/src/strategy/warrior/WarriorAiObjectContext.cpp index cbeb3a7b..0a761c56 100644 --- a/src/strategy/warrior/WarriorAiObjectContext.cpp +++ b/src/strategy/warrior/WarriorAiObjectContext.cpp @@ -101,6 +101,7 @@ public: // creators["slam"] = &WarriorTriggerFactoryInternal::slam; creators["vigilance"] = &WarriorTriggerFactoryInternal::vigilance; + creators["shattering throw trigger"] = &WarriorTriggerFactoryInternal::shattering_throw_trigger; } private: @@ -172,6 +173,7 @@ private: // static Trigger* slam(PlayerbotAI* ai) { return new SlamTrigger(ai); } static Trigger* vigilance(PlayerbotAI* botAI) { return new VigilanceTrigger(botAI); } + static Trigger* shattering_throw_trigger(PlayerbotAI* botAI) { return new ShatteringThrowTrigger(botAI); } }; class WarriorAiObjectContextInternal : public NamedObjectContext @@ -242,6 +244,7 @@ public: creators["heroic throw on snare target"] = &WarriorAiObjectContextInternal::heroic_throw_on_snare_target; creators["shattering throw"] = &WarriorAiObjectContextInternal::shattering_throw; creators["vigilance"] = &WarriorAiObjectContextInternal::vigilance; + creators["enraged regeneration"] = &WarriorAiObjectContextInternal::enraged_regeneration; } private: @@ -312,6 +315,7 @@ private: static Action* heroic_throw(PlayerbotAI* botAI) { return new CastHeroicThrowAction(botAI); } static Action* bladestorm(PlayerbotAI* botAI) { return new CastBladestormAction(botAI); } static Action* vigilance(PlayerbotAI* botAI) { return new CastVigilanceAction(botAI); } + static Action* enraged_regeneration(PlayerbotAI* botAI) { return new CastEnragedRegenerationAction(botAI); } }; WarriorAiObjectContext::WarriorAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) diff --git a/src/strategy/warrior/WarriorTriggers.cpp b/src/strategy/warrior/WarriorTriggers.cpp index 8a608690..75afb373 100644 --- a/src/strategy/warrior/WarriorTriggers.cpp +++ b/src/strategy/warrior/WarriorTriggers.cpp @@ -84,3 +84,39 @@ bool VigilanceTrigger::IsActive() return false; // No need to reassign Vigilance } + +bool ShatteringThrowTrigger::IsActive() +{ + // Spell cooldown check + if (!bot->HasSpell(64382)) + { + return false; + } + + // Spell cooldown check + if (bot->HasSpellCooldown(64382)) + { + return false; + } + + GuidVector enemies = AI_VALUE(GuidVector, "possible targets"); + + for (ObjectGuid const& guid : enemies) + { + Unit* enemy = botAI->GetUnit(guid); + if (!enemy || !enemy->IsAlive() || enemy->IsFriendlyTo(bot)) + continue; + + // Check if the enemy is within 25 yards and has the specific auras + if (bot->IsWithinDistInMap(enemy, 25.0f) && + (enemy->HasAura(642) || // Divine Shield + enemy->HasAura(45438) || // Ice Block + enemy->HasAura(41450))) // Blessing of Protection + { + return true; + } + } + + return false; // No valid targets within range +} + diff --git a/src/strategy/warrior/WarriorTriggers.h b/src/strategy/warrior/WarriorTriggers.h index 29dd62ca..b531d890 100644 --- a/src/strategy/warrior/WarriorTriggers.h +++ b/src/strategy/warrior/WarriorTriggers.h @@ -72,6 +72,15 @@ public: bool IsActive() override; }; +class ShatteringThrowTrigger : public Trigger +{ +public: + ShatteringThrowTrigger(PlayerbotAI* botAI) : Trigger(botAI, "shattering throw trigger") {} + + bool IsActive() override; +}; + + // class SlamTrigger : public HasAuraTrigger // { // public: