diff --git a/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h b/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h index fc22fdbb..fa5036e5 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h +++ b/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h @@ -12,8 +12,8 @@ class WotlkDungeonOccActionContext : public NamedObjectContext creators["avoid unstable sphere"] = &WotlkDungeonOccActionContext::avoid_unstable_sphere; creators["mount drake"] = &WotlkDungeonOccActionContext::mount_drake; creators["dismount drake"] = &WotlkDungeonOccActionContext::dismount_drake; - creators["fly drake"] = &WotlkDungeonOccActionContext::fly_drake; - creators["drake attack"] = &WotlkDungeonOccActionContext::drake_attack; + creators["occ fly drake"] = &WotlkDungeonOccActionContext::occ_fly_drake; + creators["occ drake attack"] = &WotlkDungeonOccActionContext::occ_drake_attack; creators["avoid arcane explosion"] = &WotlkDungeonOccActionContext::avoid_arcane_explosion; creators["time bomb spread"] = &WotlkDungeonOccActionContext::time_bomb_spread; } @@ -21,8 +21,8 @@ class WotlkDungeonOccActionContext : public NamedObjectContext static Action* avoid_unstable_sphere(PlayerbotAI* ai) { return new AvoidUnstableSphereAction(ai); } static Action* mount_drake(PlayerbotAI* ai) { return new MountDrakeAction(ai); } static Action* dismount_drake(PlayerbotAI* ai) { return new DismountDrakeAction(ai); } - static Action* fly_drake(PlayerbotAI* ai) { return new OccFlyDrakeAction(ai); } - static Action* drake_attack(PlayerbotAI* ai) { return new OccDrakeAttackAction(ai); } + static Action* occ_fly_drake(PlayerbotAI* ai) { return new OccFlyDrakeAction(ai); } + static Action* occ_drake_attack(PlayerbotAI* ai) { return new OccDrakeAttackAction(ai); } static Action* avoid_arcane_explosion(PlayerbotAI* ai) { return new AvoidArcaneExplosionAction(ai); } static Action* time_bomb_spread(PlayerbotAI* ai) { return new TimeBombSpreadAction(ai); } }; diff --git a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp index 9c1786b5..e64ede7a 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp @@ -15,9 +15,9 @@ void WotlkDungeonOccStrategy::InitTriggers(std::vector &triggers) triggers.push_back(new TriggerNode("drake dismount", NextAction::array(0, new NextAction("dismount drake", ACTION_RAID + 5), nullptr))); triggers.push_back(new TriggerNode("group flying", - NextAction::array(0, new NextAction("fly drake", ACTION_NORMAL + 1), nullptr))); + NextAction::array(0, new NextAction("occ fly drake", ACTION_NORMAL + 1), nullptr))); triggers.push_back(new TriggerNode("drake combat", - NextAction::array(0, new NextAction("drake attack", ACTION_NORMAL + 5), nullptr))); + NextAction::array(0, new NextAction("occ drake attack", ACTION_NORMAL + 5), nullptr))); // Varos Cloudstrider // Seems to be no way to identify the marked cores, may need to hook boss AI.. diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h b/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h index ba4d841d..620610e9 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h +++ b/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h @@ -14,8 +14,8 @@ public: creators["malygos target"] = &RaidEoEActionContext::target; // creators["pull power spark"] = &RaidEoEActionContext::pull_power_spark; // creators["kill power spark"] = &RaidEoEActionContext::kill_power_spark; - creators["fly drake"] = &RaidEoEActionContext::fly_drake; - creators["drake attack"] = &RaidEoEActionContext::drake_attack; + creators["eoe fly drake"] = &RaidEoEActionContext::eoe_fly_drake; + creators["eoe drake attack"] = &RaidEoEActionContext::eoe_drake_attack; } private: @@ -23,8 +23,8 @@ private: static Action* target(PlayerbotAI* ai) { return new MalygosTargetAction(ai); } // static Action* pull_power_spark(PlayerbotAI* ai) { return new PullPowerSparkAction(ai); } // static Action* kill_power_spark(PlayerbotAI* ai) { return new KillPowerSparkAction(ai); } - static Action* fly_drake(PlayerbotAI* ai) { return new EoEFlyDrakeAction(ai); } - static Action* drake_attack(PlayerbotAI* ai) { return new EoEDrakeAttackAction(ai); } + static Action* eoe_fly_drake(PlayerbotAI* ai) { return new EoEFlyDrakeAction(ai); } + static Action* eoe_drake_attack(PlayerbotAI* ai) { return new EoEDrakeAttackAction(ai); } }; #endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp index f30cf30a..d06aa27f 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp +++ b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp @@ -14,9 +14,9 @@ void RaidEoEStrategy::InitTriggers(std::vector& triggers) // NextAction::array(0, new NextAction("kill power spark", ACTION_RAID + 3), nullptr))); triggers.push_back(new TriggerNode("group flying", - NextAction::array(0, new NextAction("fly drake", ACTION_NORMAL + 1), nullptr))); + NextAction::array(0, new NextAction("eoe fly drake", ACTION_NORMAL + 1), nullptr))); triggers.push_back(new TriggerNode("drake combat", - NextAction::array(0, new NextAction("drake attack", ACTION_NORMAL + 5), nullptr))); + NextAction::array(0, new NextAction("eoe drake attack", ACTION_NORMAL + 5), nullptr))); } void RaidEoEStrategy::InitMultipliers(std::vector &multipliers) diff --git a/src/strategy/warrior/TankWarriorStrategy.cpp b/src/strategy/warrior/TankWarriorStrategy.cpp index d7e47f6b..5f228b09 100644 --- a/src/strategy/warrior/TankWarriorStrategy.cpp +++ b/src/strategy/warrior/TankWarriorStrategy.cpp @@ -22,6 +22,7 @@ public: creators["heroic throw taunt"] = &heroic_throw_taunt; creators["taunt"] = &taunt; creators["taunt spell"] = &taunt; + creators["vigilance"] = &vigilance; } private: @@ -41,6 +42,14 @@ private: /*A*/ NextAction::array(0, new NextAction("heroic throw taunt"), nullptr), /*C*/ nullptr); } + + static ActionNode* vigilance(PlayerbotAI* botAI) + { + return new ActionNode("vigilance", + /*P*/ nullptr, + /*A*/ nullptr, + /*C*/ nullptr); + } }; TankWarriorStrategy::TankWarriorStrategy(PlayerbotAI* botAI) : GenericWarriorStrategy(botAI) @@ -59,6 +68,9 @@ void TankWarriorStrategy::InitTriggers(std::vector& triggers) { GenericWarriorStrategy::InitTriggers(triggers); + triggers.push_back(new TriggerNode( + "vigilance", + NextAction::array(0, new NextAction("vigilance", ACTION_HIGH + 7), nullptr))); triggers.push_back( new TriggerNode("enemy out of melee", NextAction::array(0, new NextAction("heroic throw", ACTION_MOVE + 11), new NextAction("charge", ACTION_MOVE + 10), nullptr))); diff --git a/src/strategy/warrior/WarriorActions.cpp b/src/strategy/warrior/WarriorActions.cpp index eaf8f9a5..662bcc2d 100644 --- a/src/strategy/warrior/WarriorActions.cpp +++ b/src/strategy/warrior/WarriorActions.cpp @@ -12,3 +12,92 @@ bool CastSunderArmorAction::isUseful() Aura* aura = botAI->GetAura("sunder armor", GetTarget(), false, true); return !aura || aura->GetStackAmount() < 5 || aura->GetDuration() <= 6000; } + +Value* CastVigilanceAction::GetTargetValue() +{ + Group* group = bot->GetGroup(); + if (!group) + { + return new ManualSetValue(botAI, nullptr); + } + + Player* currentVigilanceTarget = nullptr; + Player* mainTank = nullptr; + Player* assistTank1 = nullptr; + Player* assistTank2 = nullptr; + Player* highestGearScorePlayer = nullptr; + uint32 highestGearScore = 0; + + // Iterate once through the group to gather all necessary information + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || member == bot || !member->IsAlive()) + continue; + + // Check if member has Vigilance applied by the bot + if (!currentVigilanceTarget && botAI->HasAura("vigilance", member, false, true)) + { + currentVigilanceTarget = member; + } + + // Identify Main Tank + if (!mainTank && botAI->IsMainTank(member)) + { + mainTank = member; + } + + // Identify Assist Tanks + if (assistTank1 == nullptr && botAI->IsAssistTankOfIndex(member, 0)) + { + assistTank1 = member; + } + else if (assistTank2 == nullptr && botAI->IsAssistTankOfIndex(member, 1)) + { + assistTank2 = member; + } + + // Determine Highest Gear Score + uint32 gearScore = botAI->GetEquipGearScore(member, false, false); + if (gearScore > highestGearScore) + { + highestGearScore = gearScore; + highestGearScorePlayer = member; + } + } + + // Determine the highest-priority target + Player* highestPriorityTarget = mainTank ? mainTank : + (assistTank1 ? assistTank1 : + (assistTank2 ? assistTank2 : highestGearScorePlayer)); + + // If no valid target, return nullptr + if (!highestPriorityTarget) + { + return new ManualSetValue(botAI, nullptr); + } + + // If the current target is already the highest-priority target, do nothing + if (currentVigilanceTarget == highestPriorityTarget) + { + return new ManualSetValue(botAI, nullptr); + } + + // Assign the new target + Unit* targetUnit = highestPriorityTarget->ToUnit(); + if (targetUnit) + { + return new ManualSetValue(botAI, targetUnit); + } + + return new ManualSetValue(botAI, nullptr); +} + +bool CastVigilanceAction::Execute(Event event) +{ + Unit* target = GetTarget(); + if (!target || target == bot) + return false; + + return botAI->CastSpell("vigilance", target); +} diff --git a/src/strategy/warrior/WarriorActions.h b/src/strategy/warrior/WarriorActions.h index 05505da0..38e14f89 100644 --- a/src/strategy/warrior/WarriorActions.h +++ b/src/strategy/warrior/WarriorActions.h @@ -135,4 +135,13 @@ public: bool isUseful() override; }; +class CastVigilanceAction : public BuffOnPartyAction +{ +public: + CastVigilanceAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "vigilance") {} + + Value* GetTargetValue() override; + bool Execute(Event event) override; +}; + #endif diff --git a/src/strategy/warrior/WarriorAiObjectContext.cpp b/src/strategy/warrior/WarriorAiObjectContext.cpp index efe4c9c1..cbeb3a7b 100644 --- a/src/strategy/warrior/WarriorAiObjectContext.cpp +++ b/src/strategy/warrior/WarriorAiObjectContext.cpp @@ -99,6 +99,8 @@ public: creators["intercept and far enemy"] = &WarriorTriggerFactoryInternal::intercept_and_far_enemy; creators["intercept and rage"] = &WarriorTriggerFactoryInternal::intercept_and_rage; // creators["slam"] = &WarriorTriggerFactoryInternal::slam; + + creators["vigilance"] = &WarriorTriggerFactoryInternal::vigilance; } private: @@ -168,6 +170,8 @@ private: static Trigger* revenge(PlayerbotAI* botAI) { return new RevengeAvailableTrigger(botAI); } static Trigger* sunder_armor(PlayerbotAI* botAI) { return new SunderArmorDebuffTrigger(botAI); } // static Trigger* slam(PlayerbotAI* ai) { return new SlamTrigger(ai); } + + static Trigger* vigilance(PlayerbotAI* botAI) { return new VigilanceTrigger(botAI); } }; class WarriorAiObjectContextInternal : public NamedObjectContext @@ -237,6 +241,7 @@ public: creators["heroic throw"] = &WarriorAiObjectContextInternal::heroic_throw; creators["heroic throw on snare target"] = &WarriorAiObjectContextInternal::heroic_throw_on_snare_target; creators["shattering throw"] = &WarriorAiObjectContextInternal::shattering_throw; + creators["vigilance"] = &WarriorAiObjectContextInternal::vigilance; } private: @@ -306,6 +311,7 @@ private: static Action* heroic_throw_on_snare_target(PlayerbotAI* botAI) { return new CastHeroicThrowSnareAction(botAI); } 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); } }; WarriorAiObjectContext::WarriorAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) diff --git a/src/strategy/warrior/WarriorTriggers.cpp b/src/strategy/warrior/WarriorTriggers.cpp index f2ea13da..8a608690 100644 --- a/src/strategy/warrior/WarriorTriggers.cpp +++ b/src/strategy/warrior/WarriorTriggers.cpp @@ -12,3 +12,75 @@ bool BloodrageBuffTrigger::IsActive() return AI_VALUE2(uint8, "health", "self target") >= sPlayerbotAIConfig->mediumHealth && AI_VALUE2(uint8, "rage", "self target") < 20; } + +bool VigilanceTrigger::IsActive() +{ + if (!bot->HasSpell(50720)) + { + return false; + } + + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + + Player* currentVigilanceTarget = nullptr; + Player* mainTank = nullptr; + Player* assistTank1 = nullptr; + Player* assistTank2 = nullptr; + Player* highestGearScorePlayer = nullptr; + uint32 highestGearScore = 0; + + // Iterate once through the group to gather all necessary information + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || member == bot || !member->IsAlive()) + continue; + + // Check if member has Vigilance applied by the bot + if (!currentVigilanceTarget && botAI->HasAura("vigilance", member, false, true)) + { + currentVigilanceTarget = member; + } + + // Identify Main Tank + if (!mainTank && botAI->IsMainTank(member)) + { + mainTank = member; + } + + // Identify Assist Tanks + if (assistTank1 == nullptr && botAI->IsAssistTankOfIndex(member, 0)) + { + assistTank1 = member; + } + else if (assistTank2 == nullptr && botAI->IsAssistTankOfIndex(member, 1)) + { + assistTank2 = member; + } + + // Determine Highest Gear Score + uint32 gearScore = botAI->GetEquipGearScore(member, false, false); + if (gearScore > highestGearScore) + { + highestGearScore = gearScore; + highestGearScorePlayer = member; + } + } + + // Determine the highest-priority target + Player* highestPriorityTarget = mainTank ? mainTank : + (assistTank1 ? assistTank1 : + (assistTank2 ? assistTank2 : highestGearScorePlayer)); + + // Trigger if no Vigilance is active or the current target is not the highest-priority target + if (!currentVigilanceTarget || currentVigilanceTarget != highestPriorityTarget) + { + return true; + } + + return false; // No need to reassign Vigilance +} diff --git a/src/strategy/warrior/WarriorTriggers.h b/src/strategy/warrior/WarriorTriggers.h index 4ce070b9..29dd62ca 100644 --- a/src/strategy/warrior/WarriorTriggers.h +++ b/src/strategy/warrior/WarriorTriggers.h @@ -64,6 +64,14 @@ public: RendDebuffTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "rend", 1, true) {} }; +class VigilanceTrigger : public BuffOnPartyTrigger +{ +public: + VigilanceTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "vigilance") {} + + bool IsActive() override; +}; + // class SlamTrigger : public HasAuraTrigger // { // public: