From 9ddc71a9d73246f10ca9ee0d08996fb45cfd7ea6 Mon Sep 17 00:00:00 2001 From: kadeshar Date: Fri, 21 Mar 2025 13:19:59 +0100 Subject: [PATCH] - Changed Eye of Eternity healers selection (#1102) - Added Iron Assembly strategies for normal mode - Improved Hodir Biting Cold strategy --- .../raids/eyeofeternity/RaidEoEActions.cpp | 87 +++++++++-------- .../raids/ulduar/RaidUlduarActionContext.h | 6 ++ .../raids/ulduar/RaidUlduarActions.cpp | 94 ++++++++++++++++++- src/strategy/raids/ulduar/RaidUlduarActions.h | 23 +++++ .../raids/ulduar/RaidUlduarStrategy.cpp | 29 ++++-- .../raids/ulduar/RaidUlduarTriggerContext.h | 4 + .../raids/ulduar/RaidUlduarTriggers.cpp | 69 +++++++++++++- .../raids/ulduar/RaidUlduarTriggers.h | 37 +++++++- 8 files changed, 295 insertions(+), 54 deletions(-) diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp b/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp index 001733d3..6582aacc 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp +++ b/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp @@ -1,9 +1,7 @@ +#include "Playerbots.h" #include "RaidEoEActions.h" #include "RaidEoETriggers.h" -#include "Playerbots.h" - - bool MalygosPositionAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); @@ -280,10 +278,14 @@ bool EoEDrakeAttackAction::isPossible() Unit* vehicleBase = bot->GetVehicleBase(); return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); } + bool EoEDrakeAttackAction::Execute(Event event) { vehicleBase = bot->GetVehicleBase(); - if (!vehicleBase) { return false; } + if (!vehicleBase) + { + return false; + } // Unit* target = AI_VALUE(Unit*, "current target"); Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); @@ -295,22 +297,55 @@ bool EoEDrakeAttackAction::Execute(Event event) for (auto& npc : npcs) { Unit* unit = botAI->GetUnit(npc); - if (!unit || unit->GetEntry() != NPC_MALYGOS) { continue; } + if (!unit || unit->GetEntry() != NPC_MALYGOS) + { + continue; + } boss = unit; break; } } // Check this again to see if a target was assigned - if (!boss) { return false; } - - if (botAI->IsHeal(bot)) + if (!boss) { - return DrakeHealAction(); + return false; + } + + uint8 numHealers; + bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL ? numHealers = 10 : numHealers = 4; + + Group* group = bot->GetGroup(); + if (!group) + return false; + std::vector> sortedMembers; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + sortedMembers.push_back(std::make_pair(member->GetGUID(), member)); + } + std::sort(sortedMembers.begin(), sortedMembers.end()); + + int botIndex = -1; + for (size_t i = 0; i < sortedMembers.size(); ++i) + { + if (sortedMembers[i].first == bot->GetGUID()) + { + botIndex = i; + break; + } + } + + if (botIndex == -1) + return false; + + if (botIndex > numHealers) + { + return DrakeDpsAction(boss); } else { - return DrakeDpsAction(boss); + return DrakeHealAction(); } return false; @@ -348,37 +383,15 @@ bool EoEDrakeAttackAction::DrakeDpsAction(Unit* target) bool EoEDrakeAttackAction::DrakeHealAction() { Unit* vehicleBase = bot->GetVehicleBase(); - if (!vehicleBase) { return false; } - - Unit* target = vehicleBase->GetComboTarget(); - if (!target) + if (!vehicleBase) { - // Unit* newTarget = nullptr; - Unit* newTarget = vehicleBase; - GuidVector members = AI_VALUE(GuidVector, "group members"); - for (auto& member : members) - { - Unit* unit = botAI->GetUnit(member); - if (!unit) - { - continue; - } - - Unit* drake = unit->GetVehicleBase(); - if (!drake || drake->IsFullHealth()) { continue; } - - if (!newTarget || drake->GetHealthPct() < newTarget->GetHealthPct() - 5.0f) - { - newTarget = drake; - } - } - target = newTarget; + return false; } - uint8 comboPoints = vehicleBase->GetComboPoints(target); + uint8 comboPoints = vehicleBase->GetComboPoints(vehicleBase); if (comboPoints >= 5) { - return CastDrakeSpellAction(target, SPELL_LIFE_BURST, 0); + return CastDrakeSpellAction(vehicleBase, SPELL_LIFE_BURST, 0); } else { @@ -386,6 +399,6 @@ bool EoEDrakeAttackAction::DrakeHealAction() // "botAI->CanCastVehicleSpell()" returns SPELL_FAILED_BAD_TARGETS when targeting drakes. // Forcing the cast attempt seems to succeed, not sure what's going on here. // return CastDrakeSpellAction(target, SPELL_REVIVIFY, 0); - return botAI->CastVehicleSpell(SPELL_REVIVIFY, target); + return botAI->CastVehicleSpell(SPELL_REVIVIFY, vehicleBase); } } diff --git a/src/strategy/raids/ulduar/RaidUlduarActionContext.h b/src/strategy/raids/ulduar/RaidUlduarActionContext.h index e3dcd97b..a801735e 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActionContext.h +++ b/src/strategy/raids/ulduar/RaidUlduarActionContext.h @@ -24,7 +24,10 @@ public: creators["razorscale grounded"] = &RaidUlduarActionContext::razorscale_grounded; creators["razorscale harpoon action"] = &RaidUlduarActionContext::razorscale_harpoon_action; creators["razorscale fuse armor action"] = &RaidUlduarActionContext::razorscale_fuse_armor_action; + creators["iron assembly lightning tendrils action"] = &RaidUlduarActionContext::iron_assembly_lightning_tendrils_action; + creators["iron assembly overload action"] = &RaidUlduarActionContext::iron_assembly_overload_action; creators["hodir move snowpacked icicle"] = &RaidUlduarActionContext::hodir_move_snowpacked_icicle; + creators["hodir biting cold jump"] = &RaidUlduarActionContext::hodir_biting_cold_jump; creators["freya move away nature bomb"] = &RaidUlduarActionContext::freya_move_away_nature_bomb; creators["freya mark eonars gift"] = &RaidUlduarActionContext::freya_mark_eonars_gift; } @@ -39,7 +42,10 @@ private: static Action* razorscale_grounded(PlayerbotAI* ai) { return new RazorscaleGroundedAction(ai); } static Action* razorscale_harpoon_action(PlayerbotAI* ai) { return new RazorscaleHarpoonAction(ai); } static Action* razorscale_fuse_armor_action(PlayerbotAI* ai) { return new RazorscaleFuseArmorAction(ai); } + static Action* iron_assembly_lightning_tendrils_action(PlayerbotAI* ai) { return new IronAssemblyLightningTendrilsAction(ai); } + static Action* iron_assembly_overload_action(PlayerbotAI* ai) { return new IronAssemblyOverloadAction(ai); } static Action* hodir_move_snowpacked_icicle(PlayerbotAI* ai) { return new HodirMoveSnowpackedIcicleAction(ai); } + static Action* hodir_biting_cold_jump(PlayerbotAI* ai) { return new HodirBitingColdJumpAction(ai); } static Action* freya_move_away_nature_bomb(PlayerbotAI* ai) { return new FreyaMoveAwayNatureBombAction(ai); } static Action* freya_mark_eonars_gift(PlayerbotAI* ai) { return new FreyaMarkEonarsGiftAction(ai); } }; diff --git a/src/strategy/raids/ulduar/RaidUlduarActions.cpp b/src/strategy/raids/ulduar/RaidUlduarActions.cpp index b3265026..de7a60e6 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActions.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarActions.cpp @@ -1166,6 +1166,54 @@ bool RazorscaleFuseArmorAction::Execute(Event event) return true; } +bool IronAssemblyLightningTendrilsAction::isUseful() +{ + IronAssemblyLightningTendrilsTrigger ironAssemblyLightningTendrilsTrigger(botAI); + return ironAssemblyLightningTendrilsTrigger.IsActive(); +} + +bool IronAssemblyLightningTendrilsAction::Execute(Event event) +{ + const float radius = 18.0f + 10.0f; // 18 yards + 10 yards for safety + + Unit* boss = AI_VALUE2(Unit*, "find target", "stormcaller brundir"); + if (!boss) + return false; + + float currentDistance = bot->GetDistance2d(boss); + + if (currentDistance < radius) + { + return MoveAway(boss, radius - currentDistance); + } + + return false; +} + +bool IronAssemblyOverloadAction::isUseful() +{ + IronAssemblyOverloadTrigger ironAssemblyOverloadTrigger(botAI); + return ironAssemblyOverloadTrigger.IsActive(); +} + +bool IronAssemblyOverloadAction::Execute(Event event) +{ + const float radius = 20.0f + 5.0f; // 20 yards + 5 yards for safety + + Unit* boss = AI_VALUE2(Unit*, "find target", "stormcaller brundir"); + if (!boss) + return false; + + float currentDistance = bot->GetDistance2d(boss); + + if (currentDistance < radius) + { + return MoveAway(boss, radius - currentDistance); + } + + return false; +} + bool HodirMoveSnowpackedIcicleAction::isUseful() { // Check boss and it is alive @@ -1175,13 +1223,19 @@ bool HodirMoveSnowpackedIcicleAction::isUseful() return false; } + // Check if boss is casting Flash Freeze + if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE)) + { + return false; + } + // Find the nearest Snowpacked Icicle Target - Creature* target = bot->FindNearestCreature(33174, 100.0f); + Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f); if (!target) return false; - // Check that boss is stacked on Snowpacked Icicle - if (bot->GetDistance2d(target->GetPositionX(), target->GetPositionY()) <= 3.0f) + // Check that bot is stacked on Snowpacked Icicle + if (bot->GetDistance2d(target->GetPositionX(), target->GetPositionY()) <= 5.0f) { return false; } @@ -1191,12 +1245,42 @@ bool HodirMoveSnowpackedIcicleAction::isUseful() bool HodirMoveSnowpackedIcicleAction::Execute(Event event) { - Creature* target = bot->FindNearestCreature(33174, 100.0f); + Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f); if (!target) return false; return MoveTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_NORMAL); + false, false, true, MovementPriority::MOVEMENT_NORMAL, true); +} + +bool HodirBitingColdJumpAction::Execute(Event event) +{ + // This needs improving but maybe it should be done in the playerbot core. + + int mapId = bot->GetMap()->GetId(); + int x = bot->GetPositionX(); + int y = bot->GetPositionY(); + int z = bot->GetPositionZ() + 3.98f; + float speed = 7.96f; + + UpdateMovementState(); + if (!IsMovingAllowed(mapId, x, y, z)) + { + return false; + } + MovementPriority priority; + if (IsWaitingForLastMove(priority)) + { + return false; + } + + MotionMaster& mm = *bot->GetMotionMaster(); + mm.Clear(); + mm.MoveJump(x, y, z, speed, speed, 1, AI_VALUE(Unit*, "current target")); + mm.MoveFall(0, true); + AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000, priority); + + return true; } bool FreyaMoveAwayNatureBombAction::isUseful() diff --git a/src/strategy/raids/ulduar/RaidUlduarActions.h b/src/strategy/raids/ulduar/RaidUlduarActions.h index 3c1a6504..d0a362e2 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActions.h +++ b/src/strategy/raids/ulduar/RaidUlduarActions.h @@ -114,6 +114,29 @@ public: bool isUseful() override; }; +class IronAssemblyLightningTendrilsAction : public MovementAction +{ +public: + IronAssemblyLightningTendrilsAction(PlayerbotAI* botAI) : MovementAction(botAI, "iron assembly lightning tendrils action") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class IronAssemblyOverloadAction : public MovementAction +{ +public: + IronAssemblyOverloadAction(PlayerbotAI* botAI) : MovementAction(botAI, "iron assembly overload action") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class HodirBitingColdJumpAction : public MovementAction +{ +public: + HodirBitingColdJumpAction(PlayerbotAI* ai) : MovementAction(ai, "hodir biting cold jump") {} + bool Execute(Event event) override; +}; + class FreyaMoveAwayNatureBombAction : public MovementAction { public: diff --git a/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp b/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp index abbd5212..a873768b 100644 --- a/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp @@ -46,25 +46,38 @@ void RaidUlduarStrategy::InitTriggers(std::vector& triggers) "razorscale fuse armor trigger", NextAction::array(0, new NextAction("razorscale fuse armor action", ACTION_RAID + 2), nullptr))); + // + // Iron Assembly + // + triggers.push_back(new TriggerNode( + "iron assembly lightning tendrils trigger", + NextAction::array(0, new NextAction("iron assembly lightning tendrils action", ACTION_RAID), nullptr))); + + triggers.push_back(new TriggerNode( + "iron assembly overload trigger", + NextAction::array(0, new NextAction("iron assembly overload action", ACTION_RAID), nullptr))); + // // Hodir // triggers.push_back(new TriggerNode( "hodir near snowpacked icicle", - NextAction::array(0, new NextAction("hodir move snowpacked icicle", ACTION_RAID + 5), nullptr))); + NextAction::array(0, new NextAction("hodir move snowpacked icicle", ACTION_RAID + 1), nullptr))); + triggers.push_back(new TriggerNode( - "hodir biting cold", NextAction::array(0, new NextAction("intense cold jump", ACTION_RAID + 4), nullptr))); + "hodir biting cold", + NextAction::array(0, new NextAction("hodir biting cold jump", ACTION_RAID), nullptr))); // // Freya // - triggers.push_back( - new TriggerNode("freya tank near eonars gift", - NextAction::array(0, new NextAction("freya mark eonars gift", ACTION_RAID + 1), nullptr))); + triggers.push_back(new TriggerNode( + "freya tank near eonars gift", + NextAction::array(0, new NextAction("freya mark eonars gift", ACTION_RAID + 1), nullptr))); - triggers.push_back( - new TriggerNode("freya near nature bomb", - NextAction::array(0, new NextAction("freya move away nature bomb", ACTION_RAID), nullptr))); + triggers.push_back(new TriggerNode( + "freya near nature bomb", + NextAction::array(0, new NextAction("freya move away nature bomb", ACTION_RAID), nullptr))); } void RaidUlduarStrategy::InitMultipliers(std::vector& multipliers) diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h b/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h index 6976b35c..953d5eda 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h +++ b/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h @@ -24,6 +24,8 @@ public: creators["razorscale grounded"] = &RaidUlduarTriggerContext::razorscale_grounded; creators["razorscale harpoon trigger"] = &RaidUlduarTriggerContext::razorscale_harpoon_trigger; creators["razorscale fuse armor trigger"] = &RaidUlduarTriggerContext::razorscale_fuse_armor_trigger; + creators["iron assembly lightning tendrils trigger"] = &RaidUlduarTriggerContext::iron_assembly_lightning_tendrils_trigger; + creators["iron assembly overload trigger"] = &RaidUlduarTriggerContext::iron_assembly_overload_trigger; creators["hodir biting cold"] = &RaidUlduarTriggerContext::hodir_biting_cold; creators["hodir near snowpacked icicle"] = &RaidUlduarTriggerContext::hodir_near_snowpacked_icicle; creators["freya near nature bomb"] = &RaidUlduarTriggerContext::freya_near_nature_bomb; @@ -40,6 +42,8 @@ private: static Trigger* razorscale_grounded(PlayerbotAI* ai) { return new RazorscaleGroundedTrigger(ai); } static Trigger* razorscale_harpoon_trigger(PlayerbotAI* ai) { return new RazorscaleHarpoonAvailableTrigger(ai); } static Trigger* razorscale_fuse_armor_trigger(PlayerbotAI* ai) { return new RazorscaleFuseArmorTrigger(ai); } + static Trigger* iron_assembly_lightning_tendrils_trigger(PlayerbotAI* ai) { return new IronAssemblyLightningTendrilsTrigger(ai); } + static Trigger* iron_assembly_overload_trigger(PlayerbotAI* ai) { return new IronAssemblyOverloadTrigger(ai); } static Trigger* hodir_biting_cold(PlayerbotAI* ai) { return new HodirBitingColdTrigger(ai); } static Trigger* hodir_near_snowpacked_icicle(PlayerbotAI* ai) { return new HodirNearSnowpackedIcicleTrigger(ai); } static Trigger* freya_near_nature_bomb(PlayerbotAI* ai) { return new FreyaNearNatureBombTrigger(ai); } diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp index b358f0ee..5ae7b193 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp @@ -242,10 +242,58 @@ bool RazorscaleFuseArmorTrigger::IsActive() return false; } +bool IronAssemblyLightningTendrilsTrigger::IsActive() +{ + // Check boss and it is alive + Unit* boss = AI_VALUE2(Unit*, "find target", "stormcaller brundir"); + if (!boss || !boss->IsAlive()) + return false; + + // Check if bot is within 35 yards of the boss + if (boss->GetDistance(bot) > 35.0f) + return false; + + // Check if the boss has the Lightning Tendrils aura + return boss->HasAura(SPELL_LIGHTNING_TENDRILS_10_MAN) || boss->HasAura(SPELL_LIGHTNING_TENDRILS_25_MAN); +} + +bool IronAssemblyOverloadTrigger::IsActive() +{ + // Check if bot is tank + if (botAI->IsTank(bot)) + return false; + + // Check boss and it is alive + Unit* boss = AI_VALUE2(Unit*, "find target", "stormcaller brundir"); + if (!boss || !boss->IsAlive()) + return false; + + // Check if bot is within 35 yards of the boss + if (boss->GetDistance(bot) > 35.0f) + return false; + + // Check if the boss has the Overload aura + return boss->HasAura(SPELL_OVERLOAD_10_MAN) || boss->HasAura(SPELL_OVERLOAD_25_MAN) || + boss->HasAura(SPELL_OVERLOAD_10_MAN_2) || boss->HasAura(SPELL_OVERLOAD_25_MAN_2); +} + bool HodirBitingColdTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "hodir"); - return boss && botAI->GetAura("biting cold", bot, false, false); + + // Check boss and it is alive + if (!boss || !boss->IsAlive()) + { + return false; + } + + // Override if boss is casting Flash Freeze + if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE)) + { + return true; + } + + return boss && botAI->GetAura("biting cold", bot, false, false, 2); } //Snowpacked Icicle Target @@ -258,9 +306,24 @@ bool HodirNearSnowpackedIcicleTrigger::IsActive() return false; } + // Check if boss is casting Flash Freeze + if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE)) + { + return false; + } + // Find the nearest Snowpacked Icicle Target - Creature* target = bot->FindNearestCreature(33174, 100.0f); - return target != nullptr; + Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f); + if (!target) + return false; + + // Check that bot is stacked on Snowpacked Icicle + if (bot->GetDistance2d(target->GetPositionX(), target->GetPositionY()) <= 5.0f) + { + return false; + } + + return true; } bool FreyaNearNatureBombTrigger::IsActive() diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggers.h b/src/strategy/raids/ulduar/RaidUlduarTriggers.h index 2d031874..1782eb7d 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggers.h +++ b/src/strategy/raids/ulduar/RaidUlduarTriggers.h @@ -9,9 +9,21 @@ enum UlduarIDs { + // Iron Assembly + SPELL_LIGHTNING_TENDRILS_10_MAN = 61887, + SPELL_LIGHTNING_TENDRILS_25_MAN = 63486, + SPELL_OVERLOAD_10_MAN = 61869, + SPELL_OVERLOAD_25_MAN = 63481, + SPELL_OVERLOAD_10_MAN_2 = 63485, + SPELL_OVERLOAD_25_MAN_2 = 61886, + + // Hodir + NPC_SNOWPACKED_ICICLE = 33174, + NPC_TOASTY_FIRE = 33342, + SPELL_FLASH_FREEZE = 61968, + // Freya NPC_EONARS_GIFT = 33228, - GOBJECT_NATURE_BOMB = 194902, }; @@ -84,6 +96,26 @@ public: bool IsActive() override; }; +// +// Iron Assembly +// +class IronAssemblyLightningTendrilsTrigger : public Trigger +{ +public: + IronAssemblyLightningTendrilsTrigger(PlayerbotAI* ai) : Trigger(ai, "iron assembly lightning tendrils trigger") {} + bool IsActive() override; +}; + +class IronAssemblyOverloadTrigger : public Trigger +{ +public: + IronAssemblyOverloadTrigger(PlayerbotAI* ai) : Trigger(ai, "iron assembly overload trigger") {} + bool IsActive() override; +}; + +// +// Hodir +// class HodirBitingColdTrigger : public Trigger { public: @@ -98,6 +130,9 @@ public: bool IsActive() override; }; +// +// Freya +// class FreyaNearNatureBombTrigger : public Trigger { public: