Small fixes to naxxramas strategy (#2201)

# Pull Request

Small fixes to naxxramas strategy

---

## Complexity & Impact

Does this change add new decision branches?
- - [x] No
- - [ ] Yes (**explain below**)

Does this change increase per-bot or per-tick processing?
- - [x] No
- - [ ] Yes (**describe and justify impact**)

Could this logic scale poorly under load?
- - [x] No
- - [ ] Yes (**explain why**)
---

## Defaults & Configuration

Does this change modify default bot behavior?
- - [x] No
- - [ ] Yes (**explain why**)

If this introduces more advanced or AI-heavy logic:
- - [x] Lightweight mode remains the default
- - [ ] More complex behavior is optional and thereby configurable
---

## AI Assistance

Was AI assistance (e.g. ChatGPT or similar tools) used while working on
this change?
- - [x] No
- - [ ] Yes (**explain below**)

If yes, please specify:

Copilot CLI to code review

---

## Final Checklist

- - [x] Stability is not compromised
- - [x] Performance impact is understood, tested, and acceptable
- - [x] Added logic complexity is justified and explained
- - [x] Documentation updated if needed
This commit is contained in:
kadeshar
2026-03-13 22:22:11 +01:00
committed by GitHub
parent 5e7613f719
commit a695ac77fa
14 changed files with 51 additions and 57 deletions

View File

@@ -37,10 +37,10 @@ public:
uint32 GetCurrWaypoint() override; uint32 GetCurrWaypoint() override;
}; };
class GrobblulusMoveCenterAction : public MoveInsideAction class GrobbulusMoveCenterAction : public MoveInsideAction
{ {
public: public:
GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {} GrobbulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {}
}; };
class GrobbulusMoveAwayAction : public MovementAction class GrobbulusMoveAwayAction : public MovementAction
@@ -173,26 +173,26 @@ private:
RazuviousBossHelper helper; RazuviousBossHelper helper;
}; };
class HorsemanAttractAlternativelyAction : public AttackAction class FourHorsemenAttractAlternativelyAction : public AttackAction
{ {
public: public:
HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attract alternatively"), helper(ai) FourHorsemenAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "four horsemen attract alternatively"), helper(ai)
{ {
} }
bool Execute(Event event) override; bool Execute(Event event) override;
protected: protected:
FourhorsemanBossHelper helper; FourHorsemenBossHelper helper;
}; };
class HorsemanAttactInOrderAction : public AttackAction class FourHorsemenAttackInOrderAction : public AttackAction
{ {
public: public:
HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attact in order"), helper(ai) {} FourHorsemenAttackInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "four horsemen attack in order"), helper(ai) {}
bool Execute(Event event) override; bool Execute(Event event) override;
protected: protected:
FourhorsemanBossHelper helper; FourHorsemenBossHelper helper;
}; };
// class SapphironGroundMainTankPositionAction : public MovementAction // class SapphironGroundMainTankPositionAction : public MovementAction

View File

@@ -1,7 +1,6 @@
#include "RaidNaxxActions.h"
#include "ObjectGuid.h" #include "ObjectGuid.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "RaidNaxxActions.h"
bool AnubrekhanChooseTargetAction::Execute(Event /*event*/) bool AnubrekhanChooseTargetAction::Execute(Event /*event*/)
{ {
@@ -66,13 +65,10 @@ bool AnubrekhanPositionAction::Execute(Event /*event*/)
{ {
uint32 nearest = FindNearestWaypoint(); uint32 nearest = FindNearestWaypoint();
uint32 next_point; uint32 next_point;
if (inPhase) next_point = (nearest + 1) % intervals;
next_point = (nearest + 1) % intervals;
else
next_point = nearest;
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), false, false, return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second,
false, false, MovementPriority::MOVEMENT_COMBAT); bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
} }
else else
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT); return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);

View File

@@ -2,7 +2,7 @@
#include "Playerbots.h" #include "Playerbots.h"
bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/) bool FourHorsemenAttractAlternativelyAction::Execute(Event /*event*/)
{ {
if (!helper.UpdateBossAI()) if (!helper.UpdateBossAI())
return false; return false;
@@ -13,13 +13,13 @@ bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
return true; return true;
Unit* attackTarget = helper.CurrentAttackTarget(); Unit* attackTarget = helper.CurrentAttackTarget();
if (context->GetValue<Unit*>("current target")->Get() != attackTarget) if (attackTarget && context->GetValue<Unit*>("current target")->Get() != attackTarget)
return Attack(attackTarget); return Attack(attackTarget);
return false; return false;
} }
bool HorsemanAttactInOrderAction::Execute(Event /*event*/) bool FourHorsemenAttackInOrderAction::Execute(Event /*event*/)
{ {
if (!helper.UpdateBossAI()) if (!helper.UpdateBossAI())
return false; return false;

View File

@@ -70,7 +70,6 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt()
if (!group) if (!group)
return false; return false;
Group::MemberSlotList const& slots = group->GetMemberSlots();
Player* playerWithIcebolt = nullptr; Player* playerWithIcebolt = nullptr;
float minDistance; float minDistance;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())

View File

@@ -13,7 +13,7 @@ bool ThaddiusAttackNearestPetAction::isUseful()
return false; return false;
Unit* target = helper.GetNearestPet(); Unit* target = helper.GetNearestPet();
if (!bot->IsWithinDistInMap(target, 50.0f)) if (!target || !bot->IsWithinDistInMap(target, 50.0f))
return false; return false;
return true; return true;
@@ -22,7 +22,7 @@ bool ThaddiusAttackNearestPetAction::isUseful()
bool ThaddiusAttackNearestPetAction::Execute(Event /*event*/) bool ThaddiusAttackNearestPetAction::Execute(Event /*event*/)
{ {
Unit* target = helper.GetNearestPet(); Unit* target = helper.GetNearestPet();
if (!bot->IsWithinLOSInMap(target)) if (!target || !bot->IsWithinLOSInMap(target))
return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT); return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
if (AI_VALUE(Unit*, "current target") != target) if (AI_VALUE(Unit*, "current target") != target)

View File

@@ -245,7 +245,7 @@ float AnubrekhanGenericMultiplier::GetValue(Action* action)
return 1.0f; return 1.0f;
} }
float FourhorsemanGenericMultiplier::GetValue(Action* action) float FourHorsemenGenericMultiplier::GetValue(Action* action)
{ {
Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek"); Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek");
if (!boss) if (!boss)

View File

@@ -1,6 +1,6 @@
#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H #ifndef _PLAYERBOT_RAIDNAXXMULTIPLIERS_H
#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H #define _PLAYERBOT_RAIDNAXXMULTIPLIERS_H
#include "Multiplier.h" #include "Multiplier.h"
#include "RaidNaxxBossHelper.h" #include "RaidNaxxBossHelper.h"
@@ -84,10 +84,10 @@ public:
virtual float GetValue(Action* action); virtual float GetValue(Action* action);
}; };
class FourhorsemanGenericMultiplier : public Multiplier class FourHorsemenGenericMultiplier : public Multiplier
{ {
public: public:
FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "fourhorseman generic") {} FourHorsemenGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "four horsemen generic") {}
public: public:
virtual float GetValue(Action* action); virtual float GetValue(Action* action);

View File

@@ -31,8 +31,8 @@ public:
creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal; creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal;
creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target; creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target;
creators["horseman attract alternatively"] = &RaidNaxxActionContext::horseman_attract_alternatively; creators["four horsemen attract alternatively"] = &RaidNaxxActionContext::four_horsemen_attract_alternatively;
creators["horseman attack in order"] = &RaidNaxxActionContext::horseman_attack_in_order; creators["four horsemen attack in order"] = &RaidNaxxActionContext::four_horsemen_attack_in_order;
creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position; creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position;
creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position; creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position;
@@ -56,7 +56,7 @@ public:
private: private:
static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(ai); } static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(ai); }
static Action* rotate_grobbulus(PlayerbotAI* ai) { return new GrobbulusRotateAction(ai); } static Action* rotate_grobbulus(PlayerbotAI* ai) { return new GrobbulusRotateAction(ai); }
static Action* grobbulus_move_center(PlayerbotAI* ai) { return new GrobblulusMoveCenterAction(ai); } static Action* grobbulus_move_center(PlayerbotAI* ai) { return new GrobbulusMoveCenterAction(ai); }
static Action* grobbulus_move_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(ai); } static Action* grobbulus_move_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(ai); }
//static Action* heigan_dance_melee(PlayerbotAI* ai) { return new HeiganDanceMeleeAction(ai); } //static Action* heigan_dance_melee(PlayerbotAI* ai) { return new HeiganDanceMeleeAction(ai); }
//static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new HeiganDanceRangedAction(ai); } //static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new HeiganDanceRangedAction(ai); }
@@ -70,11 +70,8 @@ private:
{ {
return new RazuviousUseObedienceCrystalAction(ai); return new RazuviousUseObedienceCrystalAction(ai);
} }
static Action* horseman_attract_alternatively(PlayerbotAI* ai) static Action* four_horsemen_attract_alternatively(PlayerbotAI* ai) { return new FourHorsemenAttractAlternativelyAction(ai); }
{ static Action* four_horsemen_attack_in_order(PlayerbotAI* ai) { return new FourHorsemenAttackInOrderAction(ai); }
return new HorsemanAttractAlternativelyAction(ai);
}
static Action* horseman_attack_in_order(PlayerbotAI* ai) { return new HorsemanAttactInOrderAction(ai); }
// static Action* sapphiron_ground_main_tank_position(PlayerbotAI* ai) { return new // static Action* sapphiron_ground_main_tank_position(PlayerbotAI* ai) { return new
// SapphironGroundMainTankPositionAction(ai); } // SapphironGroundMainTankPositionAction(ai); }
static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); } static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); }

View File

@@ -30,8 +30,8 @@ public:
creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank; creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank;
creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank; creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank;
creators["horseman attractors"] = &RaidNaxxTriggerContext::horseman_attractors; creators["four horsemen attractors"] = &RaidNaxxTriggerContext::four_horsemen_attractors;
creators["horseman except attractors"] = &RaidNaxxTriggerContext::horseman_except_attractors; creators["four horsemen except attractors"] = &RaidNaxxTriggerContext::four_horsemen_except_attractors;
creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground; creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground;
creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight; creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight;
@@ -66,8 +66,8 @@ private:
static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); } static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); }
static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); } static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); }
static Trigger* horseman_attractors(PlayerbotAI* ai) { return new HorsemanAttractorsTrigger(ai); } static Trigger* four_horsemen_attractors(PlayerbotAI* ai) { return new FourHorsemenAttractorsTrigger(ai); }
static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new HorsemanExceptAttractorsTrigger(ai); } static Trigger* four_horsemen_except_attractors(PlayerbotAI* ai) { return new FourHorsemenExceptAttractorsTrigger(ai); }
static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); } static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); }
static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(ai); } static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(ai); }

View File

@@ -97,13 +97,13 @@ void RaidNaxxStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{ NextAction("razuvious target", ACTION_RAID + 1) } { NextAction("razuvious target", ACTION_RAID + 1) }
)); ));
// four horseman // four horsemen
triggers.push_back(new TriggerNode("horseman attractors", triggers.push_back(new TriggerNode("four horsemen attractors",
{ NextAction("horseman attract alternatively", ACTION_RAID + 1) } { NextAction("four horsemen attract alternatively", ACTION_RAID + 1) }
)); ));
triggers.push_back(new TriggerNode("horseman except attractors", triggers.push_back(new TriggerNode("four horsemen except attractors",
{ NextAction("horseman attack in order", ACTION_RAID + 1) } { NextAction("four horsemen attack in order", ACTION_RAID + 1) }
)); ));
// sapphiron // sapphiron
@@ -150,7 +150,7 @@ void RaidNaxxStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI)); multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI));
multipliers.push_back(new KelthuzadGenericMultiplier(botAI)); multipliers.push_back(new KelthuzadGenericMultiplier(botAI));
multipliers.push_back(new AnubrekhanGenericMultiplier(botAI)); multipliers.push_back(new AnubrekhanGenericMultiplier(botAI));
multipliers.push_back(new FourhorsemanGenericMultiplier(botAI)); multipliers.push_back(new FourHorsemenGenericMultiplier(botAI));
// multipliers.push_back(new GothikGenericMultiplier(botAI)); // multipliers.push_back(new GothikGenericMultiplier(botAI));
multipliers.push_back(new GluthGenericMultiplier(botAI)); multipliers.push_back(new GluthGenericMultiplier(botAI));
} }

View File

@@ -114,7 +114,7 @@ bool RazuviousNontankTrigger::IsActive()
return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST); return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST);
} }
bool HorsemanAttractorsTrigger::IsActive() bool FourHorsemenAttractorsTrigger::IsActive()
{ {
if (!helper.UpdateBossAI()) if (!helper.UpdateBossAI())
return false; return false;
@@ -122,7 +122,7 @@ bool HorsemanAttractorsTrigger::IsActive()
return helper.IsAttracter(bot); return helper.IsAttracter(bot);
} }
bool HorsemanExceptAttractorsTrigger::IsActive() bool FourHorsemenExceptAttractorsTrigger::IsActive()
{ {
if (!helper.UpdateBossAI()) if (!helper.UpdateBossAI())
return false; return false;

View File

@@ -186,24 +186,24 @@ private:
ThaddiusBossHelper helper; ThaddiusBossHelper helper;
}; };
class HorsemanAttractorsTrigger : public Trigger class FourHorsemenAttractorsTrigger : public Trigger
{ {
public: public:
HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {} FourHorsemenAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "four horsemen attractors"), helper(ai) {}
bool IsActive() override; bool IsActive() override;
private: private:
FourhorsemanBossHelper helper; FourHorsemenBossHelper helper;
}; };
class HorsemanExceptAttractorsTrigger : public Trigger class FourHorsemenExceptAttractorsTrigger : public Trigger
{ {
public: public:
HorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen except attractors"), helper(ai) {} FourHorsemenExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "four horsemen except attractors"), helper(ai) {}
bool IsActive() override; bool IsActive() override;
private: private:
FourhorsemanBossHelper helper; FourHorsemenBossHelper helper;
}; };
class SapphironGroundTrigger : public Trigger class SapphironGroundTrigger : public Trigger

View File

@@ -202,7 +202,7 @@ public:
} }
bool FindPosToAvoidChill(std::vector<float>& dest) bool FindPosToAvoidChill(std::vector<float>& dest)
{ {
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25}); Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill10, NaxxSpellIds::Chill25});
if (!aura) if (!aura)
{ {
// Fallback to name for custom spell data. // Fallback to name for custom spell data.
@@ -363,13 +363,13 @@ private:
Unit* _unit = nullptr; Unit* _unit = nullptr;
}; };
class FourhorsemanBossHelper : public AiObject class FourHorsemenBossHelper : public AiObject
{ {
public: public:
const float posZ = 241.27f; const float posZ = 241.27f;
const std::pair<float, float> attractPos[2] = {{2502.03f, -2910.90f}, const std::pair<float, float> attractPos[2] = {{2502.03f, -2910.90f},
{2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux) {2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux)
FourhorsemanBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} FourHorsemenBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
bool UpdateBossAI() bool UpdateBossAI()
{ {
if (!bot->IsInCombat()) if (!bot->IsInCombat())
@@ -497,7 +497,8 @@ public:
if (feugen && feugen->IsAlive()) if (feugen && feugen->IsAlive())
unit = feugen; unit = feugen;
if (stalagg && stalagg->IsAlive() && (!feugen || bot->GetDistance(stalagg) < bot->GetDistance(feugen))) if (stalagg && stalagg->IsAlive() &&
(!feugen || !feugen->IsAlive() || bot->GetDistance(stalagg) < bot->GetDistance(feugen)))
unit = stalagg; unit = stalagg;
return unit; return unit;

View File

@@ -58,6 +58,7 @@ namespace NaxxSpellIds
// Sapphiron // Sapphiron
static constexpr uint32 Icebolt10 = 28522; static constexpr uint32 Icebolt10 = 28522;
static constexpr uint32 Icebolt25 = 28526; static constexpr uint32 Icebolt25 = 28526;
static constexpr uint32 Chill10 = 28547;
static constexpr uint32 Chill25 = 55699; static constexpr uint32 Chill25 = 55699;
/* /*
// Fight // Fight