- Changed Eye of Eternity healers selection (#1102)

- Added Iron Assembly strategies for normal mode
- Improved Hodir Biting Cold strategy
This commit is contained in:
kadeshar
2025-03-21 13:19:59 +01:00
committed by GitHub
parent 28be2b13c3
commit 9ddc71a9d7
8 changed files with 295 additions and 54 deletions

View File

@@ -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<std::pair<ObjectGuid, Player*>> 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);
}
}

View File

@@ -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); }
};

View File

@@ -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()

View File

@@ -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:

View File

@@ -46,25 +46,38 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& 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<Multiplier*>& multipliers)

View File

@@ -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); }

View File

@@ -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()

View File

@@ -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: