From b6af40edbb10e8dae4d8cb222d3136bcf65a815d Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Sun, 18 Jan 2026 19:37:42 +0100 Subject: [PATCH 01/42] Restore Naxx Strategies Rebases --- src/Ai/Base/ActionContext.h | 1 + src/Ai/Base/Actions/ChatShortcutActions.cpp | 14 + src/Ai/Base/Actions/ChatShortcutActions.h | 7 + src/Ai/Base/ChatActionContext.h | 2 + src/Ai/Base/ChatTriggerContext.h | 2 + .../Strategy/ChatCommandHandlerStrategy.cpp | 2 + src/Ai/Base/TriggerContext.h | 1 + src/Ai/Raid/RaidStrategyContext.h | 3 + src/Bot/Engine/AiObjectContext.cpp | 2 + src/Bot/PlayerbotAI.cpp | 3 + .../RaidAi/naxxramas/RaidNaxxActionContext.h | 95 +++ .../RaidAi/naxxramas/RaidNaxxActions.h | 342 ++++++++++ .../naxxramas/RaidNaxxActions_Anubrekhan.cpp | 99 +++ .../naxxramas/RaidNaxxActions_Faerlina.cpp | 3 + .../RaidNaxxActions_FourHorseman.cpp | 70 ++ .../naxxramas/RaidNaxxActions_Gluth.cpp | 206 ++++++ .../naxxramas/RaidNaxxActions_Gothik.cpp | 3 + .../naxxramas/RaidNaxxActions_Grobbulus.cpp | 52 ++ .../naxxramas/RaidNaxxActions_Heigan.cpp | 76 +++ .../naxxramas/RaidNaxxActions_Kelthuzad.cpp | 206 ++++++ .../naxxramas/RaidNaxxActions_Loatheb.cpp | 68 ++ .../naxxramas/RaidNaxxActions_Maexxna.cpp | 3 + .../RaidAi/naxxramas/RaidNaxxActions_Noth.cpp | 3 + .../naxxramas/RaidNaxxActions_Patchwerk.cpp | 31 + .../naxxramas/RaidNaxxActions_Razuvious.cpp | 166 +++++ .../naxxramas/RaidNaxxActions_Sapphiron.cpp | 121 ++++ .../naxxramas/RaidNaxxActions_Shared.cpp | 18 + .../naxxramas/RaidNaxxActions_Thaddius.cpp | 140 ++++ .../RaidAi/naxxramas/RaidNaxxBossHelper.h | 606 ++++++++++++++++++ .../RaidAi/naxxramas/RaidNaxxMultipliers.cpp | 344 ++++++++++ .../RaidAi/naxxramas/RaidNaxxMultipliers.h | 116 ++++ .../RaidAi/naxxramas/RaidNaxxSpellIds.h | 177 +++++ .../RaidAi/naxxramas/RaidNaxxStrategy.cpp | 156 +++++ .../RaidAi/naxxramas/RaidNaxxStrategy.h | 18 + .../RaidAi/naxxramas/RaidNaxxTriggerContext.h | 86 +++ .../RaidAi/naxxramas/RaidNaxxTriggers.cpp | 283 ++++++++ .../RaidAi/naxxramas/RaidNaxxTriggers.h | 258 ++++++++ 37 files changed, 3783 insertions(+) create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActionContext.h create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions.h create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Anubrekhan.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Faerlina.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_FourHorseman.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gluth.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gothik.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Grobbulus.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Heigan.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Kelthuzad.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Loatheb.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Maexxna.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Noth.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Patchwerk.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Razuvious.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Sapphiron.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Shared.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Thaddius.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxBossHelper.h create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.h create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxTriggerContext.h create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.cpp create mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.h diff --git a/src/Ai/Base/ActionContext.h b/src/Ai/Base/ActionContext.h index 93bba4afee..9f4e612ed4 100644 --- a/src/Ai/Base/ActionContext.h +++ b/src/Ai/Base/ActionContext.h @@ -46,6 +46,7 @@ #include "OutfitAction.h" #include "PositionAction.h" #include "DropQuestAction.h" +#include "RaidNaxxActions.h" #include "RandomBotUpdateAction.h" #include "ReachTargetActions.h" #include "ReleaseSpiritAction.h" diff --git a/src/Ai/Base/Actions/ChatShortcutActions.cpp b/src/Ai/Base/Actions/ChatShortcutActions.cpp index 02c306b8f7..40d66697b9 100644 --- a/src/Ai/Base/Actions/ChatShortcutActions.cpp +++ b/src/Ai/Base/Actions/ChatShortcutActions.cpp @@ -241,6 +241,20 @@ bool MaxDpsChatShortcutAction::Execute(Event event) return true; } +bool NaxxChatShortcutAction::Execute(Event event) +{ + Player* master = GetMaster(); + if (!master) + return false; + + botAI->Reset(); + botAI->ChangeStrategy("+naxx", BOT_STATE_NON_COMBAT); + botAI->ChangeStrategy("+naxx", BOT_STATE_COMBAT); + botAI->TellMasterNoFacing("Add Naxx Strategies!"); + // bot->Say("Add Naxx Strategies!", LANG_UNIVERSAL); + return true; +} + bool BwlChatShortcutAction::Execute(Event event) { Player* master = GetMaster(); diff --git a/src/Ai/Base/Actions/ChatShortcutActions.h b/src/Ai/Base/Actions/ChatShortcutActions.h index b7e4a9e963..fa941a4c86 100644 --- a/src/Ai/Base/Actions/ChatShortcutActions.h +++ b/src/Ai/Base/Actions/ChatShortcutActions.h @@ -85,6 +85,13 @@ class MaxDpsChatShortcutAction : public Action bool Execute(Event event) override; }; +class NaxxChatShortcutAction : public Action +{ +public: + NaxxChatShortcutAction(PlayerbotAI* ai) : Action(ai, "naxx chat shortcut") {} + virtual bool Execute(Event event); +}; + class BwlChatShortcutAction : public Action { public: diff --git a/src/Ai/Base/ChatActionContext.h b/src/Ai/Base/ChatActionContext.h index 4873f52005..66bc25c94f 100644 --- a/src/Ai/Base/ChatActionContext.h +++ b/src/Ai/Base/ChatActionContext.h @@ -186,6 +186,7 @@ class ChatActionContext : public NamedObjectContext creators["guild leave"] = &ChatActionContext::guild_leave; creators["rtsc"] = &ChatActionContext::rtsc; creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut; + creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut; creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps; creators["join"] = &ChatActionContext::join; creators["lfg"] = &ChatActionContext::lfg; @@ -297,6 +298,7 @@ class ChatActionContext : public NamedObjectContext static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); } static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); } static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); } + static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); } static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); } static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); } static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); } diff --git a/src/Ai/Base/ChatTriggerContext.h b/src/Ai/Base/ChatTriggerContext.h index b3498ce301..a299e32cc9 100644 --- a/src/Ai/Base/ChatTriggerContext.h +++ b/src/Ai/Base/ChatTriggerContext.h @@ -126,6 +126,7 @@ class ChatTriggerContext : public NamedObjectContext creators["guild leave"] = &ChatTriggerContext::guild_leave; creators["rtsc"] = &ChatTriggerContext::rtsc; creators["drink"] = &ChatTriggerContext::drink; + // creators["naxx"] = &ChatTriggerContext::naxx; // creators["bwl"] = &ChatTriggerContext::bwl; creators["dps"] = &ChatTriggerContext::dps; creators["disperse"] = &ChatTriggerContext::disperse; @@ -243,6 +244,7 @@ class ChatTriggerContext : public NamedObjectContext static Trigger* guild_leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "guild leave"); } static Trigger* rtsc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rtsc"); } static Trigger* drink(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "drink"); } + // static Trigger* naxx(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "naxx"); } // static Trigger* bwl(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "bwl"); } static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); } static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); } diff --git a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp index 0a81686a9d..4d56a83625 100644 --- a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp @@ -84,6 +84,8 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger new TriggerNode("target", { NextAction("tell target", relevance) })); triggers.push_back( new TriggerNode("ready", { NextAction("ready check", relevance) })); + triggers.push_back( + new TriggerNode("naxx", {NextAction("naxx chat shortcut", relevance)})); triggers.push_back( new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) })); triggers.push_back( diff --git a/src/Ai/Base/TriggerContext.h b/src/Ai/Base/TriggerContext.h index ceb7c001c1..1e8abf9461 100644 --- a/src/Ai/Base/TriggerContext.h +++ b/src/Ai/Base/TriggerContext.h @@ -16,6 +16,7 @@ #include "NewRpgStrategy.h" #include "NewRpgTriggers.h" #include "PvpTriggers.h" +#include "RaidNaxxTriggers.h" #include "RpgTriggers.h" #include "RtiTriggers.h" #include "StuckTriggers.h" diff --git a/src/Ai/Raid/RaidStrategyContext.h b/src/Ai/Raid/RaidStrategyContext.h index 4f7a63c7ad..ee2bac3270 100644 --- a/src/Ai/Raid/RaidStrategyContext.h +++ b/src/Ai/Raid/RaidStrategyContext.h @@ -8,6 +8,7 @@ #include "RaidKarazhanStrategy.h" #include "RaidMagtheridonStrategy.h" #include "RaidGruulsLairStrategy.h" +#include "RaidNaxxStrategy.h" #include "RaidOsStrategy.h" #include "RaidEoEStrategy.h" #include "RaidVoAStrategy.h" @@ -26,6 +27,7 @@ class RaidStrategyContext : public NamedObjectContext creators["karazhan"] = &RaidStrategyContext::karazhan; creators["magtheridon"] = &RaidStrategyContext::magtheridon; creators["gruulslair"] = &RaidStrategyContext::gruulslair; + creators["naxx"] = &RaidStrategyContext::naxx; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; creators["voa"] = &RaidStrategyContext::voa; @@ -41,6 +43,7 @@ class RaidStrategyContext : public NamedObjectContext static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); } static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); } static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); } + static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); } diff --git a/src/Bot/Engine/AiObjectContext.cpp b/src/Bot/Engine/AiObjectContext.cpp index b6d5f7de0d..84b52763d0 100644 --- a/src/Bot/Engine/AiObjectContext.cpp +++ b/src/Bot/Engine/AiObjectContext.cpp @@ -115,6 +115,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList, released under GNU AGPL v3 license, you may redistribute it +// and/or modify it under version 3 of the License, or (at your option), any later version. +// */ + +#ifndef _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H +#define _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "RaidNaxxActions.h" + +class RaidNaxxActionContext : public NamedObjectContext +{ +public: + RaidNaxxActionContext() + { + creators["grobbulus go behind the boss"] = &RaidNaxxActionContext::go_behind_the_boss; + creators["rotate grobbulus"] = &RaidNaxxActionContext::rotate_grobbulus; + creators["grobbulus move center"] = &RaidNaxxActionContext::grobbulus_move_center; + creators["grobbulus move away"] = &RaidNaxxActionContext::grobbulus_move_away; + + creators["heigan dance melee"] = &RaidNaxxActionContext::heigan_dance_melee; + creators["heigan dance ranged"] = &RaidNaxxActionContext::heigan_dance_ranged; + creators["thaddius attack nearest pet"] = &RaidNaxxActionContext::thaddius_attack_nearest_pet; + // creators["thaddius melee to place"] = &RaidNaxxActionContext::thaddius_tank_to_place; + // creators["thaddius ranged to place"] = &RaidNaxxActionContext::thaddius_ranged_to_place; + creators["thaddius move to platform"] = &RaidNaxxActionContext::thaddius_move_to_platform; + creators["thaddius move polarity"] = &RaidNaxxActionContext::thaddius_move_polarity; + + creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal; + creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target; + + creators["horseman attract alternatively"] = &RaidNaxxActionContext::horseman_attract_alternatively; + creators["horseman attack in order"] = &RaidNaxxActionContext::horseman_attack_in_order; + + creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position; + creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position; + + creators["kel'thuzad choose target"] = &RaidNaxxActionContext::kelthuzad_choose_target; + creators["kel'thuzad position"] = &RaidNaxxActionContext::kelthuzad_position; + + creators["anub'rekhan choose target"] = &RaidNaxxActionContext::anubrekhan_choose_target; + creators["anub'rekhan position"] = &RaidNaxxActionContext::anubrekhan_position; + + creators["gluth choose target"] = &RaidNaxxActionContext::gluth_choose_target; + creators["gluth position"] = &RaidNaxxActionContext::gluth_position; + creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown; + + creators["patchwerk ranged position"] = &RaidNaxxActionContext::patchwerk_ranged_position; + + creators["loatheb position"] = &RaidNaxxActionContext::loatheb_position; + creators["loatheb choose target"] = &RaidNaxxActionContext::loatheb_choose_target; + } + +private: + static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(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_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(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* thaddius_attack_nearest_pet(PlayerbotAI* ai) { return new ThaddiusAttackNearestPetAction(ai); } + // static Action* thaddius_tank_to_place(PlayerbotAI* ai) { return new ThaddiusMeleeToPlaceAction(ai); } + // static Action* thaddius_ranged_to_place(PlayerbotAI* ai) { return new ThaddiusRangedToPlaceAction(ai); } + static Action* thaddius_move_to_platform(PlayerbotAI* ai) { return new ThaddiusMoveToPlatformAction(ai); } + static Action* thaddius_move_polarity(PlayerbotAI* ai) { return new ThaddiusMovePolarityAction(ai); } + static Action* razuvious_target(PlayerbotAI* ai) { return new RazuviousTargetAction(ai); } + static Action* razuvious_use_obedience_crystal(PlayerbotAI* ai) + { + return new RazuviousUseObedienceCrystalAction(ai); + } + static Action* horseman_attract_alternatively(PlayerbotAI* 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 + // SapphironGroundMainTankPositionAction(ai); } + static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); } + static Action* sapphiron_flight_position(PlayerbotAI* ai) { return new SapphironFlightPositionAction(ai); } + // static Action* sapphiron_avoid_chill(PlayerbotAI* ai) { return new SapphironAvoidChillAction(ai); } + static Action* kelthuzad_choose_target(PlayerbotAI* ai) { return new KelthuzadChooseTargetAction(ai); } + static Action* kelthuzad_position(PlayerbotAI* ai) { return new KelthuzadPositionAction(ai); } + static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new AnubrekhanChooseTargetAction(ai); } + static Action* anubrekhan_position(PlayerbotAI* ai) { return new AnubrekhanPositionAction(ai); } + static Action* gluth_choose_target(PlayerbotAI* ai) { return new GluthChooseTargetAction(ai); } + static Action* gluth_position(PlayerbotAI* ai) { return new GluthPositionAction(ai); } + static Action* gluth_slowdown(PlayerbotAI* ai) { return new GluthSlowdownAction(ai); } + static Action* patchwerk_ranged_position(PlayerbotAI* ai) { return new PatchwerkRangedPositionAction(ai); } + static Action* loatheb_position(PlayerbotAI* ai) { return new LoathebPositionAction(ai); } + static Action* loatheb_choose_target(PlayerbotAI* ai) { return new LoathebChooseTargetAction(ai); } +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions.h new file mode 100644 index 0000000000..6cc732a28f --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions.h @@ -0,0 +1,342 @@ +#ifndef _PLAYERBOT_RAIDNAXXACTIONS_H +#define _PLAYERBOT_RAIDNAXXACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "GenericActions.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "RaidNaxxBossHelper.h" + +// just for test +// class TryToGetBossAIAction : public Action +// { +// public: +// TryToGetBossAIAction(PlayerbotAI* ai) : Action(ai, "try to get boss ai") {} + +// public: +// virtual bool Execute(Event event); +// }; + +class GrobbulusGoBehindAction : public MovementAction +{ +public: + GrobbulusGoBehindAction(PlayerbotAI* ai, float distance = 24.0f, float delta_angle = M_PI / 8) + : MovementAction(ai, "grobbulus go behind") + { + this->distance = distance; + this->delta_angle = delta_angle; + } + virtual bool Execute(Event event); + +protected: + float distance, delta_angle; +}; + +class GrobbulusRotateAction : public RotateAroundTheCenterPointAction +{ +public: + GrobbulusRotateAction(PlayerbotAI* botAI) + : RotateAroundTheCenterPointAction(botAI, "rotate grobbulus", 3281.23f, -3310.38f, 35.0f, 8, true, M_PI) + { + } + virtual bool isUseful() override + { + return RotateAroundTheCenterPointAction::isUseful() && botAI->IsMainTank(bot) && + AI_VALUE2(bool, "has aggro", "boss target"); + } + uint32 GetCurrWaypoint() override; +}; + +class GrobblulusMoveCenterAction : public MoveInsideAction +{ +public: + GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {} +}; + +class GrobbulusMoveAwayAction : public MovementAction +{ +public: + GrobbulusMoveAwayAction(PlayerbotAI* ai, float distance = 18.0f) + : MovementAction(ai, "grobbulus move away"), distance(distance) + { + } + bool Execute(Event event) override; + +private: + float distance; +}; + +class HeiganDanceAction : public MovementAction +{ +public: + HeiganDanceAction(PlayerbotAI* ai) : MovementAction(ai, "heigan dance") + { + this->last_eruption_ms = 0; + this->platform_phase = false; + ResetSafe(); + waypoints.push_back(std::make_pair(2794.88f, -3668.12f)); + waypoints.push_back(std::make_pair(2775.49f, -3674.43f)); + waypoints.push_back(std::make_pair(2762.30f, -3684.59f)); + waypoints.push_back(std::make_pair(2755.99f, -3703.96f)); + platform = std::make_pair(2794.26f, -3706.67f); + } + +protected: + bool CalculateSafe(); + void ResetSafe() + { + curr_safe = 0; + curr_dir = 1; + } + void NextSafe() + { + curr_safe += curr_dir; + if (curr_safe == 3 || curr_safe == 0) + { + curr_dir = -curr_dir; + } + } + uint32 last_eruption_ms; + bool platform_phase; + uint32 curr_safe, curr_dir; + std::vector> waypoints; + std::pair platform; +}; + +class HeiganDanceMeleeAction : public HeiganDanceAction +{ +public: + HeiganDanceMeleeAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {} + virtual bool Execute(Event event); +}; + +class HeiganDanceRangedAction : public HeiganDanceAction +{ +public: + HeiganDanceRangedAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {} + virtual bool Execute(Event event); +}; + +class ThaddiusAttackNearestPetAction : public AttackAction +{ +public: + ThaddiusAttackNearestPetAction(PlayerbotAI* ai) : AttackAction(ai, "thaddius attack nearest pet"), helper(ai) {} + virtual bool Execute(Event event); + virtual bool isUseful(); + +private: + ThaddiusBossHelper helper; +}; + +// class ThaddiusMeleeToPlaceAction : public MovementAction +// { +// public: +// ThaddiusMeleeToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius melee to place") {} +// virtual bool Execute(Event event); +// virtual bool isUseful(); +// }; + +// class ThaddiusRangedToPlaceAction : public MovementAction +// { +// public: +// ThaddiusRangedToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius ranged to place") {} +// virtual bool Execute(Event event); +// virtual bool isUseful(); +// }; + +class ThaddiusMoveToPlatformAction : public MovementAction +{ +public: + ThaddiusMoveToPlatformAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move to platform") {} + virtual bool Execute(Event event); + virtual bool isUseful(); +}; + +class ThaddiusMovePolarityAction : public MovementAction +{ +public: + ThaddiusMovePolarityAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move polarity") {} + virtual bool Execute(Event event); + virtual bool isUseful(); +}; + +class RazuviousUseObedienceCrystalAction : public MovementAction +{ +public: + RazuviousUseObedienceCrystalAction(PlayerbotAI* ai) + : MovementAction(ai, "razuvious use obedience crystal"), helper(ai) + { + } + bool Execute(Event event) override; + +private: + RazuviousBossHelper helper; +}; + +class RazuviousTargetAction : public AttackAction +{ +public: + RazuviousTargetAction(PlayerbotAI* ai) : AttackAction(ai, "razuvious target"), helper(ai) {} + bool Execute(Event event) override; + +private: + RazuviousBossHelper helper; +}; + +class HorsemanAttractAlternativelyAction : public AttackAction +{ +public: + HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attract alternatively"), helper(ai) + { + } + bool Execute(Event event) override; + +protected: + FourhorsemanBossHelper helper; +}; + +class HorsemanAttactInOrderAction : public AttackAction +{ +public: + HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attact in order"), helper(ai) {} + bool Execute(Event event) override; + +protected: + FourhorsemanBossHelper helper; +}; + +// class SapphironGroundMainTankPositionAction : public MovementAction +// { +// public: +// SapphironGroundMainTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground main tank +// position") {} virtual bool Execute(Event event); +// }; + +class SapphironGroundPositionAction : public MovementAction +{ +public: + SapphironGroundPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground position"), helper(ai) {} + bool Execute(Event event) override; + +protected: + SapphironBossHelper helper; +}; + +class SapphironFlightPositionAction : public MovementAction +{ +public: + SapphironFlightPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron flight position"), helper(ai) {} + bool Execute(Event event) override; + +protected: + SapphironBossHelper helper; + bool MoveToNearestIcebolt(); +}; + +// class SapphironAvoidChillAction : public MovementAction +// { +// public: +// SapphironAvoidChillAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron avoid chill") {} +// virtual bool Execute(Event event); +// }; + +class KelthuzadChooseTargetAction : public AttackAction +{ +public: + KelthuzadChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "kel'thuzad choose target"), helper(ai) {} + virtual bool Execute(Event event); + +private: + KelthuzadBossHelper helper; +}; + +class KelthuzadPositionAction : public MovementAction +{ +public: + KelthuzadPositionAction(PlayerbotAI* ai) : MovementAction(ai, "kel'thuzad position"), helper(ai) {} + virtual bool Execute(Event event); + +private: + KelthuzadBossHelper helper; +}; + +class AnubrekhanChooseTargetAction : public AttackAction +{ +public: + AnubrekhanChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "anub'rekhan choose target") {} + bool Execute(Event event) override; +}; + +class AnubrekhanPositionAction : public RotateAroundTheCenterPointAction +{ +public: + AnubrekhanPositionAction(PlayerbotAI* ai) + : RotateAroundTheCenterPointAction(ai, "anub'rekhan position", 3272.49f, -3476.27f, 45.0f, 16) + { + } + bool Execute(Event event) override; +}; + +class GluthChooseTargetAction : public AttackAction +{ +public: + GluthChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "gluth choose target"), helper(ai) {} + bool Execute(Event event) override; + +private: + GluthBossHelper helper; +}; + +class GluthPositionAction : public RotateAroundTheCenterPointAction +{ +public: + GluthPositionAction(PlayerbotAI* ai) + : RotateAroundTheCenterPointAction(ai, "gluth position", 3293.61f, -3149.01f, 12.0f, 12), helper(ai) + { + } + bool Execute(Event event) override; + +private: + GluthBossHelper helper; +}; + +class GluthSlowdownAction : public Action +{ +public: + GluthSlowdownAction(PlayerbotAI* ai) : Action(ai, "gluth slowdown"), helper(ai) {} + bool Execute(Event event) override; + +private: + GluthBossHelper helper; +}; + +class LoathebPositionAction : public MovementAction +{ +public: + LoathebPositionAction(PlayerbotAI* ai) : MovementAction(ai, "loatheb position"), helper(ai) {} + virtual bool Execute(Event event); + +private: + LoathebBossHelper helper; +}; + +class LoathebChooseTargetAction : public AttackAction +{ +public: + LoathebChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "loatheb choose target"), helper(ai) {} + virtual bool Execute(Event event); + +private: + LoathebBossHelper helper; +}; + +class PatchwerkRangedPositionAction : public MovementAction +{ +public: + PatchwerkRangedPositionAction(PlayerbotAI* ai) : MovementAction(ai, "patchwerk ranged position") {} + bool Execute(Event event) override; +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Anubrekhan.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Anubrekhan.cpp new file mode 100644 index 0000000000..90f14877e2 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Anubrekhan.cpp @@ -0,0 +1,99 @@ +#include "RaidNaxxActions.h" + +#include "ObjectGuid.h" +#include "Playerbots.h" + +bool AnubrekhanChooseTargetAction::Execute(Event event) +{ + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit* target_boss = nullptr; + std::vector target_guards; + for (ObjectGuid const guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit) + continue; + if (botAI->EqualLowercaseName(unit->GetName(), "crypt guard")) + { + target_guards.push_back(unit); + } + if (botAI->EqualLowercaseName(unit->GetName(), "anub'rekhan")) + { + target_boss = unit; + } + } + if (botAI->IsMainTank(bot)) + { + target = target_boss; + } + else + { + if (target_guards.size() == 0) + { + target = target_boss; + } + else + { + if (botAI->IsAssistTank(bot)) + { + for (Unit* t : target_guards) + { + if (target == nullptr || (target->GetVictim() && target->GetVictim()->ToPlayer() && + botAI->IsTank(target->GetVictim()->ToPlayer()))) + { + target = t; + } + } + } + else + { + for (Unit* t : target_guards) + { + if (target == nullptr || target->GetHealthPct() > t->GetHealthPct()) + { + target = t; + } + } + } + } + } + if (context->GetValue("current target")->Get() == target) + { + return false; + } + return Attack(target); +} + +bool AnubrekhanPositionAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); + if (!boss) + { + return false; + } + bool inPhase = botAI->HasAura("locust swarm", boss) || boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (inPhase) + { + if (botAI->IsMainTank(bot)) + { + uint32 nearest = FindNearestWaypoint(); + uint32 next_point; + if (inPhase) + { + 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, + false, false, MovementPriority::MOVEMENT_COMBAT); + } + else + { + return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Faerlina.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Faerlina.cpp new file mode 100644 index 0000000000..799dd9ca2e --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Faerlina.cpp @@ -0,0 +1,3 @@ +#include "RaidNaxxActions.h" + +// Reserved for Faerlina-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_FourHorseman.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_FourHorseman.cpp new file mode 100644 index 0000000000..0d29526fef --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_FourHorseman.cpp @@ -0,0 +1,70 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" + +bool HorsemanAttractAlternativelyAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + helper.CalculatePosToGo(bot); + auto [posX, posY] = helper.CurrentAttractPos(); + if (MoveTo(bot->GetMapId(), posX, posY, helper.posZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + Unit* attackTarget = helper.CurrentAttackTarget(); + if (context->GetValue("current target")->Get() != attackTarget) + { + return Attack(attackTarget); + } + return false; +} + +bool HorsemanAttactInOrderAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + Unit* target = nullptr; + Unit* thane = AI_VALUE2(Unit*, "find target", "thane korth'azz"); + Unit* lady = AI_VALUE2(Unit*, "find target", "lady blaumeux"); + Unit* sir = AI_VALUE2(Unit*, "find target", "sir zeliek"); + Unit* fourth = AI_VALUE2(Unit*, "find target", "baron rivendare"); + if (!fourth) + { + fourth = AI_VALUE2(Unit*, "find target", "highlord mograine"); + } + std::vector attack_order; + if (botAI->IsAssistTank(bot)) + { + attack_order = {fourth, thane, lady, sir}; + } + else + { + attack_order = {thane, fourth, lady, sir}; + } + for (Unit* t : attack_order) + { + if (t && t->IsAlive()) + { + target = t; + break; + } + } + if (target) + { + if (context->GetValue("current target")->Get() == target && botAI->GetState() == BOT_STATE_COMBAT) + { + return false; + } + if (!bot->IsWithinLOSInMap(target)) + { + return MoveNear(target, 22.0f, MovementPriority::MOVEMENT_COMBAT); + } + return Attack(target); + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gluth.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gluth.cpp new file mode 100644 index 0000000000..7192685013 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gluth.cpp @@ -0,0 +1,206 @@ +#include "RaidNaxxActions.h" + +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "SharedDefines.h" + +bool GluthChooseTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + GuidVector attackers = context->GetValue("possible targets")->Get(); + Unit* target = nullptr; + Unit* target_boss = nullptr; + std::vector target_zombies; + for (GuidVector::iterator i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (!unit->IsAlive()) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "zombie chow")) + { + target_zombies.push_back(unit); + } + if (botAI->EqualLowercaseName(unit->GetName(), "gluth")) + { + target_boss = unit; + } + } + if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) + { + target = target_boss; + } + else if (botAI->IsAssistTankOfIndex(bot, 1)) + { + for (Unit* t : target_zombies) + { + if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() != bot && t->GetDistance2d(bot) <= 10.0f) + { + if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot)) + { + target = t; + } + } + } + } + else if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0 || botAI->GetClassIndex(bot, CLASS_HUNTER) == 1) + { + // prevent zombie go straight to gluth + for (Unit* t : target_zombies) + { + if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() == target_boss && + t->GetDistance2d(bot) <= sPlayerbotAIConfig->spellDistance) + { + if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot)) + { + target = t; + } + } + } + if (!target) + { + target = target_boss; + } + } + else + { + for (Unit* t : target_zombies) + { + if (t->GetHealthPct() <= helper.decimatedZombiePct) + { + if (target == nullptr || + target->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second) > + t->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second)) + { + target = t; + } + } + } + if (target == nullptr) + { + target = target_boss; + } + } + if (!target || context->GetValue("current target")->Get() == target) + { + return false; + } + if (target_boss && target == target_boss) + return Attack(target, true); + return Attack(target, false); + // return Attack(target); +} + +bool GluthPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; + if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) + { + if (AI_VALUE2(bool, "has aggro", "boss target")) + { + if (raid25) + { + if (MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), false, false, false, + false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), 2.0f, + MovementPriority::MOVEMENT_COMBAT); + } + else + { + if (MoveTo(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), false, false, false, + false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), 2.0f, + MovementPriority::MOVEMENT_COMBAT); + } + } + } + else if (botAI->IsAssistTankOfIndex(bot, 1)) + { + if (helper.BeforeDecimate()) + { + if (MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), 2.0f, + MovementPriority::MOVEMENT_COMBAT); + } + else + { + if (AI_VALUE2(bool, "has aggro", "current target")) + { + uint32 nearest = FindNearestWaypoint(); + uint32 next_point = (nearest + 1) % intervals; + return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + } + else if (botAI->IsRangedDps(bot)) + { + if (raid25) + { + if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0) + { + return MoveInside(NAXX_MAP_ID, helper.leftSlowDownPos.first, helper.leftSlowDownPos.second, bot->GetPositionZ(), 0.0f, + MovementPriority::MOVEMENT_COMBAT); + } + if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 1) + { + return MoveInside(NAXX_MAP_ID, helper.rightSlowDownPos.first, helper.rightSlowDownPos.second, bot->GetPositionZ(), 0.0f, + MovementPriority::MOVEMENT_COMBAT); + } + } + return MoveInside(NAXX_MAP_ID, helper.rangedPos.first, helper.rangedPos.second, bot->GetPositionZ(), 3.0f, + MovementPriority::MOVEMENT_COMBAT); + } + else if (botAI->IsHeal(bot)) + { + return MoveInside(NAXX_MAP_ID, helper.healPos.first, helper.healPos.second, bot->GetPositionZ(), 0.0f, + MovementPriority::MOVEMENT_COMBAT); + } + return false; +} + +bool GluthSlowdownAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; + if (!raid25) + { + return false; + } + if (helper.JustStartCombat()) + { + return false; + } + switch (bot->getClass()) + { + case CLASS_HUNTER: + return botAI->CastSpell("frost trap", bot); + break; + default: + break; + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gothik.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gothik.cpp new file mode 100644 index 0000000000..e5c673e46e --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gothik.cpp @@ -0,0 +1,3 @@ +#include "RaidNaxxActions.h" + +// Reserved for Gothik-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Grobbulus.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Grobbulus.cpp new file mode 100644 index 0000000000..34af48b782 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Grobbulus.cpp @@ -0,0 +1,52 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" + +bool GrobbulusGoBehindAction::Execute(Event event) +{ + Unit* boss = AI_VALUE(Unit*, "boss target"); + if (!boss) + { + return false; + } + // Position* pos = boss->GetPosition(); + float orientation = boss->GetOrientation() + M_PI + delta_angle; + float x = boss->GetPositionX(); + float y = boss->GetPositionY(); + float z = boss->GetPositionZ(); + float rx = x + cos(orientation) * distance; + float ry = y + sin(orientation) * distance; + return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +} + +bool GrobbulusMoveAwayAction::Execute(Event event) +{ + Unit* boss = AI_VALUE(Unit*, "boss target"); + if (!boss) + { + return false; + } + + const float currentDistance = bot->GetExactDist2d(boss); + if (currentDistance >= distance) + { + return false; + } + + const float angle = boss->GetAngle(bot); + const float x = boss->GetPositionX() + cos(angle) * distance; + const float y = boss->GetPositionY() + sin(angle) * distance; + const float z = bot->GetPositionZ(); + + return MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +} + +uint32 GrobbulusRotateAction::GetCurrWaypoint() +{ + uint32 current = FindNearestWaypoint(); + if (clockwise) + { + return (current + 1) % intervals; + } + return (current + intervals - 1) % intervals; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Heigan.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Heigan.cpp new file mode 100644 index 0000000000..b7c19f2559 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Heigan.cpp @@ -0,0 +1,76 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" +#include "RaidNaxxSpellIds.h" +#include "Spell.h" +#include "Timer.h" + +bool HeiganDanceAction::CalculateSafe() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean"); + if (!boss) + { + return false; + } + uint32 now = getMSTime(); + platform_phase = boss->IsWithinDist2d(platform.first, platform.second, 10.0f); + if (last_eruption_ms != 0 && now - last_eruption_ms > 15000) + { + ResetSafe(); + } + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (spell) + { + SpellInfo const* info = spell->GetSpellInfo(); + bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10}); + if (!isEruption && info && info->SpellName[LOCALE_enUS]) + { + // Fallback to name for custom spell data. + isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption"); + } + if (isEruption) + { + if (last_eruption_ms == 0 || now - last_eruption_ms > 500) + { + NextSafe(); + } + last_eruption_ms = now; + } + } + } + return true; +} + +bool HeiganDanceMeleeAction::Execute(Event event) +{ + CalculateSafe(); + if (!platform_phase && botAI->IsMainTank(bot) && !AI_VALUE2(bool, "has aggro", "boss target")) + { + return false; + } + assert(curr_safe >= 0 && curr_safe <= 3); + return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), + botAI->IsMainTank(bot) ? 0 : 0, MovementPriority::MOVEMENT_COMBAT); +} + +bool HeiganDanceRangedAction::Execute(Event event) +{ + CalculateSafe(); + if (!platform_phase) + { + if (MoveTo(bot->GetMapId(), platform.first, platform.second, 276.54f, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(bot->GetMapId(), platform.first, platform.second, 276.54f, 2.0f, MovementPriority::MOVEMENT_COMBAT); + } + botAI->InterruptSpell(); + return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), 0, + MovementPriority::MOVEMENT_COMBAT); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Kelthuzad.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Kelthuzad.cpp new file mode 100644 index 0000000000..1c86769d95 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Kelthuzad.cpp @@ -0,0 +1,206 @@ +#include "RaidNaxxActions.h" + +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" + +bool KelthuzadChooseTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit *target_soldier = nullptr, *target_weaver = nullptr, *target_abomination = nullptr, *target_kelthuzad = nullptr, + *target_guardian = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + + if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown")) + { + if (!target_guardian) + { + target_guardian = unit; + } + else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() && + target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) && + botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer())) + { + target_guardian = unit; + } + else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() && + target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) && + !botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer()) && + target_guardian->GetDistance2d(helper.center.first, helper.center.second) > + bot->GetDistance2d(unit)) + { + target_guardian = unit; + } + } + + if (unit->GetDistance2d(helper.center.first, helper.center.second) > 30.0f) + { + continue; + } + if (bot->GetDistance2d(unit) > sPlayerbotAIConfig->spellDistance) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "unstoppable abomination")) + { + if (target_abomination == nullptr || + target_abomination->GetDistance2d(helper.center.first, helper.center.second) > + unit->GetDistance2d(helper.center.first, helper.center.second)) + { + target_abomination = unit; + } + } + if (botAI->EqualLowercaseName(unit->GetName(), "soldier of the frozen wastes")) + { + if (target_soldier == nullptr || + target_soldier->GetDistance2d(helper.center.first, helper.center.second) > + unit->GetDistance2d(helper.center.first, helper.center.second)) + { + target_soldier = unit; + } + } + if (botAI->EqualLowercaseName(unit->GetName(), "soul weaver")) + { + if (target_weaver == nullptr || target_weaver->GetDistance2d(helper.center.first, helper.center.second) > + unit->GetDistance2d(helper.center.first, helper.center.second)) + { + target_weaver = unit; + } + } + if (botAI->EqualLowercaseName(unit->GetName(), "kel'thuzad")) + { + target_kelthuzad = unit; + } + } + std::vector targets; + if (botAI->IsRanged(bot)) + { + if (botAI->GetRangedDpsIndex(bot) <= 1) + { + targets = {target_soldier, target_weaver, target_abomination, target_kelthuzad}; + } + else + { + targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; + } + } + else if (botAI->IsAssistTank(bot)) + { + targets = {target_abomination, target_guardian, target_kelthuzad}; + } + else + { + targets = {target_abomination, target_kelthuzad}; + } + for (Unit* t : targets) + { + if (t) + { + target = t; + break; + } + } + if (context->GetValue("current target")->Get() == target) + { + return false; + } + if (target_kelthuzad && target == target_kelthuzad) + { + return Attack(target, true); + } + return Attack(target, false); +} + +bool KelthuzadPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (helper.IsPhaseOne()) + { + if (AI_VALUE(Unit*, "current target") == nullptr) + { + return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f, + MovementPriority::MOVEMENT_COMBAT); + } + } + else if (helper.IsPhaseTwo()) + { + Unit* shadow_fissure = helper.GetAnyShadowFissure(); + if (!shadow_fissure || !bot->IsWithinDistInMap(shadow_fissure, 10.0f)) + { + float distance, angle; + if (botAI->IsMainTank(bot)) + { + if (AI_VALUE2(bool, "has aggro", "current target")) + { + return MoveTo(NAXX_MAP_ID, helper.tank_pos.first, helper.tank_pos.second, bot->GetPositionZ(), false, false, false, + false, MovementPriority::MOVEMENT_COMBAT); + } + else + { + return false; + } + } + else if (botAI->IsRanged(bot)) + { + uint32 index = botAI->GetRangedIndex(bot); + if (index < 8) + { + distance = 20.0f; + angle = index * M_PI / 4; + } + else + { + distance = 32.0f; + angle = (index - 8) * M_PI / 4; + } + float dx, dy; + dx = helper.center.first + cos(angle) * distance; + dy = helper.center.second + sin(angle) * distance; + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + else if (botAI->IsTank(bot)) + { + Unit* cur_tar = AI_VALUE(Unit*, "current target"); + if (cur_tar && cur_tar->GetVictim() && cur_tar->GetVictim()->ToPlayer() && + botAI->EqualLowercaseName(cur_tar->GetName(), "guardian of icecrown") && + botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer())) + { + return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + else + { + return false; + } + } + } + else + { + float dx, dy; + float angle; + if (!botAI->IsRanged(bot)) + { + angle = shadow_fissure->GetAngle(helper.center.first, helper.center.second); + } + else + { + angle = bot->GetAngle(shadow_fissure) + M_PI; + } + dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f; + dy = shadow_fissure->GetPositionY() + sin(angle) * 10.0f; + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Loatheb.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Loatheb.cpp new file mode 100644 index 0000000000..54a9325989 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Loatheb.cpp @@ -0,0 +1,68 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" + +bool LoathebPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (botAI->IsTank(bot)) + { + if (AI_VALUE2(bool, "has aggro", "boss target")) + { + return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + else if (botAI->IsRanged(bot)) + { + return MoveInside(533, helper.rangePos.first, helper.rangePos.second, bot->GetPositionZ(), 1.0f, + MovementPriority::MOVEMENT_COMBAT); + } + return false; +} + +bool LoathebChooseTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit* target_boss = nullptr; + Unit* target_spore = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (!unit->IsAlive()) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "spore")) + { + target_spore = unit; + } + if (botAI->EqualLowercaseName(unit->GetName(), "loatheb")) + { + target_boss = unit; + } + } + if (target_spore && bot->GetDistance2d(target_spore) <= 1.0f) + { + target = target_spore; + } + else + { + target = target_boss; + } + if (!target || context->GetValue("current target")->Get() == target) + { + return false; + } + return Attack(target); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Maexxna.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Maexxna.cpp new file mode 100644 index 0000000000..67ef9930f0 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Maexxna.cpp @@ -0,0 +1,3 @@ +#include "RaidNaxxActions.h" + +// Reserved for Maexxna-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Noth.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Noth.cpp new file mode 100644 index 0000000000..ff3ce98e70 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Noth.cpp @@ -0,0 +1,3 @@ +#include "RaidNaxxActions.h" + +// Reserved for Noth-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Patchwerk.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Patchwerk.cpp new file mode 100644 index 0000000000..6ef1480cf2 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Patchwerk.cpp @@ -0,0 +1,31 @@ +#include "RaidNaxxActions.h" + +#include +#include + +bool PatchwerkRangedPositionAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); + if (!boss) + return false; + + constexpr float minDistance = 12.0f; + constexpr float maxDistance = 15.0f; + const float distance = bot->GetExactDist2d(boss); + + if (distance >= minDistance && distance <= maxDistance) + return false; + + const float desiredDistance = std::clamp(distance, minDistance, maxDistance); + float angle = boss->GetAngle(bot); + + if (distance < 0.1f) + angle = boss->GetOrientation(); + + const float x = boss->GetPositionX() + std::cos(angle) * desiredDistance; + const float y = boss->GetPositionY() + std::sin(angle) * desiredDistance; + const float z = bot->GetPositionZ(); + + return MoveTo(boss->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, + false); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Razuvious.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Razuvious.cpp new file mode 100644 index 0000000000..a16469bf15 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Razuvious.cpp @@ -0,0 +1,166 @@ +#include "RaidNaxxActions.h" + +#include "ObjectGuid.h" +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "SharedDefines.h" + +bool RazuviousUseObedienceCrystalAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + // bot->GetCharm + if (Unit* charm = bot->GetCharm()) + { + Unit* target = AI_VALUE2(Unit*, "find target", "instructor razuvious"); + if (!target) + { + return false; + } + if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == NULL_MOTION_TYPE) + { + charm->GetMotionMaster()->Clear(); + charm->GetMotionMaster()->MoveChase(target); + charm->GetAI()->AttackStart(target); + } + Aura* forceObedience = botAI->GetAura("force obedience", charm); + uint32 duration_time; + if (!forceObedience) + { + forceObedience = botAI->GetAura("mind control", charm); + duration_time = 60000; + } + else + { + duration_time = 90000; + } + if (!forceObedience) + { + return false; + } + if (charm->GetDistance(target) <= 0.51f) + { + // taunt + bool tauntUseful = true; + if (forceObedience->GetDuration() <= (duration_time - 5000)) + { + if (target->GetVictim() && botAI->HasAura(29061, target->GetVictim())) + { + tauntUseful = false; + } + if (forceObedience->GetDuration() <= 3000) + { + tauntUseful = false; + } + } + if (forceObedience->GetDuration() >= (duration_time - 500)) + { + tauntUseful = false; + } + if (tauntUseful && !charm->HasSpellCooldown(29060)) + { + // shield + if (!charm->HasSpellCooldown(29061)) + { + charm->CastSpell(charm, 29061, true); + charm->AddSpellCooldown(29061, 0, 30 * 1000); + } + charm->CastSpell(target, 29060, true); + charm->AddSpellCooldown(29060, 0, 20 * 1000); + } + // strike + if (!charm->HasSpellCooldown(61696)) + { + charm->CastSpell(target, 61696, true); + charm->AddSpellCooldown(61696, 0, 4 * 1000); + } + } + } + else + { + Difficulty diff = bot->GetRaidDifficulty(); + if (diff == RAID_DIFFICULTY_10MAN_NORMAL) + { + GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); + for (auto i = npcs.begin(); i != npcs.end(); i++) + { + Creature* unit = botAI->GetCreature(*i); + if (!unit) + { + continue; + } + if (botAI->IsMainTank(bot) && unit->GetSpawnId() != 128352) + { + continue; + } + if (!botAI->IsMainTank(bot) && unit->GetSpawnId() != 128353) + { + continue; + } + if (MoveTo(unit, 0.0f, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + Creature* creature = bot->GetNPCIfCanInteractWith(*i, UNIT_NPC_FLAG_SPELLCLICK); + if (!creature) + continue; + creature->HandleSpellClick(bot); + return true; + } + } + else + { + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (botAI->EqualLowercaseName(unit->GetName(), "death knight understudy")) + { + target = unit; + break; + } + } + if (target) + { + if (bot->GetDistance2d(target) > sPlayerbotAIConfig->spellDistance) + { + return MoveNear(target, sPlayerbotAIConfig->spellDistance, MovementPriority::MOVEMENT_COMBAT); + } + else + { + return botAI->CastSpell("mind control", target); + } + } + } + } + return false; +} + +bool RazuviousTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + Unit* razuvious = AI_VALUE2(Unit*, "find target", "instructor razuvious"); + Unit* understudy = AI_VALUE2(Unit*, "find target", "death knight understudy"); + Unit* target = nullptr; + if (botAI->IsTank(bot)) + { + target = understudy; + } + else + { + target = razuvious; + } + if (AI_VALUE(Unit*, "current target") == target) + { + return false; + } + return Attack(target); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Sapphiron.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Sapphiron.cpp new file mode 100644 index 0000000000..e749836619 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Sapphiron.cpp @@ -0,0 +1,121 @@ +#include "RaidNaxxActions.h" + +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "RaidNaxxBossHelper.h" +#include "RaidNaxxSpellIds.h" + +bool SapphironGroundPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (botAI->IsMainTank(bot)) + { + if (AI_VALUE2(bool, "has aggro", "current target")) + { + return MoveTo(NAXX_MAP_ID, helper.mainTankPos.first, helper.mainTankPos.second, helper.GENERIC_HEIGHT, false, false, false, + false, MovementPriority::MOVEMENT_COMBAT); + } + return false; + } + if (helper.JustLanded()) + { + uint32 index = botAI->GetGroupSlotIndex(bot); + float start_angle = 0.85 * M_PI; + float offset_angle = M_PI * 0.02 * index; + float angle = start_angle + offset_angle; + float distance; + if (botAI->IsRanged(bot)) + { + distance = 35.0f; + } + else if (botAI->IsHeal(bot)) + { + distance = 30.0f; + } + else + { + distance = 5.0f; + } + float posX = helper.center.first + cos(angle) * distance; + float posY = helper.center.second + sin(angle) * distance; + if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, 2.0f, MovementPriority::MOVEMENT_COMBAT); + } + else + { + std::vector dest; + if (helper.FindPosToAvoidChill(dest)) + { + return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} + +bool SapphironFlightPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (helper.WaitForExplosion()) + { + return MoveToNearestIcebolt(); + } + else + { + std::vector dest; + if (helper.FindPosToAvoidChill(dest)) + { + return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} + +bool SapphironFlightPositionAction::MoveToNearestIcebolt() +{ + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + Group::MemberSlotList const& slots = group->GetMemberSlots(); + Player* playerWithIcebolt = nullptr; + float minDistance; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || + botAI->HasAura("icebolt", member, false, false, -1, true)) + { + if (!playerWithIcebolt || minDistance > bot->GetDistance(member)) + { + playerWithIcebolt = member; + minDistance = bot->GetDistance(member); + } + } + } + if (playerWithIcebolt) + { + Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron"); + if (boss) + { + float angle = boss->GetAngle(playerWithIcebolt); + float posX = playerWithIcebolt->GetPositionX() + cos(angle) * 3.0f; + float posY = playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f; + if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveNear(playerWithIcebolt, 3.0f, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Shared.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Shared.cpp new file mode 100644 index 0000000000..7b399db6ba --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Shared.cpp @@ -0,0 +1,18 @@ +#include "RaidNaxxActions.h" + +uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint() +{ + float minDistance = 0; + int ret = -1; + for (int i = 0; i < intervals; i++) + { + float w_x = waypoints[i].first, w_y = waypoints[i].second; + float dis = bot->GetDistance2d(w_x, w_y); + if (ret == -1 || dis < minDistance) + { + ret = i; + minDistance = dis; + } + } + return ret; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Thaddius.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Thaddius.cpp new file mode 100644 index 0000000000..65f7b0edd0 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Thaddius.cpp @@ -0,0 +1,140 @@ +#include "RaidNaxxActions.h" + +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "RaidNaxxSpellIds.h" + +bool ThaddiusAttackNearestPetAction::isUseful() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (!helper.IsPhasePet()) + { + return false; + } + Unit* target = helper.GetNearestPet(); + if (!bot->IsWithinDistInMap(target, 50.0f)) + { + return false; + } + return true; +} + +bool ThaddiusAttackNearestPetAction::Execute(Event event) +{ + Unit* target = helper.GetNearestPet(); + if (!bot->IsWithinLOSInMap(target)) + { + return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT); + } + if (AI_VALUE(Unit*, "current target") != target) + { + return Attack(target); + } + if (botAI->IsTank(bot) && AI_VALUE2(bool, "has aggro", "current target")) + { + std::pair posForTank = helper.PetPhaseGetPosForTank(); + return MoveTo(533, posForTank.first, posForTank.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + if (botAI->IsRanged(bot)) + { + std::pair posForRanged = helper.PetPhaseGetPosForRanged(); + return MoveTo(533, posForRanged.first, posForRanged.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return false; +} + +bool ThaddiusMoveToPlatformAction::isUseful() { return true; } + +bool ThaddiusMoveToPlatformAction::Execute(Event event) +{ + std::vector> position = { + // high left + {3462.99f, -2918.90f}, + // high right + {3520.65f, -2976.51f}, + // low left + {3471.36f, -2910.65f}, + // low right + {3528.80f, -2967.04f}, + // center + {3512.19f, -2928.58f}, + }; + float high_z = 312.00f, low_z = 304.02f; + bool is_left = bot->GetDistance2d(position[0].first, position[0].second) < + bot->GetDistance2d(position[1].first, position[1].second); + if (bot->GetPositionZ() >= (high_z - 3.0f)) + { + if (is_left) + { + if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + float distance = bot->GetExactDist2d(position[0].first, position[0].second); + if (distance < sPlayerbotAIConfig->contactDistance) + JumpTo(bot->GetMapId(), position[2].first, position[2].second, low_z, MovementPriority::MOVEMENT_COMBAT); + // bot->TeleportTo(bot->GetMapId(), position[2].first, position[2].second, low_z, bot->GetOrientation()); + } + } + else + { + if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + float distance = bot->GetExactDist2d(position[1].first, position[1].second); + if (distance < sPlayerbotAIConfig->contactDistance) + JumpTo(bot->GetMapId(), position[3].first, position[3].second, low_z, MovementPriority::MOVEMENT_COMBAT); + // bot->TeleportTo(bot->GetMapId(), position[3].first, position[3].second, low_z, bot->GetOrientation()); + } + } + } + else + { + return MoveTo(bot->GetMapId(), position[4].first, position[4].second, low_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return true; +} + +bool ThaddiusMovePolarityAction::isUseful() +{ + return !botAI->IsMainTank(bot) || AI_VALUE2(bool, "has aggro", "current target"); +} + +bool ThaddiusMovePolarityAction::Execute(Event event) +{ + std::vector> position = { + // left melee + {3508.29f, -2920.12f}, + // left ranged + {3501.72f, -2913.36f}, + // right melee + {3519.74f, -2931.69f}, + // right ranged + {3524.32f, -2936.26f}, + // center melee + {3512.19f, -2928.58f}, + // center ranged + {3504.68f, -2936.68f}, + }; + uint32 idx; + if (NaxxSpellIds::HasAnyAura( + botAI, bot, + {NaxxSpellIds::NegativeCharge10, NaxxSpellIds::NegativeCharge25, NaxxSpellIds::NegativeChargeStack}) || + botAI->HasAura("negative charge", bot, false, false, -1, true)) + { + idx = 0; + } + else if (NaxxSpellIds::HasAnyAura( + botAI, bot, + {NaxxSpellIds::PositiveCharge10, NaxxSpellIds::PositiveCharge25, NaxxSpellIds::PositiveChargeStack}) || + botAI->HasAura("positive charge", bot, false, false, -1, true)) + { + idx = 1; + } + else + { + idx = 2; + } + idx = idx * 2 + botAI->IsRanged(bot); + return MoveTo(bot->GetMapId(), position[idx].first, position[idx].second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxBossHelper.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxBossHelper.h new file mode 100644 index 0000000000..b64ede3217 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxBossHelper.h @@ -0,0 +1,606 @@ +#ifndef _PLAYERBOT_RAIDNAXXBOSSHELPER_H +#define _PLAYERBOT_RAIDNAXXBOSSHELPER_H + +#include + +#include "AiObject.h" +#include "AiObjectContext.h" +#include "EventMap.h" +#include "Log.h" +#include "NamedObjectContext.h" +#include "ObjectGuid.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "ScriptedCreature.h" +#include "SharedDefines.h" +#include "Spell.h" +#include "Timer.h" +#include "RaidNaxxSpellIds.h" + +const uint32 NAXX_MAP_ID = 533; + +template +class GenericBossHelper : public AiObject +{ +public: + GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {} + virtual bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + _unit = nullptr; + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + _unit = nullptr; + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", _name); + if (!_unit) + { + return false; + } + _target = _unit->ToCreature(); + if (!_target) + { + return false; + } + _ai = dynamic_cast(_target->GetAI()); + if (!_ai) + { + return false; + } + _event_map = &_ai->events; + if (!_event_map) + { + return false; + } + } + if (!_event_map) + { + return false; + } + _timer = getMSTime(); + return true; + } + virtual void Reset() + { + _unit = nullptr; + _target = nullptr; + _ai = nullptr; + _event_map = nullptr; + _timer = 0; + } + +protected: + std::string _name; + Unit* _unit = nullptr; + Creature* _target = nullptr; + BossAiType* _ai = nullptr; + EventMap* _event_map = nullptr; + uint32 _timer = 0; +}; + +class KelthuzadBossHelper : public AiObject +{ +public: + KelthuzadBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + const std::pair center = {3716.19f, -5106.58f}; + const std::pair tank_pos = {3709.19f, -5104.86f}; + const std::pair assist_tank_pos = {3746.05f, -5112.74f}; + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "kel'thuzad"); + } + return _unit != nullptr; + } + bool IsPhaseOne() { return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } + bool IsPhaseTwo() { return _unit && !_unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } + Unit* GetAnyShadowFissure() + { + Unit* shadow_fissure = nullptr; + GuidVector units = *context->GetValue("nearest triggers"); + for (auto i = units.begin(); i != units.end(); i++) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (botAI->EqualLowercaseName(unit->GetName(), "shadow fissure")) + { + shadow_fissure = unit; + } + } + return shadow_fissure; + } + +private: + void Reset() { _unit = nullptr; } + + Unit* _unit = nullptr; +}; + +class RazuviousBossHelper : public AiObject +{ +public: + RazuviousBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "instructor razuvious"); + } + return _unit != nullptr; + } + +private: + void Reset() { _unit = nullptr; } + + Unit* _unit = nullptr; +}; + +class SapphironBossHelper : public AiObject +{ +public: + const std::pair mainTankPos = {3512.07f, -5274.06f}; + const std::pair center = {3517.31f, -5253.74f}; + const float GENERIC_HEIGHT = 137.29f; + SapphironBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "sapphiron"); + if (!_unit) + { + return false; + } + } + bool now_flying = _unit->IsFlying(); + if (_was_flying && !now_flying) + { + _last_land_ms = getMSTime(); + } + _was_flying = now_flying; + return true; + } + bool IsPhaseGround() { return _unit && !_unit->IsFlying(); } + bool IsPhaseFlight() { return _unit && _unit->IsFlying(); } + bool JustLanded() + { + if (!_last_land_ms) + { + return false; + } + return getMSTime() - _last_land_ms <= POSITION_TIME_AFTER_LANDED; + } + bool WaitForExplosion() + { + if (!IsPhaseFlight()) + { + return false; + } + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && + (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || + botAI->HasAura("icebolt", member, false, false, -1, true))) + { + return true; + } + } + return false; + } + bool FindPosToAvoidChill(std::vector& dest) + { + Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25}); + if (!aura) + { + // Fallback to name for custom spell data. + aura = botAI->GetAura("chill", bot); + } + if (!aura) + { + return false; + } + DynamicObject* dyn_obj = aura->GetDynobjOwner(); + if (!dyn_obj) + { + return false; + } + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + float angle = 0; + uint32 index = botAI->GetGroupSlotIndex(bot); + if (currentTarget) + { + if (botAI->IsRanged(bot)) + { + if (bot->GetExactDist2d(currentTarget) <= 45.0f) + { + angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2; + } + else + { + if (index % 2 == 0) + { + angle = bot->GetAngle(currentTarget) + M_PI / 2; + } + else + { + angle = bot->GetAngle(currentTarget) - M_PI / 2; + } + } + } + else + { + if (index % 3 == 0) + { + angle = bot->GetAngle(currentTarget); + } + else if (index % 3 == 1) + { + angle = bot->GetAngle(currentTarget) + M_PI / 2; + } + else + { + angle = bot->GetAngle(currentTarget) - M_PI / 2; + } + } + } + else + { + angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2; + } + dest = {bot->GetPositionX() + cos(angle) * 5.0f, bot->GetPositionY() + sin(angle) * 5.0f, bot->GetPositionZ()}; + return true; + } + +private: + void Reset() + { + _unit = nullptr; + _was_flying = false; + _last_land_ms = 0; + } + + const uint32 POSITION_TIME_AFTER_LANDED = 5000; + Unit* _unit = nullptr; + bool _was_flying = false; + uint32 _last_land_ms = 0; +}; + +class GluthBossHelper : public AiObject +{ +public: + const std::pair mainTankPos25 = {3331.48f, -3109.06f}; + const std::pair mainTankPos10 = {3278.29f, -3162.06f}; + const std::pair beforeDecimatePos = {3267.34f, -3175.68f}; + const std::pair leftSlowDownPos = {3290.68f, -3141.65f}; + const std::pair rightSlowDownPos = {3300.78f, -3151.98f}; + const std::pair rangedPos = {3301.45f, -3139.29f}; + const std::pair healPos = {3303.09f, -3135.24f}; + + const float decimatedZombiePct = 10.0f; + GluthBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "gluth"); + if (!_unit) + { + return false; + } + } + if (_unit->IsInCombat()) + { + if (_combat_start_ms == 0) + { + _combat_start_ms = getMSTime(); + } + } + else + { + _combat_start_ms = 0; + } + return true; + } + bool BeforeDecimate() + { + if (!_unit || !_unit->HasUnitState(UNIT_STATE_CASTING)) + { + return false; + } + Spell* spell = _unit->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = _unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (!spell) + { + return false; + } + SpellInfo const* info = spell->GetSpellInfo(); + if (!info) + { + return false; + } + if (NaxxSpellIds::MatchesAnySpellId( + info, {NaxxSpellIds::Decimate10, NaxxSpellIds::Decimate25, NaxxSpellIds::Decimate25Alt})) + { + return true; + } + // Fallback to name for custom spell data. + return info->SpellName[LOCALE_enUS] && botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "decimate"); + } + bool JustStartCombat() const { return _combat_start_ms != 0 && getMSTime() - _combat_start_ms < 10000; } + bool IsZombieChow(Unit* unit) const { return unit && botAI->EqualLowercaseName(unit->GetName(), "zombie chow"); } + +private: + void Reset() + { + _unit = nullptr; + _combat_start_ms = 0; + } + + Unit* _unit = nullptr; + uint32 _combat_start_ms = 0; +}; + +class LoathebBossHelper : public AiObject +{ +public: + const std::pair mainTankPos = {2877.57f, -3967.00f}; + const std::pair rangePos = {2896.96f, -3980.61f}; + LoathebBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "loatheb"); + } + return _unit != nullptr; + } + +private: + void Reset() { _unit = nullptr; } + + Unit* _unit = nullptr; +}; + +class FourhorsemanBossHelper : public AiObject +{ +public: + const float posZ = 241.27f; + const std::pair attractPos[2] = {{2502.03f, -2910.90f}, + {2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux) + FourhorsemanBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + else if (_combat_start_ms == 0) + { + _combat_start_ms = getMSTime(); + } + if (_sir && (!_sir->IsInWorld() || !_sir->IsAlive())) + { + Reset(); + } + if (!_sir) + { + _sir = AI_VALUE2(Unit*, "find target", "sir zeliek"); + if (!_sir) + { + return false; + } + } + _lady = AI_VALUE2(Unit*, "find target", "lady blaumeux"); + return true; + } + void Reset() + { + _sir = nullptr; + _lady = nullptr; + _combat_start_ms = 0; + posToGo = 0; + } + bool IsAttracter(Player* bot) + { + Difficulty diff = bot->GetRaidDifficulty(); + if (diff == RAID_DIFFICULTY_25MAN_NORMAL) + { + return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0) || + botAI->IsHealAssistantOfIndex(bot, 1) || botAI->IsHealAssistantOfIndex(bot, 2); + } + return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0); + } + void CalculatePosToGo(Player* bot) + { + bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; + Unit* lady = _lady; + if (!lady) + { + posToGo = 0; + } + else + { + uint32 elapsed_ms = _combat_start_ms ? getMSTime() - _combat_start_ms : 0; + // Interval: 24s - 15s - 15s - ... + posToGo = !(elapsed_ms <= 9000 || ((elapsed_ms - 9000) / 67500) % 2 == 0); + if (botAI->IsRangedDpsAssistantOfIndex(bot, 0) || (raid25 && botAI->IsHealAssistantOfIndex(bot, 1))) + { + posToGo = 1 - posToGo; + } + } + } + std::pair CurrentAttractPos() + { + bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; + float posX = attractPos[posToGo].first, posY = attractPos[posToGo].second; + if (posToGo == 1) + { + float offset_x = 0.0f; + float offset_y = 0.0f; + float bias = 4.5f; + if (raid25) + { + offset_x = -bias; + offset_y = bias; + } + posX += offset_x; + posY += offset_y; + } + return {posX, posY}; + } + Unit* CurrentAttackTarget() + { + if (posToGo == 0) + { + return _sir; + } + return _lady; + } + +protected: + Unit* _sir = nullptr; + Unit* _lady = nullptr; + uint32 _combat_start_ms = 0; + int posToGo = 0; +}; +class ThaddiusBossHelper : public AiObject +{ +public: + const std::pair tankPosFeugen = {3522.94f, -3002.60f}; + const std::pair tankPosStalagg = {3436.14f, -2919.98f}; + const std::pair rangedPosFeugen = {3500.45f, -2997.92f}; + const std::pair rangedPosStalagg = {3441.01f, -2942.04f}; + const float tankPosZ = 312.61f; + ThaddiusBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "thaddius"); + if (!_unit) + { + return false; + } + } + feugen = AI_VALUE2(Unit*, "find target", "feugen"); + stalagg = AI_VALUE2(Unit*, "find target", "stalagg"); + return true; + } + bool IsPhasePet() { return (feugen && feugen->IsAlive()) || (stalagg && stalagg->IsAlive()); } + bool IsPhaseTransition() + { + if (IsPhasePet()) + { + return false; + } + return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + } + bool IsPhaseThaddius() { return !IsPhasePet() && !IsPhaseTransition(); } + Unit* GetNearestPet() + { + Unit* unit = nullptr; + if (feugen && feugen->IsAlive()) + { + unit = feugen; + } + if (stalagg && stalagg->IsAlive() && (!feugen || bot->GetDistance(stalagg) < bot->GetDistance(feugen))) + { + unit = stalagg; + } + return unit; + } + std::pair PetPhaseGetPosForTank() + { + if (GetNearestPet() == feugen) + { + return tankPosFeugen; + } + return tankPosStalagg; + } + std::pair PetPhaseGetPosForRanged() + { + if (GetNearestPet() == feugen) + { + return rangedPosFeugen; + } + return rangedPosStalagg; + } + +protected: + void Reset() + { + _unit = nullptr; + feugen = nullptr; + stalagg = nullptr; + } + + Unit* _unit = nullptr; + Unit* feugen = nullptr; + Unit* stalagg = nullptr; +}; + +#endif diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.cpp new file mode 100644 index 0000000000..ef5913b5bc --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.cpp @@ -0,0 +1,344 @@ +#include "RaidNaxxMultipliers.h" + +#include "ChooseTargetActions.h" +#include "DKActions.h" +#include "DruidActions.h" +#include "DruidBearActions.h" +#include "FollowActions.h" +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "HunterActions.h" +#include "MageActions.h" +#include "MovementActions.h" +#include "PaladinActions.h" +#include "PriestActions.h" +#include "RaidNaxxActions.h" +#include "RaidNaxxSpellIds.h" +#include "ReachTargetActions.h" +#include "RogueActions.h" +#include "ScriptedCreature.h" +#include "ShamanActions.h" +#include "Spell.h" +#include "UseMeetingStoneAction.h" +#include "WarriorActions.h" + +float GrobbulusMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return 1.0f; + } + if (dynamic_cast(action)) + { + return botAI->IsMainTank(bot) ? 0.0f : 1.0f; + } + if (dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} + +float HeiganDanceMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean"); + if (!boss) + { + return 1.0f; + } + bool platform_phase = boss->IsWithinDist2d(2794.26f, -3706.67f, 10.0f); + bool eruption_casting = false; + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (spell) + { + SpellInfo const* info = spell->GetSpellInfo(); + bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10}); + if (!isEruption && info && info->SpellName[LOCALE_enUS]) + { + // Fallback to name for custom spell data. + isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption"); + } + if (isEruption) + { + eruption_casting = true; + } + } + } + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) ) + { + return 0.0f; + } + if (!platform_phase && !eruption_casting) + { + return 1.0f; + } + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 1.0f; + } + if (dynamic_cast(action) && !dynamic_cast(action)) + { + CastSpellAction* spellAction = dynamic_cast(action); + uint32 spellId = AI_VALUE2(uint32, "spell id", spellAction->getSpell()); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + { + return 0.0f; + } + uint32 castTime = spellInfo->CalcCastTime(); + if (castTime == 0 && !spellInfo->IsChanneled()) + { + return 1.0f; + } + } + return 0.0f; +} + +float LoathebGenericMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loatheb"); + if (!boss) + { + return 1.0f; + } + context->GetValue("neglect threat")->Set(true); + if (botAI->GetState() == BOT_STATE_COMBAT && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) + { + return 0.0f; + } + if (!dynamic_cast(action)) + { + return 1.0f; + } + Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::NecroticAura10}); + if (!aura) + { + // Fallback to name for custom spell data. + aura = botAI->GetAura("necrotic aura", bot); + } + if (!aura || aura->GetDuration() <= 1500) + { + return 1.0f; + } + return 0.0f; +} + +float ThaddiusGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if (dynamic_cast(action)) + return 0.0f; + // pet phase + if (helper.IsPhasePet() && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + // die at the same time + Unit* target = AI_VALUE(Unit*, "current target"); + Unit* feugen = AI_VALUE2(Unit*, "find target", "feugen"); + Unit* stalagg = AI_VALUE2(Unit*, "find target", "stalagg"); + if (helper.IsPhasePet() && target && feugen && stalagg && target->GetHealthPct() <= 40 && + (feugen->GetHealthPct() >= target->GetHealthPct() + 3 || stalagg->GetHealthPct() >= target->GetHealthPct() + 3)) + { + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + // magnetic pull + // uint32 curr_timer = eventMap->GetTimer(); + // // if (curr_phase == 2 && bot->GetPositionZ() > 312.5f && dynamic_cast(action)) + // { + // if (curr_phase == 2 && (curr_timer % 20000 >= 18000 || curr_timer % 20000 <= 2000) && + // dynamic_cast(action)) + // { + // // MotionMaster *mm = bot->GetMotionMaster(); + // // mm->Clear(); + // return 0.0f; + // } + // thaddius phase + // if (curr_phase == 8 && dynamic_cast(action)) + // { + // return 0.0f; + // } + return 1.0f; +} + +float SapphironGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} + +float InstructorRazuviousGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + context->GetValue("neglect threat")->Set(true); + if (botAI->GetState() == BOT_STATE_COMBAT && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + return 1.0f; +} + +float KelthuzadGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if ((dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + if (helper.IsPhaseOne()) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + { + return 0.0f; + } + } + if (helper.IsPhaseTwo()) + { + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float AnubrekhanGenericMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); + if (!boss) + { + return 1.0f; + } + if (NaxxSpellIds::HasAnyAura( + botAI, boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) || + botAI->HasAura("locust swarm", boss)) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float FourhorsemanGenericMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek"); + if (!boss) + { + return 1.0f; + } + context->GetValue("neglect threat")->Set(true); + if ((dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + return 1.0f; +} + +// float GothikGenericMultiplier::GetValue(Action* action) +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); +// if (!boss) +// { +// return 1.0f; +// } +// BossAI* boss_ai = dynamic_cast(boss->GetAI()); +// EventMap* eventMap = boss_botAI->GetEvents(); +// uint32 curr_phase = eventMap->GetPhaseMask(); +// if (curr_phase == 1 && (dynamic_cast(action))) +// { +// return 0.0f; +// } +// if (curr_phase == 1 && (dynamic_cast(action))) +// { +// Unit* target = action->GetTarget(); +// if (target == boss) +// { +// return 0.0f; +// } +// } +// return 1.0f; +// } + +float GluthGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if ((dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) + { + return 0.0f; + } + + if (botAI->IsMainTank(bot)) + { + Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25}); + if (!aura) + { + // Fallback to name for custom spell data. + aura = botAI->GetAura("mortal wound", bot, false, true); + } + if (aura && aura->GetStackAmount() >= 5) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + { + return 0.0f; + } + } + } + if (dynamic_cast(action)) + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (helper.IsZombieChow(target)) + { + return 0.0f; + } + } + return 1.0f; +} diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h new file mode 100644 index 0000000000..2b2723ea08 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h @@ -0,0 +1,116 @@ + +#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_ +#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_ + +#include "Multiplier.h" +#include "raids/naxxramas/RaidNaxxBossHelper.h" + +class GrobbulusMultiplier : public Multiplier +{ +public: + GrobbulusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grobbulus") {} + +public: + virtual float GetValue(Action* action); +}; +class HeiganDanceMultiplier : public Multiplier +{ +public: + HeiganDanceMultiplier(PlayerbotAI* ai) : Multiplier(ai, "helgan dance") {} + +public: + virtual float GetValue(Action* action); +}; + +class LoathebGenericMultiplier : public Multiplier +{ +public: + LoathebGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loatheb generic") {} + +public: + virtual float GetValue(Action* action); +}; + +class ThaddiusGenericMultiplier : public Multiplier +{ +public: + ThaddiusGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "thaddius generic"), helper(ai) {} + +public: + virtual float GetValue(Action* action); + +private: + ThaddiusBossHelper helper; +}; + +class SapphironGenericMultiplier : public Multiplier +{ +public: + SapphironGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sapphiron generic"), helper(ai) {} + + virtual float GetValue(Action* action); + +private: + SapphironBossHelper helper; +}; + +class InstructorRazuviousGenericMultiplier : public Multiplier +{ +public: + InstructorRazuviousGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "instructor razuvious generic"), helper(ai) + { + } + virtual float GetValue(Action* action); + +private: + RazuviousBossHelper helper; +}; + +class KelthuzadGenericMultiplier : public Multiplier +{ +public: + KelthuzadGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "kelthuzad generic"), helper(ai) {} + virtual float GetValue(Action* action); + +private: + KelthuzadBossHelper helper; +}; + +class AnubrekhanGenericMultiplier : public Multiplier +{ +public: + AnubrekhanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anubrekhan generic") {} + +public: + virtual float GetValue(Action* action); +}; + +class FourhorsemanGenericMultiplier : public Multiplier +{ +public: + FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "fourhorseman generic") {} + +public: + virtual float GetValue(Action* action); +}; + +// class GothikGenericMultiplier : public Multiplier +// { +// public: +// GothikGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gothik generic") {} + +// public: +// virtual float GetValue(Action* action); +// }; + +class GluthGenericMultiplier : public Multiplier +{ +public: + GluthGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gluth generic"), helper(ai) {} + float GetValue(Action* action) override; + +private: + GluthBossHelper helper; +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h new file mode 100644 index 0000000000..078a281274 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h @@ -0,0 +1,177 @@ +#ifndef _PLAYERBOT_RAIDNAXXSPELLIDS_H +#define _PLAYERBOT_RAIDNAXXSPELLIDS_H + +#include + +#include "PlayerbotAI.h" + +// use src/server/scripts/Northrend/Naxxramas/naxxramas.h for CreatureId, NaxxramasSay, NaxxramasEvent, NaxxramasMisc +namespace NaxxSpellIds +{ + // Heigan + static constexpr uint32 Eruption10 = 29371; +/* + SPELL_SPELL_DISRUPTION = 29310, + SPELL_DECREPIT_FEVER = 29998, + SPELL_PLAGUE_CLOUD = 29350, + SPELL_TELEPORT_SELF = 30211 +*/ + + // Grobbulus + static constexpr uint32 PoisonCloud = 28240; + + // Thaddius polarity + static constexpr uint32 PositiveCharge10 = 28059; + static constexpr uint32 PositiveCharge25 = 28062; + static constexpr uint32 PositiveChargeStack = 29659; + static constexpr uint32 NegativeCharge10 = 28084; + static constexpr uint32 NegativeCharge25 = 28085; + static constexpr uint32 NegativeChargeStack = 29660; +/* + SPELL_MAGNETIC_PULL = 28337, + SPELL_TESLA_SHOCK = 28099, + SPELL_SHOCK_VISUAL = 28159, + + // Stalagg + SPELL_POWER_SURGE = 54529, + SPELL_STALAGG_CHAIN = 28096, + + // Feugen + SPELL_STATIC_FIELD = 28135, + SPELL_FEUGEN_CHAIN = 28111, + + // Thaddius + SPELL_POLARITY_SHIFT = 28089, + SPELL_BALL_LIGHTNING = 28299, + SPELL_CHAIN_LIGHTNING = 28167, + SPELL_BERSERK = 27680, + SPELL_THADDIUS_VISUAL_LIGHTNING = 28136, + SPELL_THADDIUS_SPAWN_STUN = 28160, + + SPELL_POSITIVE_CHARGE = 28062, + SPELL_POSITIVE_CHARGE_STACK = 29659, + SPELL_NEGATIVE_CHARGE = 28085, + SPELL_NEGATIVE_CHARGE_STACK = 29660, + SPELL_POSITIVE_POLARITY = 28059, + SPELL_NEGATIVE_POLARITY = 28084 +*/ + // Sapphiron + static constexpr uint32 Icebolt10 = 28522; + static constexpr uint32 Icebolt25 = 28526; + static constexpr uint32 Chill25 = 55699; +/* + // Fight + SPELL_FROST_AURA = 28531, + SPELL_CLEAVE = 19983, + SPELL_TAIL_SWEEP = 55697, + SPELL_SUMMON_BLIZZARD = 28560, + SPELL_LIFE_DRAIN = 28542, + SPELL_BERSERK = 26662, + + // Ice block + SPELL_ICEBOLT_CAST = 28526, + SPELL_ICEBOLT_TRIGGER = 28522, + SPELL_FROST_MISSILE = 30101, + SPELL_FROST_EXPLOSION = 28524, + + // Visuals + SPELL_SAPPHIRON_DIES = 29357 +*/ + // Gluth + static constexpr uint32 Decimate10 = 28374; + static constexpr uint32 Decimate25 = 54426; + static constexpr uint32 Decimate25Alt = 28375; + static constexpr uint32 MortalWound10 = 25646; + static constexpr uint32 MortalWound25 = 54378; +/* + SPELL_MORTAL_WOUND = 25646, + SPELL_ENRAGE = 28371, + SPELL_DECIMATE = 28374, + SPELL_DECIMATE_DAMAGE = 28375, + SPELL_BERSERK = 26662, + SPELL_INFECTED_WOUND = 29306, + SPELL_CHOW_SEARCHER = 28404 +*/ + // Anub'Rekhan + static constexpr uint32 LocustSwarm10 = 28785; + static constexpr uint32 LocustSwarm10Alt = 28786; + static constexpr uint32 LocustSwarm25 = 54021; // 25-man Locust Swarm +/* + SPELL_IMPALE = 28783, + SPELL_LOCUST_SWARM = 28785, + SPELL_SUMMON_CORPSE_SCARABS_5 = 29105, + SPELL_SUMMON_CORPSE_SCARABS_10 = 28864, + SPELL_BERSERK = 26662 + ACHIEV_TIMED_START_EVENT = 9891, + EVENT_SPAWN_CRYPT_GUARDS_1 = 0, + EVENT_BERSERK = 1, + //// + Position const cryptguardPositions[] = { + { 3299.732f, -3502.489f, 287.077f, 2.378f }, + { 3299.086f, -3450.929f, 287.077f, 3.999f }, + { 3331.217f, -3476.607f, 287.074f, 3.269f } +}; + +*/ + // Loatheb + static constexpr uint32 NecroticAura10 = 55593; +/* + SPELL_NECROTIC_AURA = 55593, + SPELL_SUMMON_SPORE = 29234, + SPELL_DEATHBLOOM = 29865, + SPELL_INEVITABLE_DOOM = 29204, + SPELL_BERSERK = 26662 +*/ + inline bool HasAnyAura(PlayerbotAI* botAI, Unit* unit, std::initializer_list spellIds) + { + if (!botAI || !unit) + { + return false; + } + + for (uint32 spellId : spellIds) + { + if (botAI->HasAura(spellId, unit)) + { + return true; + } + } + return false; + } + + inline Aura* GetAnyAura(Unit* unit, std::initializer_list spellIds) + { + if (!unit) + { + return nullptr; + } + + for (uint32 spellId : spellIds) + { + if (Aura* aura = unit->GetAura(spellId)) + { + return aura; + } + } + return nullptr; + } + + inline bool MatchesAnySpellId(SpellInfo const* info, std::initializer_list spellIds) + { + if (!info) + { + return false; + } + + for (uint32 spellId : spellIds) + { + if (info->Id == spellId) + { + return true; + } + } + return false; + } +} // namespace NaxxSpellIds + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.cpp new file mode 100644 index 0000000000..f2ebd24a3e --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.cpp @@ -0,0 +1,156 @@ +#include "RaidNaxxStrategy.h" + +#include "RaidNaxxMultipliers.h" + +void RaidNaxxStrategy::InitTriggers(std::vector& triggers) +{ + // Grobbulus + triggers.push_back(new TriggerNode("mutating injection melee", + { NextAction("grobbulus move away", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("mutating injection ranged", + { NextAction("grobbulus go behind the boss", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("mutating injection removed", + { NextAction("grobbulus move center", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("grobbulus cloud", + { NextAction("rotate grobbulus", ACTION_RAID + 1) } + )); + + // Heigan the Unclean + triggers.push_back(new TriggerNode("heigan melee", + { NextAction("heigan dance melee", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("heigan ranged", + { NextAction("heigan dance ranged", ACTION_RAID + 1) } + )); + + // Kel'Thuzad + triggers.push_back( + new TriggerNode("kel'thuzad", + { + NextAction("kel'thuzad position", ACTION_RAID + 2), + NextAction("kel'thuzad choose target", ACTION_RAID + 1) + }) + ); + + // Anub'Rekhan + triggers.push_back(new TriggerNode("anub'rekhan", + { NextAction("anub'rekhan position", ACTION_RAID + 1) } + )); + + // Grand Widow Faerlina + triggers.push_back(new TriggerNode("faerlina", + { NextAction("avoid aoe", ACTION_RAID + 1) } + )); + + // Maexxna + triggers.push_back( + new TriggerNode("maexxna", + { + NextAction("rear flank", ACTION_RAID + 1), + NextAction("avoid aoe", ACTION_RAID + 1) + }) + ); + + // Patchwerk + triggers.push_back(new TriggerNode("patchwerk tank", + { NextAction("tank face", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("patchwerk ranged", + { NextAction("patchwerk ranged position", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("patchwerk non-tank", + { NextAction("rear flank", ACTION_RAID + 1) } + )); + + // Thaddius + triggers.push_back(new TriggerNode("thaddius phase pet", + { NextAction("thaddius attack nearest pet", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("thaddius phase pet lose aggro", + { NextAction("taunt spell", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("thaddius phase transition", + { NextAction("thaddius move to platform", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("thaddius phase thaddius", + { NextAction("thaddius move polarity", ACTION_RAID + 1) } + )); + + // Instructor Razuvious + triggers.push_back(new TriggerNode("razuvious tank", + { NextAction("razuvious use obedience crystal", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("razuvious nontank", + { NextAction("razuvious target", ACTION_RAID + 1) } + )); + + // four horseman + triggers.push_back(new TriggerNode("horseman attractors", + { NextAction("horseman attract alternatively", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("horseman except attractors", + { NextAction("horseman attack in order", ACTION_RAID + 1) } + )); + + // sapphiron + triggers.push_back(new TriggerNode("sapphiron ground", + { NextAction("sapphiron ground position", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("sapphiron flight", + { NextAction("sapphiron flight position", ACTION_RAID + 1) } + )); + + // Gluth + triggers.push_back( + new TriggerNode("gluth", + { + NextAction("gluth choose target", ACTION_RAID + 1), + NextAction("gluth position", ACTION_RAID + 1), + NextAction("gluth slowdown", ACTION_RAID) + }) + ); + + triggers.push_back(new TriggerNode("gluth main tank mortal wound", + { NextAction("taunt spell", ACTION_RAID + 1) } + )); + + // Loatheb + triggers.push_back( + new TriggerNode("loatheb", + { + NextAction("loatheb position", ACTION_RAID + 1), + NextAction("loatheb choose target", ACTION_RAID + 1) + }) + ); + +} + +void RaidNaxxStrategy::InitMultipliers(std::vector& multipliers) +{ + multipliers.push_back(new GrobbulusMultiplier(botAI)); + multipliers.push_back(new HeiganDanceMultiplier(botAI)); + multipliers.push_back(new LoathebGenericMultiplier(botAI)); + multipliers.push_back(new ThaddiusGenericMultiplier(botAI)); + multipliers.push_back(new SapphironGenericMultiplier(botAI)); + multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI)); + multipliers.push_back(new KelthuzadGenericMultiplier(botAI)); + multipliers.push_back(new AnubrekhanGenericMultiplier(botAI)); + multipliers.push_back(new FourhorsemanGenericMultiplier(botAI)); + // multipliers.push_back(new GothikGenericMultiplier(botAI)); + multipliers.push_back(new GluthGenericMultiplier(botAI)); +} diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.h new file mode 100644 index 0000000000..dee1720bd8 --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.h @@ -0,0 +1,18 @@ + +#ifndef _PLAYERBOT_RAIDNAXXSTRATEGY_H +#define _PLAYERBOT_RAIDNAXXSTRATEGY_H + +#include "AiObjectContext.h" +#include "Multiplier.h" +#include "Strategy.h" + +class RaidNaxxStrategy : public Strategy +{ +public: + RaidNaxxStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "naxx"; } + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggerContext.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggerContext.h new file mode 100644 index 0000000000..c8ae79c18a --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggerContext.h @@ -0,0 +1,86 @@ +// /* +// * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it +// and/or modify it under version 3 of the License, or (at your option), any later version. +// */ + +#ifndef _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H + +#include "AiObjectContext.h" +#include "NamedObjectContext.h" +#include "RaidNaxxTriggers.h" + +class RaidNaxxTriggerContext : public NamedObjectContext +{ +public: + RaidNaxxTriggerContext() + { + creators["mutating injection melee"] = &RaidNaxxTriggerContext::mutating_injection_melee; + creators["mutating injection ranged"] = &RaidNaxxTriggerContext::mutating_injection_ranged; + creators["mutating injection removed"] = &RaidNaxxTriggerContext::mutating_injection_removed; + creators["grobbulus cloud"] = &RaidNaxxTriggerContext::grobbulus_cloud; + creators["heigan melee"] = &RaidNaxxTriggerContext::heigan_melee; + creators["heigan ranged"] = &RaidNaxxTriggerContext::heigan_ranged; + + creators["thaddius phase pet"] = &RaidNaxxTriggerContext::thaddius_phase_pet; + creators["thaddius phase pet lose aggro"] = &RaidNaxxTriggerContext::thaddius_phase_pet_lose_aggro; + creators["thaddius phase transition"] = &RaidNaxxTriggerContext::thaddius_phase_transition; + creators["thaddius phase thaddius"] = &RaidNaxxTriggerContext::thaddius_phase_thaddius; + + creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank; + creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank; + + creators["horseman attractors"] = &RaidNaxxTriggerContext::horseman_attractors; + creators["horseman except attractors"] = &RaidNaxxTriggerContext::horseman_except_attractors; + + creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground; + creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight; + + creators["kel'thuzad"] = &RaidNaxxTriggerContext::kelthuzad; + + creators["anub'rekhan"] = &RaidNaxxTriggerContext::anubrekhan; + creators["faerlina"] = &RaidNaxxTriggerContext::faerlina; + creators["maexxna"] = &RaidNaxxTriggerContext::maexxna; + creators["patchwerk tank"] = &RaidNaxxTriggerContext::patchwerk_tank; + creators["patchwerk non-tank"] = &RaidNaxxTriggerContext::patchwerk_non_tank; + creators["patchwerk ranged"] = &RaidNaxxTriggerContext::patchwerk_ranged; + + creators["gluth"] = &RaidNaxxTriggerContext::gluth; + creators["gluth main tank mortal wound"] = &RaidNaxxTriggerContext::gluth_main_tank_mortal_wound; + + creators["loatheb"] = &RaidNaxxTriggerContext::loatheb; + } + +private: + static Trigger* mutating_injection_melee(PlayerbotAI* ai) { return new MutatingInjectionMeleeTrigger(ai); } + static Trigger* mutating_injection_ranged(PlayerbotAI* ai) { return new MutatingInjectionRangedTrigger(ai); } + static Trigger* mutating_injection_removed(PlayerbotAI* ai) { return new MutatingInjectionRemovedTrigger(ai); } + static Trigger* grobbulus_cloud(PlayerbotAI* ai) { return new GrobbulusCloudTrigger(ai); } + static Trigger* heigan_melee(PlayerbotAI* ai) { return new HeiganMeleeTrigger(ai); } + static Trigger* heigan_ranged(PlayerbotAI* ai) { return new HeiganRangedTrigger(ai); } + + static Trigger* thaddius_phase_pet(PlayerbotAI* ai) { return new ThaddiusPhasePetTrigger(ai); } + static Trigger* thaddius_phase_pet_lose_aggro(PlayerbotAI* ai) { return new ThaddiusPhasePetLoseAggroTrigger(ai); } + static Trigger* thaddius_phase_transition(PlayerbotAI* ai) { return new ThaddiusPhaseTransitionTrigger(ai); } + static Trigger* thaddius_phase_thaddius(PlayerbotAI* ai) { return new ThaddiusPhaseThaddiusTrigger(ai); } + static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); } + static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); } + + static Trigger* horseman_attractors(PlayerbotAI* ai) { return new HorsemanAttractorsTrigger(ai); } + static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new HorsemanExceptAttractorsTrigger(ai); } + + static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); } + static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(ai); } + static Trigger* kelthuzad(PlayerbotAI* ai) { return new KelthuzadTrigger(ai); } + static Trigger* anubrekhan(PlayerbotAI* ai) { return new AnubrekhanTrigger(ai); } + static Trigger* faerlina(PlayerbotAI* ai) { return new FaerlinaTrigger(ai); } + static Trigger* maexxna(PlayerbotAI* ai) { return new MaexxnaTrigger(ai); } + static Trigger* patchwerk_tank(PlayerbotAI* ai) { return new PatchwerkTankTrigger(ai); } + static Trigger* patchwerk_non_tank(PlayerbotAI* ai) { return new PatchwerkNonTankTrigger(ai); } + static Trigger* patchwerk_ranged(PlayerbotAI* ai) { return new PatchwerkRangedTrigger(ai); } + static Trigger* gluth(PlayerbotAI* ai) { return new GluthTrigger(ai); } + static Trigger* gluth_main_tank_mortal_wound(PlayerbotAI* ai) { return new GluthMainTankMortalWoundTrigger(ai); } + static Trigger* loatheb(PlayerbotAI* ai) { return new LoathebTrigger(ai); } +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.cpp new file mode 100644 index 0000000000..a38f800f7c --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.cpp @@ -0,0 +1,283 @@ +#include "RaidNaxxTriggers.h" + +#include "Playerbots.h" +#include "RaidNaxxSpellIds.h" +#include "Timer.h" +#include "Trigger.h" + +bool MutatingInjectionMeleeTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return false; + } + return MutatingInjectionTrigger::IsActive() && !botAI->IsRanged(bot); +} + +bool MutatingInjectionRangedTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return false; + } + return MutatingInjectionTrigger::IsActive() && botAI->IsRanged(bot); +} + +bool AuraRemovedTrigger::IsActive() +{ + bool check = botAI->HasAura(name, bot, false, false, -1, true); + bool ret = false; + if (prev_check && !check) + { + ret = true; + } + prev_check = check; + return ret; +} + +bool MutatingInjectionRemovedTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return false; + } + return HasNoAuraTrigger::IsActive() && botAI->GetState() == BOT_STATE_COMBAT && botAI->IsRanged(bot); +} + +bool GrobbulusCloudTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return false; + } + if (!botAI->IsMainTank(bot)) + { + return false; + } + // bot->Yell("has aggro on " + boss->GetName() + " : " + to_string(AI_VALUE2(bool, "has aggro", "boss target")), + // LANG_UNIVERSAL); + if (!AI_VALUE2(bool, "has aggro", "boss target")) + { + return false; + } + uint32 now = getMSTime(); + bool poison_cloud_casting = false; + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (spell) + { + poison_cloud_casting = NaxxSpellIds::MatchesAnySpellId(spell->GetSpellInfo(), {NaxxSpellIds::PoisonCloud}); + } + } + if (!poison_cloud_casting && last_cloud_ms != 0 && now - last_cloud_ms < CloudRotationDelayMs) + { + return false; + } + last_cloud_ms = now; + return true; +} + +bool HeiganMeleeTrigger::IsActive() +{ + Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean"); + if (!heigan) + { + return false; + } + return !botAI->IsRanged(bot); +} + +bool HeiganRangedTrigger::IsActive() +{ + Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean"); + if (!heigan) + { + return false; + } + return botAI->IsRanged(bot); +} + +bool RazuviousTankTrigger::IsActive() +{ + Difficulty diff = bot->GetRaidDifficulty(); + if (diff == RAID_DIFFICULTY_10MAN_NORMAL) + { + return helper.UpdateBossAI() && botAI->IsTank(bot); + } + return helper.UpdateBossAI() && bot->getClass() == CLASS_PRIEST; +} + +bool RazuviousNontankTrigger::IsActive() +{ + Difficulty diff = bot->GetRaidDifficulty(); + if (diff == RAID_DIFFICULTY_10MAN_NORMAL) + { + return helper.UpdateBossAI() && !(botAI->IsTank(bot)); + } + return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST); +} + +bool HorsemanAttractorsTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsAttracter(bot); +} + +bool HorsemanExceptAttractorsTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return !helper.IsAttracter(bot); +} + +bool SapphironGroundTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhaseGround(); +} + +bool SapphironFlightTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhaseFlight(); +} + +bool GluthTrigger::IsActive() { return helper.UpdateBossAI(); } + +bool GluthMainTankMortalWoundTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (!botAI->IsAssistTankOfIndex(bot, 0)) + { + return false; + } + Unit* mt = AI_VALUE(Unit*, "main tank"); + if (!mt) + { + return false; + } + Aura* aura = NaxxSpellIds::GetAnyAura(mt, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25}); + if (!aura) + { + // Fallback to name for custom spell data. + aura = botAI->GetAura("mortal wound", mt, false, true); + } + if (!aura || aura->GetStackAmount() < 5) + { + return false; + } + return true; +} + +bool KelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); } + +bool AnubrekhanTrigger::IsActive() { + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); + if (!boss) + { + return false; + } + return true; +} + +bool FaerlinaTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grand widow faerlina"); + if (!boss) + { + return false; + } + return true; +} + +bool MaexxnaTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna"); + if (!boss) + { + return false; + } + return !botAI->IsTank(bot); +} + +bool PatchwerkTankTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); + if (!boss) + { + return false; + } + return !botAI->IsTank(bot) && !botAI->IsRanged(bot); +} + +bool PatchwerkRangedTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); + if (!boss) + { + return false; + } + return !botAI->IsTank(bot) && botAI->IsRanged(bot); +} + +bool PatchwerkNonTankTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); + if (!boss) + { + return false; + } + return !botAI->IsTank(bot); +} + +bool LoathebTrigger::IsActive() { return helper.UpdateBossAI(); } + +bool ThaddiusPhasePetTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhasePet(); +} + +bool ThaddiusPhaseTransitionTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhaseTransition(); +} + +bool ThaddiusPhaseThaddiusTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhaseThaddius(); +} diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.h new file mode 100644 index 0000000000..d02a88a9eb --- /dev/null +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.h @@ -0,0 +1,258 @@ + +#ifndef _PLAYERBOT_RAIDNAXXTRIGGERS_H +#define _PLAYERBOT_RAIDNAXXTRIGGERS_H + +#include "EventMap.h" +#include "GenericTriggers.h" +#include "PlayerbotAIConfig.h" +#include "RaidNaxxBossHelper.h" +#include "Trigger.h" + +class MutatingInjectionTrigger : public HasAuraTrigger +{ +public: + MutatingInjectionTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "mutating injection", 1) {} +}; + +class MutatingInjectionMeleeTrigger : public MutatingInjectionTrigger +{ +public: + MutatingInjectionMeleeTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {} + bool IsActive() override; +}; + +class MutatingInjectionRangedTrigger : public MutatingInjectionTrigger +{ +public: + MutatingInjectionRangedTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {} + bool IsActive() override; +}; + +class AuraRemovedTrigger : public Trigger +{ +public: + AuraRemovedTrigger(PlayerbotAI* botAI, std::string name) : Trigger(botAI, name, 1) { this->prev_check = false; } + virtual bool IsActive() override; + +protected: + bool prev_check; +}; + +class MutatingInjectionRemovedTrigger : public HasNoAuraTrigger +{ +public: + MutatingInjectionRemovedTrigger(PlayerbotAI* ai) : HasNoAuraTrigger(ai, "mutating injection") {} + virtual bool IsActive(); +}; + +class GrobbulusCloudTrigger : public Trigger +{ +public: + GrobbulusCloudTrigger(PlayerbotAI* ai) : Trigger(ai, "grobbulus cloud event"), last_cloud_ms(0) {} + bool IsActive() override; + +private: + uint32 last_cloud_ms; + static constexpr uint32 CloudRotationDelayMs = 15000; +}; + +class HeiganMeleeTrigger : public Trigger +{ +public: + HeiganMeleeTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan melee") {} + virtual bool IsActive(); +}; + +class HeiganRangedTrigger : public Trigger +{ +public: + HeiganRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan ranged") {} + bool IsActive() override; +}; + +class RazuviousTankTrigger : public Trigger +{ +public: + RazuviousTankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious tank"), helper(ai) {} + bool IsActive() override; + +private: + RazuviousBossHelper helper; +}; + +class RazuviousNontankTrigger : public Trigger +{ +public: + RazuviousNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious non-tank"), helper(ai) {} + bool IsActive() override; + +private: + RazuviousBossHelper helper; +}; + +class KelthuzadTrigger : public Trigger +{ +public: + KelthuzadTrigger(PlayerbotAI* ai) : Trigger(ai, "kel'thuzad trigger"), helper(ai) {} + bool IsActive() override; + +private: + KelthuzadBossHelper helper; +}; + +class AnubrekhanTrigger : public Trigger +{ +public: + AnubrekhanTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'rekhan") {} + bool IsActive() override; +}; + + class FaerlinaTrigger : public Trigger + { + public: + FaerlinaTrigger(PlayerbotAI* ai) : Trigger(ai, "faerlina") {} + bool IsActive() override; + }; + +class MaexxnaTrigger : public Trigger +{ +public: + MaexxnaTrigger(PlayerbotAI* ai) : Trigger(ai, "maexxna") {} + bool IsActive() override; +}; + +class PatchwerkTankTrigger : public Trigger +{ +public: + PatchwerkTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk tank") {} + bool IsActive() override; +}; + +class PatchwerkNonTankTrigger : public Trigger +{ +public: + PatchwerkNonTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk non-tank") {} + bool IsActive() override; +}; + +class PatchwerkRangedTrigger : public Trigger +{ +public: + PatchwerkRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk ranged") {} + bool IsActive() override; +}; + +class ThaddiusPhasePetTrigger : public Trigger +{ +public: + ThaddiusPhasePetTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase pet"), helper(ai) {} + bool IsActive() override; + +private: + ThaddiusBossHelper helper; +}; + +class ThaddiusPhasePetLoseAggroTrigger : public ThaddiusPhasePetTrigger +{ +public: + ThaddiusPhasePetLoseAggroTrigger(PlayerbotAI* ai) : ThaddiusPhasePetTrigger(ai) {} + virtual bool IsActive() + { + Unit* target = AI_VALUE(Unit*, "current target"); + return ThaddiusPhasePetTrigger::IsActive() && botAI->IsTank(bot) && target && target->GetVictim() != bot; + } +}; + +class ThaddiusPhaseTransitionTrigger : public Trigger +{ +public: + ThaddiusPhaseTransitionTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase transition"), helper(ai) {} + bool IsActive() override; + +private: + ThaddiusBossHelper helper; +}; + +class ThaddiusPhaseThaddiusTrigger : public Trigger +{ +public: + ThaddiusPhaseThaddiusTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase thaddius"), helper(ai) {} + bool IsActive() override; + +private: + ThaddiusBossHelper helper; +}; + +class HorsemanAttractorsTrigger : public Trigger +{ +public: + HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {} + bool IsActive() override; + +private: + FourhorsemanBossHelper helper; +}; + +class HorsemanExceptAttractorsTrigger : public Trigger +{ +public: + HorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen except attractors"), helper(ai) {} + bool IsActive() override; + +private: + FourhorsemanBossHelper helper; +}; + +class SapphironGroundTrigger : public Trigger +{ +public: + SapphironGroundTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron ground"), helper(ai) {} + bool IsActive() override; + +private: + SapphironBossHelper helper; +}; + +class SapphironFlightTrigger : public Trigger +{ +public: + SapphironFlightTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron flight"), helper(ai) {} + bool IsActive() override; + +private: + SapphironBossHelper helper; +}; + +class GluthTrigger : public Trigger +{ +public: + GluthTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth trigger"), helper(ai) {} + bool IsActive() override; + +private: + GluthBossHelper helper; +}; + +class GluthMainTankMortalWoundTrigger : public Trigger +{ +public: + GluthMainTankMortalWoundTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth main tank mortal wound trigger"), helper(ai) + { + } + bool IsActive() override; + +private: + GluthBossHelper helper; +}; + +class LoathebTrigger : public Trigger +{ +public: + LoathebTrigger(PlayerbotAI* ai) : Trigger(ai, "loatheb"), helper(ai) {} + bool IsActive() override; + +private: + LoathebBossHelper helper; +}; + +#endif From 11d79ffa393fa045da59ab76fba0a55d5709edd7 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Sun, 18 Jan 2026 19:42:19 +0100 Subject: [PATCH 02/42] codestyle --- src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h index 078a281274..858911c6b3 100644 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h @@ -105,8 +105,8 @@ namespace NaxxSpellIds ACHIEV_TIMED_START_EVENT = 9891, EVENT_SPAWN_CRYPT_GUARDS_1 = 0, EVENT_BERSERK = 1, - //// - Position const cryptguardPositions[] = { + //// + Position const cryptguardPositions[] = { { 3299.732f, -3502.489f, 287.077f, 2.378f }, { 3299.086f, -3450.929f, 287.077f, 3.999f }, { 3331.217f, -3476.607f, 287.074f, 3.269f } From f213527615de8f6f32db19b6e89c8fbcd4a0ea42 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Sun, 18 Jan 2026 21:35:49 +0100 Subject: [PATCH 03/42] Update include path for RaidNaxxBossHelper.h --- src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h index 2b2723ea08..ecc335c910 100644 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h +++ b/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h @@ -3,7 +3,7 @@ #define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_ #include "Multiplier.h" -#include "raids/naxxramas/RaidNaxxBossHelper.h" +#include "RaidNaxxBossHelper.h" class GrobbulusMultiplier : public Multiplier { @@ -113,4 +113,4 @@ class GluthGenericMultiplier : public Multiplier GluthBossHelper helper; }; -#endif \ No newline at end of file +#endif From 990863e5d02785320f39be2b1af6fc35e06fbdbc Mon Sep 17 00:00:00 2001 From: Wishmaster117 <140754794+Wishmaster117@users.noreply.github.com> Date: Mon, 19 Jan 2026 11:07:12 +0100 Subject: [PATCH 04/42] remove folder --- .../RaidAi/naxxramas/RaidNaxxActionContext.h | 95 --- .../RaidAi/naxxramas/RaidNaxxActions.h | 342 ---------- .../naxxramas/RaidNaxxActions_Anubrekhan.cpp | 99 --- .../naxxramas/RaidNaxxActions_Faerlina.cpp | 3 - .../RaidNaxxActions_FourHorseman.cpp | 70 -- .../naxxramas/RaidNaxxActions_Gluth.cpp | 206 ------ .../naxxramas/RaidNaxxActions_Gothik.cpp | 3 - .../naxxramas/RaidNaxxActions_Grobbulus.cpp | 52 -- .../naxxramas/RaidNaxxActions_Heigan.cpp | 76 --- .../naxxramas/RaidNaxxActions_Kelthuzad.cpp | 206 ------ .../naxxramas/RaidNaxxActions_Loatheb.cpp | 68 -- .../naxxramas/RaidNaxxActions_Maexxna.cpp | 3 - .../RaidAi/naxxramas/RaidNaxxActions_Noth.cpp | 3 - .../naxxramas/RaidNaxxActions_Patchwerk.cpp | 31 - .../naxxramas/RaidNaxxActions_Razuvious.cpp | 166 ----- .../naxxramas/RaidNaxxActions_Sapphiron.cpp | 121 ---- .../naxxramas/RaidNaxxActions_Shared.cpp | 18 - .../naxxramas/RaidNaxxActions_Thaddius.cpp | 140 ---- .../RaidAi/naxxramas/RaidNaxxBossHelper.h | 606 ------------------ .../RaidAi/naxxramas/RaidNaxxMultipliers.cpp | 344 ---------- .../RaidAi/naxxramas/RaidNaxxMultipliers.h | 116 ---- .../RaidAi/naxxramas/RaidNaxxSpellIds.h | 177 ----- .../RaidAi/naxxramas/RaidNaxxStrategy.cpp | 156 ----- .../RaidAi/naxxramas/RaidNaxxStrategy.h | 18 - .../RaidAi/naxxramas/RaidNaxxTriggerContext.h | 86 --- .../RaidAi/naxxramas/RaidNaxxTriggers.cpp | 283 -------- .../RaidAi/naxxramas/RaidNaxxTriggers.h | 258 -------- 27 files changed, 3746 deletions(-) delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActionContext.h delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions.h delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Anubrekhan.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Faerlina.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_FourHorseman.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gluth.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gothik.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Grobbulus.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Heigan.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Kelthuzad.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Loatheb.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Maexxna.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Noth.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Patchwerk.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Razuvious.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Sapphiron.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Shared.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Thaddius.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxBossHelper.h delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.h delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxTriggerContext.h delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.cpp delete mode 100644 src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.h diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActionContext.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxActionContext.h deleted file mode 100644 index acb9a2deef..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActionContext.h +++ /dev/null @@ -1,95 +0,0 @@ -// /* -// * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it -// and/or modify it under version 3 of the License, or (at your option), any later version. -// */ - -#ifndef _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H -#define _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H - -#include "Action.h" -#include "NamedObjectContext.h" -#include "RaidNaxxActions.h" - -class RaidNaxxActionContext : public NamedObjectContext -{ -public: - RaidNaxxActionContext() - { - creators["grobbulus go behind the boss"] = &RaidNaxxActionContext::go_behind_the_boss; - creators["rotate grobbulus"] = &RaidNaxxActionContext::rotate_grobbulus; - creators["grobbulus move center"] = &RaidNaxxActionContext::grobbulus_move_center; - creators["grobbulus move away"] = &RaidNaxxActionContext::grobbulus_move_away; - - creators["heigan dance melee"] = &RaidNaxxActionContext::heigan_dance_melee; - creators["heigan dance ranged"] = &RaidNaxxActionContext::heigan_dance_ranged; - creators["thaddius attack nearest pet"] = &RaidNaxxActionContext::thaddius_attack_nearest_pet; - // creators["thaddius melee to place"] = &RaidNaxxActionContext::thaddius_tank_to_place; - // creators["thaddius ranged to place"] = &RaidNaxxActionContext::thaddius_ranged_to_place; - creators["thaddius move to platform"] = &RaidNaxxActionContext::thaddius_move_to_platform; - creators["thaddius move polarity"] = &RaidNaxxActionContext::thaddius_move_polarity; - - creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal; - creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target; - - creators["horseman attract alternatively"] = &RaidNaxxActionContext::horseman_attract_alternatively; - creators["horseman attack in order"] = &RaidNaxxActionContext::horseman_attack_in_order; - - creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position; - creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position; - - creators["kel'thuzad choose target"] = &RaidNaxxActionContext::kelthuzad_choose_target; - creators["kel'thuzad position"] = &RaidNaxxActionContext::kelthuzad_position; - - creators["anub'rekhan choose target"] = &RaidNaxxActionContext::anubrekhan_choose_target; - creators["anub'rekhan position"] = &RaidNaxxActionContext::anubrekhan_position; - - creators["gluth choose target"] = &RaidNaxxActionContext::gluth_choose_target; - creators["gluth position"] = &RaidNaxxActionContext::gluth_position; - creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown; - - creators["patchwerk ranged position"] = &RaidNaxxActionContext::patchwerk_ranged_position; - - creators["loatheb position"] = &RaidNaxxActionContext::loatheb_position; - creators["loatheb choose target"] = &RaidNaxxActionContext::loatheb_choose_target; - } - -private: - static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(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_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(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* thaddius_attack_nearest_pet(PlayerbotAI* ai) { return new ThaddiusAttackNearestPetAction(ai); } - // static Action* thaddius_tank_to_place(PlayerbotAI* ai) { return new ThaddiusMeleeToPlaceAction(ai); } - // static Action* thaddius_ranged_to_place(PlayerbotAI* ai) { return new ThaddiusRangedToPlaceAction(ai); } - static Action* thaddius_move_to_platform(PlayerbotAI* ai) { return new ThaddiusMoveToPlatformAction(ai); } - static Action* thaddius_move_polarity(PlayerbotAI* ai) { return new ThaddiusMovePolarityAction(ai); } - static Action* razuvious_target(PlayerbotAI* ai) { return new RazuviousTargetAction(ai); } - static Action* razuvious_use_obedience_crystal(PlayerbotAI* ai) - { - return new RazuviousUseObedienceCrystalAction(ai); - } - static Action* horseman_attract_alternatively(PlayerbotAI* 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 - // SapphironGroundMainTankPositionAction(ai); } - static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); } - static Action* sapphiron_flight_position(PlayerbotAI* ai) { return new SapphironFlightPositionAction(ai); } - // static Action* sapphiron_avoid_chill(PlayerbotAI* ai) { return new SapphironAvoidChillAction(ai); } - static Action* kelthuzad_choose_target(PlayerbotAI* ai) { return new KelthuzadChooseTargetAction(ai); } - static Action* kelthuzad_position(PlayerbotAI* ai) { return new KelthuzadPositionAction(ai); } - static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new AnubrekhanChooseTargetAction(ai); } - static Action* anubrekhan_position(PlayerbotAI* ai) { return new AnubrekhanPositionAction(ai); } - static Action* gluth_choose_target(PlayerbotAI* ai) { return new GluthChooseTargetAction(ai); } - static Action* gluth_position(PlayerbotAI* ai) { return new GluthPositionAction(ai); } - static Action* gluth_slowdown(PlayerbotAI* ai) { return new GluthSlowdownAction(ai); } - static Action* patchwerk_ranged_position(PlayerbotAI* ai) { return new PatchwerkRangedPositionAction(ai); } - static Action* loatheb_position(PlayerbotAI* ai) { return new LoathebPositionAction(ai); } - static Action* loatheb_choose_target(PlayerbotAI* ai) { return new LoathebChooseTargetAction(ai); } -}; - -#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions.h deleted file mode 100644 index 6cc732a28f..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions.h +++ /dev/null @@ -1,342 +0,0 @@ -#ifndef _PLAYERBOT_RAIDNAXXACTIONS_H -#define _PLAYERBOT_RAIDNAXXACTIONS_H - -#include "Action.h" -#include "AttackAction.h" -#include "GenericActions.h" -#include "MovementActions.h" -#include "PlayerbotAI.h" -#include "Playerbots.h" -#include "RaidNaxxBossHelper.h" - -// just for test -// class TryToGetBossAIAction : public Action -// { -// public: -// TryToGetBossAIAction(PlayerbotAI* ai) : Action(ai, "try to get boss ai") {} - -// public: -// virtual bool Execute(Event event); -// }; - -class GrobbulusGoBehindAction : public MovementAction -{ -public: - GrobbulusGoBehindAction(PlayerbotAI* ai, float distance = 24.0f, float delta_angle = M_PI / 8) - : MovementAction(ai, "grobbulus go behind") - { - this->distance = distance; - this->delta_angle = delta_angle; - } - virtual bool Execute(Event event); - -protected: - float distance, delta_angle; -}; - -class GrobbulusRotateAction : public RotateAroundTheCenterPointAction -{ -public: - GrobbulusRotateAction(PlayerbotAI* botAI) - : RotateAroundTheCenterPointAction(botAI, "rotate grobbulus", 3281.23f, -3310.38f, 35.0f, 8, true, M_PI) - { - } - virtual bool isUseful() override - { - return RotateAroundTheCenterPointAction::isUseful() && botAI->IsMainTank(bot) && - AI_VALUE2(bool, "has aggro", "boss target"); - } - uint32 GetCurrWaypoint() override; -}; - -class GrobblulusMoveCenterAction : public MoveInsideAction -{ -public: - GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {} -}; - -class GrobbulusMoveAwayAction : public MovementAction -{ -public: - GrobbulusMoveAwayAction(PlayerbotAI* ai, float distance = 18.0f) - : MovementAction(ai, "grobbulus move away"), distance(distance) - { - } - bool Execute(Event event) override; - -private: - float distance; -}; - -class HeiganDanceAction : public MovementAction -{ -public: - HeiganDanceAction(PlayerbotAI* ai) : MovementAction(ai, "heigan dance") - { - this->last_eruption_ms = 0; - this->platform_phase = false; - ResetSafe(); - waypoints.push_back(std::make_pair(2794.88f, -3668.12f)); - waypoints.push_back(std::make_pair(2775.49f, -3674.43f)); - waypoints.push_back(std::make_pair(2762.30f, -3684.59f)); - waypoints.push_back(std::make_pair(2755.99f, -3703.96f)); - platform = std::make_pair(2794.26f, -3706.67f); - } - -protected: - bool CalculateSafe(); - void ResetSafe() - { - curr_safe = 0; - curr_dir = 1; - } - void NextSafe() - { - curr_safe += curr_dir; - if (curr_safe == 3 || curr_safe == 0) - { - curr_dir = -curr_dir; - } - } - uint32 last_eruption_ms; - bool platform_phase; - uint32 curr_safe, curr_dir; - std::vector> waypoints; - std::pair platform; -}; - -class HeiganDanceMeleeAction : public HeiganDanceAction -{ -public: - HeiganDanceMeleeAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {} - virtual bool Execute(Event event); -}; - -class HeiganDanceRangedAction : public HeiganDanceAction -{ -public: - HeiganDanceRangedAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {} - virtual bool Execute(Event event); -}; - -class ThaddiusAttackNearestPetAction : public AttackAction -{ -public: - ThaddiusAttackNearestPetAction(PlayerbotAI* ai) : AttackAction(ai, "thaddius attack nearest pet"), helper(ai) {} - virtual bool Execute(Event event); - virtual bool isUseful(); - -private: - ThaddiusBossHelper helper; -}; - -// class ThaddiusMeleeToPlaceAction : public MovementAction -// { -// public: -// ThaddiusMeleeToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius melee to place") {} -// virtual bool Execute(Event event); -// virtual bool isUseful(); -// }; - -// class ThaddiusRangedToPlaceAction : public MovementAction -// { -// public: -// ThaddiusRangedToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius ranged to place") {} -// virtual bool Execute(Event event); -// virtual bool isUseful(); -// }; - -class ThaddiusMoveToPlatformAction : public MovementAction -{ -public: - ThaddiusMoveToPlatformAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move to platform") {} - virtual bool Execute(Event event); - virtual bool isUseful(); -}; - -class ThaddiusMovePolarityAction : public MovementAction -{ -public: - ThaddiusMovePolarityAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move polarity") {} - virtual bool Execute(Event event); - virtual bool isUseful(); -}; - -class RazuviousUseObedienceCrystalAction : public MovementAction -{ -public: - RazuviousUseObedienceCrystalAction(PlayerbotAI* ai) - : MovementAction(ai, "razuvious use obedience crystal"), helper(ai) - { - } - bool Execute(Event event) override; - -private: - RazuviousBossHelper helper; -}; - -class RazuviousTargetAction : public AttackAction -{ -public: - RazuviousTargetAction(PlayerbotAI* ai) : AttackAction(ai, "razuvious target"), helper(ai) {} - bool Execute(Event event) override; - -private: - RazuviousBossHelper helper; -}; - -class HorsemanAttractAlternativelyAction : public AttackAction -{ -public: - HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attract alternatively"), helper(ai) - { - } - bool Execute(Event event) override; - -protected: - FourhorsemanBossHelper helper; -}; - -class HorsemanAttactInOrderAction : public AttackAction -{ -public: - HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attact in order"), helper(ai) {} - bool Execute(Event event) override; - -protected: - FourhorsemanBossHelper helper; -}; - -// class SapphironGroundMainTankPositionAction : public MovementAction -// { -// public: -// SapphironGroundMainTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground main tank -// position") {} virtual bool Execute(Event event); -// }; - -class SapphironGroundPositionAction : public MovementAction -{ -public: - SapphironGroundPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground position"), helper(ai) {} - bool Execute(Event event) override; - -protected: - SapphironBossHelper helper; -}; - -class SapphironFlightPositionAction : public MovementAction -{ -public: - SapphironFlightPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron flight position"), helper(ai) {} - bool Execute(Event event) override; - -protected: - SapphironBossHelper helper; - bool MoveToNearestIcebolt(); -}; - -// class SapphironAvoidChillAction : public MovementAction -// { -// public: -// SapphironAvoidChillAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron avoid chill") {} -// virtual bool Execute(Event event); -// }; - -class KelthuzadChooseTargetAction : public AttackAction -{ -public: - KelthuzadChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "kel'thuzad choose target"), helper(ai) {} - virtual bool Execute(Event event); - -private: - KelthuzadBossHelper helper; -}; - -class KelthuzadPositionAction : public MovementAction -{ -public: - KelthuzadPositionAction(PlayerbotAI* ai) : MovementAction(ai, "kel'thuzad position"), helper(ai) {} - virtual bool Execute(Event event); - -private: - KelthuzadBossHelper helper; -}; - -class AnubrekhanChooseTargetAction : public AttackAction -{ -public: - AnubrekhanChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "anub'rekhan choose target") {} - bool Execute(Event event) override; -}; - -class AnubrekhanPositionAction : public RotateAroundTheCenterPointAction -{ -public: - AnubrekhanPositionAction(PlayerbotAI* ai) - : RotateAroundTheCenterPointAction(ai, "anub'rekhan position", 3272.49f, -3476.27f, 45.0f, 16) - { - } - bool Execute(Event event) override; -}; - -class GluthChooseTargetAction : public AttackAction -{ -public: - GluthChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "gluth choose target"), helper(ai) {} - bool Execute(Event event) override; - -private: - GluthBossHelper helper; -}; - -class GluthPositionAction : public RotateAroundTheCenterPointAction -{ -public: - GluthPositionAction(PlayerbotAI* ai) - : RotateAroundTheCenterPointAction(ai, "gluth position", 3293.61f, -3149.01f, 12.0f, 12), helper(ai) - { - } - bool Execute(Event event) override; - -private: - GluthBossHelper helper; -}; - -class GluthSlowdownAction : public Action -{ -public: - GluthSlowdownAction(PlayerbotAI* ai) : Action(ai, "gluth slowdown"), helper(ai) {} - bool Execute(Event event) override; - -private: - GluthBossHelper helper; -}; - -class LoathebPositionAction : public MovementAction -{ -public: - LoathebPositionAction(PlayerbotAI* ai) : MovementAction(ai, "loatheb position"), helper(ai) {} - virtual bool Execute(Event event); - -private: - LoathebBossHelper helper; -}; - -class LoathebChooseTargetAction : public AttackAction -{ -public: - LoathebChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "loatheb choose target"), helper(ai) {} - virtual bool Execute(Event event); - -private: - LoathebBossHelper helper; -}; - -class PatchwerkRangedPositionAction : public MovementAction -{ -public: - PatchwerkRangedPositionAction(PlayerbotAI* ai) : MovementAction(ai, "patchwerk ranged position") {} - bool Execute(Event event) override; -}; - -#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Anubrekhan.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Anubrekhan.cpp deleted file mode 100644 index 90f14877e2..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Anubrekhan.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "ObjectGuid.h" -#include "Playerbots.h" - -bool AnubrekhanChooseTargetAction::Execute(Event event) -{ - GuidVector attackers = context->GetValue("attackers")->Get(); - Unit* target = nullptr; - Unit* target_boss = nullptr; - std::vector target_guards; - for (ObjectGuid const guid : attackers) - { - Unit* unit = botAI->GetUnit(guid); - if (!unit) - continue; - if (botAI->EqualLowercaseName(unit->GetName(), "crypt guard")) - { - target_guards.push_back(unit); - } - if (botAI->EqualLowercaseName(unit->GetName(), "anub'rekhan")) - { - target_boss = unit; - } - } - if (botAI->IsMainTank(bot)) - { - target = target_boss; - } - else - { - if (target_guards.size() == 0) - { - target = target_boss; - } - else - { - if (botAI->IsAssistTank(bot)) - { - for (Unit* t : target_guards) - { - if (target == nullptr || (target->GetVictim() && target->GetVictim()->ToPlayer() && - botAI->IsTank(target->GetVictim()->ToPlayer()))) - { - target = t; - } - } - } - else - { - for (Unit* t : target_guards) - { - if (target == nullptr || target->GetHealthPct() > t->GetHealthPct()) - { - target = t; - } - } - } - } - } - if (context->GetValue("current target")->Get() == target) - { - return false; - } - return Attack(target); -} - -bool AnubrekhanPositionAction::Execute(Event event) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); - if (!boss) - { - return false; - } - bool inPhase = botAI->HasAura("locust swarm", boss) || boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (inPhase) - { - if (botAI->IsMainTank(bot)) - { - uint32 nearest = FindNearestWaypoint(); - uint32 next_point; - if (inPhase) - { - 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, - false, false, MovementPriority::MOVEMENT_COMBAT); - } - else - { - return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT); - } - } - return false; -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Faerlina.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Faerlina.cpp deleted file mode 100644 index 799dd9ca2e..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Faerlina.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "RaidNaxxActions.h" - -// Reserved for Faerlina-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_FourHorseman.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_FourHorseman.cpp deleted file mode 100644 index 0d29526fef..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_FourHorseman.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "Playerbots.h" - -bool HorsemanAttractAlternativelyAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - helper.CalculatePosToGo(bot); - auto [posX, posY] = helper.CurrentAttractPos(); - if (MoveTo(bot->GetMapId(), posX, posY, helper.posZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) - { - return true; - } - Unit* attackTarget = helper.CurrentAttackTarget(); - if (context->GetValue("current target")->Get() != attackTarget) - { - return Attack(attackTarget); - } - return false; -} - -bool HorsemanAttactInOrderAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - Unit* target = nullptr; - Unit* thane = AI_VALUE2(Unit*, "find target", "thane korth'azz"); - Unit* lady = AI_VALUE2(Unit*, "find target", "lady blaumeux"); - Unit* sir = AI_VALUE2(Unit*, "find target", "sir zeliek"); - Unit* fourth = AI_VALUE2(Unit*, "find target", "baron rivendare"); - if (!fourth) - { - fourth = AI_VALUE2(Unit*, "find target", "highlord mograine"); - } - std::vector attack_order; - if (botAI->IsAssistTank(bot)) - { - attack_order = {fourth, thane, lady, sir}; - } - else - { - attack_order = {thane, fourth, lady, sir}; - } - for (Unit* t : attack_order) - { - if (t && t->IsAlive()) - { - target = t; - break; - } - } - if (target) - { - if (context->GetValue("current target")->Get() == target && botAI->GetState() == BOT_STATE_COMBAT) - { - return false; - } - if (!bot->IsWithinLOSInMap(target)) - { - return MoveNear(target, 22.0f, MovementPriority::MOVEMENT_COMBAT); - } - return Attack(target); - } - return false; -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gluth.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gluth.cpp deleted file mode 100644 index 7192685013..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gluth.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "PlayerbotAIConfig.h" -#include "Playerbots.h" -#include "SharedDefines.h" - -bool GluthChooseTargetAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - GuidVector attackers = context->GetValue("possible targets")->Get(); - Unit* target = nullptr; - Unit* target_boss = nullptr; - std::vector target_zombies; - for (GuidVector::iterator i = attackers.begin(); i != attackers.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (!unit) - continue; - if (!unit->IsAlive()) - { - continue; - } - if (botAI->EqualLowercaseName(unit->GetName(), "zombie chow")) - { - target_zombies.push_back(unit); - } - if (botAI->EqualLowercaseName(unit->GetName(), "gluth")) - { - target_boss = unit; - } - } - if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) - { - target = target_boss; - } - else if (botAI->IsAssistTankOfIndex(bot, 1)) - { - for (Unit* t : target_zombies) - { - if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() != bot && t->GetDistance2d(bot) <= 10.0f) - { - if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot)) - { - target = t; - } - } - } - } - else if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0 || botAI->GetClassIndex(bot, CLASS_HUNTER) == 1) - { - // prevent zombie go straight to gluth - for (Unit* t : target_zombies) - { - if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() == target_boss && - t->GetDistance2d(bot) <= sPlayerbotAIConfig->spellDistance) - { - if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot)) - { - target = t; - } - } - } - if (!target) - { - target = target_boss; - } - } - else - { - for (Unit* t : target_zombies) - { - if (t->GetHealthPct() <= helper.decimatedZombiePct) - { - if (target == nullptr || - target->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second) > - t->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second)) - { - target = t; - } - } - } - if (target == nullptr) - { - target = target_boss; - } - } - if (!target || context->GetValue("current target")->Get() == target) - { - return false; - } - if (target_boss && target == target_boss) - return Attack(target, true); - return Attack(target, false); - // return Attack(target); -} - -bool GluthPositionAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; - if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) - { - if (AI_VALUE2(bool, "has aggro", "boss target")) - { - if (raid25) - { - if (MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), false, false, false, - false, MovementPriority::MOVEMENT_COMBAT)) - { - return true; - } - return MoveInside(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), 2.0f, - MovementPriority::MOVEMENT_COMBAT); - } - else - { - if (MoveTo(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), false, false, false, - false, MovementPriority::MOVEMENT_COMBAT)) - { - return true; - } - return MoveInside(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), 2.0f, - MovementPriority::MOVEMENT_COMBAT); - } - } - } - else if (botAI->IsAssistTankOfIndex(bot, 1)) - { - if (helper.BeforeDecimate()) - { - if (MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), false, false, - false, false, MovementPriority::MOVEMENT_COMBAT)) - { - return true; - } - return MoveInside(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), 2.0f, - MovementPriority::MOVEMENT_COMBAT); - } - else - { - if (AI_VALUE2(bool, "has aggro", "current target")) - { - uint32 nearest = FindNearestWaypoint(); - uint32 next_point = (nearest + 1) % intervals; - return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - } - } - else if (botAI->IsRangedDps(bot)) - { - if (raid25) - { - if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0) - { - return MoveInside(NAXX_MAP_ID, helper.leftSlowDownPos.first, helper.leftSlowDownPos.second, bot->GetPositionZ(), 0.0f, - MovementPriority::MOVEMENT_COMBAT); - } - if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 1) - { - return MoveInside(NAXX_MAP_ID, helper.rightSlowDownPos.first, helper.rightSlowDownPos.second, bot->GetPositionZ(), 0.0f, - MovementPriority::MOVEMENT_COMBAT); - } - } - return MoveInside(NAXX_MAP_ID, helper.rangedPos.first, helper.rangedPos.second, bot->GetPositionZ(), 3.0f, - MovementPriority::MOVEMENT_COMBAT); - } - else if (botAI->IsHeal(bot)) - { - return MoveInside(NAXX_MAP_ID, helper.healPos.first, helper.healPos.second, bot->GetPositionZ(), 0.0f, - MovementPriority::MOVEMENT_COMBAT); - } - return false; -} - -bool GluthSlowdownAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; - if (!raid25) - { - return false; - } - if (helper.JustStartCombat()) - { - return false; - } - switch (bot->getClass()) - { - case CLASS_HUNTER: - return botAI->CastSpell("frost trap", bot); - break; - default: - break; - } - return false; -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gothik.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gothik.cpp deleted file mode 100644 index e5c673e46e..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Gothik.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "RaidNaxxActions.h" - -// Reserved for Gothik-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Grobbulus.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Grobbulus.cpp deleted file mode 100644 index 34af48b782..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Grobbulus.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "Playerbots.h" - -bool GrobbulusGoBehindAction::Execute(Event event) -{ - Unit* boss = AI_VALUE(Unit*, "boss target"); - if (!boss) - { - return false; - } - // Position* pos = boss->GetPosition(); - float orientation = boss->GetOrientation() + M_PI + delta_angle; - float x = boss->GetPositionX(); - float y = boss->GetPositionY(); - float z = boss->GetPositionZ(); - float rx = x + cos(orientation) * distance; - float ry = y + sin(orientation) * distance; - return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -} - -bool GrobbulusMoveAwayAction::Execute(Event event) -{ - Unit* boss = AI_VALUE(Unit*, "boss target"); - if (!boss) - { - return false; - } - - const float currentDistance = bot->GetExactDist2d(boss); - if (currentDistance >= distance) - { - return false; - } - - const float angle = boss->GetAngle(bot); - const float x = boss->GetPositionX() + cos(angle) * distance; - const float y = boss->GetPositionY() + sin(angle) * distance; - const float z = bot->GetPositionZ(); - - return MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -} - -uint32 GrobbulusRotateAction::GetCurrWaypoint() -{ - uint32 current = FindNearestWaypoint(); - if (clockwise) - { - return (current + 1) % intervals; - } - return (current + intervals - 1) % intervals; -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Heigan.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Heigan.cpp deleted file mode 100644 index b7c19f2559..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Heigan.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "Playerbots.h" -#include "RaidNaxxSpellIds.h" -#include "Spell.h" -#include "Timer.h" - -bool HeiganDanceAction::CalculateSafe() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean"); - if (!boss) - { - return false; - } - uint32 now = getMSTime(); - platform_phase = boss->IsWithinDist2d(platform.first, platform.second, 10.0f); - if (last_eruption_ms != 0 && now - last_eruption_ms > 15000) - { - ResetSafe(); - } - if (boss->HasUnitState(UNIT_STATE_CASTING)) - { - Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!spell) - { - spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); - } - if (spell) - { - SpellInfo const* info = spell->GetSpellInfo(); - bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10}); - if (!isEruption && info && info->SpellName[LOCALE_enUS]) - { - // Fallback to name for custom spell data. - isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption"); - } - if (isEruption) - { - if (last_eruption_ms == 0 || now - last_eruption_ms > 500) - { - NextSafe(); - } - last_eruption_ms = now; - } - } - } - return true; -} - -bool HeiganDanceMeleeAction::Execute(Event event) -{ - CalculateSafe(); - if (!platform_phase && botAI->IsMainTank(bot) && !AI_VALUE2(bool, "has aggro", "boss target")) - { - return false; - } - assert(curr_safe >= 0 && curr_safe <= 3); - return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), - botAI->IsMainTank(bot) ? 0 : 0, MovementPriority::MOVEMENT_COMBAT); -} - -bool HeiganDanceRangedAction::Execute(Event event) -{ - CalculateSafe(); - if (!platform_phase) - { - if (MoveTo(bot->GetMapId(), platform.first, platform.second, 276.54f, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) - { - return true; - } - return MoveInside(bot->GetMapId(), platform.first, platform.second, 276.54f, 2.0f, MovementPriority::MOVEMENT_COMBAT); - } - botAI->InterruptSpell(); - return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), 0, - MovementPriority::MOVEMENT_COMBAT); -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Kelthuzad.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Kelthuzad.cpp deleted file mode 100644 index 1c86769d95..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Kelthuzad.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "PlayerbotAIConfig.h" -#include "Playerbots.h" - -bool KelthuzadChooseTargetAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - GuidVector attackers = context->GetValue("attackers")->Get(); - Unit* target = nullptr; - Unit *target_soldier = nullptr, *target_weaver = nullptr, *target_abomination = nullptr, *target_kelthuzad = nullptr, - *target_guardian = nullptr; - for (auto i = attackers.begin(); i != attackers.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (!unit) - continue; - - if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown")) - { - if (!target_guardian) - { - target_guardian = unit; - } - else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() && - target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) && - botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer())) - { - target_guardian = unit; - } - else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() && - target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) && - !botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer()) && - target_guardian->GetDistance2d(helper.center.first, helper.center.second) > - bot->GetDistance2d(unit)) - { - target_guardian = unit; - } - } - - if (unit->GetDistance2d(helper.center.first, helper.center.second) > 30.0f) - { - continue; - } - if (bot->GetDistance2d(unit) > sPlayerbotAIConfig->spellDistance) - { - continue; - } - if (botAI->EqualLowercaseName(unit->GetName(), "unstoppable abomination")) - { - if (target_abomination == nullptr || - target_abomination->GetDistance2d(helper.center.first, helper.center.second) > - unit->GetDistance2d(helper.center.first, helper.center.second)) - { - target_abomination = unit; - } - } - if (botAI->EqualLowercaseName(unit->GetName(), "soldier of the frozen wastes")) - { - if (target_soldier == nullptr || - target_soldier->GetDistance2d(helper.center.first, helper.center.second) > - unit->GetDistance2d(helper.center.first, helper.center.second)) - { - target_soldier = unit; - } - } - if (botAI->EqualLowercaseName(unit->GetName(), "soul weaver")) - { - if (target_weaver == nullptr || target_weaver->GetDistance2d(helper.center.first, helper.center.second) > - unit->GetDistance2d(helper.center.first, helper.center.second)) - { - target_weaver = unit; - } - } - if (botAI->EqualLowercaseName(unit->GetName(), "kel'thuzad")) - { - target_kelthuzad = unit; - } - } - std::vector targets; - if (botAI->IsRanged(bot)) - { - if (botAI->GetRangedDpsIndex(bot) <= 1) - { - targets = {target_soldier, target_weaver, target_abomination, target_kelthuzad}; - } - else - { - targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; - } - } - else if (botAI->IsAssistTank(bot)) - { - targets = {target_abomination, target_guardian, target_kelthuzad}; - } - else - { - targets = {target_abomination, target_kelthuzad}; - } - for (Unit* t : targets) - { - if (t) - { - target = t; - break; - } - } - if (context->GetValue("current target")->Get() == target) - { - return false; - } - if (target_kelthuzad && target == target_kelthuzad) - { - return Attack(target, true); - } - return Attack(target, false); -} - -bool KelthuzadPositionAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - if (helper.IsPhaseOne()) - { - if (AI_VALUE(Unit*, "current target") == nullptr) - { - return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f, - MovementPriority::MOVEMENT_COMBAT); - } - } - else if (helper.IsPhaseTwo()) - { - Unit* shadow_fissure = helper.GetAnyShadowFissure(); - if (!shadow_fissure || !bot->IsWithinDistInMap(shadow_fissure, 10.0f)) - { - float distance, angle; - if (botAI->IsMainTank(bot)) - { - if (AI_VALUE2(bool, "has aggro", "current target")) - { - return MoveTo(NAXX_MAP_ID, helper.tank_pos.first, helper.tank_pos.second, bot->GetPositionZ(), false, false, false, - false, MovementPriority::MOVEMENT_COMBAT); - } - else - { - return false; - } - } - else if (botAI->IsRanged(bot)) - { - uint32 index = botAI->GetRangedIndex(bot); - if (index < 8) - { - distance = 20.0f; - angle = index * M_PI / 4; - } - else - { - distance = 32.0f; - angle = (index - 8) * M_PI / 4; - } - float dx, dy; - dx = helper.center.first + cos(angle) * distance; - dy = helper.center.second + sin(angle) * distance; - return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - else if (botAI->IsTank(bot)) - { - Unit* cur_tar = AI_VALUE(Unit*, "current target"); - if (cur_tar && cur_tar->GetVictim() && cur_tar->GetVictim()->ToPlayer() && - botAI->EqualLowercaseName(cur_tar->GetName(), "guardian of icecrown") && - botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer())) - { - return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, bot->GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - else - { - return false; - } - } - } - else - { - float dx, dy; - float angle; - if (!botAI->IsRanged(bot)) - { - angle = shadow_fissure->GetAngle(helper.center.first, helper.center.second); - } - else - { - angle = bot->GetAngle(shadow_fissure) + M_PI; - } - dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f; - dy = shadow_fissure->GetPositionY() + sin(angle) * 10.0f; - return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - } - return false; -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Loatheb.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Loatheb.cpp deleted file mode 100644 index 54a9325989..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Loatheb.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "Playerbots.h" - -bool LoathebPositionAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - if (botAI->IsTank(bot)) - { - if (AI_VALUE2(bool, "has aggro", "boss target")) - { - return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - } - } - else if (botAI->IsRanged(bot)) - { - return MoveInside(533, helper.rangePos.first, helper.rangePos.second, bot->GetPositionZ(), 1.0f, - MovementPriority::MOVEMENT_COMBAT); - } - return false; -} - -bool LoathebChooseTargetAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - GuidVector attackers = context->GetValue("attackers")->Get(); - Unit* target = nullptr; - Unit* target_boss = nullptr; - Unit* target_spore = nullptr; - for (auto i = attackers.begin(); i != attackers.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (!unit) - continue; - if (!unit->IsAlive()) - { - continue; - } - if (botAI->EqualLowercaseName(unit->GetName(), "spore")) - { - target_spore = unit; - } - if (botAI->EqualLowercaseName(unit->GetName(), "loatheb")) - { - target_boss = unit; - } - } - if (target_spore && bot->GetDistance2d(target_spore) <= 1.0f) - { - target = target_spore; - } - else - { - target = target_boss; - } - if (!target || context->GetValue("current target")->Get() == target) - { - return false; - } - return Attack(target); -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Maexxna.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Maexxna.cpp deleted file mode 100644 index 67ef9930f0..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Maexxna.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "RaidNaxxActions.h" - -// Reserved for Maexxna-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Noth.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Noth.cpp deleted file mode 100644 index ff3ce98e70..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Noth.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "RaidNaxxActions.h" - -// Reserved for Noth-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Patchwerk.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Patchwerk.cpp deleted file mode 100644 index 6ef1480cf2..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Patchwerk.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "RaidNaxxActions.h" - -#include -#include - -bool PatchwerkRangedPositionAction::Execute(Event event) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); - if (!boss) - return false; - - constexpr float minDistance = 12.0f; - constexpr float maxDistance = 15.0f; - const float distance = bot->GetExactDist2d(boss); - - if (distance >= minDistance && distance <= maxDistance) - return false; - - const float desiredDistance = std::clamp(distance, minDistance, maxDistance); - float angle = boss->GetAngle(bot); - - if (distance < 0.1f) - angle = boss->GetOrientation(); - - const float x = boss->GetPositionX() + std::cos(angle) * desiredDistance; - const float y = boss->GetPositionY() + std::sin(angle) * desiredDistance; - const float z = bot->GetPositionZ(); - - return MoveTo(boss->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, - false); -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Razuvious.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Razuvious.cpp deleted file mode 100644 index a16469bf15..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Razuvious.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "ObjectGuid.h" -#include "PlayerbotAIConfig.h" -#include "Playerbots.h" -#include "SharedDefines.h" - -bool RazuviousUseObedienceCrystalAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - // bot->GetCharm - if (Unit* charm = bot->GetCharm()) - { - Unit* target = AI_VALUE2(Unit*, "find target", "instructor razuvious"); - if (!target) - { - return false; - } - if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == NULL_MOTION_TYPE) - { - charm->GetMotionMaster()->Clear(); - charm->GetMotionMaster()->MoveChase(target); - charm->GetAI()->AttackStart(target); - } - Aura* forceObedience = botAI->GetAura("force obedience", charm); - uint32 duration_time; - if (!forceObedience) - { - forceObedience = botAI->GetAura("mind control", charm); - duration_time = 60000; - } - else - { - duration_time = 90000; - } - if (!forceObedience) - { - return false; - } - if (charm->GetDistance(target) <= 0.51f) - { - // taunt - bool tauntUseful = true; - if (forceObedience->GetDuration() <= (duration_time - 5000)) - { - if (target->GetVictim() && botAI->HasAura(29061, target->GetVictim())) - { - tauntUseful = false; - } - if (forceObedience->GetDuration() <= 3000) - { - tauntUseful = false; - } - } - if (forceObedience->GetDuration() >= (duration_time - 500)) - { - tauntUseful = false; - } - if (tauntUseful && !charm->HasSpellCooldown(29060)) - { - // shield - if (!charm->HasSpellCooldown(29061)) - { - charm->CastSpell(charm, 29061, true); - charm->AddSpellCooldown(29061, 0, 30 * 1000); - } - charm->CastSpell(target, 29060, true); - charm->AddSpellCooldown(29060, 0, 20 * 1000); - } - // strike - if (!charm->HasSpellCooldown(61696)) - { - charm->CastSpell(target, 61696, true); - charm->AddSpellCooldown(61696, 0, 4 * 1000); - } - } - } - else - { - Difficulty diff = bot->GetRaidDifficulty(); - if (diff == RAID_DIFFICULTY_10MAN_NORMAL) - { - GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); - for (auto i = npcs.begin(); i != npcs.end(); i++) - { - Creature* unit = botAI->GetCreature(*i); - if (!unit) - { - continue; - } - if (botAI->IsMainTank(bot) && unit->GetSpawnId() != 128352) - { - continue; - } - if (!botAI->IsMainTank(bot) && unit->GetSpawnId() != 128353) - { - continue; - } - if (MoveTo(unit, 0.0f, MovementPriority::MOVEMENT_COMBAT)) - { - return true; - } - Creature* creature = bot->GetNPCIfCanInteractWith(*i, UNIT_NPC_FLAG_SPELLCLICK); - if (!creature) - continue; - creature->HandleSpellClick(bot); - return true; - } - } - else - { - GuidVector attackers = context->GetValue("attackers")->Get(); - Unit* target = nullptr; - for (auto i = attackers.begin(); i != attackers.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (!unit) - continue; - if (botAI->EqualLowercaseName(unit->GetName(), "death knight understudy")) - { - target = unit; - break; - } - } - if (target) - { - if (bot->GetDistance2d(target) > sPlayerbotAIConfig->spellDistance) - { - return MoveNear(target, sPlayerbotAIConfig->spellDistance, MovementPriority::MOVEMENT_COMBAT); - } - else - { - return botAI->CastSpell("mind control", target); - } - } - } - } - return false; -} - -bool RazuviousTargetAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - Unit* razuvious = AI_VALUE2(Unit*, "find target", "instructor razuvious"); - Unit* understudy = AI_VALUE2(Unit*, "find target", "death knight understudy"); - Unit* target = nullptr; - if (botAI->IsTank(bot)) - { - target = understudy; - } - else - { - target = razuvious; - } - if (AI_VALUE(Unit*, "current target") == target) - { - return false; - } - return Attack(target); -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Sapphiron.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Sapphiron.cpp deleted file mode 100644 index e749836619..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Sapphiron.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "PlayerbotAIConfig.h" -#include "Playerbots.h" -#include "RaidNaxxBossHelper.h" -#include "RaidNaxxSpellIds.h" - -bool SapphironGroundPositionAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - if (botAI->IsMainTank(bot)) - { - if (AI_VALUE2(bool, "has aggro", "current target")) - { - return MoveTo(NAXX_MAP_ID, helper.mainTankPos.first, helper.mainTankPos.second, helper.GENERIC_HEIGHT, false, false, false, - false, MovementPriority::MOVEMENT_COMBAT); - } - return false; - } - if (helper.JustLanded()) - { - uint32 index = botAI->GetGroupSlotIndex(bot); - float start_angle = 0.85 * M_PI; - float offset_angle = M_PI * 0.02 * index; - float angle = start_angle + offset_angle; - float distance; - if (botAI->IsRanged(bot)) - { - distance = 35.0f; - } - else if (botAI->IsHeal(bot)) - { - distance = 30.0f; - } - else - { - distance = 5.0f; - } - float posX = helper.center.first + cos(angle) * distance; - float posY = helper.center.second + sin(angle) * distance; - if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) - { - return true; - } - return MoveInside(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, 2.0f, MovementPriority::MOVEMENT_COMBAT); - } - else - { - std::vector dest; - if (helper.FindPosToAvoidChill(dest)) - { - return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - } - return false; -} - -bool SapphironFlightPositionAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - if (helper.WaitForExplosion()) - { - return MoveToNearestIcebolt(); - } - else - { - std::vector dest; - if (helper.FindPosToAvoidChill(dest)) - { - return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - } - return false; -} - -bool SapphironFlightPositionAction::MoveToNearestIcebolt() -{ - Group* group = bot->GetGroup(); - if (!group) - { - return false; - } - Group::MemberSlotList const& slots = group->GetMemberSlots(); - Player* playerWithIcebolt = nullptr; - float minDistance; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || - botAI->HasAura("icebolt", member, false, false, -1, true)) - { - if (!playerWithIcebolt || minDistance > bot->GetDistance(member)) - { - playerWithIcebolt = member; - minDistance = bot->GetDistance(member); - } - } - } - if (playerWithIcebolt) - { - Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron"); - if (boss) - { - float angle = boss->GetAngle(playerWithIcebolt); - float posX = playerWithIcebolt->GetPositionX() + cos(angle) * 3.0f; - float posY = playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f; - if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) - { - return true; - } - return MoveNear(playerWithIcebolt, 3.0f, MovementPriority::MOVEMENT_COMBAT); - } - } - return false; -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Shared.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Shared.cpp deleted file mode 100644 index 7b399db6ba..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Shared.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "RaidNaxxActions.h" - -uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint() -{ - float minDistance = 0; - int ret = -1; - for (int i = 0; i < intervals; i++) - { - float w_x = waypoints[i].first, w_y = waypoints[i].second; - float dis = bot->GetDistance2d(w_x, w_y); - if (ret == -1 || dis < minDistance) - { - ret = i; - minDistance = dis; - } - } - return ret; -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Thaddius.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Thaddius.cpp deleted file mode 100644 index 65f7b0edd0..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxActions_Thaddius.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "PlayerbotAIConfig.h" -#include "Playerbots.h" -#include "RaidNaxxSpellIds.h" - -bool ThaddiusAttackNearestPetAction::isUseful() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - if (!helper.IsPhasePet()) - { - return false; - } - Unit* target = helper.GetNearestPet(); - if (!bot->IsWithinDistInMap(target, 50.0f)) - { - return false; - } - return true; -} - -bool ThaddiusAttackNearestPetAction::Execute(Event event) -{ - Unit* target = helper.GetNearestPet(); - if (!bot->IsWithinLOSInMap(target)) - { - return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT); - } - if (AI_VALUE(Unit*, "current target") != target) - { - return Attack(target); - } - if (botAI->IsTank(bot) && AI_VALUE2(bool, "has aggro", "current target")) - { - std::pair posForTank = helper.PetPhaseGetPosForTank(); - return MoveTo(533, posForTank.first, posForTank.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - if (botAI->IsRanged(bot)) - { - std::pair posForRanged = helper.PetPhaseGetPosForRanged(); - return MoveTo(533, posForRanged.first, posForRanged.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - return false; -} - -bool ThaddiusMoveToPlatformAction::isUseful() { return true; } - -bool ThaddiusMoveToPlatformAction::Execute(Event event) -{ - std::vector> position = { - // high left - {3462.99f, -2918.90f}, - // high right - {3520.65f, -2976.51f}, - // low left - {3471.36f, -2910.65f}, - // low right - {3528.80f, -2967.04f}, - // center - {3512.19f, -2928.58f}, - }; - float high_z = 312.00f, low_z = 304.02f; - bool is_left = bot->GetDistance2d(position[0].first, position[0].second) < - bot->GetDistance2d(position[1].first, position[1].second); - if (bot->GetPositionZ() >= (high_z - 3.0f)) - { - if (is_left) - { - if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) - { - float distance = bot->GetExactDist2d(position[0].first, position[0].second); - if (distance < sPlayerbotAIConfig->contactDistance) - JumpTo(bot->GetMapId(), position[2].first, position[2].second, low_z, MovementPriority::MOVEMENT_COMBAT); - // bot->TeleportTo(bot->GetMapId(), position[2].first, position[2].second, low_z, bot->GetOrientation()); - } - } - else - { - if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) - { - float distance = bot->GetExactDist2d(position[1].first, position[1].second); - if (distance < sPlayerbotAIConfig->contactDistance) - JumpTo(bot->GetMapId(), position[3].first, position[3].second, low_z, MovementPriority::MOVEMENT_COMBAT); - // bot->TeleportTo(bot->GetMapId(), position[3].first, position[3].second, low_z, bot->GetOrientation()); - } - } - } - else - { - return MoveTo(bot->GetMapId(), position[4].first, position[4].second, low_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - return true; -} - -bool ThaddiusMovePolarityAction::isUseful() -{ - return !botAI->IsMainTank(bot) || AI_VALUE2(bool, "has aggro", "current target"); -} - -bool ThaddiusMovePolarityAction::Execute(Event event) -{ - std::vector> position = { - // left melee - {3508.29f, -2920.12f}, - // left ranged - {3501.72f, -2913.36f}, - // right melee - {3519.74f, -2931.69f}, - // right ranged - {3524.32f, -2936.26f}, - // center melee - {3512.19f, -2928.58f}, - // center ranged - {3504.68f, -2936.68f}, - }; - uint32 idx; - if (NaxxSpellIds::HasAnyAura( - botAI, bot, - {NaxxSpellIds::NegativeCharge10, NaxxSpellIds::NegativeCharge25, NaxxSpellIds::NegativeChargeStack}) || - botAI->HasAura("negative charge", bot, false, false, -1, true)) - { - idx = 0; - } - else if (NaxxSpellIds::HasAnyAura( - botAI, bot, - {NaxxSpellIds::PositiveCharge10, NaxxSpellIds::PositiveCharge25, NaxxSpellIds::PositiveChargeStack}) || - botAI->HasAura("positive charge", bot, false, false, -1, true)) - { - idx = 1; - } - else - { - idx = 2; - } - idx = idx * 2 + botAI->IsRanged(bot); - return MoveTo(bot->GetMapId(), position[idx].first, position[idx].second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -} \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxBossHelper.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxBossHelper.h deleted file mode 100644 index b64ede3217..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxBossHelper.h +++ /dev/null @@ -1,606 +0,0 @@ -#ifndef _PLAYERBOT_RAIDNAXXBOSSHELPER_H -#define _PLAYERBOT_RAIDNAXXBOSSHELPER_H - -#include - -#include "AiObject.h" -#include "AiObjectContext.h" -#include "EventMap.h" -#include "Log.h" -#include "NamedObjectContext.h" -#include "ObjectGuid.h" -#include "Player.h" -#include "PlayerbotAI.h" -#include "Playerbots.h" -#include "ScriptedCreature.h" -#include "SharedDefines.h" -#include "Spell.h" -#include "Timer.h" -#include "RaidNaxxSpellIds.h" - -const uint32 NAXX_MAP_ID = 533; - -template -class GenericBossHelper : public AiObject -{ -public: - GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {} - virtual bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - _unit = nullptr; - } - if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) - { - _unit = nullptr; - } - if (!_unit) - { - _unit = AI_VALUE2(Unit*, "find target", _name); - if (!_unit) - { - return false; - } - _target = _unit->ToCreature(); - if (!_target) - { - return false; - } - _ai = dynamic_cast(_target->GetAI()); - if (!_ai) - { - return false; - } - _event_map = &_ai->events; - if (!_event_map) - { - return false; - } - } - if (!_event_map) - { - return false; - } - _timer = getMSTime(); - return true; - } - virtual void Reset() - { - _unit = nullptr; - _target = nullptr; - _ai = nullptr; - _event_map = nullptr; - _timer = 0; - } - -protected: - std::string _name; - Unit* _unit = nullptr; - Creature* _target = nullptr; - BossAiType* _ai = nullptr; - EventMap* _event_map = nullptr; - uint32 _timer = 0; -}; - -class KelthuzadBossHelper : public AiObject -{ -public: - KelthuzadBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} - const std::pair center = {3716.19f, -5106.58f}; - const std::pair tank_pos = {3709.19f, -5104.86f}; - const std::pair assist_tank_pos = {3746.05f, -5112.74f}; - bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - Reset(); - } - if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) - { - Reset(); - } - if (!_unit) - { - _unit = AI_VALUE2(Unit*, "find target", "kel'thuzad"); - } - return _unit != nullptr; - } - bool IsPhaseOne() { return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } - bool IsPhaseTwo() { return _unit && !_unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } - Unit* GetAnyShadowFissure() - { - Unit* shadow_fissure = nullptr; - GuidVector units = *context->GetValue("nearest triggers"); - for (auto i = units.begin(); i != units.end(); i++) - { - Unit* unit = botAI->GetUnit(*i); - if (!unit) - continue; - if (botAI->EqualLowercaseName(unit->GetName(), "shadow fissure")) - { - shadow_fissure = unit; - } - } - return shadow_fissure; - } - -private: - void Reset() { _unit = nullptr; } - - Unit* _unit = nullptr; -}; - -class RazuviousBossHelper : public AiObject -{ -public: - RazuviousBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} - bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - Reset(); - } - if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) - { - Reset(); - } - if (!_unit) - { - _unit = AI_VALUE2(Unit*, "find target", "instructor razuvious"); - } - return _unit != nullptr; - } - -private: - void Reset() { _unit = nullptr; } - - Unit* _unit = nullptr; -}; - -class SapphironBossHelper : public AiObject -{ -public: - const std::pair mainTankPos = {3512.07f, -5274.06f}; - const std::pair center = {3517.31f, -5253.74f}; - const float GENERIC_HEIGHT = 137.29f; - SapphironBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} - bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - Reset(); - } - if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) - { - Reset(); - } - if (!_unit) - { - _unit = AI_VALUE2(Unit*, "find target", "sapphiron"); - if (!_unit) - { - return false; - } - } - bool now_flying = _unit->IsFlying(); - if (_was_flying && !now_flying) - { - _last_land_ms = getMSTime(); - } - _was_flying = now_flying; - return true; - } - bool IsPhaseGround() { return _unit && !_unit->IsFlying(); } - bool IsPhaseFlight() { return _unit && _unit->IsFlying(); } - bool JustLanded() - { - if (!_last_land_ms) - { - return false; - } - return getMSTime() - _last_land_ms <= POSITION_TIME_AFTER_LANDED; - } - bool WaitForExplosion() - { - if (!IsPhaseFlight()) - { - return false; - } - Group* group = bot->GetGroup(); - if (!group) - { - return false; - } - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (member && - (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || - botAI->HasAura("icebolt", member, false, false, -1, true))) - { - return true; - } - } - return false; - } - bool FindPosToAvoidChill(std::vector& dest) - { - Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25}); - if (!aura) - { - // Fallback to name for custom spell data. - aura = botAI->GetAura("chill", bot); - } - if (!aura) - { - return false; - } - DynamicObject* dyn_obj = aura->GetDynobjOwner(); - if (!dyn_obj) - { - return false; - } - Unit* currentTarget = AI_VALUE(Unit*, "current target"); - float angle = 0; - uint32 index = botAI->GetGroupSlotIndex(bot); - if (currentTarget) - { - if (botAI->IsRanged(bot)) - { - if (bot->GetExactDist2d(currentTarget) <= 45.0f) - { - angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2; - } - else - { - if (index % 2 == 0) - { - angle = bot->GetAngle(currentTarget) + M_PI / 2; - } - else - { - angle = bot->GetAngle(currentTarget) - M_PI / 2; - } - } - } - else - { - if (index % 3 == 0) - { - angle = bot->GetAngle(currentTarget); - } - else if (index % 3 == 1) - { - angle = bot->GetAngle(currentTarget) + M_PI / 2; - } - else - { - angle = bot->GetAngle(currentTarget) - M_PI / 2; - } - } - } - else - { - angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2; - } - dest = {bot->GetPositionX() + cos(angle) * 5.0f, bot->GetPositionY() + sin(angle) * 5.0f, bot->GetPositionZ()}; - return true; - } - -private: - void Reset() - { - _unit = nullptr; - _was_flying = false; - _last_land_ms = 0; - } - - const uint32 POSITION_TIME_AFTER_LANDED = 5000; - Unit* _unit = nullptr; - bool _was_flying = false; - uint32 _last_land_ms = 0; -}; - -class GluthBossHelper : public AiObject -{ -public: - const std::pair mainTankPos25 = {3331.48f, -3109.06f}; - const std::pair mainTankPos10 = {3278.29f, -3162.06f}; - const std::pair beforeDecimatePos = {3267.34f, -3175.68f}; - const std::pair leftSlowDownPos = {3290.68f, -3141.65f}; - const std::pair rightSlowDownPos = {3300.78f, -3151.98f}; - const std::pair rangedPos = {3301.45f, -3139.29f}; - const std::pair healPos = {3303.09f, -3135.24f}; - - const float decimatedZombiePct = 10.0f; - GluthBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} - bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - Reset(); - } - if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) - { - Reset(); - } - if (!_unit) - { - _unit = AI_VALUE2(Unit*, "find target", "gluth"); - if (!_unit) - { - return false; - } - } - if (_unit->IsInCombat()) - { - if (_combat_start_ms == 0) - { - _combat_start_ms = getMSTime(); - } - } - else - { - _combat_start_ms = 0; - } - return true; - } - bool BeforeDecimate() - { - if (!_unit || !_unit->HasUnitState(UNIT_STATE_CASTING)) - { - return false; - } - Spell* spell = _unit->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!spell) - { - spell = _unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL); - } - if (!spell) - { - return false; - } - SpellInfo const* info = spell->GetSpellInfo(); - if (!info) - { - return false; - } - if (NaxxSpellIds::MatchesAnySpellId( - info, {NaxxSpellIds::Decimate10, NaxxSpellIds::Decimate25, NaxxSpellIds::Decimate25Alt})) - { - return true; - } - // Fallback to name for custom spell data. - return info->SpellName[LOCALE_enUS] && botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "decimate"); - } - bool JustStartCombat() const { return _combat_start_ms != 0 && getMSTime() - _combat_start_ms < 10000; } - bool IsZombieChow(Unit* unit) const { return unit && botAI->EqualLowercaseName(unit->GetName(), "zombie chow"); } - -private: - void Reset() - { - _unit = nullptr; - _combat_start_ms = 0; - } - - Unit* _unit = nullptr; - uint32 _combat_start_ms = 0; -}; - -class LoathebBossHelper : public AiObject -{ -public: - const std::pair mainTankPos = {2877.57f, -3967.00f}; - const std::pair rangePos = {2896.96f, -3980.61f}; - LoathebBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} - bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - Reset(); - } - if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) - { - Reset(); - } - if (!_unit) - { - _unit = AI_VALUE2(Unit*, "find target", "loatheb"); - } - return _unit != nullptr; - } - -private: - void Reset() { _unit = nullptr; } - - Unit* _unit = nullptr; -}; - -class FourhorsemanBossHelper : public AiObject -{ -public: - const float posZ = 241.27f; - const std::pair attractPos[2] = {{2502.03f, -2910.90f}, - {2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux) - FourhorsemanBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} - bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - Reset(); - } - else if (_combat_start_ms == 0) - { - _combat_start_ms = getMSTime(); - } - if (_sir && (!_sir->IsInWorld() || !_sir->IsAlive())) - { - Reset(); - } - if (!_sir) - { - _sir = AI_VALUE2(Unit*, "find target", "sir zeliek"); - if (!_sir) - { - return false; - } - } - _lady = AI_VALUE2(Unit*, "find target", "lady blaumeux"); - return true; - } - void Reset() - { - _sir = nullptr; - _lady = nullptr; - _combat_start_ms = 0; - posToGo = 0; - } - bool IsAttracter(Player* bot) - { - Difficulty diff = bot->GetRaidDifficulty(); - if (diff == RAID_DIFFICULTY_25MAN_NORMAL) - { - return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0) || - botAI->IsHealAssistantOfIndex(bot, 1) || botAI->IsHealAssistantOfIndex(bot, 2); - } - return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0); - } - void CalculatePosToGo(Player* bot) - { - bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; - Unit* lady = _lady; - if (!lady) - { - posToGo = 0; - } - else - { - uint32 elapsed_ms = _combat_start_ms ? getMSTime() - _combat_start_ms : 0; - // Interval: 24s - 15s - 15s - ... - posToGo = !(elapsed_ms <= 9000 || ((elapsed_ms - 9000) / 67500) % 2 == 0); - if (botAI->IsRangedDpsAssistantOfIndex(bot, 0) || (raid25 && botAI->IsHealAssistantOfIndex(bot, 1))) - { - posToGo = 1 - posToGo; - } - } - } - std::pair CurrentAttractPos() - { - bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; - float posX = attractPos[posToGo].first, posY = attractPos[posToGo].second; - if (posToGo == 1) - { - float offset_x = 0.0f; - float offset_y = 0.0f; - float bias = 4.5f; - if (raid25) - { - offset_x = -bias; - offset_y = bias; - } - posX += offset_x; - posY += offset_y; - } - return {posX, posY}; - } - Unit* CurrentAttackTarget() - { - if (posToGo == 0) - { - return _sir; - } - return _lady; - } - -protected: - Unit* _sir = nullptr; - Unit* _lady = nullptr; - uint32 _combat_start_ms = 0; - int posToGo = 0; -}; -class ThaddiusBossHelper : public AiObject -{ -public: - const std::pair tankPosFeugen = {3522.94f, -3002.60f}; - const std::pair tankPosStalagg = {3436.14f, -2919.98f}; - const std::pair rangedPosFeugen = {3500.45f, -2997.92f}; - const std::pair rangedPosStalagg = {3441.01f, -2942.04f}; - const float tankPosZ = 312.61f; - ThaddiusBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} - bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - Reset(); - } - if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) - { - Reset(); - } - if (!_unit) - { - _unit = AI_VALUE2(Unit*, "find target", "thaddius"); - if (!_unit) - { - return false; - } - } - feugen = AI_VALUE2(Unit*, "find target", "feugen"); - stalagg = AI_VALUE2(Unit*, "find target", "stalagg"); - return true; - } - bool IsPhasePet() { return (feugen && feugen->IsAlive()) || (stalagg && stalagg->IsAlive()); } - bool IsPhaseTransition() - { - if (IsPhasePet()) - { - return false; - } - return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - } - bool IsPhaseThaddius() { return !IsPhasePet() && !IsPhaseTransition(); } - Unit* GetNearestPet() - { - Unit* unit = nullptr; - if (feugen && feugen->IsAlive()) - { - unit = feugen; - } - if (stalagg && stalagg->IsAlive() && (!feugen || bot->GetDistance(stalagg) < bot->GetDistance(feugen))) - { - unit = stalagg; - } - return unit; - } - std::pair PetPhaseGetPosForTank() - { - if (GetNearestPet() == feugen) - { - return tankPosFeugen; - } - return tankPosStalagg; - } - std::pair PetPhaseGetPosForRanged() - { - if (GetNearestPet() == feugen) - { - return rangedPosFeugen; - } - return rangedPosStalagg; - } - -protected: - void Reset() - { - _unit = nullptr; - feugen = nullptr; - stalagg = nullptr; - } - - Unit* _unit = nullptr; - Unit* feugen = nullptr; - Unit* stalagg = nullptr; -}; - -#endif diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.cpp deleted file mode 100644 index ef5913b5bc..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.cpp +++ /dev/null @@ -1,344 +0,0 @@ -#include "RaidNaxxMultipliers.h" - -#include "ChooseTargetActions.h" -#include "DKActions.h" -#include "DruidActions.h" -#include "DruidBearActions.h" -#include "FollowActions.h" -#include "GenericActions.h" -#include "GenericSpellActions.h" -#include "HunterActions.h" -#include "MageActions.h" -#include "MovementActions.h" -#include "PaladinActions.h" -#include "PriestActions.h" -#include "RaidNaxxActions.h" -#include "RaidNaxxSpellIds.h" -#include "ReachTargetActions.h" -#include "RogueActions.h" -#include "ScriptedCreature.h" -#include "ShamanActions.h" -#include "Spell.h" -#include "UseMeetingStoneAction.h" -#include "WarriorActions.h" - -float GrobbulusMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); - if (!boss) - { - return 1.0f; - } - if (dynamic_cast(action)) - { - return botAI->IsMainTank(bot) ? 0.0f : 1.0f; - } - if (dynamic_cast(action)) - { - return 0.0f; - } - return 1.0f; -} - -float HeiganDanceMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean"); - if (!boss) - { - return 1.0f; - } - bool platform_phase = boss->IsWithinDist2d(2794.26f, -3706.67f, 10.0f); - bool eruption_casting = false; - if (boss->HasUnitState(UNIT_STATE_CASTING)) - { - Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!spell) - { - spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); - } - if (spell) - { - SpellInfo const* info = spell->GetSpellInfo(); - bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10}); - if (!isEruption && info && info->SpellName[LOCALE_enUS]) - { - // Fallback to name for custom spell data. - isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption"); - } - if (isEruption) - { - eruption_casting = true; - } - } - } - if (dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action) ) - { - return 0.0f; - } - if (!platform_phase && !eruption_casting) - { - return 1.0f; - } - if (dynamic_cast(action) || dynamic_cast(action)) - { - return 1.0f; - } - if (dynamic_cast(action) && !dynamic_cast(action)) - { - CastSpellAction* spellAction = dynamic_cast(action); - uint32 spellId = AI_VALUE2(uint32, "spell id", spellAction->getSpell()); - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!spellInfo) - { - return 0.0f; - } - uint32 castTime = spellInfo->CalcCastTime(); - if (castTime == 0 && !spellInfo->IsChanneled()) - { - return 1.0f; - } - } - return 0.0f; -} - -float LoathebGenericMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "loatheb"); - if (!boss) - { - return 1.0f; - } - context->GetValue("neglect threat")->Set(true); - if (botAI->GetState() == BOT_STATE_COMBAT && - (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action))) - { - return 0.0f; - } - if (!dynamic_cast(action)) - { - return 1.0f; - } - Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::NecroticAura10}); - if (!aura) - { - // Fallback to name for custom spell data. - aura = botAI->GetAura("necrotic aura", bot); - } - if (!aura || aura->GetDuration() <= 1500) - { - return 1.0f; - } - return 0.0f; -} - -float ThaddiusGenericMultiplier::GetValue(Action* action) -{ - if (!helper.UpdateBossAI()) - { - return 1.0f; - } - if (dynamic_cast(action)) - return 0.0f; - // pet phase - if (helper.IsPhasePet() && - (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action))) - { - return 0.0f; - } - // die at the same time - Unit* target = AI_VALUE(Unit*, "current target"); - Unit* feugen = AI_VALUE2(Unit*, "find target", "feugen"); - Unit* stalagg = AI_VALUE2(Unit*, "find target", "stalagg"); - if (helper.IsPhasePet() && target && feugen && stalagg && target->GetHealthPct() <= 40 && - (feugen->GetHealthPct() >= target->GetHealthPct() + 3 || stalagg->GetHealthPct() >= target->GetHealthPct() + 3)) - { - if (dynamic_cast(action) && !dynamic_cast(action)) - { - return 0.0f; - } - } - // magnetic pull - // uint32 curr_timer = eventMap->GetTimer(); - // // if (curr_phase == 2 && bot->GetPositionZ() > 312.5f && dynamic_cast(action)) - // { - // if (curr_phase == 2 && (curr_timer % 20000 >= 18000 || curr_timer % 20000 <= 2000) && - // dynamic_cast(action)) - // { - // // MotionMaster *mm = bot->GetMotionMaster(); - // // mm->Clear(); - // return 0.0f; - // } - // thaddius phase - // if (curr_phase == 8 && dynamic_cast(action)) - // { - // return 0.0f; - // } - return 1.0f; -} - -float SapphironGenericMultiplier::GetValue(Action* action) -{ - if (!helper.UpdateBossAI()) - { - return 1.0f; - } - if (dynamic_cast(action) || dynamic_cast(action)) - { - return 0.0f; - } - return 1.0f; -} - -float InstructorRazuviousGenericMultiplier::GetValue(Action* action) -{ - if (!helper.UpdateBossAI()) - { - return 1.0f; - } - context->GetValue("neglect threat")->Set(true); - if (botAI->GetState() == BOT_STATE_COMBAT && - (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action))) - { - return 0.0f; - } - return 1.0f; -} - -float KelthuzadGenericMultiplier::GetValue(Action* action) -{ - if (!helper.UpdateBossAI()) - { - return 1.0f; - } - if ((dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action))) - { - return 0.0f; - } - if (helper.IsPhaseOne()) - { - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action)) - { - return 0.0f; - } - } - if (helper.IsPhaseTwo()) - { - if (dynamic_cast(action) || dynamic_cast(action)) - { - return 0.0f; - } - } - return 1.0f; -} - -float AnubrekhanGenericMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); - if (!boss) - { - return 1.0f; - } - if (NaxxSpellIds::HasAnyAura( - botAI, boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) || - botAI->HasAura("locust swarm", boss)) - { - if (dynamic_cast(action)) - { - return 0.0f; - } - } - return 1.0f; -} - -float FourhorsemanGenericMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek"); - if (!boss) - { - return 1.0f; - } - context->GetValue("neglect threat")->Set(true); - if ((dynamic_cast(action) || dynamic_cast(action))) - { - return 0.0f; - } - return 1.0f; -} - -// float GothikGenericMultiplier::GetValue(Action* action) -// { -// Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); -// if (!boss) -// { -// return 1.0f; -// } -// BossAI* boss_ai = dynamic_cast(boss->GetAI()); -// EventMap* eventMap = boss_botAI->GetEvents(); -// uint32 curr_phase = eventMap->GetPhaseMask(); -// if (curr_phase == 1 && (dynamic_cast(action))) -// { -// return 0.0f; -// } -// if (curr_phase == 1 && (dynamic_cast(action))) -// { -// Unit* target = action->GetTarget(); -// if (target == boss) -// { -// return 0.0f; -// } -// } -// return 1.0f; -// } - -float GluthGenericMultiplier::GetValue(Action* action) -{ - if (!helper.UpdateBossAI()) - { - return 1.0f; - } - if ((dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action))) - { - return 0.0f; - } - - if (botAI->IsMainTank(bot)) - { - Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25}); - if (!aura) - { - // Fallback to name for custom spell data. - aura = botAI->GetAura("mortal wound", bot, false, true); - } - if (aura && aura->GetStackAmount() >= 5) - { - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - { - return 0.0f; - } - } - } - if (dynamic_cast(action)) - { - Unit* target = AI_VALUE(Unit*, "current target"); - if (helper.IsZombieChow(target)) - { - return 0.0f; - } - } - return 1.0f; -} diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h deleted file mode 100644 index ecc335c910..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxMultipliers.h +++ /dev/null @@ -1,116 +0,0 @@ - -#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_ -#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_ - -#include "Multiplier.h" -#include "RaidNaxxBossHelper.h" - -class GrobbulusMultiplier : public Multiplier -{ -public: - GrobbulusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grobbulus") {} - -public: - virtual float GetValue(Action* action); -}; -class HeiganDanceMultiplier : public Multiplier -{ -public: - HeiganDanceMultiplier(PlayerbotAI* ai) : Multiplier(ai, "helgan dance") {} - -public: - virtual float GetValue(Action* action); -}; - -class LoathebGenericMultiplier : public Multiplier -{ -public: - LoathebGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loatheb generic") {} - -public: - virtual float GetValue(Action* action); -}; - -class ThaddiusGenericMultiplier : public Multiplier -{ -public: - ThaddiusGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "thaddius generic"), helper(ai) {} - -public: - virtual float GetValue(Action* action); - -private: - ThaddiusBossHelper helper; -}; - -class SapphironGenericMultiplier : public Multiplier -{ -public: - SapphironGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sapphiron generic"), helper(ai) {} - - virtual float GetValue(Action* action); - -private: - SapphironBossHelper helper; -}; - -class InstructorRazuviousGenericMultiplier : public Multiplier -{ -public: - InstructorRazuviousGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "instructor razuvious generic"), helper(ai) - { - } - virtual float GetValue(Action* action); - -private: - RazuviousBossHelper helper; -}; - -class KelthuzadGenericMultiplier : public Multiplier -{ -public: - KelthuzadGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "kelthuzad generic"), helper(ai) {} - virtual float GetValue(Action* action); - -private: - KelthuzadBossHelper helper; -}; - -class AnubrekhanGenericMultiplier : public Multiplier -{ -public: - AnubrekhanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anubrekhan generic") {} - -public: - virtual float GetValue(Action* action); -}; - -class FourhorsemanGenericMultiplier : public Multiplier -{ -public: - FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "fourhorseman generic") {} - -public: - virtual float GetValue(Action* action); -}; - -// class GothikGenericMultiplier : public Multiplier -// { -// public: -// GothikGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gothik generic") {} - -// public: -// virtual float GetValue(Action* action); -// }; - -class GluthGenericMultiplier : public Multiplier -{ -public: - GluthGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gluth generic"), helper(ai) {} - float GetValue(Action* action) override; - -private: - GluthBossHelper helper; -}; - -#endif diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h deleted file mode 100644 index 858911c6b3..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxSpellIds.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef _PLAYERBOT_RAIDNAXXSPELLIDS_H -#define _PLAYERBOT_RAIDNAXXSPELLIDS_H - -#include - -#include "PlayerbotAI.h" - -// use src/server/scripts/Northrend/Naxxramas/naxxramas.h for CreatureId, NaxxramasSay, NaxxramasEvent, NaxxramasMisc -namespace NaxxSpellIds -{ - // Heigan - static constexpr uint32 Eruption10 = 29371; -/* - SPELL_SPELL_DISRUPTION = 29310, - SPELL_DECREPIT_FEVER = 29998, - SPELL_PLAGUE_CLOUD = 29350, - SPELL_TELEPORT_SELF = 30211 -*/ - - // Grobbulus - static constexpr uint32 PoisonCloud = 28240; - - // Thaddius polarity - static constexpr uint32 PositiveCharge10 = 28059; - static constexpr uint32 PositiveCharge25 = 28062; - static constexpr uint32 PositiveChargeStack = 29659; - static constexpr uint32 NegativeCharge10 = 28084; - static constexpr uint32 NegativeCharge25 = 28085; - static constexpr uint32 NegativeChargeStack = 29660; -/* - SPELL_MAGNETIC_PULL = 28337, - SPELL_TESLA_SHOCK = 28099, - SPELL_SHOCK_VISUAL = 28159, - - // Stalagg - SPELL_POWER_SURGE = 54529, - SPELL_STALAGG_CHAIN = 28096, - - // Feugen - SPELL_STATIC_FIELD = 28135, - SPELL_FEUGEN_CHAIN = 28111, - - // Thaddius - SPELL_POLARITY_SHIFT = 28089, - SPELL_BALL_LIGHTNING = 28299, - SPELL_CHAIN_LIGHTNING = 28167, - SPELL_BERSERK = 27680, - SPELL_THADDIUS_VISUAL_LIGHTNING = 28136, - SPELL_THADDIUS_SPAWN_STUN = 28160, - - SPELL_POSITIVE_CHARGE = 28062, - SPELL_POSITIVE_CHARGE_STACK = 29659, - SPELL_NEGATIVE_CHARGE = 28085, - SPELL_NEGATIVE_CHARGE_STACK = 29660, - SPELL_POSITIVE_POLARITY = 28059, - SPELL_NEGATIVE_POLARITY = 28084 -*/ - // Sapphiron - static constexpr uint32 Icebolt10 = 28522; - static constexpr uint32 Icebolt25 = 28526; - static constexpr uint32 Chill25 = 55699; -/* - // Fight - SPELL_FROST_AURA = 28531, - SPELL_CLEAVE = 19983, - SPELL_TAIL_SWEEP = 55697, - SPELL_SUMMON_BLIZZARD = 28560, - SPELL_LIFE_DRAIN = 28542, - SPELL_BERSERK = 26662, - - // Ice block - SPELL_ICEBOLT_CAST = 28526, - SPELL_ICEBOLT_TRIGGER = 28522, - SPELL_FROST_MISSILE = 30101, - SPELL_FROST_EXPLOSION = 28524, - - // Visuals - SPELL_SAPPHIRON_DIES = 29357 -*/ - // Gluth - static constexpr uint32 Decimate10 = 28374; - static constexpr uint32 Decimate25 = 54426; - static constexpr uint32 Decimate25Alt = 28375; - static constexpr uint32 MortalWound10 = 25646; - static constexpr uint32 MortalWound25 = 54378; -/* - SPELL_MORTAL_WOUND = 25646, - SPELL_ENRAGE = 28371, - SPELL_DECIMATE = 28374, - SPELL_DECIMATE_DAMAGE = 28375, - SPELL_BERSERK = 26662, - SPELL_INFECTED_WOUND = 29306, - SPELL_CHOW_SEARCHER = 28404 -*/ - // Anub'Rekhan - static constexpr uint32 LocustSwarm10 = 28785; - static constexpr uint32 LocustSwarm10Alt = 28786; - static constexpr uint32 LocustSwarm25 = 54021; // 25-man Locust Swarm -/* - SPELL_IMPALE = 28783, - SPELL_LOCUST_SWARM = 28785, - SPELL_SUMMON_CORPSE_SCARABS_5 = 29105, - SPELL_SUMMON_CORPSE_SCARABS_10 = 28864, - SPELL_BERSERK = 26662 - ACHIEV_TIMED_START_EVENT = 9891, - EVENT_SPAWN_CRYPT_GUARDS_1 = 0, - EVENT_BERSERK = 1, - //// - Position const cryptguardPositions[] = { - { 3299.732f, -3502.489f, 287.077f, 2.378f }, - { 3299.086f, -3450.929f, 287.077f, 3.999f }, - { 3331.217f, -3476.607f, 287.074f, 3.269f } -}; - -*/ - // Loatheb - static constexpr uint32 NecroticAura10 = 55593; -/* - SPELL_NECROTIC_AURA = 55593, - SPELL_SUMMON_SPORE = 29234, - SPELL_DEATHBLOOM = 29865, - SPELL_INEVITABLE_DOOM = 29204, - SPELL_BERSERK = 26662 -*/ - inline bool HasAnyAura(PlayerbotAI* botAI, Unit* unit, std::initializer_list spellIds) - { - if (!botAI || !unit) - { - return false; - } - - for (uint32 spellId : spellIds) - { - if (botAI->HasAura(spellId, unit)) - { - return true; - } - } - return false; - } - - inline Aura* GetAnyAura(Unit* unit, std::initializer_list spellIds) - { - if (!unit) - { - return nullptr; - } - - for (uint32 spellId : spellIds) - { - if (Aura* aura = unit->GetAura(spellId)) - { - return aura; - } - } - return nullptr; - } - - inline bool MatchesAnySpellId(SpellInfo const* info, std::initializer_list spellIds) - { - if (!info) - { - return false; - } - - for (uint32 spellId : spellIds) - { - if (info->Id == spellId) - { - return true; - } - } - return false; - } -} // namespace NaxxSpellIds - -#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.cpp deleted file mode 100644 index f2ebd24a3e..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "RaidNaxxStrategy.h" - -#include "RaidNaxxMultipliers.h" - -void RaidNaxxStrategy::InitTriggers(std::vector& triggers) -{ - // Grobbulus - triggers.push_back(new TriggerNode("mutating injection melee", - { NextAction("grobbulus move away", ACTION_RAID + 2) } - )); - - triggers.push_back(new TriggerNode("mutating injection ranged", - { NextAction("grobbulus go behind the boss", ACTION_RAID + 2) } - )); - - triggers.push_back(new TriggerNode("mutating injection removed", - { NextAction("grobbulus move center", ACTION_RAID + 1) } - )); - - triggers.push_back(new TriggerNode("grobbulus cloud", - { NextAction("rotate grobbulus", ACTION_RAID + 1) } - )); - - // Heigan the Unclean - triggers.push_back(new TriggerNode("heigan melee", - { NextAction("heigan dance melee", ACTION_RAID + 1) } - )); - - triggers.push_back(new TriggerNode("heigan ranged", - { NextAction("heigan dance ranged", ACTION_RAID + 1) } - )); - - // Kel'Thuzad - triggers.push_back( - new TriggerNode("kel'thuzad", - { - NextAction("kel'thuzad position", ACTION_RAID + 2), - NextAction("kel'thuzad choose target", ACTION_RAID + 1) - }) - ); - - // Anub'Rekhan - triggers.push_back(new TriggerNode("anub'rekhan", - { NextAction("anub'rekhan position", ACTION_RAID + 1) } - )); - - // Grand Widow Faerlina - triggers.push_back(new TriggerNode("faerlina", - { NextAction("avoid aoe", ACTION_RAID + 1) } - )); - - // Maexxna - triggers.push_back( - new TriggerNode("maexxna", - { - NextAction("rear flank", ACTION_RAID + 1), - NextAction("avoid aoe", ACTION_RAID + 1) - }) - ); - - // Patchwerk - triggers.push_back(new TriggerNode("patchwerk tank", - { NextAction("tank face", ACTION_RAID + 2) } - )); - - triggers.push_back(new TriggerNode("patchwerk ranged", - { NextAction("patchwerk ranged position", ACTION_RAID + 2) } - )); - - triggers.push_back(new TriggerNode("patchwerk non-tank", - { NextAction("rear flank", ACTION_RAID + 1) } - )); - - // Thaddius - triggers.push_back(new TriggerNode("thaddius phase pet", - { NextAction("thaddius attack nearest pet", ACTION_RAID + 1) } - )); - - triggers.push_back(new TriggerNode("thaddius phase pet lose aggro", - { NextAction("taunt spell", ACTION_RAID + 2) } - )); - - triggers.push_back(new TriggerNode("thaddius phase transition", - { NextAction("thaddius move to platform", ACTION_RAID + 1) } - )); - - triggers.push_back(new TriggerNode("thaddius phase thaddius", - { NextAction("thaddius move polarity", ACTION_RAID + 1) } - )); - - // Instructor Razuvious - triggers.push_back(new TriggerNode("razuvious tank", - { NextAction("razuvious use obedience crystal", ACTION_RAID + 1) } - )); - - triggers.push_back(new TriggerNode("razuvious nontank", - { NextAction("razuvious target", ACTION_RAID + 1) } - )); - - // four horseman - triggers.push_back(new TriggerNode("horseman attractors", - { NextAction("horseman attract alternatively", ACTION_RAID + 1) } - )); - - triggers.push_back(new TriggerNode("horseman except attractors", - { NextAction("horseman attack in order", ACTION_RAID + 1) } - )); - - // sapphiron - triggers.push_back(new TriggerNode("sapphiron ground", - { NextAction("sapphiron ground position", ACTION_RAID + 1) } - )); - - triggers.push_back(new TriggerNode("sapphiron flight", - { NextAction("sapphiron flight position", ACTION_RAID + 1) } - )); - - // Gluth - triggers.push_back( - new TriggerNode("gluth", - { - NextAction("gluth choose target", ACTION_RAID + 1), - NextAction("gluth position", ACTION_RAID + 1), - NextAction("gluth slowdown", ACTION_RAID) - }) - ); - - triggers.push_back(new TriggerNode("gluth main tank mortal wound", - { NextAction("taunt spell", ACTION_RAID + 1) } - )); - - // Loatheb - triggers.push_back( - new TriggerNode("loatheb", - { - NextAction("loatheb position", ACTION_RAID + 1), - NextAction("loatheb choose target", ACTION_RAID + 1) - }) - ); - -} - -void RaidNaxxStrategy::InitMultipliers(std::vector& multipliers) -{ - multipliers.push_back(new GrobbulusMultiplier(botAI)); - multipliers.push_back(new HeiganDanceMultiplier(botAI)); - multipliers.push_back(new LoathebGenericMultiplier(botAI)); - multipliers.push_back(new ThaddiusGenericMultiplier(botAI)); - multipliers.push_back(new SapphironGenericMultiplier(botAI)); - multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI)); - multipliers.push_back(new KelthuzadGenericMultiplier(botAI)); - multipliers.push_back(new AnubrekhanGenericMultiplier(botAI)); - multipliers.push_back(new FourhorsemanGenericMultiplier(botAI)); - // multipliers.push_back(new GothikGenericMultiplier(botAI)); - multipliers.push_back(new GluthGenericMultiplier(botAI)); -} diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.h deleted file mode 100644 index dee1720bd8..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxStrategy.h +++ /dev/null @@ -1,18 +0,0 @@ - -#ifndef _PLAYERBOT_RAIDNAXXSTRATEGY_H -#define _PLAYERBOT_RAIDNAXXSTRATEGY_H - -#include "AiObjectContext.h" -#include "Multiplier.h" -#include "Strategy.h" - -class RaidNaxxStrategy : public Strategy -{ -public: - RaidNaxxStrategy(PlayerbotAI* ai) : Strategy(ai) {} - virtual std::string const getName() override { return "naxx"; } - virtual void InitTriggers(std::vector& triggers) override; - virtual void InitMultipliers(std::vector& multipliers) override; -}; - -#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggerContext.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggerContext.h deleted file mode 100644 index c8ae79c18a..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggerContext.h +++ /dev/null @@ -1,86 +0,0 @@ -// /* -// * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it -// and/or modify it under version 3 of the License, or (at your option), any later version. -// */ - -#ifndef _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H -#define _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H - -#include "AiObjectContext.h" -#include "NamedObjectContext.h" -#include "RaidNaxxTriggers.h" - -class RaidNaxxTriggerContext : public NamedObjectContext -{ -public: - RaidNaxxTriggerContext() - { - creators["mutating injection melee"] = &RaidNaxxTriggerContext::mutating_injection_melee; - creators["mutating injection ranged"] = &RaidNaxxTriggerContext::mutating_injection_ranged; - creators["mutating injection removed"] = &RaidNaxxTriggerContext::mutating_injection_removed; - creators["grobbulus cloud"] = &RaidNaxxTriggerContext::grobbulus_cloud; - creators["heigan melee"] = &RaidNaxxTriggerContext::heigan_melee; - creators["heigan ranged"] = &RaidNaxxTriggerContext::heigan_ranged; - - creators["thaddius phase pet"] = &RaidNaxxTriggerContext::thaddius_phase_pet; - creators["thaddius phase pet lose aggro"] = &RaidNaxxTriggerContext::thaddius_phase_pet_lose_aggro; - creators["thaddius phase transition"] = &RaidNaxxTriggerContext::thaddius_phase_transition; - creators["thaddius phase thaddius"] = &RaidNaxxTriggerContext::thaddius_phase_thaddius; - - creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank; - creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank; - - creators["horseman attractors"] = &RaidNaxxTriggerContext::horseman_attractors; - creators["horseman except attractors"] = &RaidNaxxTriggerContext::horseman_except_attractors; - - creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground; - creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight; - - creators["kel'thuzad"] = &RaidNaxxTriggerContext::kelthuzad; - - creators["anub'rekhan"] = &RaidNaxxTriggerContext::anubrekhan; - creators["faerlina"] = &RaidNaxxTriggerContext::faerlina; - creators["maexxna"] = &RaidNaxxTriggerContext::maexxna; - creators["patchwerk tank"] = &RaidNaxxTriggerContext::patchwerk_tank; - creators["patchwerk non-tank"] = &RaidNaxxTriggerContext::patchwerk_non_tank; - creators["patchwerk ranged"] = &RaidNaxxTriggerContext::patchwerk_ranged; - - creators["gluth"] = &RaidNaxxTriggerContext::gluth; - creators["gluth main tank mortal wound"] = &RaidNaxxTriggerContext::gluth_main_tank_mortal_wound; - - creators["loatheb"] = &RaidNaxxTriggerContext::loatheb; - } - -private: - static Trigger* mutating_injection_melee(PlayerbotAI* ai) { return new MutatingInjectionMeleeTrigger(ai); } - static Trigger* mutating_injection_ranged(PlayerbotAI* ai) { return new MutatingInjectionRangedTrigger(ai); } - static Trigger* mutating_injection_removed(PlayerbotAI* ai) { return new MutatingInjectionRemovedTrigger(ai); } - static Trigger* grobbulus_cloud(PlayerbotAI* ai) { return new GrobbulusCloudTrigger(ai); } - static Trigger* heigan_melee(PlayerbotAI* ai) { return new HeiganMeleeTrigger(ai); } - static Trigger* heigan_ranged(PlayerbotAI* ai) { return new HeiganRangedTrigger(ai); } - - static Trigger* thaddius_phase_pet(PlayerbotAI* ai) { return new ThaddiusPhasePetTrigger(ai); } - static Trigger* thaddius_phase_pet_lose_aggro(PlayerbotAI* ai) { return new ThaddiusPhasePetLoseAggroTrigger(ai); } - static Trigger* thaddius_phase_transition(PlayerbotAI* ai) { return new ThaddiusPhaseTransitionTrigger(ai); } - static Trigger* thaddius_phase_thaddius(PlayerbotAI* ai) { return new ThaddiusPhaseThaddiusTrigger(ai); } - static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); } - static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); } - - static Trigger* horseman_attractors(PlayerbotAI* ai) { return new HorsemanAttractorsTrigger(ai); } - static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new HorsemanExceptAttractorsTrigger(ai); } - - static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); } - static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(ai); } - static Trigger* kelthuzad(PlayerbotAI* ai) { return new KelthuzadTrigger(ai); } - static Trigger* anubrekhan(PlayerbotAI* ai) { return new AnubrekhanTrigger(ai); } - static Trigger* faerlina(PlayerbotAI* ai) { return new FaerlinaTrigger(ai); } - static Trigger* maexxna(PlayerbotAI* ai) { return new MaexxnaTrigger(ai); } - static Trigger* patchwerk_tank(PlayerbotAI* ai) { return new PatchwerkTankTrigger(ai); } - static Trigger* patchwerk_non_tank(PlayerbotAI* ai) { return new PatchwerkNonTankTrigger(ai); } - static Trigger* patchwerk_ranged(PlayerbotAI* ai) { return new PatchwerkRangedTrigger(ai); } - static Trigger* gluth(PlayerbotAI* ai) { return new GluthTrigger(ai); } - static Trigger* gluth_main_tank_mortal_wound(PlayerbotAI* ai) { return new GluthMainTankMortalWoundTrigger(ai); } - static Trigger* loatheb(PlayerbotAI* ai) { return new LoathebTrigger(ai); } -}; - -#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.cpp b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.cpp deleted file mode 100644 index a38f800f7c..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "RaidNaxxTriggers.h" - -#include "Playerbots.h" -#include "RaidNaxxSpellIds.h" -#include "Timer.h" -#include "Trigger.h" - -bool MutatingInjectionMeleeTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); - if (!boss) - { - return false; - } - return MutatingInjectionTrigger::IsActive() && !botAI->IsRanged(bot); -} - -bool MutatingInjectionRangedTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); - if (!boss) - { - return false; - } - return MutatingInjectionTrigger::IsActive() && botAI->IsRanged(bot); -} - -bool AuraRemovedTrigger::IsActive() -{ - bool check = botAI->HasAura(name, bot, false, false, -1, true); - bool ret = false; - if (prev_check && !check) - { - ret = true; - } - prev_check = check; - return ret; -} - -bool MutatingInjectionRemovedTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); - if (!boss) - { - return false; - } - return HasNoAuraTrigger::IsActive() && botAI->GetState() == BOT_STATE_COMBAT && botAI->IsRanged(bot); -} - -bool GrobbulusCloudTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); - if (!boss) - { - return false; - } - if (!botAI->IsMainTank(bot)) - { - return false; - } - // bot->Yell("has aggro on " + boss->GetName() + " : " + to_string(AI_VALUE2(bool, "has aggro", "boss target")), - // LANG_UNIVERSAL); - if (!AI_VALUE2(bool, "has aggro", "boss target")) - { - return false; - } - uint32 now = getMSTime(); - bool poison_cloud_casting = false; - if (boss->HasUnitState(UNIT_STATE_CASTING)) - { - Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!spell) - { - spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); - } - if (spell) - { - poison_cloud_casting = NaxxSpellIds::MatchesAnySpellId(spell->GetSpellInfo(), {NaxxSpellIds::PoisonCloud}); - } - } - if (!poison_cloud_casting && last_cloud_ms != 0 && now - last_cloud_ms < CloudRotationDelayMs) - { - return false; - } - last_cloud_ms = now; - return true; -} - -bool HeiganMeleeTrigger::IsActive() -{ - Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean"); - if (!heigan) - { - return false; - } - return !botAI->IsRanged(bot); -} - -bool HeiganRangedTrigger::IsActive() -{ - Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean"); - if (!heigan) - { - return false; - } - return botAI->IsRanged(bot); -} - -bool RazuviousTankTrigger::IsActive() -{ - Difficulty diff = bot->GetRaidDifficulty(); - if (diff == RAID_DIFFICULTY_10MAN_NORMAL) - { - return helper.UpdateBossAI() && botAI->IsTank(bot); - } - return helper.UpdateBossAI() && bot->getClass() == CLASS_PRIEST; -} - -bool RazuviousNontankTrigger::IsActive() -{ - Difficulty diff = bot->GetRaidDifficulty(); - if (diff == RAID_DIFFICULTY_10MAN_NORMAL) - { - return helper.UpdateBossAI() && !(botAI->IsTank(bot)); - } - return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST); -} - -bool HorsemanAttractorsTrigger::IsActive() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - return helper.IsAttracter(bot); -} - -bool HorsemanExceptAttractorsTrigger::IsActive() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - return !helper.IsAttracter(bot); -} - -bool SapphironGroundTrigger::IsActive() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - return helper.IsPhaseGround(); -} - -bool SapphironFlightTrigger::IsActive() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - return helper.IsPhaseFlight(); -} - -bool GluthTrigger::IsActive() { return helper.UpdateBossAI(); } - -bool GluthMainTankMortalWoundTrigger::IsActive() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - if (!botAI->IsAssistTankOfIndex(bot, 0)) - { - return false; - } - Unit* mt = AI_VALUE(Unit*, "main tank"); - if (!mt) - { - return false; - } - Aura* aura = NaxxSpellIds::GetAnyAura(mt, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25}); - if (!aura) - { - // Fallback to name for custom spell data. - aura = botAI->GetAura("mortal wound", mt, false, true); - } - if (!aura || aura->GetStackAmount() < 5) - { - return false; - } - return true; -} - -bool KelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); } - -bool AnubrekhanTrigger::IsActive() { - Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); - if (!boss) - { - return false; - } - return true; -} - -bool FaerlinaTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "grand widow faerlina"); - if (!boss) - { - return false; - } - return true; -} - -bool MaexxnaTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna"); - if (!boss) - { - return false; - } - return !botAI->IsTank(bot); -} - -bool PatchwerkTankTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); - if (!boss) - { - return false; - } - return !botAI->IsTank(bot) && !botAI->IsRanged(bot); -} - -bool PatchwerkRangedTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); - if (!boss) - { - return false; - } - return !botAI->IsTank(bot) && botAI->IsRanged(bot); -} - -bool PatchwerkNonTankTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); - if (!boss) - { - return false; - } - return !botAI->IsTank(bot); -} - -bool LoathebTrigger::IsActive() { return helper.UpdateBossAI(); } - -bool ThaddiusPhasePetTrigger::IsActive() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - return helper.IsPhasePet(); -} - -bool ThaddiusPhaseTransitionTrigger::IsActive() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - return helper.IsPhaseTransition(); -} - -bool ThaddiusPhaseThaddiusTrigger::IsActive() -{ - if (!helper.UpdateBossAI()) - { - return false; - } - return helper.IsPhaseThaddius(); -} diff --git a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.h b/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.h deleted file mode 100644 index d02a88a9eb..0000000000 --- a/src/Scenario/RaidAi/naxxramas/RaidNaxxTriggers.h +++ /dev/null @@ -1,258 +0,0 @@ - -#ifndef _PLAYERBOT_RAIDNAXXTRIGGERS_H -#define _PLAYERBOT_RAIDNAXXTRIGGERS_H - -#include "EventMap.h" -#include "GenericTriggers.h" -#include "PlayerbotAIConfig.h" -#include "RaidNaxxBossHelper.h" -#include "Trigger.h" - -class MutatingInjectionTrigger : public HasAuraTrigger -{ -public: - MutatingInjectionTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "mutating injection", 1) {} -}; - -class MutatingInjectionMeleeTrigger : public MutatingInjectionTrigger -{ -public: - MutatingInjectionMeleeTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {} - bool IsActive() override; -}; - -class MutatingInjectionRangedTrigger : public MutatingInjectionTrigger -{ -public: - MutatingInjectionRangedTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {} - bool IsActive() override; -}; - -class AuraRemovedTrigger : public Trigger -{ -public: - AuraRemovedTrigger(PlayerbotAI* botAI, std::string name) : Trigger(botAI, name, 1) { this->prev_check = false; } - virtual bool IsActive() override; - -protected: - bool prev_check; -}; - -class MutatingInjectionRemovedTrigger : public HasNoAuraTrigger -{ -public: - MutatingInjectionRemovedTrigger(PlayerbotAI* ai) : HasNoAuraTrigger(ai, "mutating injection") {} - virtual bool IsActive(); -}; - -class GrobbulusCloudTrigger : public Trigger -{ -public: - GrobbulusCloudTrigger(PlayerbotAI* ai) : Trigger(ai, "grobbulus cloud event"), last_cloud_ms(0) {} - bool IsActive() override; - -private: - uint32 last_cloud_ms; - static constexpr uint32 CloudRotationDelayMs = 15000; -}; - -class HeiganMeleeTrigger : public Trigger -{ -public: - HeiganMeleeTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan melee") {} - virtual bool IsActive(); -}; - -class HeiganRangedTrigger : public Trigger -{ -public: - HeiganRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan ranged") {} - bool IsActive() override; -}; - -class RazuviousTankTrigger : public Trigger -{ -public: - RazuviousTankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious tank"), helper(ai) {} - bool IsActive() override; - -private: - RazuviousBossHelper helper; -}; - -class RazuviousNontankTrigger : public Trigger -{ -public: - RazuviousNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious non-tank"), helper(ai) {} - bool IsActive() override; - -private: - RazuviousBossHelper helper; -}; - -class KelthuzadTrigger : public Trigger -{ -public: - KelthuzadTrigger(PlayerbotAI* ai) : Trigger(ai, "kel'thuzad trigger"), helper(ai) {} - bool IsActive() override; - -private: - KelthuzadBossHelper helper; -}; - -class AnubrekhanTrigger : public Trigger -{ -public: - AnubrekhanTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'rekhan") {} - bool IsActive() override; -}; - - class FaerlinaTrigger : public Trigger - { - public: - FaerlinaTrigger(PlayerbotAI* ai) : Trigger(ai, "faerlina") {} - bool IsActive() override; - }; - -class MaexxnaTrigger : public Trigger -{ -public: - MaexxnaTrigger(PlayerbotAI* ai) : Trigger(ai, "maexxna") {} - bool IsActive() override; -}; - -class PatchwerkTankTrigger : public Trigger -{ -public: - PatchwerkTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk tank") {} - bool IsActive() override; -}; - -class PatchwerkNonTankTrigger : public Trigger -{ -public: - PatchwerkNonTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk non-tank") {} - bool IsActive() override; -}; - -class PatchwerkRangedTrigger : public Trigger -{ -public: - PatchwerkRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk ranged") {} - bool IsActive() override; -}; - -class ThaddiusPhasePetTrigger : public Trigger -{ -public: - ThaddiusPhasePetTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase pet"), helper(ai) {} - bool IsActive() override; - -private: - ThaddiusBossHelper helper; -}; - -class ThaddiusPhasePetLoseAggroTrigger : public ThaddiusPhasePetTrigger -{ -public: - ThaddiusPhasePetLoseAggroTrigger(PlayerbotAI* ai) : ThaddiusPhasePetTrigger(ai) {} - virtual bool IsActive() - { - Unit* target = AI_VALUE(Unit*, "current target"); - return ThaddiusPhasePetTrigger::IsActive() && botAI->IsTank(bot) && target && target->GetVictim() != bot; - } -}; - -class ThaddiusPhaseTransitionTrigger : public Trigger -{ -public: - ThaddiusPhaseTransitionTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase transition"), helper(ai) {} - bool IsActive() override; - -private: - ThaddiusBossHelper helper; -}; - -class ThaddiusPhaseThaddiusTrigger : public Trigger -{ -public: - ThaddiusPhaseThaddiusTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase thaddius"), helper(ai) {} - bool IsActive() override; - -private: - ThaddiusBossHelper helper; -}; - -class HorsemanAttractorsTrigger : public Trigger -{ -public: - HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {} - bool IsActive() override; - -private: - FourhorsemanBossHelper helper; -}; - -class HorsemanExceptAttractorsTrigger : public Trigger -{ -public: - HorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen except attractors"), helper(ai) {} - bool IsActive() override; - -private: - FourhorsemanBossHelper helper; -}; - -class SapphironGroundTrigger : public Trigger -{ -public: - SapphironGroundTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron ground"), helper(ai) {} - bool IsActive() override; - -private: - SapphironBossHelper helper; -}; - -class SapphironFlightTrigger : public Trigger -{ -public: - SapphironFlightTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron flight"), helper(ai) {} - bool IsActive() override; - -private: - SapphironBossHelper helper; -}; - -class GluthTrigger : public Trigger -{ -public: - GluthTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth trigger"), helper(ai) {} - bool IsActive() override; - -private: - GluthBossHelper helper; -}; - -class GluthMainTankMortalWoundTrigger : public Trigger -{ -public: - GluthMainTankMortalWoundTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth main tank mortal wound trigger"), helper(ai) - { - } - bool IsActive() override; - -private: - GluthBossHelper helper; -}; - -class LoathebTrigger : public Trigger -{ -public: - LoathebTrigger(PlayerbotAI* ai) : Trigger(ai, "loatheb"), helper(ai) {} - bool IsActive() override; - -private: - LoathebBossHelper helper; -}; - -#endif From 8d9efb863aa6c645ea2d654457c236a17b59cd0c Mon Sep 17 00:00:00 2001 From: Wishmaster117 <140754794+Wishmaster117@users.noreply.github.com> Date: Mon, 19 Jan 2026 11:07:50 +0100 Subject: [PATCH 05/42] Restore folder --- .../RaidAi/Naxxramas/RaidNaxxActionContext.h | 95 +++ .../RaidAi/Naxxramas/RaidNaxxActions.h | 342 ++++++++++ .../Naxxramas/RaidNaxxActions_Anubrekhan.cpp | 99 +++ .../Naxxramas/RaidNaxxActions_Faerlina.cpp | 3 + .../RaidNaxxActions_FourHorseman.cpp | 70 ++ .../Naxxramas/RaidNaxxActions_Gluth.cpp | 206 ++++++ .../Naxxramas/RaidNaxxActions_Gothik.cpp | 3 + .../Naxxramas/RaidNaxxActions_Grobbulus.cpp | 52 ++ .../Naxxramas/RaidNaxxActions_Heigan.cpp | 76 +++ .../Naxxramas/RaidNaxxActions_Kelthuzad.cpp | 206 ++++++ .../Naxxramas/RaidNaxxActions_Loatheb.cpp | 68 ++ .../Naxxramas/RaidNaxxActions_Maexxna.cpp | 3 + .../RaidAi/Naxxramas/RaidNaxxActions_Noth.cpp | 3 + .../Naxxramas/RaidNaxxActions_Patchwerk.cpp | 31 + .../Naxxramas/RaidNaxxActions_Razuvious.cpp | 166 +++++ .../Naxxramas/RaidNaxxActions_Sapphiron.cpp | 121 ++++ .../Naxxramas/RaidNaxxActions_Shared.cpp | 18 + .../Naxxramas/RaidNaxxActions_Thaddius.cpp | 140 ++++ .../RaidAi/Naxxramas/RaidNaxxBossHelper.h | 606 ++++++++++++++++++ .../RaidAi/Naxxramas/RaidNaxxMultipliers.cpp | 344 ++++++++++ .../RaidAi/Naxxramas/RaidNaxxMultipliers.h | 116 ++++ .../RaidAi/Naxxramas/RaidNaxxSpellIds.h | 177 +++++ .../RaidAi/Naxxramas/RaidNaxxStrategy.cpp | 156 +++++ .../RaidAi/Naxxramas/RaidNaxxStrategy.h | 18 + .../RaidAi/Naxxramas/RaidNaxxTriggerContext.h | 86 +++ .../RaidAi/Naxxramas/RaidNaxxTriggers.cpp | 283 ++++++++ .../RaidAi/Naxxramas/RaidNaxxTriggers.h | 258 ++++++++ 27 files changed, 3746 insertions(+) create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActionContext.h create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions.h create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Anubrekhan.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Faerlina.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_FourHorseman.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gluth.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gothik.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Grobbulus.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Heigan.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Kelthuzad.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Loatheb.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Maexxna.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Noth.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Patchwerk.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Razuvious.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Sapphiron.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Shared.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Thaddius.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxBossHelper.h create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.h create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxSpellIds.h create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.h create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggerContext.h create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.cpp create mode 100644 src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.h diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActionContext.h b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActionContext.h new file mode 100644 index 0000000000..acb9a2deef --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActionContext.h @@ -0,0 +1,95 @@ +// /* +// * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it +// and/or modify it under version 3 of the License, or (at your option), any later version. +// */ + +#ifndef _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H +#define _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "RaidNaxxActions.h" + +class RaidNaxxActionContext : public NamedObjectContext +{ +public: + RaidNaxxActionContext() + { + creators["grobbulus go behind the boss"] = &RaidNaxxActionContext::go_behind_the_boss; + creators["rotate grobbulus"] = &RaidNaxxActionContext::rotate_grobbulus; + creators["grobbulus move center"] = &RaidNaxxActionContext::grobbulus_move_center; + creators["grobbulus move away"] = &RaidNaxxActionContext::grobbulus_move_away; + + creators["heigan dance melee"] = &RaidNaxxActionContext::heigan_dance_melee; + creators["heigan dance ranged"] = &RaidNaxxActionContext::heigan_dance_ranged; + creators["thaddius attack nearest pet"] = &RaidNaxxActionContext::thaddius_attack_nearest_pet; + // creators["thaddius melee to place"] = &RaidNaxxActionContext::thaddius_tank_to_place; + // creators["thaddius ranged to place"] = &RaidNaxxActionContext::thaddius_ranged_to_place; + creators["thaddius move to platform"] = &RaidNaxxActionContext::thaddius_move_to_platform; + creators["thaddius move polarity"] = &RaidNaxxActionContext::thaddius_move_polarity; + + creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal; + creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target; + + creators["horseman attract alternatively"] = &RaidNaxxActionContext::horseman_attract_alternatively; + creators["horseman attack in order"] = &RaidNaxxActionContext::horseman_attack_in_order; + + creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position; + creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position; + + creators["kel'thuzad choose target"] = &RaidNaxxActionContext::kelthuzad_choose_target; + creators["kel'thuzad position"] = &RaidNaxxActionContext::kelthuzad_position; + + creators["anub'rekhan choose target"] = &RaidNaxxActionContext::anubrekhan_choose_target; + creators["anub'rekhan position"] = &RaidNaxxActionContext::anubrekhan_position; + + creators["gluth choose target"] = &RaidNaxxActionContext::gluth_choose_target; + creators["gluth position"] = &RaidNaxxActionContext::gluth_position; + creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown; + + creators["patchwerk ranged position"] = &RaidNaxxActionContext::patchwerk_ranged_position; + + creators["loatheb position"] = &RaidNaxxActionContext::loatheb_position; + creators["loatheb choose target"] = &RaidNaxxActionContext::loatheb_choose_target; + } + +private: + static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(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_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(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* thaddius_attack_nearest_pet(PlayerbotAI* ai) { return new ThaddiusAttackNearestPetAction(ai); } + // static Action* thaddius_tank_to_place(PlayerbotAI* ai) { return new ThaddiusMeleeToPlaceAction(ai); } + // static Action* thaddius_ranged_to_place(PlayerbotAI* ai) { return new ThaddiusRangedToPlaceAction(ai); } + static Action* thaddius_move_to_platform(PlayerbotAI* ai) { return new ThaddiusMoveToPlatformAction(ai); } + static Action* thaddius_move_polarity(PlayerbotAI* ai) { return new ThaddiusMovePolarityAction(ai); } + static Action* razuvious_target(PlayerbotAI* ai) { return new RazuviousTargetAction(ai); } + static Action* razuvious_use_obedience_crystal(PlayerbotAI* ai) + { + return new RazuviousUseObedienceCrystalAction(ai); + } + static Action* horseman_attract_alternatively(PlayerbotAI* 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 + // SapphironGroundMainTankPositionAction(ai); } + static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); } + static Action* sapphiron_flight_position(PlayerbotAI* ai) { return new SapphironFlightPositionAction(ai); } + // static Action* sapphiron_avoid_chill(PlayerbotAI* ai) { return new SapphironAvoidChillAction(ai); } + static Action* kelthuzad_choose_target(PlayerbotAI* ai) { return new KelthuzadChooseTargetAction(ai); } + static Action* kelthuzad_position(PlayerbotAI* ai) { return new KelthuzadPositionAction(ai); } + static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new AnubrekhanChooseTargetAction(ai); } + static Action* anubrekhan_position(PlayerbotAI* ai) { return new AnubrekhanPositionAction(ai); } + static Action* gluth_choose_target(PlayerbotAI* ai) { return new GluthChooseTargetAction(ai); } + static Action* gluth_position(PlayerbotAI* ai) { return new GluthPositionAction(ai); } + static Action* gluth_slowdown(PlayerbotAI* ai) { return new GluthSlowdownAction(ai); } + static Action* patchwerk_ranged_position(PlayerbotAI* ai) { return new PatchwerkRangedPositionAction(ai); } + static Action* loatheb_position(PlayerbotAI* ai) { return new LoathebPositionAction(ai); } + static Action* loatheb_choose_target(PlayerbotAI* ai) { return new LoathebChooseTargetAction(ai); } +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions.h b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions.h new file mode 100644 index 0000000000..6cc732a28f --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions.h @@ -0,0 +1,342 @@ +#ifndef _PLAYERBOT_RAIDNAXXACTIONS_H +#define _PLAYERBOT_RAIDNAXXACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "GenericActions.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "RaidNaxxBossHelper.h" + +// just for test +// class TryToGetBossAIAction : public Action +// { +// public: +// TryToGetBossAIAction(PlayerbotAI* ai) : Action(ai, "try to get boss ai") {} + +// public: +// virtual bool Execute(Event event); +// }; + +class GrobbulusGoBehindAction : public MovementAction +{ +public: + GrobbulusGoBehindAction(PlayerbotAI* ai, float distance = 24.0f, float delta_angle = M_PI / 8) + : MovementAction(ai, "grobbulus go behind") + { + this->distance = distance; + this->delta_angle = delta_angle; + } + virtual bool Execute(Event event); + +protected: + float distance, delta_angle; +}; + +class GrobbulusRotateAction : public RotateAroundTheCenterPointAction +{ +public: + GrobbulusRotateAction(PlayerbotAI* botAI) + : RotateAroundTheCenterPointAction(botAI, "rotate grobbulus", 3281.23f, -3310.38f, 35.0f, 8, true, M_PI) + { + } + virtual bool isUseful() override + { + return RotateAroundTheCenterPointAction::isUseful() && botAI->IsMainTank(bot) && + AI_VALUE2(bool, "has aggro", "boss target"); + } + uint32 GetCurrWaypoint() override; +}; + +class GrobblulusMoveCenterAction : public MoveInsideAction +{ +public: + GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {} +}; + +class GrobbulusMoveAwayAction : public MovementAction +{ +public: + GrobbulusMoveAwayAction(PlayerbotAI* ai, float distance = 18.0f) + : MovementAction(ai, "grobbulus move away"), distance(distance) + { + } + bool Execute(Event event) override; + +private: + float distance; +}; + +class HeiganDanceAction : public MovementAction +{ +public: + HeiganDanceAction(PlayerbotAI* ai) : MovementAction(ai, "heigan dance") + { + this->last_eruption_ms = 0; + this->platform_phase = false; + ResetSafe(); + waypoints.push_back(std::make_pair(2794.88f, -3668.12f)); + waypoints.push_back(std::make_pair(2775.49f, -3674.43f)); + waypoints.push_back(std::make_pair(2762.30f, -3684.59f)); + waypoints.push_back(std::make_pair(2755.99f, -3703.96f)); + platform = std::make_pair(2794.26f, -3706.67f); + } + +protected: + bool CalculateSafe(); + void ResetSafe() + { + curr_safe = 0; + curr_dir = 1; + } + void NextSafe() + { + curr_safe += curr_dir; + if (curr_safe == 3 || curr_safe == 0) + { + curr_dir = -curr_dir; + } + } + uint32 last_eruption_ms; + bool platform_phase; + uint32 curr_safe, curr_dir; + std::vector> waypoints; + std::pair platform; +}; + +class HeiganDanceMeleeAction : public HeiganDanceAction +{ +public: + HeiganDanceMeleeAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {} + virtual bool Execute(Event event); +}; + +class HeiganDanceRangedAction : public HeiganDanceAction +{ +public: + HeiganDanceRangedAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {} + virtual bool Execute(Event event); +}; + +class ThaddiusAttackNearestPetAction : public AttackAction +{ +public: + ThaddiusAttackNearestPetAction(PlayerbotAI* ai) : AttackAction(ai, "thaddius attack nearest pet"), helper(ai) {} + virtual bool Execute(Event event); + virtual bool isUseful(); + +private: + ThaddiusBossHelper helper; +}; + +// class ThaddiusMeleeToPlaceAction : public MovementAction +// { +// public: +// ThaddiusMeleeToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius melee to place") {} +// virtual bool Execute(Event event); +// virtual bool isUseful(); +// }; + +// class ThaddiusRangedToPlaceAction : public MovementAction +// { +// public: +// ThaddiusRangedToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius ranged to place") {} +// virtual bool Execute(Event event); +// virtual bool isUseful(); +// }; + +class ThaddiusMoveToPlatformAction : public MovementAction +{ +public: + ThaddiusMoveToPlatformAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move to platform") {} + virtual bool Execute(Event event); + virtual bool isUseful(); +}; + +class ThaddiusMovePolarityAction : public MovementAction +{ +public: + ThaddiusMovePolarityAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move polarity") {} + virtual bool Execute(Event event); + virtual bool isUseful(); +}; + +class RazuviousUseObedienceCrystalAction : public MovementAction +{ +public: + RazuviousUseObedienceCrystalAction(PlayerbotAI* ai) + : MovementAction(ai, "razuvious use obedience crystal"), helper(ai) + { + } + bool Execute(Event event) override; + +private: + RazuviousBossHelper helper; +}; + +class RazuviousTargetAction : public AttackAction +{ +public: + RazuviousTargetAction(PlayerbotAI* ai) : AttackAction(ai, "razuvious target"), helper(ai) {} + bool Execute(Event event) override; + +private: + RazuviousBossHelper helper; +}; + +class HorsemanAttractAlternativelyAction : public AttackAction +{ +public: + HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attract alternatively"), helper(ai) + { + } + bool Execute(Event event) override; + +protected: + FourhorsemanBossHelper helper; +}; + +class HorsemanAttactInOrderAction : public AttackAction +{ +public: + HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attact in order"), helper(ai) {} + bool Execute(Event event) override; + +protected: + FourhorsemanBossHelper helper; +}; + +// class SapphironGroundMainTankPositionAction : public MovementAction +// { +// public: +// SapphironGroundMainTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground main tank +// position") {} virtual bool Execute(Event event); +// }; + +class SapphironGroundPositionAction : public MovementAction +{ +public: + SapphironGroundPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground position"), helper(ai) {} + bool Execute(Event event) override; + +protected: + SapphironBossHelper helper; +}; + +class SapphironFlightPositionAction : public MovementAction +{ +public: + SapphironFlightPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron flight position"), helper(ai) {} + bool Execute(Event event) override; + +protected: + SapphironBossHelper helper; + bool MoveToNearestIcebolt(); +}; + +// class SapphironAvoidChillAction : public MovementAction +// { +// public: +// SapphironAvoidChillAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron avoid chill") {} +// virtual bool Execute(Event event); +// }; + +class KelthuzadChooseTargetAction : public AttackAction +{ +public: + KelthuzadChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "kel'thuzad choose target"), helper(ai) {} + virtual bool Execute(Event event); + +private: + KelthuzadBossHelper helper; +}; + +class KelthuzadPositionAction : public MovementAction +{ +public: + KelthuzadPositionAction(PlayerbotAI* ai) : MovementAction(ai, "kel'thuzad position"), helper(ai) {} + virtual bool Execute(Event event); + +private: + KelthuzadBossHelper helper; +}; + +class AnubrekhanChooseTargetAction : public AttackAction +{ +public: + AnubrekhanChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "anub'rekhan choose target") {} + bool Execute(Event event) override; +}; + +class AnubrekhanPositionAction : public RotateAroundTheCenterPointAction +{ +public: + AnubrekhanPositionAction(PlayerbotAI* ai) + : RotateAroundTheCenterPointAction(ai, "anub'rekhan position", 3272.49f, -3476.27f, 45.0f, 16) + { + } + bool Execute(Event event) override; +}; + +class GluthChooseTargetAction : public AttackAction +{ +public: + GluthChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "gluth choose target"), helper(ai) {} + bool Execute(Event event) override; + +private: + GluthBossHelper helper; +}; + +class GluthPositionAction : public RotateAroundTheCenterPointAction +{ +public: + GluthPositionAction(PlayerbotAI* ai) + : RotateAroundTheCenterPointAction(ai, "gluth position", 3293.61f, -3149.01f, 12.0f, 12), helper(ai) + { + } + bool Execute(Event event) override; + +private: + GluthBossHelper helper; +}; + +class GluthSlowdownAction : public Action +{ +public: + GluthSlowdownAction(PlayerbotAI* ai) : Action(ai, "gluth slowdown"), helper(ai) {} + bool Execute(Event event) override; + +private: + GluthBossHelper helper; +}; + +class LoathebPositionAction : public MovementAction +{ +public: + LoathebPositionAction(PlayerbotAI* ai) : MovementAction(ai, "loatheb position"), helper(ai) {} + virtual bool Execute(Event event); + +private: + LoathebBossHelper helper; +}; + +class LoathebChooseTargetAction : public AttackAction +{ +public: + LoathebChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "loatheb choose target"), helper(ai) {} + virtual bool Execute(Event event); + +private: + LoathebBossHelper helper; +}; + +class PatchwerkRangedPositionAction : public MovementAction +{ +public: + PatchwerkRangedPositionAction(PlayerbotAI* ai) : MovementAction(ai, "patchwerk ranged position") {} + bool Execute(Event event) override; +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Anubrekhan.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Anubrekhan.cpp new file mode 100644 index 0000000000..90f14877e2 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Anubrekhan.cpp @@ -0,0 +1,99 @@ +#include "RaidNaxxActions.h" + +#include "ObjectGuid.h" +#include "Playerbots.h" + +bool AnubrekhanChooseTargetAction::Execute(Event event) +{ + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit* target_boss = nullptr; + std::vector target_guards; + for (ObjectGuid const guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit) + continue; + if (botAI->EqualLowercaseName(unit->GetName(), "crypt guard")) + { + target_guards.push_back(unit); + } + if (botAI->EqualLowercaseName(unit->GetName(), "anub'rekhan")) + { + target_boss = unit; + } + } + if (botAI->IsMainTank(bot)) + { + target = target_boss; + } + else + { + if (target_guards.size() == 0) + { + target = target_boss; + } + else + { + if (botAI->IsAssistTank(bot)) + { + for (Unit* t : target_guards) + { + if (target == nullptr || (target->GetVictim() && target->GetVictim()->ToPlayer() && + botAI->IsTank(target->GetVictim()->ToPlayer()))) + { + target = t; + } + } + } + else + { + for (Unit* t : target_guards) + { + if (target == nullptr || target->GetHealthPct() > t->GetHealthPct()) + { + target = t; + } + } + } + } + } + if (context->GetValue("current target")->Get() == target) + { + return false; + } + return Attack(target); +} + +bool AnubrekhanPositionAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); + if (!boss) + { + return false; + } + bool inPhase = botAI->HasAura("locust swarm", boss) || boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (inPhase) + { + if (botAI->IsMainTank(bot)) + { + uint32 nearest = FindNearestWaypoint(); + uint32 next_point; + if (inPhase) + { + 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, + false, false, MovementPriority::MOVEMENT_COMBAT); + } + else + { + return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Faerlina.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Faerlina.cpp new file mode 100644 index 0000000000..799dd9ca2e --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Faerlina.cpp @@ -0,0 +1,3 @@ +#include "RaidNaxxActions.h" + +// Reserved for Faerlina-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_FourHorseman.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_FourHorseman.cpp new file mode 100644 index 0000000000..0d29526fef --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_FourHorseman.cpp @@ -0,0 +1,70 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" + +bool HorsemanAttractAlternativelyAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + helper.CalculatePosToGo(bot); + auto [posX, posY] = helper.CurrentAttractPos(); + if (MoveTo(bot->GetMapId(), posX, posY, helper.posZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + Unit* attackTarget = helper.CurrentAttackTarget(); + if (context->GetValue("current target")->Get() != attackTarget) + { + return Attack(attackTarget); + } + return false; +} + +bool HorsemanAttactInOrderAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + Unit* target = nullptr; + Unit* thane = AI_VALUE2(Unit*, "find target", "thane korth'azz"); + Unit* lady = AI_VALUE2(Unit*, "find target", "lady blaumeux"); + Unit* sir = AI_VALUE2(Unit*, "find target", "sir zeliek"); + Unit* fourth = AI_VALUE2(Unit*, "find target", "baron rivendare"); + if (!fourth) + { + fourth = AI_VALUE2(Unit*, "find target", "highlord mograine"); + } + std::vector attack_order; + if (botAI->IsAssistTank(bot)) + { + attack_order = {fourth, thane, lady, sir}; + } + else + { + attack_order = {thane, fourth, lady, sir}; + } + for (Unit* t : attack_order) + { + if (t && t->IsAlive()) + { + target = t; + break; + } + } + if (target) + { + if (context->GetValue("current target")->Get() == target && botAI->GetState() == BOT_STATE_COMBAT) + { + return false; + } + if (!bot->IsWithinLOSInMap(target)) + { + return MoveNear(target, 22.0f, MovementPriority::MOVEMENT_COMBAT); + } + return Attack(target); + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gluth.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gluth.cpp new file mode 100644 index 0000000000..7192685013 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gluth.cpp @@ -0,0 +1,206 @@ +#include "RaidNaxxActions.h" + +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "SharedDefines.h" + +bool GluthChooseTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + GuidVector attackers = context->GetValue("possible targets")->Get(); + Unit* target = nullptr; + Unit* target_boss = nullptr; + std::vector target_zombies; + for (GuidVector::iterator i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (!unit->IsAlive()) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "zombie chow")) + { + target_zombies.push_back(unit); + } + if (botAI->EqualLowercaseName(unit->GetName(), "gluth")) + { + target_boss = unit; + } + } + if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) + { + target = target_boss; + } + else if (botAI->IsAssistTankOfIndex(bot, 1)) + { + for (Unit* t : target_zombies) + { + if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() != bot && t->GetDistance2d(bot) <= 10.0f) + { + if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot)) + { + target = t; + } + } + } + } + else if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0 || botAI->GetClassIndex(bot, CLASS_HUNTER) == 1) + { + // prevent zombie go straight to gluth + for (Unit* t : target_zombies) + { + if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() == target_boss && + t->GetDistance2d(bot) <= sPlayerbotAIConfig->spellDistance) + { + if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot)) + { + target = t; + } + } + } + if (!target) + { + target = target_boss; + } + } + else + { + for (Unit* t : target_zombies) + { + if (t->GetHealthPct() <= helper.decimatedZombiePct) + { + if (target == nullptr || + target->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second) > + t->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second)) + { + target = t; + } + } + } + if (target == nullptr) + { + target = target_boss; + } + } + if (!target || context->GetValue("current target")->Get() == target) + { + return false; + } + if (target_boss && target == target_boss) + return Attack(target, true); + return Attack(target, false); + // return Attack(target); +} + +bool GluthPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; + if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) + { + if (AI_VALUE2(bool, "has aggro", "boss target")) + { + if (raid25) + { + if (MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), false, false, false, + false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), 2.0f, + MovementPriority::MOVEMENT_COMBAT); + } + else + { + if (MoveTo(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), false, false, false, + false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), 2.0f, + MovementPriority::MOVEMENT_COMBAT); + } + } + } + else if (botAI->IsAssistTankOfIndex(bot, 1)) + { + if (helper.BeforeDecimate()) + { + if (MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), 2.0f, + MovementPriority::MOVEMENT_COMBAT); + } + else + { + if (AI_VALUE2(bool, "has aggro", "current target")) + { + uint32 nearest = FindNearestWaypoint(); + uint32 next_point = (nearest + 1) % intervals; + return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + } + else if (botAI->IsRangedDps(bot)) + { + if (raid25) + { + if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0) + { + return MoveInside(NAXX_MAP_ID, helper.leftSlowDownPos.first, helper.leftSlowDownPos.second, bot->GetPositionZ(), 0.0f, + MovementPriority::MOVEMENT_COMBAT); + } + if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 1) + { + return MoveInside(NAXX_MAP_ID, helper.rightSlowDownPos.first, helper.rightSlowDownPos.second, bot->GetPositionZ(), 0.0f, + MovementPriority::MOVEMENT_COMBAT); + } + } + return MoveInside(NAXX_MAP_ID, helper.rangedPos.first, helper.rangedPos.second, bot->GetPositionZ(), 3.0f, + MovementPriority::MOVEMENT_COMBAT); + } + else if (botAI->IsHeal(bot)) + { + return MoveInside(NAXX_MAP_ID, helper.healPos.first, helper.healPos.second, bot->GetPositionZ(), 0.0f, + MovementPriority::MOVEMENT_COMBAT); + } + return false; +} + +bool GluthSlowdownAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; + if (!raid25) + { + return false; + } + if (helper.JustStartCombat()) + { + return false; + } + switch (bot->getClass()) + { + case CLASS_HUNTER: + return botAI->CastSpell("frost trap", bot); + break; + default: + break; + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gothik.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gothik.cpp new file mode 100644 index 0000000000..e5c673e46e --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gothik.cpp @@ -0,0 +1,3 @@ +#include "RaidNaxxActions.h" + +// Reserved for Gothik-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Grobbulus.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Grobbulus.cpp new file mode 100644 index 0000000000..34af48b782 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Grobbulus.cpp @@ -0,0 +1,52 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" + +bool GrobbulusGoBehindAction::Execute(Event event) +{ + Unit* boss = AI_VALUE(Unit*, "boss target"); + if (!boss) + { + return false; + } + // Position* pos = boss->GetPosition(); + float orientation = boss->GetOrientation() + M_PI + delta_angle; + float x = boss->GetPositionX(); + float y = boss->GetPositionY(); + float z = boss->GetPositionZ(); + float rx = x + cos(orientation) * distance; + float ry = y + sin(orientation) * distance; + return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +} + +bool GrobbulusMoveAwayAction::Execute(Event event) +{ + Unit* boss = AI_VALUE(Unit*, "boss target"); + if (!boss) + { + return false; + } + + const float currentDistance = bot->GetExactDist2d(boss); + if (currentDistance >= distance) + { + return false; + } + + const float angle = boss->GetAngle(bot); + const float x = boss->GetPositionX() + cos(angle) * distance; + const float y = boss->GetPositionY() + sin(angle) * distance; + const float z = bot->GetPositionZ(); + + return MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +} + +uint32 GrobbulusRotateAction::GetCurrWaypoint() +{ + uint32 current = FindNearestWaypoint(); + if (clockwise) + { + return (current + 1) % intervals; + } + return (current + intervals - 1) % intervals; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Heigan.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Heigan.cpp new file mode 100644 index 0000000000..b7c19f2559 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Heigan.cpp @@ -0,0 +1,76 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" +#include "RaidNaxxSpellIds.h" +#include "Spell.h" +#include "Timer.h" + +bool HeiganDanceAction::CalculateSafe() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean"); + if (!boss) + { + return false; + } + uint32 now = getMSTime(); + platform_phase = boss->IsWithinDist2d(platform.first, platform.second, 10.0f); + if (last_eruption_ms != 0 && now - last_eruption_ms > 15000) + { + ResetSafe(); + } + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (spell) + { + SpellInfo const* info = spell->GetSpellInfo(); + bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10}); + if (!isEruption && info && info->SpellName[LOCALE_enUS]) + { + // Fallback to name for custom spell data. + isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption"); + } + if (isEruption) + { + if (last_eruption_ms == 0 || now - last_eruption_ms > 500) + { + NextSafe(); + } + last_eruption_ms = now; + } + } + } + return true; +} + +bool HeiganDanceMeleeAction::Execute(Event event) +{ + CalculateSafe(); + if (!platform_phase && botAI->IsMainTank(bot) && !AI_VALUE2(bool, "has aggro", "boss target")) + { + return false; + } + assert(curr_safe >= 0 && curr_safe <= 3); + return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), + botAI->IsMainTank(bot) ? 0 : 0, MovementPriority::MOVEMENT_COMBAT); +} + +bool HeiganDanceRangedAction::Execute(Event event) +{ + CalculateSafe(); + if (!platform_phase) + { + if (MoveTo(bot->GetMapId(), platform.first, platform.second, 276.54f, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(bot->GetMapId(), platform.first, platform.second, 276.54f, 2.0f, MovementPriority::MOVEMENT_COMBAT); + } + botAI->InterruptSpell(); + return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), 0, + MovementPriority::MOVEMENT_COMBAT); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Kelthuzad.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Kelthuzad.cpp new file mode 100644 index 0000000000..1c86769d95 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Kelthuzad.cpp @@ -0,0 +1,206 @@ +#include "RaidNaxxActions.h" + +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" + +bool KelthuzadChooseTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit *target_soldier = nullptr, *target_weaver = nullptr, *target_abomination = nullptr, *target_kelthuzad = nullptr, + *target_guardian = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + + if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown")) + { + if (!target_guardian) + { + target_guardian = unit; + } + else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() && + target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) && + botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer())) + { + target_guardian = unit; + } + else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() && + target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) && + !botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer()) && + target_guardian->GetDistance2d(helper.center.first, helper.center.second) > + bot->GetDistance2d(unit)) + { + target_guardian = unit; + } + } + + if (unit->GetDistance2d(helper.center.first, helper.center.second) > 30.0f) + { + continue; + } + if (bot->GetDistance2d(unit) > sPlayerbotAIConfig->spellDistance) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "unstoppable abomination")) + { + if (target_abomination == nullptr || + target_abomination->GetDistance2d(helper.center.first, helper.center.second) > + unit->GetDistance2d(helper.center.first, helper.center.second)) + { + target_abomination = unit; + } + } + if (botAI->EqualLowercaseName(unit->GetName(), "soldier of the frozen wastes")) + { + if (target_soldier == nullptr || + target_soldier->GetDistance2d(helper.center.first, helper.center.second) > + unit->GetDistance2d(helper.center.first, helper.center.second)) + { + target_soldier = unit; + } + } + if (botAI->EqualLowercaseName(unit->GetName(), "soul weaver")) + { + if (target_weaver == nullptr || target_weaver->GetDistance2d(helper.center.first, helper.center.second) > + unit->GetDistance2d(helper.center.first, helper.center.second)) + { + target_weaver = unit; + } + } + if (botAI->EqualLowercaseName(unit->GetName(), "kel'thuzad")) + { + target_kelthuzad = unit; + } + } + std::vector targets; + if (botAI->IsRanged(bot)) + { + if (botAI->GetRangedDpsIndex(bot) <= 1) + { + targets = {target_soldier, target_weaver, target_abomination, target_kelthuzad}; + } + else + { + targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; + } + } + else if (botAI->IsAssistTank(bot)) + { + targets = {target_abomination, target_guardian, target_kelthuzad}; + } + else + { + targets = {target_abomination, target_kelthuzad}; + } + for (Unit* t : targets) + { + if (t) + { + target = t; + break; + } + } + if (context->GetValue("current target")->Get() == target) + { + return false; + } + if (target_kelthuzad && target == target_kelthuzad) + { + return Attack(target, true); + } + return Attack(target, false); +} + +bool KelthuzadPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (helper.IsPhaseOne()) + { + if (AI_VALUE(Unit*, "current target") == nullptr) + { + return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f, + MovementPriority::MOVEMENT_COMBAT); + } + } + else if (helper.IsPhaseTwo()) + { + Unit* shadow_fissure = helper.GetAnyShadowFissure(); + if (!shadow_fissure || !bot->IsWithinDistInMap(shadow_fissure, 10.0f)) + { + float distance, angle; + if (botAI->IsMainTank(bot)) + { + if (AI_VALUE2(bool, "has aggro", "current target")) + { + return MoveTo(NAXX_MAP_ID, helper.tank_pos.first, helper.tank_pos.second, bot->GetPositionZ(), false, false, false, + false, MovementPriority::MOVEMENT_COMBAT); + } + else + { + return false; + } + } + else if (botAI->IsRanged(bot)) + { + uint32 index = botAI->GetRangedIndex(bot); + if (index < 8) + { + distance = 20.0f; + angle = index * M_PI / 4; + } + else + { + distance = 32.0f; + angle = (index - 8) * M_PI / 4; + } + float dx, dy; + dx = helper.center.first + cos(angle) * distance; + dy = helper.center.second + sin(angle) * distance; + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + else if (botAI->IsTank(bot)) + { + Unit* cur_tar = AI_VALUE(Unit*, "current target"); + if (cur_tar && cur_tar->GetVictim() && cur_tar->GetVictim()->ToPlayer() && + botAI->EqualLowercaseName(cur_tar->GetName(), "guardian of icecrown") && + botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer())) + { + return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + else + { + return false; + } + } + } + else + { + float dx, dy; + float angle; + if (!botAI->IsRanged(bot)) + { + angle = shadow_fissure->GetAngle(helper.center.first, helper.center.second); + } + else + { + angle = bot->GetAngle(shadow_fissure) + M_PI; + } + dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f; + dy = shadow_fissure->GetPositionY() + sin(angle) * 10.0f; + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Loatheb.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Loatheb.cpp new file mode 100644 index 0000000000..54a9325989 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Loatheb.cpp @@ -0,0 +1,68 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" + +bool LoathebPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (botAI->IsTank(bot)) + { + if (AI_VALUE2(bool, "has aggro", "boss target")) + { + return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + else if (botAI->IsRanged(bot)) + { + return MoveInside(533, helper.rangePos.first, helper.rangePos.second, bot->GetPositionZ(), 1.0f, + MovementPriority::MOVEMENT_COMBAT); + } + return false; +} + +bool LoathebChooseTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit* target_boss = nullptr; + Unit* target_spore = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (!unit->IsAlive()) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "spore")) + { + target_spore = unit; + } + if (botAI->EqualLowercaseName(unit->GetName(), "loatheb")) + { + target_boss = unit; + } + } + if (target_spore && bot->GetDistance2d(target_spore) <= 1.0f) + { + target = target_spore; + } + else + { + target = target_boss; + } + if (!target || context->GetValue("current target")->Get() == target) + { + return false; + } + return Attack(target); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Maexxna.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Maexxna.cpp new file mode 100644 index 0000000000..67ef9930f0 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Maexxna.cpp @@ -0,0 +1,3 @@ +#include "RaidNaxxActions.h" + +// Reserved for Maexxna-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Noth.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Noth.cpp new file mode 100644 index 0000000000..ff3ce98e70 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Noth.cpp @@ -0,0 +1,3 @@ +#include "RaidNaxxActions.h" + +// Reserved for Noth-specific actions. \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Patchwerk.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Patchwerk.cpp new file mode 100644 index 0000000000..6ef1480cf2 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Patchwerk.cpp @@ -0,0 +1,31 @@ +#include "RaidNaxxActions.h" + +#include +#include + +bool PatchwerkRangedPositionAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); + if (!boss) + return false; + + constexpr float minDistance = 12.0f; + constexpr float maxDistance = 15.0f; + const float distance = bot->GetExactDist2d(boss); + + if (distance >= minDistance && distance <= maxDistance) + return false; + + const float desiredDistance = std::clamp(distance, minDistance, maxDistance); + float angle = boss->GetAngle(bot); + + if (distance < 0.1f) + angle = boss->GetOrientation(); + + const float x = boss->GetPositionX() + std::cos(angle) * desiredDistance; + const float y = boss->GetPositionY() + std::sin(angle) * desiredDistance; + const float z = bot->GetPositionZ(); + + return MoveTo(boss->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, + false); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Razuvious.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Razuvious.cpp new file mode 100644 index 0000000000..a16469bf15 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Razuvious.cpp @@ -0,0 +1,166 @@ +#include "RaidNaxxActions.h" + +#include "ObjectGuid.h" +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "SharedDefines.h" + +bool RazuviousUseObedienceCrystalAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + // bot->GetCharm + if (Unit* charm = bot->GetCharm()) + { + Unit* target = AI_VALUE2(Unit*, "find target", "instructor razuvious"); + if (!target) + { + return false; + } + if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == NULL_MOTION_TYPE) + { + charm->GetMotionMaster()->Clear(); + charm->GetMotionMaster()->MoveChase(target); + charm->GetAI()->AttackStart(target); + } + Aura* forceObedience = botAI->GetAura("force obedience", charm); + uint32 duration_time; + if (!forceObedience) + { + forceObedience = botAI->GetAura("mind control", charm); + duration_time = 60000; + } + else + { + duration_time = 90000; + } + if (!forceObedience) + { + return false; + } + if (charm->GetDistance(target) <= 0.51f) + { + // taunt + bool tauntUseful = true; + if (forceObedience->GetDuration() <= (duration_time - 5000)) + { + if (target->GetVictim() && botAI->HasAura(29061, target->GetVictim())) + { + tauntUseful = false; + } + if (forceObedience->GetDuration() <= 3000) + { + tauntUseful = false; + } + } + if (forceObedience->GetDuration() >= (duration_time - 500)) + { + tauntUseful = false; + } + if (tauntUseful && !charm->HasSpellCooldown(29060)) + { + // shield + if (!charm->HasSpellCooldown(29061)) + { + charm->CastSpell(charm, 29061, true); + charm->AddSpellCooldown(29061, 0, 30 * 1000); + } + charm->CastSpell(target, 29060, true); + charm->AddSpellCooldown(29060, 0, 20 * 1000); + } + // strike + if (!charm->HasSpellCooldown(61696)) + { + charm->CastSpell(target, 61696, true); + charm->AddSpellCooldown(61696, 0, 4 * 1000); + } + } + } + else + { + Difficulty diff = bot->GetRaidDifficulty(); + if (diff == RAID_DIFFICULTY_10MAN_NORMAL) + { + GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); + for (auto i = npcs.begin(); i != npcs.end(); i++) + { + Creature* unit = botAI->GetCreature(*i); + if (!unit) + { + continue; + } + if (botAI->IsMainTank(bot) && unit->GetSpawnId() != 128352) + { + continue; + } + if (!botAI->IsMainTank(bot) && unit->GetSpawnId() != 128353) + { + continue; + } + if (MoveTo(unit, 0.0f, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + Creature* creature = bot->GetNPCIfCanInteractWith(*i, UNIT_NPC_FLAG_SPELLCLICK); + if (!creature) + continue; + creature->HandleSpellClick(bot); + return true; + } + } + else + { + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (botAI->EqualLowercaseName(unit->GetName(), "death knight understudy")) + { + target = unit; + break; + } + } + if (target) + { + if (bot->GetDistance2d(target) > sPlayerbotAIConfig->spellDistance) + { + return MoveNear(target, sPlayerbotAIConfig->spellDistance, MovementPriority::MOVEMENT_COMBAT); + } + else + { + return botAI->CastSpell("mind control", target); + } + } + } + } + return false; +} + +bool RazuviousTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + Unit* razuvious = AI_VALUE2(Unit*, "find target", "instructor razuvious"); + Unit* understudy = AI_VALUE2(Unit*, "find target", "death knight understudy"); + Unit* target = nullptr; + if (botAI->IsTank(bot)) + { + target = understudy; + } + else + { + target = razuvious; + } + if (AI_VALUE(Unit*, "current target") == target) + { + return false; + } + return Attack(target); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Sapphiron.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Sapphiron.cpp new file mode 100644 index 0000000000..e749836619 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Sapphiron.cpp @@ -0,0 +1,121 @@ +#include "RaidNaxxActions.h" + +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "RaidNaxxBossHelper.h" +#include "RaidNaxxSpellIds.h" + +bool SapphironGroundPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (botAI->IsMainTank(bot)) + { + if (AI_VALUE2(bool, "has aggro", "current target")) + { + return MoveTo(NAXX_MAP_ID, helper.mainTankPos.first, helper.mainTankPos.second, helper.GENERIC_HEIGHT, false, false, false, + false, MovementPriority::MOVEMENT_COMBAT); + } + return false; + } + if (helper.JustLanded()) + { + uint32 index = botAI->GetGroupSlotIndex(bot); + float start_angle = 0.85 * M_PI; + float offset_angle = M_PI * 0.02 * index; + float angle = start_angle + offset_angle; + float distance; + if (botAI->IsRanged(bot)) + { + distance = 35.0f; + } + else if (botAI->IsHeal(bot)) + { + distance = 30.0f; + } + else + { + distance = 5.0f; + } + float posX = helper.center.first + cos(angle) * distance; + float posY = helper.center.second + sin(angle) * distance; + if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, 2.0f, MovementPriority::MOVEMENT_COMBAT); + } + else + { + std::vector dest; + if (helper.FindPosToAvoidChill(dest)) + { + return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} + +bool SapphironFlightPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (helper.WaitForExplosion()) + { + return MoveToNearestIcebolt(); + } + else + { + std::vector dest; + if (helper.FindPosToAvoidChill(dest)) + { + return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} + +bool SapphironFlightPositionAction::MoveToNearestIcebolt() +{ + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + Group::MemberSlotList const& slots = group->GetMemberSlots(); + Player* playerWithIcebolt = nullptr; + float minDistance; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || + botAI->HasAura("icebolt", member, false, false, -1, true)) + { + if (!playerWithIcebolt || minDistance > bot->GetDistance(member)) + { + playerWithIcebolt = member; + minDistance = bot->GetDistance(member); + } + } + } + if (playerWithIcebolt) + { + Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron"); + if (boss) + { + float angle = boss->GetAngle(playerWithIcebolt); + float posX = playerWithIcebolt->GetPositionX() + cos(angle) * 3.0f; + float posY = playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f; + if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveNear(playerWithIcebolt, 3.0f, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Shared.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Shared.cpp new file mode 100644 index 0000000000..7b399db6ba --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Shared.cpp @@ -0,0 +1,18 @@ +#include "RaidNaxxActions.h" + +uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint() +{ + float minDistance = 0; + int ret = -1; + for (int i = 0; i < intervals; i++) + { + float w_x = waypoints[i].first, w_y = waypoints[i].second; + float dis = bot->GetDistance2d(w_x, w_y); + if (ret == -1 || dis < minDistance) + { + ret = i; + minDistance = dis; + } + } + return ret; +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Thaddius.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Thaddius.cpp new file mode 100644 index 0000000000..65f7b0edd0 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Thaddius.cpp @@ -0,0 +1,140 @@ +#include "RaidNaxxActions.h" + +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "RaidNaxxSpellIds.h" + +bool ThaddiusAttackNearestPetAction::isUseful() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (!helper.IsPhasePet()) + { + return false; + } + Unit* target = helper.GetNearestPet(); + if (!bot->IsWithinDistInMap(target, 50.0f)) + { + return false; + } + return true; +} + +bool ThaddiusAttackNearestPetAction::Execute(Event event) +{ + Unit* target = helper.GetNearestPet(); + if (!bot->IsWithinLOSInMap(target)) + { + return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT); + } + if (AI_VALUE(Unit*, "current target") != target) + { + return Attack(target); + } + if (botAI->IsTank(bot) && AI_VALUE2(bool, "has aggro", "current target")) + { + std::pair posForTank = helper.PetPhaseGetPosForTank(); + return MoveTo(533, posForTank.first, posForTank.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + if (botAI->IsRanged(bot)) + { + std::pair posForRanged = helper.PetPhaseGetPosForRanged(); + return MoveTo(533, posForRanged.first, posForRanged.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return false; +} + +bool ThaddiusMoveToPlatformAction::isUseful() { return true; } + +bool ThaddiusMoveToPlatformAction::Execute(Event event) +{ + std::vector> position = { + // high left + {3462.99f, -2918.90f}, + // high right + {3520.65f, -2976.51f}, + // low left + {3471.36f, -2910.65f}, + // low right + {3528.80f, -2967.04f}, + // center + {3512.19f, -2928.58f}, + }; + float high_z = 312.00f, low_z = 304.02f; + bool is_left = bot->GetDistance2d(position[0].first, position[0].second) < + bot->GetDistance2d(position[1].first, position[1].second); + if (bot->GetPositionZ() >= (high_z - 3.0f)) + { + if (is_left) + { + if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + float distance = bot->GetExactDist2d(position[0].first, position[0].second); + if (distance < sPlayerbotAIConfig->contactDistance) + JumpTo(bot->GetMapId(), position[2].first, position[2].second, low_z, MovementPriority::MOVEMENT_COMBAT); + // bot->TeleportTo(bot->GetMapId(), position[2].first, position[2].second, low_z, bot->GetOrientation()); + } + } + else + { + if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + float distance = bot->GetExactDist2d(position[1].first, position[1].second); + if (distance < sPlayerbotAIConfig->contactDistance) + JumpTo(bot->GetMapId(), position[3].first, position[3].second, low_z, MovementPriority::MOVEMENT_COMBAT); + // bot->TeleportTo(bot->GetMapId(), position[3].first, position[3].second, low_z, bot->GetOrientation()); + } + } + } + else + { + return MoveTo(bot->GetMapId(), position[4].first, position[4].second, low_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return true; +} + +bool ThaddiusMovePolarityAction::isUseful() +{ + return !botAI->IsMainTank(bot) || AI_VALUE2(bool, "has aggro", "current target"); +} + +bool ThaddiusMovePolarityAction::Execute(Event event) +{ + std::vector> position = { + // left melee + {3508.29f, -2920.12f}, + // left ranged + {3501.72f, -2913.36f}, + // right melee + {3519.74f, -2931.69f}, + // right ranged + {3524.32f, -2936.26f}, + // center melee + {3512.19f, -2928.58f}, + // center ranged + {3504.68f, -2936.68f}, + }; + uint32 idx; + if (NaxxSpellIds::HasAnyAura( + botAI, bot, + {NaxxSpellIds::NegativeCharge10, NaxxSpellIds::NegativeCharge25, NaxxSpellIds::NegativeChargeStack}) || + botAI->HasAura("negative charge", bot, false, false, -1, true)) + { + idx = 0; + } + else if (NaxxSpellIds::HasAnyAura( + botAI, bot, + {NaxxSpellIds::PositiveCharge10, NaxxSpellIds::PositiveCharge25, NaxxSpellIds::PositiveChargeStack}) || + botAI->HasAura("positive charge", bot, false, false, -1, true)) + { + idx = 1; + } + else + { + idx = 2; + } + idx = idx * 2 + botAI->IsRanged(bot); + return MoveTo(bot->GetMapId(), position[idx].first, position[idx].second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +} \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxBossHelper.h b/src/Scenario/RaidAi/Naxxramas/RaidNaxxBossHelper.h new file mode 100644 index 0000000000..b64ede3217 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxBossHelper.h @@ -0,0 +1,606 @@ +#ifndef _PLAYERBOT_RAIDNAXXBOSSHELPER_H +#define _PLAYERBOT_RAIDNAXXBOSSHELPER_H + +#include + +#include "AiObject.h" +#include "AiObjectContext.h" +#include "EventMap.h" +#include "Log.h" +#include "NamedObjectContext.h" +#include "ObjectGuid.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "ScriptedCreature.h" +#include "SharedDefines.h" +#include "Spell.h" +#include "Timer.h" +#include "RaidNaxxSpellIds.h" + +const uint32 NAXX_MAP_ID = 533; + +template +class GenericBossHelper : public AiObject +{ +public: + GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {} + virtual bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + _unit = nullptr; + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + _unit = nullptr; + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", _name); + if (!_unit) + { + return false; + } + _target = _unit->ToCreature(); + if (!_target) + { + return false; + } + _ai = dynamic_cast(_target->GetAI()); + if (!_ai) + { + return false; + } + _event_map = &_ai->events; + if (!_event_map) + { + return false; + } + } + if (!_event_map) + { + return false; + } + _timer = getMSTime(); + return true; + } + virtual void Reset() + { + _unit = nullptr; + _target = nullptr; + _ai = nullptr; + _event_map = nullptr; + _timer = 0; + } + +protected: + std::string _name; + Unit* _unit = nullptr; + Creature* _target = nullptr; + BossAiType* _ai = nullptr; + EventMap* _event_map = nullptr; + uint32 _timer = 0; +}; + +class KelthuzadBossHelper : public AiObject +{ +public: + KelthuzadBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + const std::pair center = {3716.19f, -5106.58f}; + const std::pair tank_pos = {3709.19f, -5104.86f}; + const std::pair assist_tank_pos = {3746.05f, -5112.74f}; + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "kel'thuzad"); + } + return _unit != nullptr; + } + bool IsPhaseOne() { return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } + bool IsPhaseTwo() { return _unit && !_unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } + Unit* GetAnyShadowFissure() + { + Unit* shadow_fissure = nullptr; + GuidVector units = *context->GetValue("nearest triggers"); + for (auto i = units.begin(); i != units.end(); i++) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (botAI->EqualLowercaseName(unit->GetName(), "shadow fissure")) + { + shadow_fissure = unit; + } + } + return shadow_fissure; + } + +private: + void Reset() { _unit = nullptr; } + + Unit* _unit = nullptr; +}; + +class RazuviousBossHelper : public AiObject +{ +public: + RazuviousBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "instructor razuvious"); + } + return _unit != nullptr; + } + +private: + void Reset() { _unit = nullptr; } + + Unit* _unit = nullptr; +}; + +class SapphironBossHelper : public AiObject +{ +public: + const std::pair mainTankPos = {3512.07f, -5274.06f}; + const std::pair center = {3517.31f, -5253.74f}; + const float GENERIC_HEIGHT = 137.29f; + SapphironBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "sapphiron"); + if (!_unit) + { + return false; + } + } + bool now_flying = _unit->IsFlying(); + if (_was_flying && !now_flying) + { + _last_land_ms = getMSTime(); + } + _was_flying = now_flying; + return true; + } + bool IsPhaseGround() { return _unit && !_unit->IsFlying(); } + bool IsPhaseFlight() { return _unit && _unit->IsFlying(); } + bool JustLanded() + { + if (!_last_land_ms) + { + return false; + } + return getMSTime() - _last_land_ms <= POSITION_TIME_AFTER_LANDED; + } + bool WaitForExplosion() + { + if (!IsPhaseFlight()) + { + return false; + } + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && + (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || + botAI->HasAura("icebolt", member, false, false, -1, true))) + { + return true; + } + } + return false; + } + bool FindPosToAvoidChill(std::vector& dest) + { + Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25}); + if (!aura) + { + // Fallback to name for custom spell data. + aura = botAI->GetAura("chill", bot); + } + if (!aura) + { + return false; + } + DynamicObject* dyn_obj = aura->GetDynobjOwner(); + if (!dyn_obj) + { + return false; + } + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + float angle = 0; + uint32 index = botAI->GetGroupSlotIndex(bot); + if (currentTarget) + { + if (botAI->IsRanged(bot)) + { + if (bot->GetExactDist2d(currentTarget) <= 45.0f) + { + angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2; + } + else + { + if (index % 2 == 0) + { + angle = bot->GetAngle(currentTarget) + M_PI / 2; + } + else + { + angle = bot->GetAngle(currentTarget) - M_PI / 2; + } + } + } + else + { + if (index % 3 == 0) + { + angle = bot->GetAngle(currentTarget); + } + else if (index % 3 == 1) + { + angle = bot->GetAngle(currentTarget) + M_PI / 2; + } + else + { + angle = bot->GetAngle(currentTarget) - M_PI / 2; + } + } + } + else + { + angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2; + } + dest = {bot->GetPositionX() + cos(angle) * 5.0f, bot->GetPositionY() + sin(angle) * 5.0f, bot->GetPositionZ()}; + return true; + } + +private: + void Reset() + { + _unit = nullptr; + _was_flying = false; + _last_land_ms = 0; + } + + const uint32 POSITION_TIME_AFTER_LANDED = 5000; + Unit* _unit = nullptr; + bool _was_flying = false; + uint32 _last_land_ms = 0; +}; + +class GluthBossHelper : public AiObject +{ +public: + const std::pair mainTankPos25 = {3331.48f, -3109.06f}; + const std::pair mainTankPos10 = {3278.29f, -3162.06f}; + const std::pair beforeDecimatePos = {3267.34f, -3175.68f}; + const std::pair leftSlowDownPos = {3290.68f, -3141.65f}; + const std::pair rightSlowDownPos = {3300.78f, -3151.98f}; + const std::pair rangedPos = {3301.45f, -3139.29f}; + const std::pair healPos = {3303.09f, -3135.24f}; + + const float decimatedZombiePct = 10.0f; + GluthBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "gluth"); + if (!_unit) + { + return false; + } + } + if (_unit->IsInCombat()) + { + if (_combat_start_ms == 0) + { + _combat_start_ms = getMSTime(); + } + } + else + { + _combat_start_ms = 0; + } + return true; + } + bool BeforeDecimate() + { + if (!_unit || !_unit->HasUnitState(UNIT_STATE_CASTING)) + { + return false; + } + Spell* spell = _unit->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = _unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (!spell) + { + return false; + } + SpellInfo const* info = spell->GetSpellInfo(); + if (!info) + { + return false; + } + if (NaxxSpellIds::MatchesAnySpellId( + info, {NaxxSpellIds::Decimate10, NaxxSpellIds::Decimate25, NaxxSpellIds::Decimate25Alt})) + { + return true; + } + // Fallback to name for custom spell data. + return info->SpellName[LOCALE_enUS] && botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "decimate"); + } + bool JustStartCombat() const { return _combat_start_ms != 0 && getMSTime() - _combat_start_ms < 10000; } + bool IsZombieChow(Unit* unit) const { return unit && botAI->EqualLowercaseName(unit->GetName(), "zombie chow"); } + +private: + void Reset() + { + _unit = nullptr; + _combat_start_ms = 0; + } + + Unit* _unit = nullptr; + uint32 _combat_start_ms = 0; +}; + +class LoathebBossHelper : public AiObject +{ +public: + const std::pair mainTankPos = {2877.57f, -3967.00f}; + const std::pair rangePos = {2896.96f, -3980.61f}; + LoathebBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "loatheb"); + } + return _unit != nullptr; + } + +private: + void Reset() { _unit = nullptr; } + + Unit* _unit = nullptr; +}; + +class FourhorsemanBossHelper : public AiObject +{ +public: + const float posZ = 241.27f; + const std::pair attractPos[2] = {{2502.03f, -2910.90f}, + {2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux) + FourhorsemanBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + else if (_combat_start_ms == 0) + { + _combat_start_ms = getMSTime(); + } + if (_sir && (!_sir->IsInWorld() || !_sir->IsAlive())) + { + Reset(); + } + if (!_sir) + { + _sir = AI_VALUE2(Unit*, "find target", "sir zeliek"); + if (!_sir) + { + return false; + } + } + _lady = AI_VALUE2(Unit*, "find target", "lady blaumeux"); + return true; + } + void Reset() + { + _sir = nullptr; + _lady = nullptr; + _combat_start_ms = 0; + posToGo = 0; + } + bool IsAttracter(Player* bot) + { + Difficulty diff = bot->GetRaidDifficulty(); + if (diff == RAID_DIFFICULTY_25MAN_NORMAL) + { + return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0) || + botAI->IsHealAssistantOfIndex(bot, 1) || botAI->IsHealAssistantOfIndex(bot, 2); + } + return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0); + } + void CalculatePosToGo(Player* bot) + { + bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; + Unit* lady = _lady; + if (!lady) + { + posToGo = 0; + } + else + { + uint32 elapsed_ms = _combat_start_ms ? getMSTime() - _combat_start_ms : 0; + // Interval: 24s - 15s - 15s - ... + posToGo = !(elapsed_ms <= 9000 || ((elapsed_ms - 9000) / 67500) % 2 == 0); + if (botAI->IsRangedDpsAssistantOfIndex(bot, 0) || (raid25 && botAI->IsHealAssistantOfIndex(bot, 1))) + { + posToGo = 1 - posToGo; + } + } + } + std::pair CurrentAttractPos() + { + bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL; + float posX = attractPos[posToGo].first, posY = attractPos[posToGo].second; + if (posToGo == 1) + { + float offset_x = 0.0f; + float offset_y = 0.0f; + float bias = 4.5f; + if (raid25) + { + offset_x = -bias; + offset_y = bias; + } + posX += offset_x; + posY += offset_y; + } + return {posX, posY}; + } + Unit* CurrentAttackTarget() + { + if (posToGo == 0) + { + return _sir; + } + return _lady; + } + +protected: + Unit* _sir = nullptr; + Unit* _lady = nullptr; + uint32 _combat_start_ms = 0; + int posToGo = 0; +}; +class ThaddiusBossHelper : public AiObject +{ +public: + const std::pair tankPosFeugen = {3522.94f, -3002.60f}; + const std::pair tankPosStalagg = {3436.14f, -2919.98f}; + const std::pair rangedPosFeugen = {3500.45f, -2997.92f}; + const std::pair rangedPosStalagg = {3441.01f, -2942.04f}; + const float tankPosZ = 312.61f; + ThaddiusBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "thaddius"); + if (!_unit) + { + return false; + } + } + feugen = AI_VALUE2(Unit*, "find target", "feugen"); + stalagg = AI_VALUE2(Unit*, "find target", "stalagg"); + return true; + } + bool IsPhasePet() { return (feugen && feugen->IsAlive()) || (stalagg && stalagg->IsAlive()); } + bool IsPhaseTransition() + { + if (IsPhasePet()) + { + return false; + } + return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + } + bool IsPhaseThaddius() { return !IsPhasePet() && !IsPhaseTransition(); } + Unit* GetNearestPet() + { + Unit* unit = nullptr; + if (feugen && feugen->IsAlive()) + { + unit = feugen; + } + if (stalagg && stalagg->IsAlive() && (!feugen || bot->GetDistance(stalagg) < bot->GetDistance(feugen))) + { + unit = stalagg; + } + return unit; + } + std::pair PetPhaseGetPosForTank() + { + if (GetNearestPet() == feugen) + { + return tankPosFeugen; + } + return tankPosStalagg; + } + std::pair PetPhaseGetPosForRanged() + { + if (GetNearestPet() == feugen) + { + return rangedPosFeugen; + } + return rangedPosStalagg; + } + +protected: + void Reset() + { + _unit = nullptr; + feugen = nullptr; + stalagg = nullptr; + } + + Unit* _unit = nullptr; + Unit* feugen = nullptr; + Unit* stalagg = nullptr; +}; + +#endif diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.cpp new file mode 100644 index 0000000000..ef5913b5bc --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.cpp @@ -0,0 +1,344 @@ +#include "RaidNaxxMultipliers.h" + +#include "ChooseTargetActions.h" +#include "DKActions.h" +#include "DruidActions.h" +#include "DruidBearActions.h" +#include "FollowActions.h" +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "HunterActions.h" +#include "MageActions.h" +#include "MovementActions.h" +#include "PaladinActions.h" +#include "PriestActions.h" +#include "RaidNaxxActions.h" +#include "RaidNaxxSpellIds.h" +#include "ReachTargetActions.h" +#include "RogueActions.h" +#include "ScriptedCreature.h" +#include "ShamanActions.h" +#include "Spell.h" +#include "UseMeetingStoneAction.h" +#include "WarriorActions.h" + +float GrobbulusMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return 1.0f; + } + if (dynamic_cast(action)) + { + return botAI->IsMainTank(bot) ? 0.0f : 1.0f; + } + if (dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} + +float HeiganDanceMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean"); + if (!boss) + { + return 1.0f; + } + bool platform_phase = boss->IsWithinDist2d(2794.26f, -3706.67f, 10.0f); + bool eruption_casting = false; + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (spell) + { + SpellInfo const* info = spell->GetSpellInfo(); + bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10}); + if (!isEruption && info && info->SpellName[LOCALE_enUS]) + { + // Fallback to name for custom spell data. + isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption"); + } + if (isEruption) + { + eruption_casting = true; + } + } + } + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) ) + { + return 0.0f; + } + if (!platform_phase && !eruption_casting) + { + return 1.0f; + } + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 1.0f; + } + if (dynamic_cast(action) && !dynamic_cast(action)) + { + CastSpellAction* spellAction = dynamic_cast(action); + uint32 spellId = AI_VALUE2(uint32, "spell id", spellAction->getSpell()); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + { + return 0.0f; + } + uint32 castTime = spellInfo->CalcCastTime(); + if (castTime == 0 && !spellInfo->IsChanneled()) + { + return 1.0f; + } + } + return 0.0f; +} + +float LoathebGenericMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loatheb"); + if (!boss) + { + return 1.0f; + } + context->GetValue("neglect threat")->Set(true); + if (botAI->GetState() == BOT_STATE_COMBAT && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) + { + return 0.0f; + } + if (!dynamic_cast(action)) + { + return 1.0f; + } + Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::NecroticAura10}); + if (!aura) + { + // Fallback to name for custom spell data. + aura = botAI->GetAura("necrotic aura", bot); + } + if (!aura || aura->GetDuration() <= 1500) + { + return 1.0f; + } + return 0.0f; +} + +float ThaddiusGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if (dynamic_cast(action)) + return 0.0f; + // pet phase + if (helper.IsPhasePet() && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + // die at the same time + Unit* target = AI_VALUE(Unit*, "current target"); + Unit* feugen = AI_VALUE2(Unit*, "find target", "feugen"); + Unit* stalagg = AI_VALUE2(Unit*, "find target", "stalagg"); + if (helper.IsPhasePet() && target && feugen && stalagg && target->GetHealthPct() <= 40 && + (feugen->GetHealthPct() >= target->GetHealthPct() + 3 || stalagg->GetHealthPct() >= target->GetHealthPct() + 3)) + { + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + // magnetic pull + // uint32 curr_timer = eventMap->GetTimer(); + // // if (curr_phase == 2 && bot->GetPositionZ() > 312.5f && dynamic_cast(action)) + // { + // if (curr_phase == 2 && (curr_timer % 20000 >= 18000 || curr_timer % 20000 <= 2000) && + // dynamic_cast(action)) + // { + // // MotionMaster *mm = bot->GetMotionMaster(); + // // mm->Clear(); + // return 0.0f; + // } + // thaddius phase + // if (curr_phase == 8 && dynamic_cast(action)) + // { + // return 0.0f; + // } + return 1.0f; +} + +float SapphironGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} + +float InstructorRazuviousGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + context->GetValue("neglect threat")->Set(true); + if (botAI->GetState() == BOT_STATE_COMBAT && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + return 1.0f; +} + +float KelthuzadGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if ((dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + if (helper.IsPhaseOne()) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + { + return 0.0f; + } + } + if (helper.IsPhaseTwo()) + { + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float AnubrekhanGenericMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); + if (!boss) + { + return 1.0f; + } + if (NaxxSpellIds::HasAnyAura( + botAI, boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) || + botAI->HasAura("locust swarm", boss)) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float FourhorsemanGenericMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek"); + if (!boss) + { + return 1.0f; + } + context->GetValue("neglect threat")->Set(true); + if ((dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + return 1.0f; +} + +// float GothikGenericMultiplier::GetValue(Action* action) +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); +// if (!boss) +// { +// return 1.0f; +// } +// BossAI* boss_ai = dynamic_cast(boss->GetAI()); +// EventMap* eventMap = boss_botAI->GetEvents(); +// uint32 curr_phase = eventMap->GetPhaseMask(); +// if (curr_phase == 1 && (dynamic_cast(action))) +// { +// return 0.0f; +// } +// if (curr_phase == 1 && (dynamic_cast(action))) +// { +// Unit* target = action->GetTarget(); +// if (target == boss) +// { +// return 0.0f; +// } +// } +// return 1.0f; +// } + +float GluthGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if ((dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) + { + return 0.0f; + } + + if (botAI->IsMainTank(bot)) + { + Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25}); + if (!aura) + { + // Fallback to name for custom spell data. + aura = botAI->GetAura("mortal wound", bot, false, true); + } + if (aura && aura->GetStackAmount() >= 5) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + { + return 0.0f; + } + } + } + if (dynamic_cast(action)) + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (helper.IsZombieChow(target)) + { + return 0.0f; + } + } + return 1.0f; +} diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.h b/src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.h new file mode 100644 index 0000000000..ecc335c910 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.h @@ -0,0 +1,116 @@ + +#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_ +#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H_ + +#include "Multiplier.h" +#include "RaidNaxxBossHelper.h" + +class GrobbulusMultiplier : public Multiplier +{ +public: + GrobbulusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grobbulus") {} + +public: + virtual float GetValue(Action* action); +}; +class HeiganDanceMultiplier : public Multiplier +{ +public: + HeiganDanceMultiplier(PlayerbotAI* ai) : Multiplier(ai, "helgan dance") {} + +public: + virtual float GetValue(Action* action); +}; + +class LoathebGenericMultiplier : public Multiplier +{ +public: + LoathebGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loatheb generic") {} + +public: + virtual float GetValue(Action* action); +}; + +class ThaddiusGenericMultiplier : public Multiplier +{ +public: + ThaddiusGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "thaddius generic"), helper(ai) {} + +public: + virtual float GetValue(Action* action); + +private: + ThaddiusBossHelper helper; +}; + +class SapphironGenericMultiplier : public Multiplier +{ +public: + SapphironGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sapphiron generic"), helper(ai) {} + + virtual float GetValue(Action* action); + +private: + SapphironBossHelper helper; +}; + +class InstructorRazuviousGenericMultiplier : public Multiplier +{ +public: + InstructorRazuviousGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "instructor razuvious generic"), helper(ai) + { + } + virtual float GetValue(Action* action); + +private: + RazuviousBossHelper helper; +}; + +class KelthuzadGenericMultiplier : public Multiplier +{ +public: + KelthuzadGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "kelthuzad generic"), helper(ai) {} + virtual float GetValue(Action* action); + +private: + KelthuzadBossHelper helper; +}; + +class AnubrekhanGenericMultiplier : public Multiplier +{ +public: + AnubrekhanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anubrekhan generic") {} + +public: + virtual float GetValue(Action* action); +}; + +class FourhorsemanGenericMultiplier : public Multiplier +{ +public: + FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "fourhorseman generic") {} + +public: + virtual float GetValue(Action* action); +}; + +// class GothikGenericMultiplier : public Multiplier +// { +// public: +// GothikGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gothik generic") {} + +// public: +// virtual float GetValue(Action* action); +// }; + +class GluthGenericMultiplier : public Multiplier +{ +public: + GluthGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gluth generic"), helper(ai) {} + float GetValue(Action* action) override; + +private: + GluthBossHelper helper; +}; + +#endif diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxSpellIds.h b/src/Scenario/RaidAi/Naxxramas/RaidNaxxSpellIds.h new file mode 100644 index 0000000000..858911c6b3 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxSpellIds.h @@ -0,0 +1,177 @@ +#ifndef _PLAYERBOT_RAIDNAXXSPELLIDS_H +#define _PLAYERBOT_RAIDNAXXSPELLIDS_H + +#include + +#include "PlayerbotAI.h" + +// use src/server/scripts/Northrend/Naxxramas/naxxramas.h for CreatureId, NaxxramasSay, NaxxramasEvent, NaxxramasMisc +namespace NaxxSpellIds +{ + // Heigan + static constexpr uint32 Eruption10 = 29371; +/* + SPELL_SPELL_DISRUPTION = 29310, + SPELL_DECREPIT_FEVER = 29998, + SPELL_PLAGUE_CLOUD = 29350, + SPELL_TELEPORT_SELF = 30211 +*/ + + // Grobbulus + static constexpr uint32 PoisonCloud = 28240; + + // Thaddius polarity + static constexpr uint32 PositiveCharge10 = 28059; + static constexpr uint32 PositiveCharge25 = 28062; + static constexpr uint32 PositiveChargeStack = 29659; + static constexpr uint32 NegativeCharge10 = 28084; + static constexpr uint32 NegativeCharge25 = 28085; + static constexpr uint32 NegativeChargeStack = 29660; +/* + SPELL_MAGNETIC_PULL = 28337, + SPELL_TESLA_SHOCK = 28099, + SPELL_SHOCK_VISUAL = 28159, + + // Stalagg + SPELL_POWER_SURGE = 54529, + SPELL_STALAGG_CHAIN = 28096, + + // Feugen + SPELL_STATIC_FIELD = 28135, + SPELL_FEUGEN_CHAIN = 28111, + + // Thaddius + SPELL_POLARITY_SHIFT = 28089, + SPELL_BALL_LIGHTNING = 28299, + SPELL_CHAIN_LIGHTNING = 28167, + SPELL_BERSERK = 27680, + SPELL_THADDIUS_VISUAL_LIGHTNING = 28136, + SPELL_THADDIUS_SPAWN_STUN = 28160, + + SPELL_POSITIVE_CHARGE = 28062, + SPELL_POSITIVE_CHARGE_STACK = 29659, + SPELL_NEGATIVE_CHARGE = 28085, + SPELL_NEGATIVE_CHARGE_STACK = 29660, + SPELL_POSITIVE_POLARITY = 28059, + SPELL_NEGATIVE_POLARITY = 28084 +*/ + // Sapphiron + static constexpr uint32 Icebolt10 = 28522; + static constexpr uint32 Icebolt25 = 28526; + static constexpr uint32 Chill25 = 55699; +/* + // Fight + SPELL_FROST_AURA = 28531, + SPELL_CLEAVE = 19983, + SPELL_TAIL_SWEEP = 55697, + SPELL_SUMMON_BLIZZARD = 28560, + SPELL_LIFE_DRAIN = 28542, + SPELL_BERSERK = 26662, + + // Ice block + SPELL_ICEBOLT_CAST = 28526, + SPELL_ICEBOLT_TRIGGER = 28522, + SPELL_FROST_MISSILE = 30101, + SPELL_FROST_EXPLOSION = 28524, + + // Visuals + SPELL_SAPPHIRON_DIES = 29357 +*/ + // Gluth + static constexpr uint32 Decimate10 = 28374; + static constexpr uint32 Decimate25 = 54426; + static constexpr uint32 Decimate25Alt = 28375; + static constexpr uint32 MortalWound10 = 25646; + static constexpr uint32 MortalWound25 = 54378; +/* + SPELL_MORTAL_WOUND = 25646, + SPELL_ENRAGE = 28371, + SPELL_DECIMATE = 28374, + SPELL_DECIMATE_DAMAGE = 28375, + SPELL_BERSERK = 26662, + SPELL_INFECTED_WOUND = 29306, + SPELL_CHOW_SEARCHER = 28404 +*/ + // Anub'Rekhan + static constexpr uint32 LocustSwarm10 = 28785; + static constexpr uint32 LocustSwarm10Alt = 28786; + static constexpr uint32 LocustSwarm25 = 54021; // 25-man Locust Swarm +/* + SPELL_IMPALE = 28783, + SPELL_LOCUST_SWARM = 28785, + SPELL_SUMMON_CORPSE_SCARABS_5 = 29105, + SPELL_SUMMON_CORPSE_SCARABS_10 = 28864, + SPELL_BERSERK = 26662 + ACHIEV_TIMED_START_EVENT = 9891, + EVENT_SPAWN_CRYPT_GUARDS_1 = 0, + EVENT_BERSERK = 1, + //// + Position const cryptguardPositions[] = { + { 3299.732f, -3502.489f, 287.077f, 2.378f }, + { 3299.086f, -3450.929f, 287.077f, 3.999f }, + { 3331.217f, -3476.607f, 287.074f, 3.269f } +}; + +*/ + // Loatheb + static constexpr uint32 NecroticAura10 = 55593; +/* + SPELL_NECROTIC_AURA = 55593, + SPELL_SUMMON_SPORE = 29234, + SPELL_DEATHBLOOM = 29865, + SPELL_INEVITABLE_DOOM = 29204, + SPELL_BERSERK = 26662 +*/ + inline bool HasAnyAura(PlayerbotAI* botAI, Unit* unit, std::initializer_list spellIds) + { + if (!botAI || !unit) + { + return false; + } + + for (uint32 spellId : spellIds) + { + if (botAI->HasAura(spellId, unit)) + { + return true; + } + } + return false; + } + + inline Aura* GetAnyAura(Unit* unit, std::initializer_list spellIds) + { + if (!unit) + { + return nullptr; + } + + for (uint32 spellId : spellIds) + { + if (Aura* aura = unit->GetAura(spellId)) + { + return aura; + } + } + return nullptr; + } + + inline bool MatchesAnySpellId(SpellInfo const* info, std::initializer_list spellIds) + { + if (!info) + { + return false; + } + + for (uint32 spellId : spellIds) + { + if (info->Id == spellId) + { + return true; + } + } + return false; + } +} // namespace NaxxSpellIds + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.cpp new file mode 100644 index 0000000000..f2ebd24a3e --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.cpp @@ -0,0 +1,156 @@ +#include "RaidNaxxStrategy.h" + +#include "RaidNaxxMultipliers.h" + +void RaidNaxxStrategy::InitTriggers(std::vector& triggers) +{ + // Grobbulus + triggers.push_back(new TriggerNode("mutating injection melee", + { NextAction("grobbulus move away", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("mutating injection ranged", + { NextAction("grobbulus go behind the boss", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("mutating injection removed", + { NextAction("grobbulus move center", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("grobbulus cloud", + { NextAction("rotate grobbulus", ACTION_RAID + 1) } + )); + + // Heigan the Unclean + triggers.push_back(new TriggerNode("heigan melee", + { NextAction("heigan dance melee", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("heigan ranged", + { NextAction("heigan dance ranged", ACTION_RAID + 1) } + )); + + // Kel'Thuzad + triggers.push_back( + new TriggerNode("kel'thuzad", + { + NextAction("kel'thuzad position", ACTION_RAID + 2), + NextAction("kel'thuzad choose target", ACTION_RAID + 1) + }) + ); + + // Anub'Rekhan + triggers.push_back(new TriggerNode("anub'rekhan", + { NextAction("anub'rekhan position", ACTION_RAID + 1) } + )); + + // Grand Widow Faerlina + triggers.push_back(new TriggerNode("faerlina", + { NextAction("avoid aoe", ACTION_RAID + 1) } + )); + + // Maexxna + triggers.push_back( + new TriggerNode("maexxna", + { + NextAction("rear flank", ACTION_RAID + 1), + NextAction("avoid aoe", ACTION_RAID + 1) + }) + ); + + // Patchwerk + triggers.push_back(new TriggerNode("patchwerk tank", + { NextAction("tank face", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("patchwerk ranged", + { NextAction("patchwerk ranged position", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("patchwerk non-tank", + { NextAction("rear flank", ACTION_RAID + 1) } + )); + + // Thaddius + triggers.push_back(new TriggerNode("thaddius phase pet", + { NextAction("thaddius attack nearest pet", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("thaddius phase pet lose aggro", + { NextAction("taunt spell", ACTION_RAID + 2) } + )); + + triggers.push_back(new TriggerNode("thaddius phase transition", + { NextAction("thaddius move to platform", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("thaddius phase thaddius", + { NextAction("thaddius move polarity", ACTION_RAID + 1) } + )); + + // Instructor Razuvious + triggers.push_back(new TriggerNode("razuvious tank", + { NextAction("razuvious use obedience crystal", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("razuvious nontank", + { NextAction("razuvious target", ACTION_RAID + 1) } + )); + + // four horseman + triggers.push_back(new TriggerNode("horseman attractors", + { NextAction("horseman attract alternatively", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("horseman except attractors", + { NextAction("horseman attack in order", ACTION_RAID + 1) } + )); + + // sapphiron + triggers.push_back(new TriggerNode("sapphiron ground", + { NextAction("sapphiron ground position", ACTION_RAID + 1) } + )); + + triggers.push_back(new TriggerNode("sapphiron flight", + { NextAction("sapphiron flight position", ACTION_RAID + 1) } + )); + + // Gluth + triggers.push_back( + new TriggerNode("gluth", + { + NextAction("gluth choose target", ACTION_RAID + 1), + NextAction("gluth position", ACTION_RAID + 1), + NextAction("gluth slowdown", ACTION_RAID) + }) + ); + + triggers.push_back(new TriggerNode("gluth main tank mortal wound", + { NextAction("taunt spell", ACTION_RAID + 1) } + )); + + // Loatheb + triggers.push_back( + new TriggerNode("loatheb", + { + NextAction("loatheb position", ACTION_RAID + 1), + NextAction("loatheb choose target", ACTION_RAID + 1) + }) + ); + +} + +void RaidNaxxStrategy::InitMultipliers(std::vector& multipliers) +{ + multipliers.push_back(new GrobbulusMultiplier(botAI)); + multipliers.push_back(new HeiganDanceMultiplier(botAI)); + multipliers.push_back(new LoathebGenericMultiplier(botAI)); + multipliers.push_back(new ThaddiusGenericMultiplier(botAI)); + multipliers.push_back(new SapphironGenericMultiplier(botAI)); + multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI)); + multipliers.push_back(new KelthuzadGenericMultiplier(botAI)); + multipliers.push_back(new AnubrekhanGenericMultiplier(botAI)); + multipliers.push_back(new FourhorsemanGenericMultiplier(botAI)); + // multipliers.push_back(new GothikGenericMultiplier(botAI)); + multipliers.push_back(new GluthGenericMultiplier(botAI)); +} diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.h b/src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.h new file mode 100644 index 0000000000..dee1720bd8 --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.h @@ -0,0 +1,18 @@ + +#ifndef _PLAYERBOT_RAIDNAXXSTRATEGY_H +#define _PLAYERBOT_RAIDNAXXSTRATEGY_H + +#include "AiObjectContext.h" +#include "Multiplier.h" +#include "Strategy.h" + +class RaidNaxxStrategy : public Strategy +{ +public: + RaidNaxxStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "naxx"; } + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggerContext.h b/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggerContext.h new file mode 100644 index 0000000000..c8ae79c18a --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggerContext.h @@ -0,0 +1,86 @@ +// /* +// * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it +// and/or modify it under version 3 of the License, or (at your option), any later version. +// */ + +#ifndef _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H + +#include "AiObjectContext.h" +#include "NamedObjectContext.h" +#include "RaidNaxxTriggers.h" + +class RaidNaxxTriggerContext : public NamedObjectContext +{ +public: + RaidNaxxTriggerContext() + { + creators["mutating injection melee"] = &RaidNaxxTriggerContext::mutating_injection_melee; + creators["mutating injection ranged"] = &RaidNaxxTriggerContext::mutating_injection_ranged; + creators["mutating injection removed"] = &RaidNaxxTriggerContext::mutating_injection_removed; + creators["grobbulus cloud"] = &RaidNaxxTriggerContext::grobbulus_cloud; + creators["heigan melee"] = &RaidNaxxTriggerContext::heigan_melee; + creators["heigan ranged"] = &RaidNaxxTriggerContext::heigan_ranged; + + creators["thaddius phase pet"] = &RaidNaxxTriggerContext::thaddius_phase_pet; + creators["thaddius phase pet lose aggro"] = &RaidNaxxTriggerContext::thaddius_phase_pet_lose_aggro; + creators["thaddius phase transition"] = &RaidNaxxTriggerContext::thaddius_phase_transition; + creators["thaddius phase thaddius"] = &RaidNaxxTriggerContext::thaddius_phase_thaddius; + + creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank; + creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank; + + creators["horseman attractors"] = &RaidNaxxTriggerContext::horseman_attractors; + creators["horseman except attractors"] = &RaidNaxxTriggerContext::horseman_except_attractors; + + creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground; + creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight; + + creators["kel'thuzad"] = &RaidNaxxTriggerContext::kelthuzad; + + creators["anub'rekhan"] = &RaidNaxxTriggerContext::anubrekhan; + creators["faerlina"] = &RaidNaxxTriggerContext::faerlina; + creators["maexxna"] = &RaidNaxxTriggerContext::maexxna; + creators["patchwerk tank"] = &RaidNaxxTriggerContext::patchwerk_tank; + creators["patchwerk non-tank"] = &RaidNaxxTriggerContext::patchwerk_non_tank; + creators["patchwerk ranged"] = &RaidNaxxTriggerContext::patchwerk_ranged; + + creators["gluth"] = &RaidNaxxTriggerContext::gluth; + creators["gluth main tank mortal wound"] = &RaidNaxxTriggerContext::gluth_main_tank_mortal_wound; + + creators["loatheb"] = &RaidNaxxTriggerContext::loatheb; + } + +private: + static Trigger* mutating_injection_melee(PlayerbotAI* ai) { return new MutatingInjectionMeleeTrigger(ai); } + static Trigger* mutating_injection_ranged(PlayerbotAI* ai) { return new MutatingInjectionRangedTrigger(ai); } + static Trigger* mutating_injection_removed(PlayerbotAI* ai) { return new MutatingInjectionRemovedTrigger(ai); } + static Trigger* grobbulus_cloud(PlayerbotAI* ai) { return new GrobbulusCloudTrigger(ai); } + static Trigger* heigan_melee(PlayerbotAI* ai) { return new HeiganMeleeTrigger(ai); } + static Trigger* heigan_ranged(PlayerbotAI* ai) { return new HeiganRangedTrigger(ai); } + + static Trigger* thaddius_phase_pet(PlayerbotAI* ai) { return new ThaddiusPhasePetTrigger(ai); } + static Trigger* thaddius_phase_pet_lose_aggro(PlayerbotAI* ai) { return new ThaddiusPhasePetLoseAggroTrigger(ai); } + static Trigger* thaddius_phase_transition(PlayerbotAI* ai) { return new ThaddiusPhaseTransitionTrigger(ai); } + static Trigger* thaddius_phase_thaddius(PlayerbotAI* ai) { return new ThaddiusPhaseThaddiusTrigger(ai); } + static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); } + static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); } + + static Trigger* horseman_attractors(PlayerbotAI* ai) { return new HorsemanAttractorsTrigger(ai); } + static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new HorsemanExceptAttractorsTrigger(ai); } + + static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); } + static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(ai); } + static Trigger* kelthuzad(PlayerbotAI* ai) { return new KelthuzadTrigger(ai); } + static Trigger* anubrekhan(PlayerbotAI* ai) { return new AnubrekhanTrigger(ai); } + static Trigger* faerlina(PlayerbotAI* ai) { return new FaerlinaTrigger(ai); } + static Trigger* maexxna(PlayerbotAI* ai) { return new MaexxnaTrigger(ai); } + static Trigger* patchwerk_tank(PlayerbotAI* ai) { return new PatchwerkTankTrigger(ai); } + static Trigger* patchwerk_non_tank(PlayerbotAI* ai) { return new PatchwerkNonTankTrigger(ai); } + static Trigger* patchwerk_ranged(PlayerbotAI* ai) { return new PatchwerkRangedTrigger(ai); } + static Trigger* gluth(PlayerbotAI* ai) { return new GluthTrigger(ai); } + static Trigger* gluth_main_tank_mortal_wound(PlayerbotAI* ai) { return new GluthMainTankMortalWoundTrigger(ai); } + static Trigger* loatheb(PlayerbotAI* ai) { return new LoathebTrigger(ai); } +}; + +#endif \ No newline at end of file diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.cpp b/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.cpp new file mode 100644 index 0000000000..a38f800f7c --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.cpp @@ -0,0 +1,283 @@ +#include "RaidNaxxTriggers.h" + +#include "Playerbots.h" +#include "RaidNaxxSpellIds.h" +#include "Timer.h" +#include "Trigger.h" + +bool MutatingInjectionMeleeTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return false; + } + return MutatingInjectionTrigger::IsActive() && !botAI->IsRanged(bot); +} + +bool MutatingInjectionRangedTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return false; + } + return MutatingInjectionTrigger::IsActive() && botAI->IsRanged(bot); +} + +bool AuraRemovedTrigger::IsActive() +{ + bool check = botAI->HasAura(name, bot, false, false, -1, true); + bool ret = false; + if (prev_check && !check) + { + ret = true; + } + prev_check = check; + return ret; +} + +bool MutatingInjectionRemovedTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return false; + } + return HasNoAuraTrigger::IsActive() && botAI->GetState() == BOT_STATE_COMBAT && botAI->IsRanged(bot); +} + +bool GrobbulusCloudTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus"); + if (!boss) + { + return false; + } + if (!botAI->IsMainTank(bot)) + { + return false; + } + // bot->Yell("has aggro on " + boss->GetName() + " : " + to_string(AI_VALUE2(bool, "has aggro", "boss target")), + // LANG_UNIVERSAL); + if (!AI_VALUE2(bool, "has aggro", "boss target")) + { + return false; + } + uint32 now = getMSTime(); + bool poison_cloud_casting = false; + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (spell) + { + poison_cloud_casting = NaxxSpellIds::MatchesAnySpellId(spell->GetSpellInfo(), {NaxxSpellIds::PoisonCloud}); + } + } + if (!poison_cloud_casting && last_cloud_ms != 0 && now - last_cloud_ms < CloudRotationDelayMs) + { + return false; + } + last_cloud_ms = now; + return true; +} + +bool HeiganMeleeTrigger::IsActive() +{ + Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean"); + if (!heigan) + { + return false; + } + return !botAI->IsRanged(bot); +} + +bool HeiganRangedTrigger::IsActive() +{ + Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean"); + if (!heigan) + { + return false; + } + return botAI->IsRanged(bot); +} + +bool RazuviousTankTrigger::IsActive() +{ + Difficulty diff = bot->GetRaidDifficulty(); + if (diff == RAID_DIFFICULTY_10MAN_NORMAL) + { + return helper.UpdateBossAI() && botAI->IsTank(bot); + } + return helper.UpdateBossAI() && bot->getClass() == CLASS_PRIEST; +} + +bool RazuviousNontankTrigger::IsActive() +{ + Difficulty diff = bot->GetRaidDifficulty(); + if (diff == RAID_DIFFICULTY_10MAN_NORMAL) + { + return helper.UpdateBossAI() && !(botAI->IsTank(bot)); + } + return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST); +} + +bool HorsemanAttractorsTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsAttracter(bot); +} + +bool HorsemanExceptAttractorsTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return !helper.IsAttracter(bot); +} + +bool SapphironGroundTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhaseGround(); +} + +bool SapphironFlightTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhaseFlight(); +} + +bool GluthTrigger::IsActive() { return helper.UpdateBossAI(); } + +bool GluthMainTankMortalWoundTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + if (!botAI->IsAssistTankOfIndex(bot, 0)) + { + return false; + } + Unit* mt = AI_VALUE(Unit*, "main tank"); + if (!mt) + { + return false; + } + Aura* aura = NaxxSpellIds::GetAnyAura(mt, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25}); + if (!aura) + { + // Fallback to name for custom spell data. + aura = botAI->GetAura("mortal wound", mt, false, true); + } + if (!aura || aura->GetStackAmount() < 5) + { + return false; + } + return true; +} + +bool KelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); } + +bool AnubrekhanTrigger::IsActive() { + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); + if (!boss) + { + return false; + } + return true; +} + +bool FaerlinaTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grand widow faerlina"); + if (!boss) + { + return false; + } + return true; +} + +bool MaexxnaTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna"); + if (!boss) + { + return false; + } + return !botAI->IsTank(bot); +} + +bool PatchwerkTankTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); + if (!boss) + { + return false; + } + return !botAI->IsTank(bot) && !botAI->IsRanged(bot); +} + +bool PatchwerkRangedTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); + if (!boss) + { + return false; + } + return !botAI->IsTank(bot) && botAI->IsRanged(bot); +} + +bool PatchwerkNonTankTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); + if (!boss) + { + return false; + } + return !botAI->IsTank(bot); +} + +bool LoathebTrigger::IsActive() { return helper.UpdateBossAI(); } + +bool ThaddiusPhasePetTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhasePet(); +} + +bool ThaddiusPhaseTransitionTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhaseTransition(); +} + +bool ThaddiusPhaseThaddiusTrigger::IsActive() +{ + if (!helper.UpdateBossAI()) + { + return false; + } + return helper.IsPhaseThaddius(); +} diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.h b/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.h new file mode 100644 index 0000000000..d02a88a9eb --- /dev/null +++ b/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.h @@ -0,0 +1,258 @@ + +#ifndef _PLAYERBOT_RAIDNAXXTRIGGERS_H +#define _PLAYERBOT_RAIDNAXXTRIGGERS_H + +#include "EventMap.h" +#include "GenericTriggers.h" +#include "PlayerbotAIConfig.h" +#include "RaidNaxxBossHelper.h" +#include "Trigger.h" + +class MutatingInjectionTrigger : public HasAuraTrigger +{ +public: + MutatingInjectionTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "mutating injection", 1) {} +}; + +class MutatingInjectionMeleeTrigger : public MutatingInjectionTrigger +{ +public: + MutatingInjectionMeleeTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {} + bool IsActive() override; +}; + +class MutatingInjectionRangedTrigger : public MutatingInjectionTrigger +{ +public: + MutatingInjectionRangedTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {} + bool IsActive() override; +}; + +class AuraRemovedTrigger : public Trigger +{ +public: + AuraRemovedTrigger(PlayerbotAI* botAI, std::string name) : Trigger(botAI, name, 1) { this->prev_check = false; } + virtual bool IsActive() override; + +protected: + bool prev_check; +}; + +class MutatingInjectionRemovedTrigger : public HasNoAuraTrigger +{ +public: + MutatingInjectionRemovedTrigger(PlayerbotAI* ai) : HasNoAuraTrigger(ai, "mutating injection") {} + virtual bool IsActive(); +}; + +class GrobbulusCloudTrigger : public Trigger +{ +public: + GrobbulusCloudTrigger(PlayerbotAI* ai) : Trigger(ai, "grobbulus cloud event"), last_cloud_ms(0) {} + bool IsActive() override; + +private: + uint32 last_cloud_ms; + static constexpr uint32 CloudRotationDelayMs = 15000; +}; + +class HeiganMeleeTrigger : public Trigger +{ +public: + HeiganMeleeTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan melee") {} + virtual bool IsActive(); +}; + +class HeiganRangedTrigger : public Trigger +{ +public: + HeiganRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan ranged") {} + bool IsActive() override; +}; + +class RazuviousTankTrigger : public Trigger +{ +public: + RazuviousTankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious tank"), helper(ai) {} + bool IsActive() override; + +private: + RazuviousBossHelper helper; +}; + +class RazuviousNontankTrigger : public Trigger +{ +public: + RazuviousNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious non-tank"), helper(ai) {} + bool IsActive() override; + +private: + RazuviousBossHelper helper; +}; + +class KelthuzadTrigger : public Trigger +{ +public: + KelthuzadTrigger(PlayerbotAI* ai) : Trigger(ai, "kel'thuzad trigger"), helper(ai) {} + bool IsActive() override; + +private: + KelthuzadBossHelper helper; +}; + +class AnubrekhanTrigger : public Trigger +{ +public: + AnubrekhanTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'rekhan") {} + bool IsActive() override; +}; + + class FaerlinaTrigger : public Trigger + { + public: + FaerlinaTrigger(PlayerbotAI* ai) : Trigger(ai, "faerlina") {} + bool IsActive() override; + }; + +class MaexxnaTrigger : public Trigger +{ +public: + MaexxnaTrigger(PlayerbotAI* ai) : Trigger(ai, "maexxna") {} + bool IsActive() override; +}; + +class PatchwerkTankTrigger : public Trigger +{ +public: + PatchwerkTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk tank") {} + bool IsActive() override; +}; + +class PatchwerkNonTankTrigger : public Trigger +{ +public: + PatchwerkNonTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk non-tank") {} + bool IsActive() override; +}; + +class PatchwerkRangedTrigger : public Trigger +{ +public: + PatchwerkRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk ranged") {} + bool IsActive() override; +}; + +class ThaddiusPhasePetTrigger : public Trigger +{ +public: + ThaddiusPhasePetTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase pet"), helper(ai) {} + bool IsActive() override; + +private: + ThaddiusBossHelper helper; +}; + +class ThaddiusPhasePetLoseAggroTrigger : public ThaddiusPhasePetTrigger +{ +public: + ThaddiusPhasePetLoseAggroTrigger(PlayerbotAI* ai) : ThaddiusPhasePetTrigger(ai) {} + virtual bool IsActive() + { + Unit* target = AI_VALUE(Unit*, "current target"); + return ThaddiusPhasePetTrigger::IsActive() && botAI->IsTank(bot) && target && target->GetVictim() != bot; + } +}; + +class ThaddiusPhaseTransitionTrigger : public Trigger +{ +public: + ThaddiusPhaseTransitionTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase transition"), helper(ai) {} + bool IsActive() override; + +private: + ThaddiusBossHelper helper; +}; + +class ThaddiusPhaseThaddiusTrigger : public Trigger +{ +public: + ThaddiusPhaseThaddiusTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase thaddius"), helper(ai) {} + bool IsActive() override; + +private: + ThaddiusBossHelper helper; +}; + +class HorsemanAttractorsTrigger : public Trigger +{ +public: + HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {} + bool IsActive() override; + +private: + FourhorsemanBossHelper helper; +}; + +class HorsemanExceptAttractorsTrigger : public Trigger +{ +public: + HorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen except attractors"), helper(ai) {} + bool IsActive() override; + +private: + FourhorsemanBossHelper helper; +}; + +class SapphironGroundTrigger : public Trigger +{ +public: + SapphironGroundTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron ground"), helper(ai) {} + bool IsActive() override; + +private: + SapphironBossHelper helper; +}; + +class SapphironFlightTrigger : public Trigger +{ +public: + SapphironFlightTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron flight"), helper(ai) {} + bool IsActive() override; + +private: + SapphironBossHelper helper; +}; + +class GluthTrigger : public Trigger +{ +public: + GluthTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth trigger"), helper(ai) {} + bool IsActive() override; + +private: + GluthBossHelper helper; +}; + +class GluthMainTankMortalWoundTrigger : public Trigger +{ +public: + GluthMainTankMortalWoundTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth main tank mortal wound trigger"), helper(ai) + { + } + bool IsActive() override; + +private: + GluthBossHelper helper; +}; + +class LoathebTrigger : public Trigger +{ +public: + LoathebTrigger(PlayerbotAI* ai) : Trigger(ai, "loatheb"), helper(ai) {} + bool IsActive() override; + +private: + LoathebBossHelper helper; +}; + +#endif From 0485ae34bd249c332b9de003cfd068c31e550a54 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Tue, 20 Jan 2026 12:01:29 +0100 Subject: [PATCH 06/42] Update AiObjectContext.cpp --- src/Bot/Engine/AiObjectContext.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Bot/Engine/AiObjectContext.cpp b/src/Bot/Engine/AiObjectContext.cpp index 84b52763d0..1100e1a835 100644 --- a/src/Bot/Engine/AiObjectContext.cpp +++ b/src/Bot/Engine/AiObjectContext.cpp @@ -43,6 +43,8 @@ #include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h" #include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h" #include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h" +#include "Ai/Raid/Naxxramas/RaidNaxxActionContext.h" +#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h" #include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h" From 30dccf5040f31e114ca89c601cc50963625b24de Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:01:39 +0100 Subject: [PATCH 07/42] Move Naxx Folder --- .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActionContext.h | 0 src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions.h | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Anubrekhan.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Faerlina.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_FourHorseman.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Gluth.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Gothik.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Grobbulus.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Heigan.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Kelthuzad.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Loatheb.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Maexxna.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Noth.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Patchwerk.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Razuvious.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Sapphiron.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Shared.cpp | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Thaddius.cpp | 0 src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxBossHelper.h | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxMultipliers.cpp | 0 src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxMultipliers.h | 0 src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxSpellIds.h | 0 src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxStrategy.cpp | 0 src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxStrategy.h | 0 .../RaidAi => Ai/Raid}/Naxxramas/RaidNaxxTriggerContext.h | 0 src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxTriggers.cpp | 0 src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxTriggers.h | 0 27 files changed, 0 insertions(+), 0 deletions(-) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActionContext.h (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions.h (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Anubrekhan.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Faerlina.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_FourHorseman.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Gluth.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Gothik.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Grobbulus.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Heigan.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Kelthuzad.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Loatheb.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Maexxna.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Noth.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Patchwerk.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Razuvious.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Sapphiron.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Shared.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxActions_Thaddius.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxBossHelper.h (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxMultipliers.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxMultipliers.h (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxSpellIds.h (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxStrategy.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxStrategy.h (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxTriggerContext.h (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxTriggers.cpp (100%) rename src/{Scenario/RaidAi => Ai/Raid}/Naxxramas/RaidNaxxTriggers.h (100%) diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActionContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActionContext.h rename to src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions.h b/src/Ai/Raid/Naxxramas/RaidNaxxActions.h similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions.h rename to src/Ai/Raid/Naxxramas/RaidNaxxActions.h diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Anubrekhan.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Anubrekhan.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Anubrekhan.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Anubrekhan.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Faerlina.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Faerlina.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Faerlina.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Faerlina.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_FourHorseman.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_FourHorseman.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_FourHorseman.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_FourHorseman.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gluth.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Gluth.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gluth.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Gluth.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gothik.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Gothik.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Gothik.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Gothik.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Grobbulus.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Grobbulus.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Grobbulus.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Grobbulus.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Heigan.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Heigan.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Heigan.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Heigan.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Kelthuzad.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Kelthuzad.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Loatheb.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Loatheb.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Loatheb.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Loatheb.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Maexxna.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Maexxna.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Maexxna.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Maexxna.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Noth.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Noth.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Noth.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Noth.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Patchwerk.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Patchwerk.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Patchwerk.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Patchwerk.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Razuvious.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Razuvious.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Razuvious.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Razuvious.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Sapphiron.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Sapphiron.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Sapphiron.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Sapphiron.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Shared.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Shared.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Shared.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Shared.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Thaddius.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Thaddius.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxActions_Thaddius.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxActions_Thaddius.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxBossHelper.h b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxBossHelper.h rename to src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.h b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxMultipliers.h rename to src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxSpellIds.h rename to src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.h b/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.h similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxStrategy.h rename to src/Ai/Raid/Naxxramas/RaidNaxxStrategy.h diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggerContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggerContext.h rename to src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.cpp rename to src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp diff --git a/src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.h b/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h similarity index 100% rename from src/Scenario/RaidAi/Naxxramas/RaidNaxxTriggers.h rename to src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h From 3d467ce3bb4f846d512324a729f2237db8a8236a Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sat, 24 Jan 2026 20:41:12 +0100 Subject: [PATCH 08/42] Added some additional defense checks around isHostile and unit/target (#2056) Needs second pair of eyes, they appear in crash logs here and there. Its merely a patch on a open wound. ---- As in aslong there multithreads in mapupdate, which we need for decent performance and core calls are not done correctly due various reasons. These type of issues remain. Although i am planning to experiment a little with threadsafe execution of our strategies vs performance. The most effective thing we could do is check every single action and check its stateless and where it does effect the state or read the state of a core object its done in the safest way. flags, worldthread where possible and/ot simply taking into account the state might be invalid. --- src/Ai/Base/Actions/TravelAction.cpp | 2 +- src/Ai/Base/Actions/UseItemAction.cpp | 6 ++-- src/Ai/Base/Value/AttackersValue.cpp | 3 ++ src/Ai/Base/Value/GrindTargetValue.cpp | 10 ++---- src/Ai/Base/Value/NearestNpcsValue.cpp | 11 ++++++- src/Ai/Base/Value/PossibleRpgTargetsValue.cpp | 9 ++++- .../Raid/Icecrown/Trigger/RaidIccTriggers.cpp | 10 ++++-- .../Ulduar/Trigger/RaidUlduarTriggers.cpp | 33 ++++++++++++++----- 8 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/Ai/Base/Actions/TravelAction.cpp b/src/Ai/Base/Actions/TravelAction.cpp index f99f8b29d3..a290605718 100644 --- a/src/Ai/Base/Actions/TravelAction.cpp +++ b/src/Ai/Base/Actions/TravelAction.cpp @@ -28,7 +28,7 @@ bool TravelAction::Execute(Event event) for (Unit* unit : targets) { newTarget = unit; - if (!newTarget) + if (!newTarget || !newTarget->IsInWorld() || newTarget->IsDuringRemoveFromWorld()) continue; if (newTarget->GetMapId() != bot->GetMapId()) diff --git a/src/Ai/Base/Actions/UseItemAction.cpp b/src/Ai/Base/Actions/UseItemAction.cpp index 690d2d4b77..bfb86ef0bd 100644 --- a/src/Ai/Base/Actions/UseItemAction.cpp +++ b/src/Ai/Base/Actions/UseItemAction.cpp @@ -245,8 +245,10 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni { packet << unitTarget->GetGUID(); targetSelected = true; - // If the target is bot or is an enemy, say "on self" - if (unitTarget == bot || (unitTarget->IsHostileTo(bot))) + + if (unitTarget == bot || !unitTarget->IsInWorld() || unitTarget->IsDuringRemoveFromWorld()) + out << " on self"; + else if (unitTarget->IsHostileTo(bot)) out << " on self"; else out << " on " << unitTarget->GetName(); diff --git a/src/Ai/Base/Value/AttackersValue.cpp b/src/Ai/Base/Value/AttackersValue.cpp index 3c9685895a..f6da0aa7a6 100644 --- a/src/Ai/Base/Value/AttackersValue.cpp +++ b/src/Ai/Base/Value/AttackersValue.cpp @@ -258,6 +258,9 @@ bool PossibleAddsValue::Calculate() if (Unit* add = botAI->GetUnit(guid)) { + if (!add->IsInWorld() || add->IsDuringRemoveFromWorld()) + continue; + if (!add->GetTarget() && !add->GetThreatMgr().getCurrentVictim() && add->IsHostileTo(bot)) { for (ObjectGuid const attackerGUID : attackers) diff --git a/src/Ai/Base/Value/GrindTargetValue.cpp b/src/Ai/Base/Value/GrindTargetValue.cpp index d1d7f912ca..7e6168c776 100644 --- a/src/Ai/Base/Value/GrindTargetValue.cpp +++ b/src/Ai/Base/Value/GrindTargetValue.cpp @@ -59,26 +59,22 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) for (ObjectGuid const guid : targets) { Unit* unit = botAI->GetUnit(guid); - if (!unit) continue; + if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) + continue; + auto& rep = bot->ToPlayer()->GetReputationMgr(); if (unit->ToCreature() && !unit->ToCreature()->GetCreatureTemplate()->lootid && bot->GetReactionTo(unit) >= REP_NEUTRAL) - { continue; - } if (!bot->IsHostileTo(unit) && unit->GetNpcFlags() != UNIT_NPC_FLAG_NONE) - { continue; - } if (!bot->isHonorOrXPTarget(unit)) - { continue; - } if (abs(bot->GetPositionZ() - unit->GetPositionZ()) > INTERACTION_DISTANCE) continue; diff --git a/src/Ai/Base/Value/NearestNpcsValue.cpp b/src/Ai/Base/Value/NearestNpcsValue.cpp index 1c188d2980..a32c007f10 100644 --- a/src/Ai/Base/Value/NearestNpcsValue.cpp +++ b/src/Ai/Base/Value/NearestNpcsValue.cpp @@ -27,7 +27,16 @@ void NearestHostileNpcsValue::FindUnits(std::list& targets) Cell::VisitObjects(bot, searcher, range); } -bool NearestHostileNpcsValue::AcceptUnit(Unit* unit) { return unit->IsHostileTo(bot) && !unit->IsPlayer(); } +bool NearestHostileNpcsValue::AcceptUnit(Unit* unit) +{ + if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) + return false; + + if (unit->IsPlayer()) + return false; + + return unit->IsHostileTo(bot); +} void NearestVehiclesValue::FindUnits(std::list& targets) { diff --git a/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp b/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp index 6e78f37f9a..e9b79debf8 100644 --- a/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp +++ b/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp @@ -54,6 +54,9 @@ void PossibleRpgTargetsValue::FindUnits(std::list& targets) bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit) { + if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) + return false; + if (unit->IsHostileTo(bot) || unit->IsPlayer()) return false; @@ -70,7 +73,8 @@ bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit) } TravelTarget* travelTarget = context->GetValue("travel target")->Get(); - if (travelTarget->getDestination() && travelTarget->getDestination()->getEntry() == unit->GetEntry()) + if (travelTarget && travelTarget->getDestination() && + travelTarget->getDestination()->getEntry() == unit->GetEntry()) return true; if (urand(1, 100) < 25 && unit->IsFriendlyTo(bot)) @@ -145,6 +149,9 @@ void PossibleNewRpgTargetsValue::FindUnits(std::list& targets) bool PossibleNewRpgTargetsValue::AcceptUnit(Unit* unit) { + if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) + return false; + if (unit->IsHostileTo(bot) || unit->IsPlayer()) return false; diff --git a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp index 55cfe9fc93..b9ebb8ca8d 100644 --- a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp +++ b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp @@ -99,7 +99,10 @@ bool IccGunshipCannonNearTrigger::IsActive() bool IccGunshipTeleportAllyTrigger::IsActive() { Unit* boss = bot->FindNearestCreature(NPC_HIGH_OVERLORD_SAURFANG, 100.0f); - if (!boss) + if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) + return false; + + if (!boss->IsAlive()) return false; if (!boss->IsHostileTo(bot)) @@ -111,7 +114,10 @@ bool IccGunshipTeleportAllyTrigger::IsActive() bool IccGunshipTeleportHordeTrigger::IsActive() { Unit* boss = bot->FindNearestCreature(NPC_MURADIN_BRONZEBEARD, 100.0f); - if (!boss) + if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) + return false; + + if (!boss->IsAlive()) return false; if (!boss->IsHostileTo(bot)) diff --git a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp index 89ad7ac5fb..f883917ed0 100644 --- a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp +++ b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp @@ -765,9 +765,13 @@ bool FreyaMoveToHealingSporeTrigger::IsActive() bool ThorimUnbalancingStrikeTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "thorim"); + if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) + return false; - // Check boss and it is alive - if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot)) + if (!boss->IsAlive()) + return false; + + if (!boss->IsHostileTo(bot)) return false; return bot->HasAura(SPELL_UNBALANCING_STRIKE); @@ -804,8 +808,13 @@ bool ThorimMarkDpsTargetTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "thorim"); - // Check boss and it is alive - if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot)) + if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) + return false; + + if (!boss->IsAlive()) + return false; + + if (!boss->IsHostileTo(bot)) return false; if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD && (!currentSkullUnit || !currentSkullUnit->IsAlive())) @@ -982,9 +991,13 @@ bool ThorimGauntletPositioningTrigger::IsActive() bool ThorimArenaPositioningTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "thorim"); + if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) + return false; - // Check boss and it is alive - if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot)) + if (!boss->IsAlive()) + return false; + + if (!boss->IsHostileTo(bot)) return false; if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD) @@ -1080,9 +1093,13 @@ bool ThorimPhase2PositioningTrigger::IsActive() return false; Unit* boss = AI_VALUE2(Unit*, "find target", "thorim"); + if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) + return false; - // Check boss and it is alive - if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot)) + if (!boss->IsAlive()) + return false; + + if (!boss->IsHostileTo(bot)) return false; if (boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD) From 378254af3f1f8db9a6116a1a158da8a41916fb82 Mon Sep 17 00:00:00 2001 From: Crow Date: Sat, 24 Jan 2026 14:26:49 -0600 Subject: [PATCH 09/42] Fix Assistant Assignment Functions (#1930) IsHealAssistantOfIndex() and IsRangedDpsAssistantOfIndex() are supposed to iterate through the group and first return members with the applicable role that have the assistant flag, and then iterate through non-assistants only if there are not enough assistants for the designated index. They are not written properly and actually completely ignore the assistant flag. I rely on these functions for significant roles in SSC and TK (which I have decided I'll PR in the same way as SSC, as a long-term draft). I have them fixed on my own fork, but it is problematic for testers if these functions do not work. So I've done three things here: 1. Fixed the functions to prefer members with the assistant flag. 2. Added a third parameter for ignoreDeadPlayers, like IsAssistTankOfIndex() has. Note that the parameter is by default false for IsAssistTankOfIndex(), meaning dead players are _not_ ignored. This is not my preferred design choice--I think the default should be to ignore dead players, but I have not changed the default and have made the default the same for IsAssistHealOfIndex() and IsAssistRangedDpsOfIndex(), since I don't know the intent of the pre-existing boss strats that use the functions. 3. Changed the names to IsAssistHealOfIndex() and IsAssistRangedDpsOfIndex() so they parallel IsAssistTankOfIndex(), and made corresponding changes in the few boss strats that use the functions. Also, note that the functions _do _not_ exclude real players. I think there are arguments for and against excluding real players. A fourth parameter for this could be useful, but I've not made any change in that regard. --- .../Trigger/RaidOsTriggers.cpp | 6 +- src/Bot/PlayerbotAI.cpp | 92 +++++++++++-------- src/Bot/PlayerbotAI.h | 4 +- 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp b/src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp index 2c3758856d..710e3cac10 100644 --- a/src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp +++ b/src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp @@ -78,19 +78,19 @@ bool SartharionMeleePositioningTrigger::IsActive() bool TwilightPortalEnterTrigger::IsActive() { - if (botAI->IsMainTank(bot) || botAI->IsHealAssistantOfIndex(bot, 0)) { return false; } + if (botAI->IsMainTank(bot) || botAI->IsAssistHealOfIndex(bot, 0)) { return false; } // In 25-man, take two healers in. Otherwise just take one // if (bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) // { - // if (botAI->IsHealAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 1)) + // if (botAI->IsAssistHealOfIndex(bot, 0) || botAI->IsAssistHealOfIndex(bot, 1)) // { // return false; // } // } // else // { - // if (botAI->IsHealAssistantOfIndex(bot, 0)) + // if (botAI->IsAssistHealOfIndex(bot, 0)) // { // return false; // } diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 49efe09275..39db7c5948 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -1796,35 +1796,46 @@ bool PlayerbotAI::IsCombo(Player* player) bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); } -bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index) +bool PlayerbotAI::IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers) { Group* group = player->GetGroup(); if (!group) - { return false; - } int counter = 0; + // First, assistants for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member) - { continue; - } - if (IsHeal(member)) // Check if the member is a healer - { - bool isAssistant = group->IsAssistant(member->GetGUID()); + if (ignoreDeadPlayers && !member->IsAlive()) + continue; - // Check if the index matches for both assistant and non-assistant healers - if ((isAssistant && index == counter) || (!isAssistant && index == counter)) - { + if (group->IsAssistant(member->GetGUID()) && IsHeal(member)) + { + if (index == counter) return player == member; - } + counter++; + } + } + // If not enough assistants, get non-assistants + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + continue; + + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + + if (!group->IsAssistant(member->GetGUID()) && IsHeal(member)) + { + if (index == counter) + return player == member; counter++; } } @@ -1832,35 +1843,46 @@ bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index) return false; } -bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index) +bool PlayerbotAI::IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers) { Group* group = player->GetGroup(); if (!group) - { return false; - } int counter = 0; + // First, assistants for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member) - { continue; - } - if (IsRangedDps(member)) // Check if the member is a ranged DPS - { - bool isAssistant = group->IsAssistant(member->GetGUID()); + if (ignoreDeadPlayers && !member->IsAlive()) + continue; - // Check the index for both assistant and non-assistant ranges - if ((isAssistant && index == counter) || (!isAssistant && index == counter)) - { + if (group->IsAssistant(member->GetGUID()) && IsRangedDps(member)) + { + if (index == counter) return player == member; - } + counter++; + } + } + // If not enough assistants, get non-assistants + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + continue; + + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + + if (!group->IsAssistant(member->GetGUID()) && IsRangedDps(member)) + { + if (index == counter) + return player == member; counter++; } } @@ -2335,18 +2357,16 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDead { Group* group = player->GetGroup(); if (!group) - { return false; - } + int counter = 0; + + // First, assistants for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member) - { continue; - } if (ignoreDeadPlayers && !member->IsAlive()) continue; @@ -2354,21 +2374,17 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDead if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) { if (index == counter) - { return player == member; - } counter++; } } - // not enough + + // If not enough assistants, get non-assistants for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member) - { continue; - } if (ignoreDeadPlayers && !member->IsAlive()) continue; @@ -2376,9 +2392,7 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDead if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) { if (index == counter) - { return player == member; - } counter++; } } diff --git a/src/Bot/PlayerbotAI.h b/src/Bot/PlayerbotAI.h index 4de4bf4a1f..b2df4352cc 100644 --- a/src/Bot/PlayerbotAI.h +++ b/src/Bot/PlayerbotAI.h @@ -429,8 +429,8 @@ class PlayerbotAI : public PlayerbotAIBase static uint32 GetGroupTankNum(Player* player); static bool IsAssistTank(Player* player); static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); - static bool IsHealAssistantOfIndex(Player* player, int index); - static bool IsRangedDpsAssistantOfIndex(Player* player, int index); + static bool IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); + static bool IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); bool HasAggro(Unit* unit); static int32 GetAssistTankIndex(Player* player); int32 GetGroupSlotIndex(Player* player); From 5365ba86b583630bf7f7c8e36f1b842c45fdac35 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:13:36 +0100 Subject: [PATCH 10/42] Added PR template (#2063) --- PULL_REQUEST_TEMPLATE.md | 128 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..5f73d9bf00 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,128 @@ +# Pull Request + +## Summary + +Describe what this change does and why it is needed. +PULL_REQUEST_TEMPLATE +--- + +## Design Philosophy + +We prioritize **stability, performance, and predictability** over behavioral realism. +Complex player-mimicking logic is intentionally limited due to its negative impact on scalability, maintainability, and +long-term robustness. + +Excessive processing overhead can lead to server hiccups, increased CPU usage, and degraded performance for all +participants. Because every action and +decision tree is executed **per bot and per trigger**, even small increases in logic complexity can scale poorly and +negatively affect both players and +world (random) bots. Bots are not expected to behave perfectly, and perfect simulation of human decision-making is not a +project goal. Increased behavioral +realism often introduces disproportionate cost, reduced predictability, and significantly higher maintenance overhead. + +Every additional branch of logic increases long-term responsibility. All decision paths must be tested, validated, and +maintained continuously as the system evolves. +If advanced or AI-intensive behavior is introduced, the **default configuration must remain the lightweight decision +model**. More complex behavior should only be +available as an **explicit opt-in option**, clearly documented as having a measurable performance cost. + +Principles: + +- **Stability before intelligence** + A stable system is always preferred over a smarter one. + +- **Performance is a shared resource** + Any increase in bot cost affects all players and all bots. + +- **Simple logic scales better than smart logic** + Predictable behavior under load is more valuable than perfect decisions. + +- **Complexity must justify itself** + If a feature cannot clearly explain its cost, it should not exist. + +- **Defaults must be cheap** + Expensive behavior must always be optional and clearly communicated. + +- **Bots should look reasonable, not perfect** + The goal is believable behavior, not human simulation. + +Before submitting, confirm that this change aligns with those principles. + +--- + +## Feature Evaluation + +Please answer the following: + +- Describe the **minimum logic** required to achieve the intended behavior? +- Describe the **cheapest implementation** that produces an acceptable result? +- Describe the **runtime cost** when this logic executes across many bots? + +--- + +## How to Test the Changes + +- Step-by-step instructions to test the change +- Any required setup (e.g. multiple players, bots, specific configuration) +- Expected behavior and how to verify it + +## Complexity & Impact + +- Does this change add new decision branches? + - [ ] No + - [ ] Yes (**explain below**) + +- Does this change increase per-bot or per-tick processing? + - [ ] No + - [ ] Yes (**describe and justify impact**) + +- Could this logic scale poorly under load? + - [ ] No + - [ ] Yes (**explain why**) + +--- + +## Defaults & Configuration + +- Does this change modify default bot behavior? + - [ ] No + - [ ] Yes (**explain why**) + +If this introduces more advanced or AI-heavy logic: + +- [ ] 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? + - [ ] No + - [ ] Yes (**explain below**) + +If yes, please specify: + +- AI tool or model used (e.g. ChatGPT, GPT-4, Claude, etc.) +- Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation) +- Which parts of the change were influenced or generated +- Whether the result was manually reviewed and adapted + +AI assistance is allowed, but all submitted code must be fully understood, reviewed, and owned by the contributor. +Any AI-influenced changes must be verified against existing CORE and PB logic. We expect contributors to be honest +about what they do and do not understand. +--- + +## Final Checklist + +- [ ] Stability is not compromised +- [ ] Performance impact is understood, tested, and acceptable +- [ ] Added logic complexity is justified and explained +- [ ] Documentation updated if needed + +--- + +## Notes for Reviewers + +Anything that significantly improves realism at the cost of stability or performance should be carefully discussed +before merging. From 7abd8369710edf4aa8283ce4d6878847dd49e1cb Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:22:53 +0100 Subject: [PATCH 11/42] Update PULL_REQUEST_TEMPLATE.md (#2064) --- PULL_REQUEST_TEMPLATE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 5f73d9bf00..7214b7ab04 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -3,7 +3,6 @@ ## Summary Describe what this change does and why it is needed. -PULL_REQUEST_TEMPLATE --- ## Design Philosophy From c59a02ed89fb9b8a0af7b326859f25e69b73b608 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:26:15 +0100 Subject: [PATCH 12/42] Update PULL_REQUEST_TEMPLATE.md (#2065) --- PULL_REQUEST_TEMPLATE.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 7214b7ab04..bcb72d21b2 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,7 @@ # Pull Request -## Summary +Describe what this change does and why it is needed... -Describe what this change does and why it is needed. --- ## Design Philosophy From 43e8e319805ff08ffff176b1100346555c952fb7 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 25 Jan 2026 13:46:23 +0100 Subject: [PATCH 13/42] Update PULL_REQUEST_TEMPLATE.md (#2066) --- PULL_REQUEST_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index bcb72d21b2..63d8f2397f 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -109,6 +109,7 @@ If yes, please specify: AI assistance is allowed, but all submitted code must be fully understood, reviewed, and owned by the contributor. Any AI-influenced changes must be verified against existing CORE and PB logic. We expect contributors to be honest about what they do and do not understand. + --- ## Final Checklist From f5711dc6f78a4d4c4a87c83aea7c6d2a8567ee5b Mon Sep 17 00:00:00 2001 From: gtkk <100208412+gutoukuaiken@users.noreply.github.com> Date: Sun, 25 Jan 2026 21:03:53 +0800 Subject: [PATCH 14/42] FIX Onyxia Crash (#2062) Solve these two problems #2043 #1981 @Regrad is the main contributor of the code, while I was just helping to submit the pull request. Express my gratitude to him. After testing, the code is proven to be effective. --- src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp | 17 ++++++++++++++--- .../Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp | 4 ++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp index c0cd4bebab..9fa4612bf6 100644 --- a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp +++ b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp @@ -45,8 +45,19 @@ bool RaidOnyxiaSpreadOutAction::Execute(Event event) if (!boss) return false; - Player* target = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL)->m_targets.GetUnitTarget()->ToPlayer(); - if (target != bot) + // Trigger may fire on one tick, but the action can execute on a later tick. + // By that time the cast may have finished, so current spell can be null. + Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!currentSpell || !currentSpell->m_spellInfo) + return false; + + // Fireball + if (currentSpell->m_spellInfo->Id != 18392) + return false; + + Unit* unitTarget = currentSpell->m_targets.GetUnitTarget(); + Player* target = unitTarget ? unitTarget->ToPlayer() : nullptr; + if (!target || target != bot) return false; // bot->Yell("Spreading out — I'm the Fireball target!", LANG_UNIVERSAL); @@ -60,7 +71,7 @@ bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event event) return false; Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!currentSpell) + if (!currentSpell || !currentSpell->m_spellInfo) return false; uint32 spellId = currentSpell->m_spellInfo->Id; diff --git a/src/Ai/Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp b/src/Ai/Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp index aed3a82619..56486ebf96 100644 --- a/src/Ai/Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp +++ b/src/Ai/Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp @@ -17,7 +17,7 @@ bool OnyxiaDeepBreathTrigger::IsActive() // Check if Onyxia is casting Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!currentSpell) + if (!currentSpell || !currentSpell->m_spellInfo) return false; uint32 spellId = currentSpell->m_spellInfo->Id; @@ -65,7 +65,7 @@ bool RaidOnyxiaFireballSplashTrigger::IsActive() // Check if Onyxia is casting Fireball Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!currentSpell || currentSpell->m_spellInfo->Id != 18392) // 18392 is the classic Fireball ID + if (!currentSpell || !currentSpell->m_spellInfo || currentSpell->m_spellInfo->Id != 18392) // 18392 is the classic Fireball ID // 18392 is the classic Fireball ID return false; GuidVector nearbyUnits = AI_VALUE(GuidVector, "nearest friendly players"); From 2c8f8f8e3c51ead33abed077868b32a935a84123 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:55:32 +0100 Subject: [PATCH 15/42] Improve Saphiron Bots Strat --- .../Naxxramas/RaidNaxxActions_Sapphiron.cpp | 126 ++++++++++++++++-- src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h | 90 ++++++++++++- src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp | 40 ++++++ src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h | 22 +-- 4 files changed, 246 insertions(+), 32 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Sapphiron.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Sapphiron.cpp index e749836619..eba20f956c 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Sapphiron.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Sapphiron.cpp @@ -1,5 +1,8 @@ #include "RaidNaxxActions.h" +#include +#include + #include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "RaidNaxxBossHelper.h" @@ -11,6 +14,10 @@ bool SapphironGroundPositionAction::Execute(Event event) { return false; } + if (botAI->IsHeal(bot) && helper.HasLifeDrainInGroup() && !botAI->HasStrategy("cure", BOT_STATE_COMBAT)) + { + botAI->ChangeStrategy("cure", BOT_STATE_COMBAT); + } if (botAI->IsMainTank(bot)) { if (AI_VALUE2(bool, "has aggro", "current target")) @@ -20,6 +27,36 @@ bool SapphironGroundPositionAction::Execute(Event event) } return false; } + Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron"); + if (boss && helper.IsPhaseGround()) + { + bool needsSideStack = boss->isInFront(bot) || boss->isInBack(bot); + if (!needsSideStack) + { + needsSideStack = NaxxSpellIds::HasAnyAura(botAI, bot, {NaxxSpellIds::LifeDrain}) || botAI->HasAura("life drain", bot); + } + if (needsSideStack) + { + float distance; + if (botAI->IsRanged(bot) || botAI->IsHeal(bot) || + NaxxSpellIds::HasAnyAura(botAI, bot, {NaxxSpellIds::LifeDrain}) || botAI->HasAura("life drain", bot)) + { + distance = 30.0f; + } + else + { + distance = 5.0f; + } + float angle = boss->GetOrientation() + M_PI / 2; + float posX = boss->GetPositionX() + cos(angle) * distance; + float posY = boss->GetPositionY() + sin(angle) * distance; + if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + { + return true; + } + return MoveInside(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, 2.0f, MovementPriority::MOVEMENT_COMBAT); + } + } if (helper.JustLanded()) { uint32 index = botAI->GetGroupSlotIndex(bot); @@ -64,9 +101,23 @@ bool SapphironFlightPositionAction::Execute(Event event) { return false; } + if (botAI->IsHeal(bot) && helper.HasLifeDrainInGroup() && !botAI->HasStrategy("cure", BOT_STATE_COMBAT)) + { + botAI->ChangeStrategy("cure", BOT_STATE_COMBAT); + } + if (NaxxSpellIds::HasAnyAura(botAI, bot, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || + botAI->HasAura("icebolt", bot, false, false, -1, true)) + { + return false; + } if (helper.WaitForExplosion()) { - return MoveToNearestIcebolt(); + bool inShelter = MoveToNearestIcebolt(); + if (inShelter && botAI->IsHeal(bot)) + { + return false; + } + return inShelter; } else { @@ -86,12 +137,20 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt() { return false; } - Group::MemberSlotList const& slots = group->GetMemberSlots(); + Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron"); + if (!boss) + { + return false; + } Player* playerWithIcebolt = nullptr; - float minDistance; + float minDistance = std::numeric_limits::max(); for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + { + continue; + } if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || botAI->HasAura("icebolt", member, false, false, -1, true)) { @@ -104,18 +163,63 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt() } if (playerWithIcebolt) { - Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron"); - if (boss) + constexpr float shelterDistance = 9.0f; + constexpr float shelterEpsilon = 0.35f; + constexpr float lateralOffset = 1.5f; + float angle = boss->GetAngle(playerWithIcebolt); + float posX = playerWithIcebolt->GetPositionX() + cos(angle) * shelterDistance; + float posY = playerWithIcebolt->GetPositionY() + sin(angle) * shelterDistance; + float posZ = playerWithIcebolt->GetPositionZ(); + int32 slotIndex = botAI->GetGroupSlotIndex(bot); + float offsetSign = (slotIndex % 2 == 0) ? 1.0f : -1.0f; + float offsetAngle = angle + (M_PI / 2.0f); + float offsetX = cos(offsetAngle) * lateralOffset * offsetSign; + float offsetY = sin(offsetAngle) * lateralOffset * offsetSign; + float candidateX = posX + offsetX; + float candidateY = posY + offsetY; + float bossX = boss->GetPositionX(); + float bossY = boss->GetPositionY(); + float lineDx = candidateX - bossX; + float lineDy = candidateY - bossY; + float lineLen = sqrt(lineDx * lineDx + lineDy * lineDy); + if (lineLen > 0.1f) { - float angle = boss->GetAngle(playerWithIcebolt); - float posX = playerWithIcebolt->GetPositionX() + cos(angle) * 3.0f; - float posY = playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f; - if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + float relX = playerWithIcebolt->GetPositionX() - bossX; + float relY = playerWithIcebolt->GetPositionY() - bossY; + float proj = (relX * lineDx + relY * lineDy) / lineLen; + float clamped = std::max(0.0f, std::min(lineLen, proj)); + float closestX = bossX + (lineDx / lineLen) * clamped; + float closestY = bossY + (lineDy / lineLen) * clamped; + float distToLine = playerWithIcebolt->GetDistance2d(closestX, closestY); + if (distToLine <= 2.0f && playerWithIcebolt->IsWithinDist2d(candidateX, candidateY, 10.0f)) { - return true; + posX = candidateX; + posY = candidateY; } - return MoveNear(playerWithIcebolt, 3.0f, MovementPriority::MOVEMENT_COMBAT); } + float bossDist = boss->GetDistance2d(bot); + float iceboltDist = boss->GetDistance2d(playerWithIcebolt); + if (bossDist <= iceboltDist + 0.5f) + { + posX = playerWithIcebolt->GetPositionX() + cos(angle) * (shelterDistance + 1.5f); + posY = playerWithIcebolt->GetPositionY() + sin(angle) * (shelterDistance + 1.5f); + } + float distToLosPos = bot->GetDistance2d(posX, posY); + if (distToLosPos > shelterEpsilon) + { + return MoveTo(NAXX_MAP_ID, posX, posY, posZ, false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + } + if (!playerWithIcebolt->IsInBetween(boss, bot, 1.5f)) + { + return MoveTo(NAXX_MAP_ID, posX, posY, posZ, false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + } + if (botAI->IsHeal(bot)) + { + bot->StopMoving(); + } + return true; } return false; } \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h index b64ede3217..4f9f8bd5e9 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h @@ -189,6 +189,7 @@ class SapphironBossHelper : public AiObject _last_land_ms = getMSTime(); } _was_flying = now_flying; + UpdateIceboltState(); return true; } bool IsPhaseGround() { return _unit && !_unit->IsFlying(); } @@ -207,6 +208,27 @@ class SapphironBossHelper : public AiObject { return false; } + return HasIceboltInGroup(); + } + bool IsBreathWindow() + { + if (!IsPhaseFlight()) + { + return false; + } + if (IsBreathCasting()) + { + return true; + } + if (!_last_icebolt_ms) + { + return false; + } + uint32 elapsed = getMSTime() - _last_icebolt_ms; + return elapsed >= BREATH_MIN_MS && elapsed <= BREATH_MAX_MS; + } + bool HasLifeDrainInGroup() + { Group* group = bot->GetGroup(); if (!group) { @@ -215,9 +237,11 @@ class SapphironBossHelper : public AiObject for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && - (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || - botAI->HasAura("icebolt", member, false, false, -1, true))) + if (!member) + { + continue; + } + if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::LifeDrain}) || botAI->HasAura("life drain", member)) { return true; } @@ -294,12 +318,72 @@ class SapphironBossHelper : public AiObject _unit = nullptr; _was_flying = false; _last_land_ms = 0; + _last_icebolt_ms = 0; } const uint32 POSITION_TIME_AFTER_LANDED = 5000; + const uint32 BREATH_MIN_MS = 1000; + const uint32 BREATH_MAX_MS = 12000; + bool HasIceboltInGroup() + { + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + bool hasIcebolt = false; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + { + continue; + } + if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || + botAI->HasAura("icebolt", member, false, false, -1, true)) + { + hasIcebolt = true; + break; + } + } + if (hasIcebolt) + { + _last_icebolt_ms = getMSTime(); + } + return hasIcebolt; + } + void UpdateIceboltState() + { + if (!IsPhaseFlight()) + { + _last_icebolt_ms = 0; + return; + } + HasIceboltInGroup(); + } + bool IsBreathCasting() + { + if (!_unit || !_unit->HasUnitState(UNIT_STATE_CASTING)) + { + return false; + } + Spell* spell = _unit->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = _unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (!spell) + { + return false; + } + SpellInfo const* info = spell->GetSpellInfo(); + return NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::FrostMissile, NaxxSpellIds::FrostExplosion}); + } + Unit* _unit = nullptr; bool _was_flying = false; uint32 _last_land_ms = 0; + uint32 _last_icebolt_ms = 0; }; class GluthBossHelper : public AiObject diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp index ef5913b5bc..b3c1c784fb 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp @@ -188,6 +188,46 @@ float SapphironGenericMultiplier::GetValue(Action* action) { return 1.0f; } + if (botAI->IsHeal(bot)) + { + if (helper.IsBreathWindow()) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + { + return 1.0f; + } + return 0.0f; + } + if (helper.WaitForExplosion()) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + { + return 1.0f; + } + return 0.0f; + } + if (helper.HasLifeDrainInGroup()) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + { + return 1.0f; + } + return 0.0f; + } + } if (dynamic_cast(action) || dynamic_cast(action)) { return 0.0f; diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h index 858911c6b3..b719df332e 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h @@ -59,24 +59,10 @@ namespace NaxxSpellIds static constexpr uint32 Icebolt10 = 28522; static constexpr uint32 Icebolt25 = 28526; static constexpr uint32 Chill25 = 55699; -/* - // Fight - SPELL_FROST_AURA = 28531, - SPELL_CLEAVE = 19983, - SPELL_TAIL_SWEEP = 55697, - SPELL_SUMMON_BLIZZARD = 28560, - SPELL_LIFE_DRAIN = 28542, - SPELL_BERSERK = 26662, - - // Ice block - SPELL_ICEBOLT_CAST = 28526, - SPELL_ICEBOLT_TRIGGER = 28522, - SPELL_FROST_MISSILE = 30101, - SPELL_FROST_EXPLOSION = 28524, - - // Visuals - SPELL_SAPPHIRON_DIES = 29357 -*/ + static constexpr uint32 LifeDrain = 28542; + static constexpr uint32 FrostMissile = 30101; + static constexpr uint32 FrostExplosion = 28524; + // Gluth static constexpr uint32 Decimate10 = 28374; static constexpr uint32 Decimate25 = 54426; From b9d64cf0d5f3cc4a6e8c3e7ed3413f5549b4548a Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:11:19 +0100 Subject: [PATCH 16/42] Update RaidNaxxBossHelper.h --- src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h index 4f9f8bd5e9..8cc7a66d71 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h @@ -545,10 +545,10 @@ class FourhorsemanBossHelper : public AiObject Difficulty diff = bot->GetRaidDifficulty(); if (diff == RAID_DIFFICULTY_25MAN_NORMAL) { - return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0) || - botAI->IsHealAssistantOfIndex(bot, 1) || botAI->IsHealAssistantOfIndex(bot, 2); + return botAI->IsAssistRangedDpsOfIndex(bot, 0) || botAI->IsAssistHealOfIndex(bot, 0) || + botAI->IsAssistHealOfIndex(bot, 1) || botAI->IsAssistHealOfIndex(bot, 2); } - return botAI->IsRangedDpsAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 0); + return botAI->IsAssistRangedDpsOfIndex(bot, 0) || botAI->IsAssistHealOfIndex(bot, 0); } void CalculatePosToGo(Player* bot) { @@ -563,7 +563,7 @@ class FourhorsemanBossHelper : public AiObject uint32 elapsed_ms = _combat_start_ms ? getMSTime() - _combat_start_ms : 0; // Interval: 24s - 15s - 15s - ... posToGo = !(elapsed_ms <= 9000 || ((elapsed_ms - 9000) / 67500) % 2 == 0); - if (botAI->IsRangedDpsAssistantOfIndex(bot, 0) || (raid25 && botAI->IsHealAssistantOfIndex(bot, 1))) + if (botAI->IsAssistRangedDpsOfIndex(bot, 0) || (raid25 && botAI->IsAssistHealOfIndex(bot, 1))) { posToGo = 1 - posToGo; } From 3640c5871f578ac3dc2ce7d74f7618d329816bd8 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:21:52 +0100 Subject: [PATCH 17/42] Improve Kelthuzad bots strategie --- .../Naxxramas/RaidNaxxActions_Kelthuzad.cpp | 66 ++++++++++++++++--- src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h | 55 ++++++++++++++++ src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp | 43 ++++++++++++ src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h | 5 ++ 4 files changed, 161 insertions(+), 8 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp index 1c86769d95..bbcdba08ca 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp @@ -1,5 +1,7 @@ #include "RaidNaxxActions.h" +#include + #include "PlayerbotAIConfig.h" #include "Playerbots.h" @@ -83,14 +85,7 @@ bool KelthuzadChooseTargetAction::Execute(Event event) std::vector targets; if (botAI->IsRanged(bot)) { - if (botAI->GetRangedDpsIndex(bot) <= 1) - { - targets = {target_soldier, target_weaver, target_abomination, target_kelthuzad}; - } - else - { - targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; - } + targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; } else if (botAI->IsAssistTank(bot)) { @@ -102,6 +97,13 @@ bool KelthuzadChooseTargetAction::Execute(Event event) } for (Unit* t : targets) { + if (!botAI->IsRanged(bot)) + { + if (t && t->GetDistance2d(helper.center.first, helper.center.second) > 20.0f) + { + continue; + } + } if (t) { target = t; @@ -127,6 +129,21 @@ bool KelthuzadPositionAction::Execute(Event event) } if (helper.IsPhaseOne()) { + if (bot->GetDistance2d(helper.center.first, helper.center.second) > 20.0f) + { + return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f, + MovementPriority::MOVEMENT_COMBAT); + } + if (!botAI->IsRanged(bot)) + { + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (currentTarget && + currentTarget->GetDistance2d(helper.center.first, helper.center.second) <= 20.0f && + bot->GetDistance2d(currentTarget) > 3.0f) + { + return MoveNear(currentTarget, 3.0f, MovementPriority::MOVEMENT_COMBAT); + } + } if (AI_VALUE(Unit*, "current target") == nullptr) { return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f, @@ -135,6 +152,29 @@ bool KelthuzadPositionAction::Execute(Event event) } else if (helper.IsPhaseTwo()) { + if (helper.HasDetonateMana(bot)) + { + float angle = helper.center.first == bot->GetPositionX() && helper.center.second == bot->GetPositionY() + ? 0.0f + : bot->GetAngle(helper.center.first, helper.center.second) + M_PI; + float spreadDistance = std::max(35.0f, bot->GetDistance2d(helper.center.first, helper.center.second) + 10.0f); + float dx = helper.center.first + cos(angle) * spreadDistance; + float dy = helper.center.second + sin(angle) * spreadDistance; + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + if (helper.HasChains(bot)) + { + bot->AttackStop(); + return false; + } + Player* frostBlastTarget = helper.GetPlayerWithAura(NaxxSpellIds::FrostBlast); + if (frostBlastTarget && frostBlastTarget != bot && bot->GetDistance2d(frostBlastTarget) <= 8.0f) + { + float angle = frostBlastTarget->GetAngle(bot); + float dx = frostBlastTarget->GetPositionX() + cos(angle) * 8.0f; + float dy = frostBlastTarget->GetPositionY() + sin(angle) * 8.0f; + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } Unit* shadow_fissure = helper.GetAnyShadowFissure(); if (!shadow_fissure || !bot->IsWithinDistInMap(shadow_fissure, 10.0f)) { @@ -167,10 +207,20 @@ bool KelthuzadPositionAction::Execute(Event event) float dx, dy; dx = helper.center.first + cos(angle) * distance; dy = helper.center.second + sin(angle) * distance; + if (bot->GetDistance2d(dx, dy) <= 2.0f) + { + return false; + } return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } else if (botAI->IsTank(bot)) { + Unit* guardian = helper.GetGuardian(); + if (guardian && botAI->IsAssistTank(bot)) + { + return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } Unit* cur_tar = AI_VALUE(Unit*, "current target"); if (cur_tar && cur_tar->GetVictim() && cur_tar->GetVictim()->ToPlayer() && botAI->EqualLowercaseName(cur_tar->GetName(), "guardian of icecrown") && diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h index 8cc7a66d71..73e37391e3 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h @@ -108,6 +108,61 @@ class KelthuzadBossHelper : public AiObject } bool IsPhaseOne() { return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } bool IsPhaseTwo() { return _unit && !_unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } + Player* GetPlayerWithAura(uint32 spellId) + { + Group* group = bot->GetGroup(); + if (!group) + { + return nullptr; + } + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + { + continue; + } + if (botAI->HasAura(spellId, member)) + { + return member; + } + } + return nullptr; + } + bool HasAuraInGroup(uint32 spellId) { return GetPlayerWithAura(spellId) != nullptr; } + bool HasDetonateMana(Player* player) + { + if (!player) + { + return false; + } + return botAI->HasAura(NaxxSpellIds::DetonateMana, player); + } + bool HasChains(Player* player) + { + if (!player) + { + return false; + } + return botAI->HasAura(NaxxSpellIds::ChainsOfKelthuzad, player); + } + Unit* GetGuardian() + { + GuidVector attackers = context->GetValue("attackers")->Get(); + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown")) + { + return unit; + } + } + return nullptr; + } Unit* GetAnyShadowFissure() { Unit* shadow_fissure = nullptr; diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp index b3c1c784fb..a2783f4a1a 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp @@ -258,6 +258,49 @@ float KelthuzadGenericMultiplier::GetValue(Action* action) { return 1.0f; } + if (helper.HasChains(bot)) + { + if (dynamic_cast(action)) + { + return 1.0f; + } + return 0.0f; + } + if (botAI->IsHeal(bot)) + { + if (helper.HasAuraInGroup(NaxxSpellIds::FrostBlast)) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + { + return 1.0f; + } + return 0.0f; + } + if (helper.HasAuraInGroup(NaxxSpellIds::ChainsOfKelthuzad)) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + { + return 1.0f; + } + return 0.0f; + } + } + if (helper.HasDetonateMana(bot)) + { + if (dynamic_cast(action) || dynamic_cast(action)) + { + return 1.0f; + } + return 0.0f; + } if ((dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action))) { diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h index b719df332e..35a9bbbeb8 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h @@ -63,6 +63,11 @@ namespace NaxxSpellIds static constexpr uint32 FrostMissile = 30101; static constexpr uint32 FrostExplosion = 28524; + // Kel'Thuzad + static constexpr uint32 FrostBlast = 27808; + static constexpr uint32 DetonateMana = 27819; + static constexpr uint32 ChainsOfKelthuzad = 28410; + // Gluth static constexpr uint32 Decimate10 = 28374; static constexpr uint32 Decimate25 = 54426; From 941ee0f807982e8d505c0a80902bfed2939650d8 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:31:15 +0100 Subject: [PATCH 18/42] Update RaidNaxxActions_Kelthuzad.cpp --- src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp index bbcdba08ca..336b5fe51c 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp @@ -85,7 +85,14 @@ bool KelthuzadChooseTargetAction::Execute(Event event) std::vector targets; if (botAI->IsRanged(bot)) { - targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; + if (helper.IsPhaseTwo()) + { + targets = {target_kelthuzad, target_weaver, target_soldier, target_abomination}; + } + else + { + targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; + } } else if (botAI->IsAssistTank(bot)) { From 86938250d906bf787c662916a16bc336aeb9d9f5 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:27:53 +0100 Subject: [PATCH 19/42] Add strat Noth the Plaguebringer --- src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h | 5 + src/Ai/Raid/Naxxramas/RaidNaxxActions.h | 20 ++ .../Raid/Naxxramas/RaidNaxxActions_Noth.cpp | 216 +++++++++++++++++- src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h | 79 +++++++ src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp | 37 ++- src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h | 11 + src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h | 5 + src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp | 11 +- .../Raid/Naxxramas/RaidNaxxTriggerContext.h | 2 + src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp | 2 + src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h | 10 + 11 files changed, 395 insertions(+), 3 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h index acb9a2deef..ad62d74d21 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h @@ -51,6 +51,9 @@ class RaidNaxxActionContext : public NamedObjectContext creators["loatheb position"] = &RaidNaxxActionContext::loatheb_position; creators["loatheb choose target"] = &RaidNaxxActionContext::loatheb_choose_target; + + creators["noth position"] = &RaidNaxxActionContext::noth_position; + creators["noth choose target"] = &RaidNaxxActionContext::noth_choose_target; } private: @@ -90,6 +93,8 @@ class RaidNaxxActionContext : public NamedObjectContext static Action* patchwerk_ranged_position(PlayerbotAI* ai) { return new PatchwerkRangedPositionAction(ai); } static Action* loatheb_position(PlayerbotAI* ai) { return new LoathebPositionAction(ai); } static Action* loatheb_choose_target(PlayerbotAI* ai) { return new LoathebChooseTargetAction(ai); } + static Action* noth_position(PlayerbotAI* ai) { return new NothPositionAction(ai); } + static Action* noth_choose_target(PlayerbotAI* ai) { return new NothChooseTargetAction(ai); } }; #endif \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions.h b/src/Ai/Raid/Naxxramas/RaidNaxxActions.h index 6cc732a28f..b28dd16517 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActions.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActions.h @@ -332,6 +332,26 @@ class LoathebChooseTargetAction : public AttackAction LoathebBossHelper helper; }; +class NothChooseTargetAction : public AttackAction +{ +public: + NothChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "noth choose target"), helper(ai) {} + bool Execute(Event event) override; + +private: + NothBossHelper helper; +}; + +class NothPositionAction : public MovementAction +{ +public: + NothPositionAction(PlayerbotAI* ai) : MovementAction(ai, "noth position"), helper(ai) {} + bool Execute(Event event) override; + +private: + NothBossHelper helper; +}; + class PatchwerkRangedPositionAction : public MovementAction { public: diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Noth.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Noth.cpp index ff3ce98e70..af33e4f602 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Noth.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Noth.cpp @@ -1,3 +1,217 @@ #include "RaidNaxxActions.h" -// Reserved for Noth-specific actions. \ No newline at end of file +#include "PlayerbotAIConfig.h" +#include "Playerbots.h" +#include "RaidNaxxBossHelper.h" +#include "RaidNaxxSpellIds.h" + +bool NothChooseTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit* target_boss = nullptr; + Unit* target_champion = nullptr; + Unit* target_guardian = nullptr; + Unit* target_warrior = nullptr; + + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit || !unit->IsAlive()) + { + continue; + } + + if (botAI->EqualLowercaseName(unit->GetName(), "noth the plaguebringer")) + { + target_boss = unit; + } + else if (botAI->EqualLowercaseName(unit->GetName(), "plagued champion")) + { + if (!target_champion || bot->GetDistance2d(unit) < bot->GetDistance2d(target_champion)) + { + target_champion = unit; + } + } + else if (botAI->EqualLowercaseName(unit->GetName(), "plagued guardian")) + { + if (!target_guardian || bot->GetDistance2d(unit) < bot->GetDistance2d(target_guardian)) + { + target_guardian = unit; + } + } + else if (botAI->EqualLowercaseName(unit->GetName(), "plagued warrior")) + { + if (!target_warrior || bot->GetDistance2d(unit) < bot->GetDistance2d(target_warrior)) + { + target_warrior = unit; + } + } + } + + std::vector targets; + if (botAI->IsAssistTank(bot)) + { + Unit* warrior_needs_pickup = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit || !unit->IsAlive()) + { + continue; + } + if (!botAI->EqualLowercaseName(unit->GetName(), "plagued warrior")) + { + continue; + } + if (unit->GetVictim() && unit->GetVictim()->ToPlayer() && + !botAI->IsAssistTank(unit->GetVictim()->ToPlayer())) + { + warrior_needs_pickup = unit; + break; + } + } + if (helper.IsBalconyPhase()) + { + targets = {warrior_needs_pickup, target_warrior, target_champion, target_guardian}; + } + else + { + targets = {warrior_needs_pickup, target_warrior, target_boss}; + } + } + else if (helper.IsBalconyPhase()) + { + targets = {target_champion, target_guardian, target_warrior}; + } + else + { + targets = {target_boss}; + } + + for (Unit* t : targets) + { + if (t) + { + target = t; + break; + } + } + + if (!target || context->GetValue("current target")->Get() == target) + { + return false; + } + + return Attack(target); +} + +bool NothPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + + if (botAI->IsAssistTank(bot)) + { + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* loose_warrior = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit || !unit->IsAlive()) + { + continue; + } + if (!botAI->EqualLowercaseName(unit->GetName(), "plagued warrior")) + { + continue; + } + if (unit->GetVictim() && unit->GetVictim()->ToPlayer() && + !botAI->IsAssistTank(unit->GetVictim()->ToPlayer())) + { + loose_warrior = unit; + break; + } + } + if (loose_warrior && bot->GetDistance2d(loose_warrior) > 5.0f) + { + return MoveTo(NAXX_MAP_ID, loose_warrior->GetPositionX(), loose_warrior->GetPositionY(), + loose_warrior->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (currentTarget && botAI->EqualLowercaseName(currentTarget->GetName(), "plagued warrior")) + { + GuidVector friendlyPlayers = AI_VALUE(GuidVector, "nearest friendly players"); + Unit* closestPlayer = nullptr; + float closestDistance = 0.0f; + for (ObjectGuid const& guid : friendlyPlayers) + { + Unit* member = botAI->GetUnit(guid); + if (!member || member == bot) + { + continue; + } + float distance = bot->GetDistance2d(member); + if (distance <= 5.0f && (!closestPlayer || distance < closestDistance)) + { + closestPlayer = member; + closestDistance = distance; + } + } + if (closestPlayer) + { + float angle = closestPlayer->GetAngle(bot); + float dx = closestPlayer->GetPositionX() + cos(angle) * 5.0f; + float dy = closestPlayer->GetPositionY() + sin(angle) * 5.0f; + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + return false; + } + + if (!helper.IsBalconyPhase() || !botAI->IsRanged(bot)) + { + return false; + } + + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* nearest_champion = nullptr; + float nearest_distance = 0.0f; + + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit || !unit->IsAlive()) + { + continue; + } + if (!botAI->EqualLowercaseName(unit->GetName(), "plagued champion")) + { + continue; + } + float distance = bot->GetDistance2d(unit); + if (!nearest_champion || distance < nearest_distance) + { + nearest_champion = unit; + nearest_distance = distance; + } + } + + if (nearest_champion && nearest_distance < 25.0f) + { + float angle = nearest_champion->GetAngle(bot); + float dx = nearest_champion->GetPositionX() + cos(angle) * 25.0f; + float dy = nearest_champion->GetPositionY() + sin(angle) * 25.0f; + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + return false; +} \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h index 73e37391e3..e47b248558 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h @@ -556,6 +556,85 @@ class LoathebBossHelper : public AiObject Unit* _unit = nullptr; }; +class NothBossHelper : public AiObject +{ +public: + const std::pair center = {2684.94f, -3502.53f}; + NothBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + bool UpdateBossAI() + { + if (!bot->IsInCombat()) + { + Reset(); + } + if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) + { + Reset(); + } + if (!_unit) + { + _unit = AI_VALUE2(Unit*, "find target", "noth the plaguebringer"); + } + if (!_unit) + { + return false; + } + if (_unit->HasUnitState(UNIT_STATE_CASTING)) + { + Spell* spell = _unit->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!spell) + { + spell = _unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + } + if (spell) + { + SpellInfo const* info = spell->GetSpellInfo(); + bool isBlink = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Blink}); + if (!isBlink && info && info->SpellName[LOCALE_enUS]) + { + // Fallback to name for custom spell data. + isBlink = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "blink"); + } + if (isBlink) + { + _last_blink_ms = getMSTime(); + } + } + } + return true; + } + bool IsBalconyPhase() const { return _unit && _unit->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE); } + bool IsBlinkWindow() const { return _last_blink_ms != 0 && getMSTime() - _last_blink_ms < 3000; } + bool HasCurseInGroup() const + { + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (ObjectGuid const& guid : members) + { + Unit* member = botAI->GetUnit(guid); + if (!member) + { + continue; + } + if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::CurseOfThePlaguebringer}) || + botAI->HasAura("curse of the plaguebringer", member)) + { + return true; + } + } + return false; + } + +private: + void Reset() + { + _unit = nullptr; + _last_blink_ms = 0; + } + + Unit* _unit = nullptr; + uint32 _last_blink_ms = 0; +}; + class FourhorsemanBossHelper : public AiObject { public: diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp index a2783f4a1a..e4a4b35165 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp @@ -326,6 +326,41 @@ float KelthuzadGenericMultiplier::GetValue(Action* action) return 1.0f; } +float NothGenericMultiplier::GetValue(Action* action) +{ + if (!helper.UpdateBossAI()) + { + return 1.0f; + } + if (helper.HasCurseInGroup() && (bot->getClass() == CLASS_DRUID || bot->getClass() == CLASS_SHAMAN || + bot->getClass() == CLASS_MAGE)) + { + if (dynamic_cast(action)) + { + return 2.0f; + } + if (dynamic_cast(action)) + { + return 1.0f; + } + return 0.0f; + } + if (!helper.IsBlinkWindow() || botAI->IsTank(bot)) + { + return 1.0f; + } + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + { + return 0.0f; + } + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} + float AnubrekhanGenericMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); @@ -424,4 +459,4 @@ float GluthGenericMultiplier::GetValue(Action* action) } } return 1.0f; -} +} \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h index ecc335c910..1fbc5a797c 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h @@ -4,6 +4,7 @@ #include "Multiplier.h" #include "RaidNaxxBossHelper.h" +#include "GenericSpellActions.h" class GrobbulusMultiplier : public Multiplier { @@ -113,4 +114,14 @@ class GluthGenericMultiplier : public Multiplier GluthBossHelper helper; }; +class NothGenericMultiplier : public Multiplier +{ +public: + NothGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "noth generic"), helper(ai) {} + float GetValue(Action* action) override; + +private: + NothBossHelper helper; +}; + #endif diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h index 35a9bbbeb8..a56a8b0971 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h @@ -20,6 +20,11 @@ namespace NaxxSpellIds // Grobbulus static constexpr uint32 PoisonCloud = 28240; + // Noth the Plaguebringer + static constexpr uint32 CurseOfThePlaguebringer = 29213; + static constexpr uint32 Cripple = 29212; + static constexpr uint32 Blink = 29208; + // Thaddius polarity static constexpr uint32 PositiveCharge10 = 28059; static constexpr uint32 PositiveCharge25 = 28062; diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp index f2ebd24a3e..70f83cb3ad 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp @@ -137,7 +137,15 @@ void RaidNaxxStrategy::InitTriggers(std::vector& triggers) NextAction("loatheb choose target", ACTION_RAID + 1) }) ); - + + // Noth the Plaguebringer + triggers.push_back( + new TriggerNode("noth", + { + NextAction("noth position", ACTION_RAID + 1), + NextAction("noth choose target", ACTION_RAID + 1) + }) + ); } void RaidNaxxStrategy::InitMultipliers(std::vector& multipliers) @@ -153,4 +161,5 @@ void RaidNaxxStrategy::InitMultipliers(std::vector& multipliers) multipliers.push_back(new FourhorsemanGenericMultiplier(botAI)); // multipliers.push_back(new GothikGenericMultiplier(botAI)); multipliers.push_back(new GluthGenericMultiplier(botAI)); + multipliers.push_back(new NothGenericMultiplier(botAI)); } diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h index c8ae79c18a..a4ce1cedf8 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h @@ -49,6 +49,7 @@ class RaidNaxxTriggerContext : public NamedObjectContext creators["gluth main tank mortal wound"] = &RaidNaxxTriggerContext::gluth_main_tank_mortal_wound; creators["loatheb"] = &RaidNaxxTriggerContext::loatheb; + creators["noth"] = &RaidNaxxTriggerContext::noth; } private: @@ -81,6 +82,7 @@ class RaidNaxxTriggerContext : public NamedObjectContext static Trigger* gluth(PlayerbotAI* ai) { return new GluthTrigger(ai); } static Trigger* gluth_main_tank_mortal_wound(PlayerbotAI* ai) { return new GluthMainTankMortalWoundTrigger(ai); } static Trigger* loatheb(PlayerbotAI* ai) { return new LoathebTrigger(ai); } + static Trigger* noth(PlayerbotAI* ai) { return new NothTrigger(ai); } }; #endif \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp index a38f800f7c..499811b5ff 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp @@ -255,6 +255,8 @@ bool PatchwerkNonTankTrigger::IsActive() bool LoathebTrigger::IsActive() { return helper.UpdateBossAI(); } +bool NothTrigger::IsActive() { return helper.UpdateBossAI(); } + bool ThaddiusPhasePetTrigger::IsActive() { if (!helper.UpdateBossAI()) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h b/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h index d02a88a9eb..b97ae4965b 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h @@ -255,4 +255,14 @@ class LoathebTrigger : public Trigger LoathebBossHelper helper; }; +class NothTrigger : public Trigger +{ +public: + NothTrigger(PlayerbotAI* ai) : Trigger(ai, "noth"), helper(ai) {} + bool IsActive() override; + +private: + NothBossHelper helper; +}; + #endif From ff7af1024951acc23096edcecdce1ff675d52e7d Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:29:34 +0100 Subject: [PATCH 20/42] Codestyle --- src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp index 70f83cb3ad..971f231f3e 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp +++ b/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp @@ -137,7 +137,7 @@ void RaidNaxxStrategy::InitTriggers(std::vector& triggers) NextAction("loatheb choose target", ACTION_RAID + 1) }) ); - + // Noth the Plaguebringer triggers.push_back( new TriggerNode("noth", From a92886032c8eeed3b3b5ce5d8e4d307a9113273c Mon Sep 17 00:00:00 2001 From: dillyns <49765217+dillyns@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:25:50 -0500 Subject: [PATCH 21/42] Summon Logic Tweaks (#2049) Issues: - When you have selfbot enabled and use summon command, you will summon yourself. This causes odd movement if you summon while moving, and can sometimes lead to falling through the floor. - When using the summon command on bots with pets/guardians from a medium distance (like jumping down a ledge then commanding summon), the pets will pathfind run to catch up. This causes them to aggro everything on the way. Solution: Fix summon logic to prevent selfbot summon and ensure pets are teleported with bots. --------- Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com> --- src/Ai/Base/Actions/UseMeetingStoneAction.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp index 323d9d5859..ec6a060ab1 100644 --- a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp +++ b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp @@ -150,7 +150,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras) { // Player* master = GetMaster(); - if (!summoner) + if (!summoner || summoner == player) return false; if (player->GetVehicle()) @@ -212,9 +212,11 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras if (!preserveAuras) player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP); - player->TeleportTo(mapId, x, y, z, 0); - + if (player->GetPet()) + player->GetPet()->NearTeleportTo(x, y, z, player->GetOrientation()); + if (player->GetGuardianPet()) + player->GetGuardianPet()->NearTeleportTo(x, y, z, player->GetOrientation()); if (botAI->HasStrategy("stay", botAI->GetState())) { PositionMap& posMap = AI_VALUE(PositionMap&, "position"); From 47dfeea3eccf69a84c02283485771e1171b719b3 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:35:14 +0100 Subject: [PATCH 22/42] Implement Loatheb strategie + New folder structure --- .../Naxxramas/{ => Action}/RaidNaxxActions.h | 0 .../RaidNaxxActions_Anubrekhan.cpp | 0 .../{ => Action}/RaidNaxxActions_Faerlina.cpp | 0 .../RaidNaxxActions_FourHorseman.cpp | 0 .../{ => Action}/RaidNaxxActions_Gluth.cpp | 0 .../{ => Action}/RaidNaxxActions_Gothik.cpp | 0 .../RaidNaxxActions_Grobbulus.cpp | 0 .../{ => Action}/RaidNaxxActions_Heigan.cpp | 0 .../RaidNaxxActions_Kelthuzad.cpp | 0 .../Action/RaidNaxxActions_Loatheb.cpp | 290 ++++++++++++++++++ .../{ => Action}/RaidNaxxActions_Maexxna.cpp | 0 .../{ => Action}/RaidNaxxActions_Noth.cpp | 0 .../RaidNaxxActions_Patchwerk.cpp | 0 .../RaidNaxxActions_Razuvious.cpp | 0 .../RaidNaxxActions_Sapphiron.cpp | 0 .../{ => Action}/RaidNaxxActions_Shared.cpp | 0 .../{ => Action}/RaidNaxxActions_Thaddius.cpp | 0 .../{ => Multiplier}/RaidNaxxMultipliers.cpp | 0 .../{ => Multiplier}/RaidNaxxMultipliers.h | 0 .../Naxxramas/RaidNaxxActions_Loatheb.cpp | 68 ---- src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h | 3 +- .../{ => Strategy}/RaidNaxxStrategy.cpp | 0 .../{ => Strategy}/RaidNaxxStrategy.h | 0 .../{ => Triggers}/RaidNaxxTriggers.cpp | 0 .../{ => Triggers}/RaidNaxxTriggers.h | 0 25 files changed, 292 insertions(+), 69 deletions(-) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions.h (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Anubrekhan.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Faerlina.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_FourHorseman.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Gluth.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Gothik.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Grobbulus.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Heigan.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Kelthuzad.cpp (100%) create mode 100644 src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Maexxna.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Noth.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Patchwerk.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Razuvious.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Sapphiron.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Shared.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Action}/RaidNaxxActions_Thaddius.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Multiplier}/RaidNaxxMultipliers.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Multiplier}/RaidNaxxMultipliers.h (100%) delete mode 100644 src/Ai/Raid/Naxxramas/RaidNaxxActions_Loatheb.cpp rename src/Ai/Raid/Naxxramas/{ => Strategy}/RaidNaxxStrategy.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Strategy}/RaidNaxxStrategy.h (100%) rename src/Ai/Raid/Naxxramas/{ => Triggers}/RaidNaxxTriggers.cpp (100%) rename src/Ai/Raid/Naxxramas/{ => Triggers}/RaidNaxxTriggers.h (100%) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions.h b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions.h rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Anubrekhan.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Anubrekhan.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Faerlina.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Faerlina.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Faerlina.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Faerlina.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_FourHorseman.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_FourHorseman.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_FourHorseman.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_FourHorseman.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Gluth.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Gluth.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Gothik.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gothik.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Gothik.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gothik.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Grobbulus.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Grobbulus.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Grobbulus.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Grobbulus.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Heigan.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Heigan.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Heigan.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Heigan.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Kelthuzad.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp new file mode 100644 index 0000000000..f9fd5495c2 --- /dev/null +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp @@ -0,0 +1,290 @@ +#include "RaidNaxxActions.h" + +#include "Playerbots.h" +#include "PlayerbotAI.h" + +#include + +namespace +{ +constexpr float kTankZ = 273.5857f; +constexpr float kMeleeBehindDistance = 3.0f; +constexpr float kCasterBehindDistance = 12.0f; +constexpr float kCasterSideOffset = 6.0f; +constexpr float kHealerBehindDistance = 18.0f; + +uint8 GetCasterSideIndex(Player* bot) +{ + if (Group* group = bot->GetGroup()) + { + return bot->GetSubGroup() % 2; + } + return 0; +} + +float GetSideSign(uint8 sideIndex) +{ + return sideIndex == 0 ? -1.0f : 1.0f; +} + +void GetBossBehindPosition(Unit* boss, float distance, float& outX, float& outY, float& outZ) +{ + float orientation = boss->GetOrientation(); + float dirX = -std::cos(orientation); + float dirY = -std::sin(orientation); + outX = boss->GetPositionX() + dirX * distance; + outY = boss->GetPositionY() + dirY * distance; + outZ = boss->GetPositionZ(); +} + +void GetCasterGroupPosition(Unit* boss, uint8 sideIndex, float& outX, float& outY, float& outZ) +{ + float orientation = boss->GetOrientation(); + float backX = -std::cos(orientation); + float backY = -std::sin(orientation); + float rightX = -backY; + float rightY = backX; + float sideSign = GetSideSign(sideIndex); + outX = boss->GetPositionX() + backX * kCasterBehindDistance + rightX * kCasterSideOffset * sideSign; + outY = boss->GetPositionY() + backY * kCasterBehindDistance + rightY * kCasterSideOffset * sideSign; + outZ = boss->GetPositionZ(); +} + +void GetHealerPosition(Unit* boss, float& outX, float& outY, float& outZ) +{ + GetBossBehindPosition(boss, kHealerBehindDistance, outX, outY, outZ); +} + +uint8 GetSporeSideIndex(Unit* boss, Unit* spore) +{ + float orientation = boss->GetOrientation(); + float backX = -std::cos(orientation); + float backY = -std::sin(orientation); + float rightX = -backY; + float rightY = backX; + float dx = spore->GetPositionX() - boss->GetPositionX(); + float dy = spore->GetPositionY() - boss->GetPositionY(); + float side = dx * rightX + dy * rightY; + return side < 0.0f ? 0 : 1; +} + +uint8 GetCasterCountForSide(Player* bot, PlayerbotAI* botAI, uint8 sideIndex) +{ + if (!botAI) + { + return 0; + } + Group* group = bot->GetGroup(); + if (!group) + { + return 0; + } + uint8 count = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + { + continue; + } + if (member->GetSubGroup() % 2 != sideIndex) + { + continue; + } + if (bot->GetGUID() != member->GetGUID() && bot->GetMapId() != member->GetMapId()) + { + continue; + } + if (!botAI->IsRanged(member) || botAI->IsHeal(member)) + { + continue; + } + ++count; + } + return count; +} + +bool IsSporeRunner(Player* bot, PlayerbotAI* botAI, uint8 sideIndex) +{ + if (!botAI) + { + return false; + } + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + ObjectGuid selected = ObjectGuid::Empty; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + { + continue; + } + if (member->GetSubGroup() % 2 != sideIndex) + { + continue; + } + if (bot->GetGUID() != member->GetGUID() && bot->GetMapId() != member->GetMapId()) + { + continue; + } + if (!botAI->IsRanged(member) || botAI->IsHeal(member)) + { + continue; + } + if (selected.IsEmpty() || member->GetGUID() < selected) + { + selected = member->GetGUID(); + } + } + return !selected.IsEmpty() && bot->GetGUID() == selected; +} +} // namespace + +bool LoathebPositionAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + Unit* boss = helper.GetBoss(); + if (!boss) + { + return false; + } + if (botAI->IsTank(bot)) + { + if (AI_VALUE2(bool, "has aggro", "boss target")) + { + return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, kTankZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + else + { + float targetX = 0.0f; + float targetY = 0.0f; + float targetZ = boss->GetPositionZ(); + + if (botAI->IsHeal(bot)) + { + GetHealerPosition(boss, targetX, targetY, targetZ); + } + else if (botAI->IsMelee(bot)) + { + GetBossBehindPosition(boss, kMeleeBehindDistance, targetX, targetY, targetZ); + } + else + { + uint8 sideIndex = GetCasterSideIndex(bot); + uint8 otherSideIndex = sideIndex == 0 ? 1 : 0; + bool hasOtherSideCasters = GetCasterCountForSide(bot, botAI, otherSideIndex) > 0; + bool isSporeRunner = IsSporeRunner(bot, botAI, sideIndex); + Unit* sporeTarget = nullptr; + if (isSporeRunner) + { + GuidVector attackers = context->GetValue("attackers")->Get(); + for (ObjectGuid const& guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "spore") && + (!hasOtherSideCasters || GetSporeSideIndex(boss, unit) == sideIndex)) + { + if (!sporeTarget || bot->GetDistance(unit) < bot->GetDistance(sporeTarget)) + { + sporeTarget = unit; + } + } + } + } + + if (sporeTarget) + { + targetX = sporeTarget->GetPositionX(); + targetY = sporeTarget->GetPositionY(); + targetZ = sporeTarget->GetPositionZ(); + } + else + { + GetCasterGroupPosition(boss, sideIndex, targetX, targetY, targetZ); + } + } + + return MoveInside(bot->GetMapId(), targetX, targetY, targetZ, 1.5f, MovementPriority::MOVEMENT_COMBAT); + } + return false; +} + +bool LoathebChooseTargetAction::Execute(Event event) +{ + if (!helper.UpdateBossAI()) + { + return false; + } + Unit* boss = helper.GetBoss(); + if (!boss) + { + return false; + } + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit* target_boss = nullptr; + Unit* target_spore = nullptr; + bool isSporeRunner = false; + uint8 sideIndex = GetCasterSideIndex(bot); + uint8 otherSideIndex = sideIndex == 0 ? 1 : 0; + bool hasOtherSideCasters = false; + if (botAI->IsRanged(bot) && !botAI->IsHeal(bot)) + { + hasOtherSideCasters = GetCasterCountForSide(bot, botAI, otherSideIndex) > 0; + isSporeRunner = IsSporeRunner(bot, botAI, sideIndex); + } + for (auto i = attackers.begin(); i != attackers.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (!unit->IsAlive()) + { + continue; + } + if (botAI->EqualLowercaseName(unit->GetName(), "spore")) + { + if (isSporeRunner) + { + uint8 sporeSide = GetSporeSideIndex(boss, unit); + if (!hasOtherSideCasters || sporeSide == sideIndex) + { + if (!target_spore || bot->GetDistance(unit) < bot->GetDistance(target_spore)) + { + target_spore = unit; + } + } + } + } + if (botAI->EqualLowercaseName(unit->GetName(), "loatheb")) + { + target_boss = unit; + } + } + if (target_spore) + { + target = target_spore; + } + else + { + target = target_boss; + } + if (!target || context->GetValue("current target")->Get() == target) + { + return false; + } + return Attack(target); +} \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Maexxna.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Maexxna.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Maexxna.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Maexxna.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Noth.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Noth.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Noth.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Noth.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Patchwerk.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Patchwerk.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Patchwerk.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Patchwerk.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Razuvious.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Razuvious.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Sapphiron.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Sapphiron.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Shared.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Shared.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Shared.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Shared.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Thaddius.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxActions_Thaddius.cpp rename to src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp b/src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.cpp rename to src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h b/src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.h similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxMultipliers.h rename to src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.h diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Loatheb.cpp b/src/Ai/Raid/Naxxramas/RaidNaxxActions_Loatheb.cpp deleted file mode 100644 index 54a9325989..0000000000 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActions_Loatheb.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "RaidNaxxActions.h" - -#include "Playerbots.h" - -bool LoathebPositionAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - if (botAI->IsTank(bot)) - { - if (AI_VALUE2(bool, "has aggro", "boss target")) - { - return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - } - } - else if (botAI->IsRanged(bot)) - { - return MoveInside(533, helper.rangePos.first, helper.rangePos.second, bot->GetPositionZ(), 1.0f, - MovementPriority::MOVEMENT_COMBAT); - } - return false; -} - -bool LoathebChooseTargetAction::Execute(Event event) -{ - if (!helper.UpdateBossAI()) - { - return false; - } - GuidVector attackers = context->GetValue("attackers")->Get(); - Unit* target = nullptr; - Unit* target_boss = nullptr; - Unit* target_spore = nullptr; - for (auto i = attackers.begin(); i != attackers.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (!unit) - continue; - if (!unit->IsAlive()) - { - continue; - } - if (botAI->EqualLowercaseName(unit->GetName(), "spore")) - { - target_spore = unit; - } - if (botAI->EqualLowercaseName(unit->GetName(), "loatheb")) - { - target_boss = unit; - } - } - if (target_spore && bot->GetDistance2d(target_spore) <= 1.0f) - { - target = target_spore; - } - else - { - target = target_boss; - } - if (!target || context->GetValue("current target")->Get() == target) - { - return false; - } - return Attack(target); -} \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h index e47b248558..50e8388547 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h @@ -530,9 +530,10 @@ class GluthBossHelper : public AiObject class LoathebBossHelper : public AiObject { public: - const std::pair mainTankPos = {2877.57f, -3967.00f}; + const std::pair mainTankPos = {2910.1597f, -4010.0f}; const std::pair rangePos = {2896.96f, -3980.61f}; LoathebBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + Unit* GetBoss() const { return _unit; } bool UpdateBossAI() { if (!bot->IsInCombat()) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxStrategy.cpp rename to src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxStrategy.h b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.h similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxStrategy.h rename to src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.h diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxTriggers.cpp rename to src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h similarity index 100% rename from src/Ai/Raid/Naxxramas/RaidNaxxTriggers.h rename to src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h From 94926b1b0d0935d3538563260b611493a6e50db5 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:35:13 +0100 Subject: [PATCH 23/42] Update RaidNaxxActions_Loatheb.cpp --- .../Action/RaidNaxxActions_Loatheb.cpp | 243 ++++++++++++++++-- 1 file changed, 226 insertions(+), 17 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp index f9fd5495c2..58cbf93534 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Loatheb.cpp @@ -1,5 +1,6 @@ #include "RaidNaxxActions.h" +#include "Item.h" #include "Playerbots.h" #include "PlayerbotAI.h" @@ -12,6 +13,9 @@ constexpr float kMeleeBehindDistance = 3.0f; constexpr float kCasterBehindDistance = 12.0f; constexpr float kCasterSideOffset = 6.0f; constexpr float kHealerBehindDistance = 18.0f; +constexpr float kSporeOnHealerRadius = 10.0f; +constexpr float kSporeCleanupMaxRange = 35.0f; // keep it strictly "no chase" +constexpr float kSporeRunnerNearRange = 15.0f; // if runner is already near, don't steal the job uint8 GetCasterSideIndex(Player* bot) { @@ -22,6 +26,38 @@ uint8 GetCasterSideIndex(Player* bot) return 0; } +ObjectGuid SelectRangedDpsLeaderForSide(Player* bot, PlayerbotAI* botAI, uint8 sideIndex) +{ + if (!botAI) + return ObjectGuid::Empty; + + Group* group = bot->GetGroup(); + if (!group) + return ObjectGuid::Empty; + + ObjectGuid selected = ObjectGuid::Empty; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + if (member->GetSubGroup() % 2 != sideIndex) + continue; + + if (bot->GetGUID() != member->GetGUID() && bot->GetMapId() != member->GetMapId()) + continue; + + if (!botAI->IsRanged(member) || botAI->IsHeal(member)) + continue; + + if (selected.IsEmpty() || member->GetGUID() < selected) + selected = member->GetGUID(); + } + + return selected; +} + float GetSideSign(uint8 sideIndex) { return sideIndex == 0 ? -1.0f : 1.0f; @@ -50,6 +86,13 @@ void GetCasterGroupPosition(Unit* boss, uint8 sideIndex, float& outX, float& out outZ = boss->GetPositionZ(); } +float GetDistance2d(float x1, float y1, float x2, float y2) +{ + float dx = x1 - x2; + float dy = y1 - y2; + return std::sqrt(dx * dx + dy * dy); +} + void GetHealerPosition(Unit* boss, float& outX, float& outY, float& outZ) { GetBossBehindPosition(boss, kHealerBehindDistance, outX, outY, outZ); @@ -115,32 +158,127 @@ bool IsSporeRunner(Player* bot, PlayerbotAI* botAI, uint8 sideIndex) { return false; } + ObjectGuid selected = SelectRangedDpsLeaderForSide(bot, botAI, sideIndex); + return !selected.IsEmpty() && bot->GetGUID() == selected; +} + +bool IsSporeInHealerPack(Player* bot, PlayerbotAI* botAI, Unit* spore) +{ + if (!botAI || !spore) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + if (bot->GetGUID() != member->GetGUID() && bot->GetMapId() != member->GetMapId()) + continue; + + if (!botAI->IsHeal(member)) + continue; + + if (member->GetDistance(spore) <= kSporeOnHealerRadius) + return true; + } + + return false; +} + +bool HasWandEquipped(Player* bot) +{ + if (!bot) + return false; + + Item* const ranged = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); + if (!ranged || !ranged->GetTemplate()) + return false; + + return ranged->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_WAND; +} + +ObjectGuid SelectSporeCleanupCaster(Player* bot, PlayerbotAI* botAI, Unit* spore, ObjectGuid sporeRunnerGuid) +{ + if (!botAI || !spore) + return ObjectGuid::Empty; + + Group* group = bot->GetGroup(); + if (!group) + return ObjectGuid::Empty; + ObjectGuid selected = ObjectGuid::Empty; + float bestDist = 0.0f; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); if (!member || !member->IsAlive()) - { continue; - } - if (member->GetSubGroup() % 2 != sideIndex) - { + + if (member->GetGUID() == sporeRunnerGuid) continue; - } + if (bot->GetGUID() != member->GetGUID() && bot->GetMapId() != member->GetMapId()) - { continue; - } - if (!botAI->IsRanged(member) || botAI->IsHeal(member)) - { + + // We want a true caster cleanup (no healers, no hunters), and no movement commitment. + if (!botAI->IsCaster(member) || botAI->IsHeal(member) || member->getClass() == CLASS_HUNTER) continue; - } - if (selected.IsEmpty() || member->GetGUID() < selected) + + float const dist = member->GetDistance(spore); + if (dist > kSporeCleanupMaxRange) + continue; + + // Pick the nearest eligible caster (stable tie-breaker by GUID). + if (selected.IsEmpty() || dist < bestDist || (std::abs(dist - bestDist) < 0.001f && member->GetGUID() < selected)) { selected = member->GetGUID(); + bestDist = dist; } } - return !selected.IsEmpty() && bot->GetGUID() == selected; + + return selected; +} + +void DoSafeInstantRangedHit(PlayerbotAI* botAI, Player* bot, Unit* target) +{ + if (!botAI || !bot || !target) + return; + + // Prefer wand "shoot" if available: lowest commitment. + if (HasWandEquipped(bot)) + { + botAI->DoSpecificAction("shoot", Event(), true); + return; + } + + // Otherwise, use a single instant ranged spell that is generally safe and low-commitment. + switch (bot->getClass()) + { + case CLASS_WARLOCK: + botAI->DoSpecificAction("corruption", Event(), true); + break; + case CLASS_PRIEST: + botAI->DoSpecificAction("shadow word: pain", Event(), true); + break; + case CLASS_DRUID: + botAI->DoSpecificAction("moonfire", Event(), true); + break; + case CLASS_SHAMAN: + botAI->DoSpecificAction("flame shock", Event(), true); + break; + case CLASS_MAGE: + botAI->DoSpecificAction("fire blast", Event(), true); + break; + default: + // Fallback: if we are considered a caster but we don't have a known instant here, do nothing. + break; + } } } // namespace @@ -207,9 +345,14 @@ bool LoathebPositionAction::Execute(Event event) if (sporeTarget) { - targetX = sporeTarget->GetPositionX(); - targetY = sporeTarget->GetPositionY(); - targetZ = sporeTarget->GetPositionZ(); + GetCasterGroupPosition(boss, sideIndex, targetX, targetY, targetZ); + float groupDistance = GetDistance2d(targetX, targetY, sporeTarget->GetPositionX(), sporeTarget->GetPositionY()); + if (groupDistance > 6.0f && bot->GetDistance(sporeTarget) > 5.0f) + { + targetX = sporeTarget->GetPositionX(); + targetY = sporeTarget->GetPositionY(); + targetZ = sporeTarget->GetPositionZ(); + } } else { @@ -237,6 +380,7 @@ bool LoathebChooseTargetAction::Execute(Event event) Unit* target = nullptr; Unit* target_boss = nullptr; Unit* target_spore = nullptr; + Unit* target_spore_any = nullptr; bool isSporeRunner = false; uint8 sideIndex = GetCasterSideIndex(bot); uint8 otherSideIndex = sideIndex == 0 ? 1 : 0; @@ -257,6 +401,10 @@ bool LoathebChooseTargetAction::Execute(Event event) } if (botAI->EqualLowercaseName(unit->GetName(), "spore")) { + // Track a consistent spore reference for everyone (closest to boss). + if (!target_spore_any || boss->GetDistance(unit) < boss->GetDistance(target_spore_any)) + target_spore_any = unit; + if (isSporeRunner) { uint8 sporeSide = GetSporeSideIndex(boss, unit); @@ -274,6 +422,43 @@ bool LoathebChooseTargetAction::Execute(Event event) target_boss = unit; } } + + // --- Spore cleanup: if a spore sticks on the healer pack and the spore-runner isn't close, assign ONE caster to "tap" it safely. --- + if (target_spore_any && target_boss && IsSporeInHealerPack(bot, botAI, target_spore_any)) + { + // Identify the spore runner for the spore's side (if any). + uint8 const sporeSide = GetSporeSideIndex(boss, target_spore_any); + ObjectGuid const sporeRunnerGuid = SelectRangedDpsLeaderForSide(bot, botAI, sporeSide); + Player* sporeRunner = nullptr; + if (!sporeRunnerGuid.IsEmpty()) + { + if (Group* group = bot->GetGroup()) + { + sporeRunner = ObjectAccessor::FindPlayer(sporeRunnerGuid); + if (sporeRunner && (bot->GetGUID() != sporeRunner->GetGUID() && bot->GetMapId() != sporeRunner->GetMapId())) + sporeRunner = nullptr; + } + } + + bool const runnerIsNear = (sporeRunner && sporeRunner->IsAlive() && sporeRunner->GetDistance(target_spore_any) <= kSporeRunnerNearRange); + if (!runnerIsNear) + { + ObjectGuid const cleanupGuid = SelectSporeCleanupCaster(bot, botAI, target_spore_any, sporeRunnerGuid); + if (!cleanupGuid.IsEmpty() && bot->GetGUID() == cleanupGuid) + { + // No chase: only do it if we're already in a safe ranged window and LoS. + if (bot->IsWithinDistInMap(target_spore_any, kSporeCleanupMaxRange) && bot->IsWithinLOSInMap(target_spore_any)) + { + // Temporarily switch target -> apply ONE instant ranged hit -> immediately go back to boss. + Attack(target_spore_any); + DoSafeInstantRangedHit(botAI, bot, target_spore_any); + Attack(target_boss); + return true; + } + } + } + } + if (target_spore) { target = target_spore; @@ -282,9 +467,33 @@ bool LoathebChooseTargetAction::Execute(Event event) { target = target_boss; } - if (!target || context->GetValue("current target")->Get() == target) + + if (!target) + return false; + + // Spore runner: keep a "light" ranged attack running to reliably secure spore aggro. + // This is intentionally re-tried even if target didn't change (e.g. while moving into range/LoS). + if (target_spore && isSporeRunner && target == target_spore) { + if (bot->getClass() == CLASS_HUNTER) + botAI->DoSpecificAction("auto shot", Event(), true); + else if (botAI->IsCaster(bot) && HasWandEquipped(bot)) + botAI->DoSpecificAction("shoot", Event(), true); + } + + if (context->GetValue("current target")->Get() == target) return false; + + bool attacked = Attack(target); + + // Re-try after target switch so the spore runner starts the ranged attack immediately. + if (target_spore && isSporeRunner && target == target_spore) + { + if (bot->getClass() == CLASS_HUNTER) + botAI->DoSpecificAction("auto shot", Event(), true); + else if (botAI->IsCaster(bot) && HasWandEquipped(bot)) + botAI->DoSpecificAction("shoot", Event(), true); } - return Attack(target); + + return attacked; } \ No newline at end of file From 42743eaa31c8b8d76142ed9091bd42a7034bf2ad Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 30 Jan 2026 13:27:31 +0100 Subject: [PATCH 24/42] Improve Anubrekhan bots strategies --- .../Action/RaidNaxxActions_Anubrekhan.cpp | 102 +++++++++++------- src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h | 24 +---- .../Naxxramas/Strategy/RaidNaxxStrategy.cpp | 5 +- .../Naxxramas/Triggers/RaidNaxxTriggers.cpp | 9 +- 4 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp index 90f14877e2..1dd4356b42 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Anubrekhan.cpp @@ -2,62 +2,89 @@ #include "ObjectGuid.h" #include "Playerbots.h" +#include "RaidNaxxSpellIds.h" bool AnubrekhanChooseTargetAction::Execute(Event event) { GuidVector attackers = context->GetValue("attackers")->Get(); Unit* target = nullptr; - Unit* target_boss = nullptr; - std::vector target_guards; + Unit* targetBoss = nullptr; + std::vector cryptGuards; + std::vector corpseScarabs; for (ObjectGuid const guid : attackers) { Unit* unit = botAI->GetUnit(guid); if (!unit) continue; - if (botAI->EqualLowercaseName(unit->GetName(), "crypt guard")) - { - target_guards.push_back(unit); - } + if (botAI->EqualLowercaseName(unit->GetName(), "anub'rekhan")) - { - target_boss = unit; - } + targetBoss = unit; + else if (botAI->EqualLowercaseName(unit->GetName(), "crypt guard")) + cryptGuards.push_back(unit); + else if (botAI->EqualLowercaseName(unit->GetName(), "corpse scarab")) + corpseScarabs.push_back(unit); } + + if (!targetBoss && cryptGuards.empty() && corpseScarabs.empty()) + return false; + if (botAI->IsMainTank(bot)) { - target = target_boss; + target = targetBoss; } - else + else if (botAI->IsAssistTank(bot)) { - if (target_guards.size() == 0) + for (Unit* add : cryptGuards) { - target = target_boss; + Player* victim = add->GetVictim() ? add->GetVictim()->ToPlayer() : nullptr; + if (!victim || !botAI->IsTank(victim)) + { + target = add; + break; + } } - else + if (!target) + { + if (!cryptGuards.empty()) + target = cryptGuards.front(); + else + target = targetBoss; + } + } + else + { + if (!cryptGuards.empty()) { - if (botAI->IsAssistTank(bot)) + for (Unit* add : cryptGuards) { - for (Unit* t : target_guards) - { - if (target == nullptr || (target->GetVictim() && target->GetVictim()->ToPlayer() && - botAI->IsTank(target->GetVictim()->ToPlayer()))) - { - target = t; - } - } + if (!target || target->GetHealthPct() > add->GetHealthPct()) + target = add; } - else + } + else if (!corpseScarabs.empty()) + { + for (Unit* scarab : corpseScarabs) { - for (Unit* t : target_guards) + Player* victim = scarab->GetVictim() ? scarab->GetVictim()->ToPlayer() : nullptr; + if (victim && !botAI->IsTank(victim)) { - if (target == nullptr || target->GetHealthPct() > t->GetHealthPct()) - { - target = t; - } + target = scarab; + break; } } + + if (!target) + target = corpseScarabs.front(); + } + else + { + target = targetBoss; } } + + if (!target) + return false; + if (context->GetValue("current target")->Get() == target) { return false; @@ -72,22 +99,17 @@ bool AnubrekhanPositionAction::Execute(Event event) { return false; } - bool inPhase = botAI->HasAura("locust swarm", boss) || boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + + bool inPhase = NaxxSpellIds::HasAnyAura(botAI, boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, + NaxxSpellIds::LocustSwarm25}) || + botAI->HasAura("locust swarm", boss); if (inPhase) { if (botAI->IsMainTank(bot)) { uint32 nearest = FindNearestWaypoint(); - uint32 next_point; - if (inPhase) - { - 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, + uint32 nextPoint = (nearest + 1) % intervals; + return MoveTo(bot->GetMapId(), waypoints[nextPoint].first, waypoints[nextPoint].second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } else diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h index a56a8b0971..491fd34fee 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h @@ -92,32 +92,10 @@ namespace NaxxSpellIds static constexpr uint32 LocustSwarm10 = 28785; static constexpr uint32 LocustSwarm10Alt = 28786; static constexpr uint32 LocustSwarm25 = 54021; // 25-man Locust Swarm -/* - SPELL_IMPALE = 28783, - SPELL_LOCUST_SWARM = 28785, - SPELL_SUMMON_CORPSE_SCARABS_5 = 29105, - SPELL_SUMMON_CORPSE_SCARABS_10 = 28864, - SPELL_BERSERK = 26662 - ACHIEV_TIMED_START_EVENT = 9891, - EVENT_SPAWN_CRYPT_GUARDS_1 = 0, - EVENT_BERSERK = 1, - //// - Position const cryptguardPositions[] = { - { 3299.732f, -3502.489f, 287.077f, 2.378f }, - { 3299.086f, -3450.929f, 287.077f, 3.999f }, - { 3331.217f, -3476.607f, 287.074f, 3.269f } -}; -*/ // Loatheb static constexpr uint32 NecroticAura10 = 55593; -/* - SPELL_NECROTIC_AURA = 55593, - SPELL_SUMMON_SPORE = 29234, - SPELL_DEATHBLOOM = 29865, - SPELL_INEVITABLE_DOOM = 29204, - SPELL_BERSERK = 26662 -*/ + inline bool HasAnyAura(PlayerbotAI* botAI, Unit* unit, std::initializer_list spellIds) { if (!botAI || !unit) diff --git a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp index 971f231f3e..11ca8ea200 100644 --- a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp +++ b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp @@ -41,7 +41,10 @@ void RaidNaxxStrategy::InitTriggers(std::vector& triggers) // Anub'Rekhan triggers.push_back(new TriggerNode("anub'rekhan", - { NextAction("anub'rekhan position", ACTION_RAID + 1) } + { + NextAction("anub'rekhan position", ACTION_RAID + 2), + NextAction("anub'rekhan choose target", ACTION_RAID + 1) + } )); // Grand Widow Faerlina diff --git a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp index 499811b5ff..adef2d2e79 100644 --- a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp +++ b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp @@ -182,7 +182,6 @@ bool GluthMainTankMortalWoundTrigger::IsActive() Aura* aura = NaxxSpellIds::GetAnyAura(mt, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25}); if (!aura) { - // Fallback to name for custom spell data. aura = botAI->GetAura("mortal wound", mt, false, true); } if (!aura || aura->GetStackAmount() < 5) @@ -194,13 +193,13 @@ bool GluthMainTankMortalWoundTrigger::IsActive() bool KelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); } -bool AnubrekhanTrigger::IsActive() { +bool AnubrekhanTrigger::IsActive() +{ Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan"); if (!boss) - { return false; - } - return true; + + return bot->IsInCombat() || boss->IsInCombat(); } bool FaerlinaTrigger::IsActive() From 37aa78c7a47d0affb1effaa92ff5b1ba8767f6aa Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 30 Jan 2026 14:10:23 +0100 Subject: [PATCH 25/42] Implement Grand Widow Faerlina bots strategie --- .../Raid/Naxxramas/Action/RaidNaxxActions.h | 12 ++ .../Action/RaidNaxxActions_Faerlina.cpp | 106 +++++++++++++++++- src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h | 3 + src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h | 4 + .../Raid/Naxxramas/RaidNaxxTriggerContext.h | 2 + .../Naxxramas/Strategy/RaidNaxxStrategy.cpp | 9 ++ .../Naxxramas/Triggers/RaidNaxxTriggers.cpp | 23 ++++ .../Naxxramas/Triggers/RaidNaxxTriggers.h | 7 ++ 8 files changed, 165 insertions(+), 1 deletion(-) diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h index b28dd16517..4bd7ff7a74 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h @@ -68,6 +68,18 @@ class GrobbulusMoveAwayAction : public MovementAction float distance; }; +class FaerlinaSacrificeWorshipperAction : public AttackAction +{ +public: + FaerlinaSacrificeWorshipperAction(PlayerbotAI* ai) : AttackAction(ai, "faerlina sacrifice worshipper") {} + + bool Execute(Event event) override; + bool isUseful() override; + +protected: + Unit* GetTarget() override; +}; + class HeiganDanceAction : public MovementAction { public: diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Faerlina.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Faerlina.cpp index 799dd9ca2e..54d535d363 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Faerlina.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Faerlina.cpp @@ -1,3 +1,107 @@ #include "RaidNaxxActions.h" -// Reserved for Faerlina-specific actions. \ No newline at end of file +#include "RaidNaxxSpellIds.h" + +#include + +namespace +{ + constexpr uint32 NpcNaxxramasWorshipper = 16506; + constexpr uint32 NpcNaxxramasFollower = 16505; + constexpr float MaxAddDistanceToBoss = 60.0f; +} + +bool FaerlinaSacrificeWorshipperAction::Execute(Event event) +{ + return AttackAction::Execute(event); +} + +bool FaerlinaSacrificeWorshipperAction::isUseful() +{ + if (!bot->IsInCombat()) + { + return false; + } + + if (!botAI->IsAssistTankOfIndex(bot, 0)) + { + return false; + } + + Unit* boss = AI_VALUE2(Unit*, "find target", "grand widow faerlina"); + if (!boss) + { + return false; + } + + if (boss->HasAura(NaxxSpellIds::FaerlinaWidowsEmbrace)) + { + return false; + } + + if (!boss->HasAura(NaxxSpellIds::FaerlinaFrenzy)) + { + return false; + } + + return GetTarget() != nullptr; +} + +Unit* FaerlinaSacrificeWorshipperAction::GetTarget() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grand widow faerlina"); + if (!boss) + { + return nullptr; + } + + Creature* bestWorshipper = nullptr; + float bestWorshipperDist = std::numeric_limits::max(); + + Creature* bestFollower = nullptr; + float bestFollowerDist = std::numeric_limits::max(); + + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (ObjectGuid const guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || unit->isDead()) + { + continue; + } + + Creature* creature = unit->ToCreature(); + if (!creature) + { + continue; + } + + if (!creature->IsWithinDistInMap(boss, MaxAddDistanceToBoss)) + { + continue; + } + + uint32 const entry = creature->GetEntry(); + float const distToBoss = creature->GetDistance(boss); + + if (entry == NpcNaxxramasWorshipper) + { + if (distToBoss < bestWorshipperDist) + { + bestWorshipper = creature; + bestWorshipperDist = distToBoss; + } + } + else if (entry == NpcNaxxramasFollower) + { + if (distToBoss < bestFollowerDist) + { + bestFollower = creature; + bestFollowerDist = distToBoss; + } + } + } + + // Prefer worshippers for the intended mechanic. Followers are a fallback for clean-up. + return bestWorshipper ? static_cast(bestWorshipper) : static_cast(bestFollower); +} \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h index ad62d74d21..232d788953 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h @@ -43,6 +43,8 @@ class RaidNaxxActionContext : public NamedObjectContext creators["anub'rekhan choose target"] = &RaidNaxxActionContext::anubrekhan_choose_target; creators["anub'rekhan position"] = &RaidNaxxActionContext::anubrekhan_position; + creators["faerlina sacrifice worshipper"] = &RaidNaxxActionContext::faerlina_sacrifice_worshipper; + creators["gluth choose target"] = &RaidNaxxActionContext::gluth_choose_target; creators["gluth position"] = &RaidNaxxActionContext::gluth_position; creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown; @@ -87,6 +89,7 @@ class RaidNaxxActionContext : public NamedObjectContext static Action* kelthuzad_position(PlayerbotAI* ai) { return new KelthuzadPositionAction(ai); } static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new AnubrekhanChooseTargetAction(ai); } static Action* anubrekhan_position(PlayerbotAI* ai) { return new AnubrekhanPositionAction(ai); } + static Action* faerlina_sacrifice_worshipper(PlayerbotAI* ai) { return new FaerlinaSacrificeWorshipperAction(ai); } static Action* gluth_choose_target(PlayerbotAI* ai) { return new GluthChooseTargetAction(ai); } static Action* gluth_position(PlayerbotAI* ai) { return new GluthPositionAction(ai); } static Action* gluth_slowdown(PlayerbotAI* ai) { return new GluthSlowdownAction(ai); } diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h index 491fd34fee..9b8e9b5133 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h @@ -8,6 +8,10 @@ // use src/server/scripts/Northrend/Naxxramas/naxxramas.h for CreatureId, NaxxramasSay, NaxxramasEvent, NaxxramasMisc namespace NaxxSpellIds { + // Grand Widow Faerlina + static constexpr uint32 FaerlinaFrenzy = 28798; + static constexpr uint32 FaerlinaWidowsEmbrace = 28732; + // Heigan static constexpr uint32 Eruption10 = 29371; /* diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h index a4ce1cedf8..58be47705e 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h @@ -40,6 +40,7 @@ class RaidNaxxTriggerContext : public NamedObjectContext creators["anub'rekhan"] = &RaidNaxxTriggerContext::anubrekhan; creators["faerlina"] = &RaidNaxxTriggerContext::faerlina; + creators["faerlina frenzy"] = &RaidNaxxTriggerContext::faerlina_frenzy; creators["maexxna"] = &RaidNaxxTriggerContext::maexxna; creators["patchwerk tank"] = &RaidNaxxTriggerContext::patchwerk_tank; creators["patchwerk non-tank"] = &RaidNaxxTriggerContext::patchwerk_non_tank; @@ -75,6 +76,7 @@ class RaidNaxxTriggerContext : public NamedObjectContext static Trigger* kelthuzad(PlayerbotAI* ai) { return new KelthuzadTrigger(ai); } static Trigger* anubrekhan(PlayerbotAI* ai) { return new AnubrekhanTrigger(ai); } static Trigger* faerlina(PlayerbotAI* ai) { return new FaerlinaTrigger(ai); } + static Trigger* faerlina_frenzy(PlayerbotAI* ai) { return new FaerlinaFrenzyTrigger(ai); } static Trigger* maexxna(PlayerbotAI* ai) { return new MaexxnaTrigger(ai); } static Trigger* patchwerk_tank(PlayerbotAI* ai) { return new PatchwerkTankTrigger(ai); } static Trigger* patchwerk_non_tank(PlayerbotAI* ai) { return new PatchwerkNonTankTrigger(ai); } diff --git a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp index 11ca8ea200..b5682f81ee 100644 --- a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp +++ b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp @@ -52,6 +52,15 @@ void RaidNaxxStrategy::InitTriggers(std::vector& triggers) { NextAction("avoid aoe", ACTION_RAID + 1) } )); + triggers.push_back(new TriggerNode("faerlina frenzy", + { + // Hunters: remove the enrage if possible. + NextAction("tranquilizing shot", ACTION_RAID + 4), + // Otherwise fall back to the intended encounter mechanic. + NextAction("faerlina sacrifice worshipper", ACTION_RAID + 3) + } + )); + // Maexxna triggers.push_back( new TriggerNode("maexxna", diff --git a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp index adef2d2e79..370c2bcf2b 100644 --- a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp +++ b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp @@ -212,6 +212,29 @@ bool FaerlinaTrigger::IsActive() return true; } +bool FaerlinaFrenzyTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "grand widow faerlina"); + if (!boss) + { + return false; + } + + // Only relevant during the actual encounter. + if (!bot->IsInCombat() && !boss->IsInCombat()) + { + return false; + } + + // Frenzy is handled either via a sacrifice (Widow's Embrace) or by removing the enrage. + if (boss->HasAura(NaxxSpellIds::FaerlinaWidowsEmbrace)) + { + return false; + } + + return boss->HasAura(NaxxSpellIds::FaerlinaFrenzy); +} + bool MaexxnaTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna"); diff --git a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h index b97ae4965b..c85ae40346 100644 --- a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h +++ b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h @@ -114,6 +114,13 @@ class AnubrekhanTrigger : public Trigger bool IsActive() override; }; +class FaerlinaFrenzyTrigger : public Trigger +{ +public: + FaerlinaFrenzyTrigger(PlayerbotAI* ai) : Trigger(ai, "faerlina frenzy") {} + bool IsActive() override; +}; + class MaexxnaTrigger : public Trigger { public: From 1ad327e9ff62665299d083619338255e2e9b202b Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:00:23 +0100 Subject: [PATCH 26/42] Implement Maexxna raid bots strategie --- .../Raid/Naxxramas/Action/RaidNaxxActions.h | 19 +++++ .../Action/RaidNaxxActions_Maexxna.cpp | 83 ++++++++++++++++++- src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h | 5 ++ src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h | 5 ++ .../Raid/Naxxramas/RaidNaxxTriggerContext.h | 4 + .../Naxxramas/Strategy/RaidNaxxStrategy.cpp | 8 ++ .../Naxxramas/Triggers/RaidNaxxTriggers.cpp | 78 +++++++++++++++++ .../Naxxramas/Triggers/RaidNaxxTriggers.h | 14 ++++ 8 files changed, 215 insertions(+), 1 deletion(-) diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h index 4bd7ff7a74..cc9a528d37 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h @@ -371,4 +371,23 @@ class PatchwerkRangedPositionAction : public MovementAction bool Execute(Event event) override; }; +// Maexxna +class MaexxnaAttackWebWrapAction : public AttackAction +{ +public: + MaexxnaAttackWebWrapAction(PlayerbotAI* ai) : AttackAction(ai, "maexxna attack web wrap") {} + + bool Execute(Event event) override; + bool isUseful() override; +}; + +class MaexxnaTankSpiderlingsAction : public AttackAction +{ +public: + MaexxnaTankSpiderlingsAction(PlayerbotAI* ai) : AttackAction(ai, "maexxna tank spiderlings") {} + + bool Execute(Event event) override; + bool isUseful() override; +}; + #endif \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Maexxna.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Maexxna.cpp index 67ef9930f0..209e33a636 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Maexxna.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Maexxna.cpp @@ -1,3 +1,84 @@ +#include + +#include "Playerbots.h" #include "RaidNaxxActions.h" +#include "RaidNaxxSpellIds.h" + +bool MaexxnaAttackWebWrapAction::isUseful() +{ + // Prefer ranged DPS/casters for cocoon breaking to minimize movement and boss downtime. + if (botAI->IsHeal(bot) || botAI->IsTank(bot)) + return false; + + return botAI->IsRanged(bot); +} + +bool MaexxnaAttackWebWrapAction::Execute(Event /*event*/) +{ + Unit* best = nullptr; + float bestDist = std::numeric_limits::max(); + + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (ObjectGuid const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit) + continue; + + if (unit->GetEntry() != NaxxSpellIds::MaexxnaWebWrapEntry) + continue; + + float d = bot->GetDistance(unit); + if (!best || d < bestDist) + { + best = unit; + bestDist = d; + } + } + + if (!best) + return false; + + if (AI_VALUE(Unit*, "current target") == best) + return false; + + return Attack(best); +} + +bool MaexxnaTankSpiderlingsAction::isUseful() +{ + // Keep main tank on Maexxna; use this only for an off-tank (25-man typical). + return botAI->IsTank(bot) && !botAI->IsMainTank(bot); +} + +bool MaexxnaTankSpiderlingsAction::Execute(Event /*event*/) +{ + Unit* best = nullptr; + float bestDist = std::numeric_limits::max(); + + GuidVector attackers = AI_VALUE(GuidVector, "attackers"); + for (ObjectGuid const& guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit) + continue; + + if (unit->GetEntry() != NaxxSpellIds::MaexxnaSpiderlingEntry) + continue; + + float d = bot->GetDistance(unit); + if (!best || d < bestDist) + { + best = unit; + bestDist = d; + } + } + + if (!best) + return false; + + if (AI_VALUE(Unit*, "current target") == best) + return false; -// Reserved for Maexxna-specific actions. \ No newline at end of file + return Attack(best); +} \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h index 232d788953..c9b33123f3 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h @@ -45,6 +45,9 @@ class RaidNaxxActionContext : public NamedObjectContext creators["faerlina sacrifice worshipper"] = &RaidNaxxActionContext::faerlina_sacrifice_worshipper; + creators["maexxna attack web wrap"] = &RaidNaxxActionContext::maexxna_attack_web_wrap; + creators["maexxna tank spiderlings"] = &RaidNaxxActionContext::maexxna_tank_spiderlings; + creators["gluth choose target"] = &RaidNaxxActionContext::gluth_choose_target; creators["gluth position"] = &RaidNaxxActionContext::gluth_position; creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown; @@ -90,6 +93,8 @@ class RaidNaxxActionContext : public NamedObjectContext static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new AnubrekhanChooseTargetAction(ai); } static Action* anubrekhan_position(PlayerbotAI* ai) { return new AnubrekhanPositionAction(ai); } static Action* faerlina_sacrifice_worshipper(PlayerbotAI* ai) { return new FaerlinaSacrificeWorshipperAction(ai); } + static Action* maexxna_attack_web_wrap(PlayerbotAI* ai) { return new MaexxnaAttackWebWrapAction(ai); } + static Action* maexxna_tank_spiderlings(PlayerbotAI* ai) { return new MaexxnaTankSpiderlingsAction(ai); } static Action* gluth_choose_target(PlayerbotAI* ai) { return new GluthChooseTargetAction(ai); } static Action* gluth_position(PlayerbotAI* ai) { return new GluthPositionAction(ai); } static Action* gluth_slowdown(PlayerbotAI* ai) { return new GluthSlowdownAction(ai); } diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h index 9b8e9b5133..45160e0249 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h @@ -12,6 +12,11 @@ namespace NaxxSpellIds static constexpr uint32 FaerlinaFrenzy = 28798; static constexpr uint32 FaerlinaWidowsEmbrace = 28732; + // Maexxna + static constexpr uint32 MaexxnaWebWrapStun = 28622; + static constexpr uint32 MaexxnaWebWrapEntry = 16486; + static constexpr uint32 MaexxnaSpiderlingEntry = 17055; + // Heigan static constexpr uint32 Eruption10 = 29371; /* diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h index 58be47705e..3fb0b04e58 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h @@ -42,6 +42,8 @@ class RaidNaxxTriggerContext : public NamedObjectContext creators["faerlina"] = &RaidNaxxTriggerContext::faerlina; creators["faerlina frenzy"] = &RaidNaxxTriggerContext::faerlina_frenzy; creators["maexxna"] = &RaidNaxxTriggerContext::maexxna; + creators["maexxna web wrap"] = &RaidNaxxTriggerContext::maexxna_web_wrap; + creators["maexxna spiderlings"] = &RaidNaxxTriggerContext::maexxna_spiderlings; creators["patchwerk tank"] = &RaidNaxxTriggerContext::patchwerk_tank; creators["patchwerk non-tank"] = &RaidNaxxTriggerContext::patchwerk_non_tank; creators["patchwerk ranged"] = &RaidNaxxTriggerContext::patchwerk_ranged; @@ -78,6 +80,8 @@ class RaidNaxxTriggerContext : public NamedObjectContext static Trigger* faerlina(PlayerbotAI* ai) { return new FaerlinaTrigger(ai); } static Trigger* faerlina_frenzy(PlayerbotAI* ai) { return new FaerlinaFrenzyTrigger(ai); } static Trigger* maexxna(PlayerbotAI* ai) { return new MaexxnaTrigger(ai); } + static Trigger* maexxna_web_wrap(PlayerbotAI* ai) { return new MaexxnaWebWrapTrigger(ai); } + static Trigger* maexxna_spiderlings(PlayerbotAI* ai) { return new MaexxnaSpiderlingsTrigger(ai); } static Trigger* patchwerk_tank(PlayerbotAI* ai) { return new PatchwerkTankTrigger(ai); } static Trigger* patchwerk_non_tank(PlayerbotAI* ai) { return new PatchwerkNonTankTrigger(ai); } static Trigger* patchwerk_ranged(PlayerbotAI* ai) { return new PatchwerkRangedTrigger(ai); } diff --git a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp index b5682f81ee..7e4898bf8b 100644 --- a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp +++ b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp @@ -62,6 +62,14 @@ void RaidNaxxStrategy::InitTriggers(std::vector& triggers) )); // Maexxna + triggers.push_back(new TriggerNode("maexxna web wrap", + { NextAction("maexxna attack web wrap", ACTION_RAID + 5) } + )); + + triggers.push_back(new TriggerNode("maexxna spiderlings", + { NextAction("maexxna tank spiderlings", ACTION_RAID + 2) } + )); + triggers.push_back( new TriggerNode("maexxna", { diff --git a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp index 370c2bcf2b..03cd5d2972 100644 --- a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp +++ b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp @@ -245,6 +245,84 @@ bool MaexxnaTrigger::IsActive() return !botAI->IsTank(bot); } +bool MaexxnaWebWrapTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna"); + if (!boss) + { + return false; + } + + // Only relevant during the actual encounter. + if (!bot->IsInCombat() && !boss->IsInCombat()) + { + return false; + } + + // Prefer ranged DPS/casters to break cocoons with minimal movement. + if (botAI->IsTank(bot) || botAI->IsHeal(bot) || !botAI->IsRanged(bot)) + { + return false; + } + + // If any member is web-wrapped, we want to break it. + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + if (botAI->HasAura(NaxxSpellIds::MaexxnaWebWrapStun, member)) + return true; + } + } + + // Or if the cocoon NPC exists in our target list. + static constexpr uint32 MaexxnaWebWrapEntry = 16486; + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (ObjectGuid const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->GetEntry() == NaxxSpellIds::MaexxnaWebWrapEntry) + return true; + } + + return false; +} + +bool MaexxnaSpiderlingsTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna"); + if (!boss) + { + return false; + } + + // Only relevant during the actual encounter. + if (!bot->IsInCombat() && !boss->IsInCombat()) + { + return false; + } + + // In 25-man we typically have an off-tank; keep the main tank on Maexxna. + if (!botAI->IsTank(bot) || botAI->IsMainTank(bot)) + { + return false; + } + + GuidVector attackers = AI_VALUE(GuidVector, "attackers"); + for (ObjectGuid const& guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->GetEntry() == NaxxSpellIds::MaexxnaSpiderlingEntry) + return true; + } + + return false; +} + bool PatchwerkTankTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); diff --git a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h index c85ae40346..96001ffd2f 100644 --- a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h +++ b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h @@ -128,6 +128,20 @@ class MaexxnaTrigger : public Trigger bool IsActive() override; }; +class MaexxnaWebWrapTrigger : public Trigger +{ +public: + MaexxnaWebWrapTrigger(PlayerbotAI* ai) : Trigger(ai, "maexxna web wrap") {} + bool IsActive() override; +}; + +class MaexxnaSpiderlingsTrigger : public Trigger +{ +public: + MaexxnaSpiderlingsTrigger(PlayerbotAI* ai) : Trigger(ai, "maexxna spiderlings") {} + bool IsActive() override; +}; + class PatchwerkTankTrigger : public Trigger { public: From 34da26d2cad7f3330f11730ffcc18ae59a895f99 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Fri, 30 Jan 2026 20:08:27 +0100 Subject: [PATCH 27/42] Implement Gothik the Harvester Bots Strategy --- .../Raid/Naxxramas/Action/RaidNaxxActions.h | 33 +- .../Action/RaidNaxxActions_Gothik.cpp | 402 +++++++++++++++++- src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h | 13 +- src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h | 10 + .../Raid/Naxxramas/RaidNaxxTriggerContext.h | 9 + .../Naxxramas/Strategy/RaidNaxxStrategy.cpp | 12 +- .../Naxxramas/Triggers/RaidNaxxTriggers.cpp | 26 +- .../Naxxramas/Triggers/RaidNaxxTriggers.h | 14 + 8 files changed, 486 insertions(+), 33 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h index cc9a528d37..5e1290c124 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions.h @@ -219,13 +219,6 @@ class HorsemanAttactInOrderAction : public AttackAction FourhorsemanBossHelper helper; }; -// class SapphironGroundMainTankPositionAction : public MovementAction -// { -// public: -// SapphironGroundMainTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground main tank -// position") {} virtual bool Execute(Event event); -// }; - class SapphironGroundPositionAction : public MovementAction { public: @@ -247,13 +240,6 @@ class SapphironFlightPositionAction : public MovementAction bool MoveToNearestIcebolt(); }; -// class SapphironAvoidChillAction : public MovementAction -// { -// public: -// SapphironAvoidChillAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron avoid chill") {} -// virtual bool Execute(Event event); -// }; - class KelthuzadChooseTargetAction : public AttackAction { public: @@ -390,4 +376,23 @@ class MaexxnaTankSpiderlingsAction : public AttackAction bool isUseful() override; }; +// Gothik the Harvester +class GothikMoveToAssignedSideAction : public MovementAction +{ +public: + GothikMoveToAssignedSideAction(PlayerbotAI* ai) : MovementAction(ai, "gothik move to assigned side") {} + + bool Execute(Event event) override; + bool isUseful() override; +}; + +class GothikChooseTargetAction : public AttackAction +{ +public: + GothikChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "gothik choose target") {} + + bool Execute(Event event) override; + bool isUseful() override; +}; + #endif \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gothik.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gothik.cpp index e5c673e46e..5f721a09d2 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gothik.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gothik.cpp @@ -1,3 +1,403 @@ #include "RaidNaxxActions.h" -// Reserved for Gothik-specific actions. \ No newline at end of file +#include +#include + +#include "Playerbots.h" +#include "RaidNaxxSpellIds.h" + +namespace +{ + constexpr float LiveX = 2691.2f; + constexpr float LiveY = -3387.0f; + constexpr float LiveZ = 267.68f; + + constexpr float DeadX = 2693.5f; + constexpr float DeadY = -3334.6f; + constexpr float DeadZ = 267.68f; + + constexpr char const* StratCoTank = "co tank"; + constexpr char const* StratTankFace = "tank face"; + constexpr char const* StratTankAssist = "tank assist"; + constexpr char const* StratHeal = "heal"; + + enum class GothikSide : uint8 + { + Live = 0, + Dead = 1 + }; + + inline bool IsLiveSide(Unit const* who) + { + return who && who->GetPositionY() < NaxxSpellIds::GothikGateY; + } + + inline bool IsGothikAdd(uint32 entry) + { + switch (entry) + { + case NaxxSpellIds::GothikLivingTraineeEntry: + case NaxxSpellIds::GothikLivingKnightEntry: + case NaxxSpellIds::GothikLivingRiderEntry: + case NaxxSpellIds::GothikDeadTraineeEntry: + case NaxxSpellIds::GothikDeadKnightEntry: + case NaxxSpellIds::GothikDeadHorseEntry: + case NaxxSpellIds::GothikDeadRiderEntry: + return true; + } + return false; + } + + inline uint32 GetAddPriority(uint32 entry) + { + switch (entry) + { + case NaxxSpellIds::GothikLivingRiderEntry: return 70; + case NaxxSpellIds::GothikLivingKnightEntry: return 60; + case NaxxSpellIds::GothikLivingTraineeEntry: return 50; + case NaxxSpellIds::GothikDeadRiderEntry: return 40; + case NaxxSpellIds::GothikDeadKnightEntry: return 30; + case NaxxSpellIds::GothikDeadHorseEntry: return 20; + case NaxxSpellIds::GothikDeadTraineeEntry: return 10; + } + return 0; + } + + static inline bool HasStrategyAnyState(Player* p, char const* name) + { + if (!p || !name) + return false; + if (PlayerbotAI* ai = GET_PLAYERBOT_AI(p)) + { + return ai->HasStrategy(name, BOT_STATE_NON_COMBAT) || + ai->HasStrategy(name, BOT_STATE_COMBAT); + } + return false; + } + + static inline bool IsTankRoleForGothik(PlayerbotAI* localAI, Player* p) + { + if (!p) + return false; + + if (p->HasTankSpec()) + return true; + + if (localAI && localAI->IsTank(p)) + return true; + + return HasStrategyAnyState(p, StratTankFace) || HasStrategyAnyState(p, StratTankAssist) || HasStrategyAnyState(p, "tank") || + HasStrategyAnyState(p, "bear"); + } + + static inline bool IsHealerRoleForGothik(PlayerbotAI* localAI, Player* p) + { + if (!p) + return false; + + if (localAI && localAI->IsHeal(p)) + return true; + + return HasStrategyAnyState(p, StratHeal) || HasStrategyAnyState(p, "holy heal") || HasStrategyAnyState(p, "offheal"); + } + + static inline bool IsMainTankByStrategies(Player* p) + { + return HasStrategyAnyState(p, StratTankFace); + } + + static inline bool IsOffTankByStrategies(Player* p) + { + return HasStrategyAnyState(p, StratTankAssist) && !HasStrategyAnyState(p, StratTankFace); + } + + GothikSide GetAssignedSide(PlayerbotAI* localAI, Player* bot) + { + if (!localAI || !bot) + return GothikSide::Live; + + Group* group = bot->GetGroup(); + if (!group) + return GothikSide::Live; + + std::vector members; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* m = ref->GetSource(); + if (m && m->IsAlive()) + members.push_back(m); + } + + if (members.empty()) + return GothikSide::Live; + + std::sort(members.begin(), members.end(), [](Player* a, Player* b) + { + return a->GetGUID() < b->GetGUID(); + }); + + std::vector tanks, heals, dps; + tanks.reserve(2); + heals.reserve(4); + dps.reserve(25); + + for (Player* m : members) + { + if (IsTankRoleForGothik(localAI, m)) + tanks.push_back(m); + else if (IsHealerRoleForGothik(localAI, m)) + heals.push_back(m); + else + dps.push_back(m); + } + + Player* mainTank = nullptr; + Player* offTank = nullptr; + + for (Player* t : tanks) + { + if (HasStrategyAnyState(t, StratCoTank) && HasStrategyAnyState(t, StratTankFace)) + { + mainTank = t; + break; + } + } + + if (!mainTank) + { + for (Player* t : tanks) + { + if (IsMainTankByStrategies(t)) + { + mainTank = t; + break; + } + } + } + + for (Player* t : tanks) + { + if (t != mainTank && IsOffTankByStrategies(t)) + { + offTank = t; + break; + } + } + + if (!mainTank) + { + for (Player* t : tanks) + { + if (HasStrategyAnyState(t, StratTankFace) && !HasStrategyAnyState(t, StratTankAssist)) + { + mainTank = t; + break; + } + } + } + + if (!mainTank && !tanks.empty()) + mainTank = tanks.front(); + + if (!offTank) + { + for (Player* t : tanks) + { + if (t != mainTank) + { + offTank = t; + break; + } + } + } + + std::unordered_map side; + side.reserve(members.size()); + + if (mainTank) + side[mainTank->GetGUID()] = GothikSide::Live; + if (offTank) + side[offTank->GetGUID()] = GothikSide::Dead; + + for (Player* t : tanks) + { + if (t == mainTank || t == offTank) + continue; + side[t->GetGUID()] = GothikSide::Dead; + } + + if (heals.size() == 1) + { + side[heals[0]->GetGUID()] = GothikSide::Live; + } + else if (heals.size() >= 2) + { + side[heals[0]->GetGUID()] = GothikSide::Live; + side[heals[1]->GetGUID()] = GothikSide::Dead; + + uint32 liveCount = 0, deadCount = 0; + for (Player* m : members) + { + auto it = side.find(m->GetGUID()); + GothikSide s = (it != side.end()) ? it->second : GothikSide::Live; + (s == GothikSide::Live ? liveCount : deadCount)++; + } + + for (size_t i = 2; i < heals.size(); ++i) + { + GothikSide s = (liveCount <= deadCount) ? GothikSide::Live : GothikSide::Dead; + side[heals[i]->GetGUID()] = s; + (s == GothikSide::Live ? liveCount : deadCount)++; + } + } + uint32 const minLiveDps = std::min(2u, uint32(dps.size())); + uint32 liveDps = 0; + for (Player* m : dps) + { + auto it = side.find(m->GetGUID()); + if (it != side.end() && it->second == GothikSide::Live) + ++liveDps; + } + for (Player* m : dps) + { + if (liveDps >= minLiveDps) + break; + + if (side.find(m->GetGUID()) != side.end()) + continue; + + side[m->GetGUID()] = GothikSide::Live; + ++liveDps; + } + uint32 liveCount = 0, deadCount = 0; + for (Player* m : members) + { + auto it = side.find(m->GetGUID()); + GothikSide s = (it != side.end()) ? it->second : GothikSide::Live; + (s == GothikSide::Live ? liveCount : deadCount)++; + } + + for (Player* m : dps) + { + if (side.find(m->GetGUID()) != side.end()) + continue; + + GothikSide s = (liveCount <= deadCount) ? GothikSide::Live : GothikSide::Dead; + side[m->GetGUID()] = s; + (s == GothikSide::Live ? liveCount : deadCount)++; + } + + auto it = side.find(bot->GetGUID()); + return (it != side.end()) ? it->second : GothikSide::Live; + } +} // namespace + +bool GothikMoveToAssignedSideAction::isUseful() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); + if (!boss) + return false; + + if (bot->GetDistance(boss) > 160.0f) + return false; + + GothikSide assigned = GetAssignedSide(botAI, bot); + bool wantLive = (assigned == GothikSide::Live); + bool isLive = IsLiveSide(bot); + + if (wantLive != isLive) + return true; + + float ax = wantLive ? LiveX : DeadX; + float ay = wantLive ? LiveY : DeadY; + float az = wantLive ? LiveZ : DeadZ; + return bot->GetDistance(ax, ay, az) > 12.0f; +} + +bool GothikMoveToAssignedSideAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); + if (!boss) + return false; + + GothikSide assigned = GetAssignedSide(botAI, bot); + bool wantLive = (assigned == GothikSide::Live); + + float ax = wantLive ? LiveX : DeadX; + float ay = wantLive ? LiveY : DeadY; + float az = wantLive ? LiveZ : DeadZ; + + if (MoveTo(NAXX_MAP_ID, ax, ay, az, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) + return true; + + return MoveInside(NAXX_MAP_ID, ax, ay, az, 3.0f, MovementPriority::MOVEMENT_COMBAT); +} + +bool GothikChooseTargetAction::isUseful() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); + if (!boss) + return false; + + return boss->IsInCombat() || bot->IsInCombat(); +} + +bool GothikChooseTargetAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); + if (!boss) + return false; + + bool myLiveSide = IsLiveSide(bot); + + Creature* gothik = boss->ToCreature(); + bool bossAttackable = false; + if (gothik && gothik->IsAlive()) + { + bossAttackable = (gothik->GetReactState() == REACT_AGGRESSIVE) && !gothik->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE); + } + + GuidVector candidates = context->GetValue("possible targets")->Get(); + + Unit* bestAdd = nullptr; + uint32 bestPrio = 0; + float bestDist = 0.0f; + + for (ObjectGuid const& guid : candidates) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (!IsGothikAdd(unit->GetEntry())) + continue; + + if (IsLiveSide(unit) != myLiveSide) + continue; + + uint32 prio = GetAddPriority(unit->GetEntry()); + float dist = bot->GetDistance(unit); + + if (!bestAdd || prio > bestPrio || (prio == bestPrio && dist < bestDist)) + { + bestAdd = unit; + bestPrio = prio; + bestDist = dist; + } + } + + if (bestAdd) + { + if (AI_VALUE(Unit*, "current target") == bestAdd) + return false; + return Attack(bestAdd); + } + if (bossAttackable && IsLiveSide(boss) == myLiveSide) + { + if (AI_VALUE(Unit*, "current target") == boss) + return false; + return Attack(boss); + } + + return false; +} \ No newline at end of file diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h index c9b33123f3..3eaaeb0654 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxActionContext.h @@ -22,9 +22,8 @@ class RaidNaxxActionContext : public NamedObjectContext creators["heigan dance melee"] = &RaidNaxxActionContext::heigan_dance_melee; creators["heigan dance ranged"] = &RaidNaxxActionContext::heigan_dance_ranged; + creators["thaddius attack nearest pet"] = &RaidNaxxActionContext::thaddius_attack_nearest_pet; - // creators["thaddius melee to place"] = &RaidNaxxActionContext::thaddius_tank_to_place; - // creators["thaddius ranged to place"] = &RaidNaxxActionContext::thaddius_ranged_to_place; creators["thaddius move to platform"] = &RaidNaxxActionContext::thaddius_move_to_platform; creators["thaddius move polarity"] = &RaidNaxxActionContext::thaddius_move_polarity; @@ -48,6 +47,9 @@ class RaidNaxxActionContext : public NamedObjectContext creators["maexxna attack web wrap"] = &RaidNaxxActionContext::maexxna_attack_web_wrap; creators["maexxna tank spiderlings"] = &RaidNaxxActionContext::maexxna_tank_spiderlings; + creators["gothik move to assigned side"] = &RaidNaxxActionContext::gothik_move_to_assigned_side; + creators["gothik choose target"] = &RaidNaxxActionContext::gothik_choose_target; + creators["gluth choose target"] = &RaidNaxxActionContext::gluth_choose_target; creators["gluth position"] = &RaidNaxxActionContext::gluth_position; creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown; @@ -69,8 +71,6 @@ class RaidNaxxActionContext : public NamedObjectContext static Action* heigan_dance_melee(PlayerbotAI* ai) { return new HeiganDanceMeleeAction(ai); } static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new HeiganDanceRangedAction(ai); } static Action* thaddius_attack_nearest_pet(PlayerbotAI* ai) { return new ThaddiusAttackNearestPetAction(ai); } - // static Action* thaddius_tank_to_place(PlayerbotAI* ai) { return new ThaddiusMeleeToPlaceAction(ai); } - // static Action* thaddius_ranged_to_place(PlayerbotAI* ai) { return new ThaddiusRangedToPlaceAction(ai); } static Action* thaddius_move_to_platform(PlayerbotAI* ai) { return new ThaddiusMoveToPlatformAction(ai); } static Action* thaddius_move_polarity(PlayerbotAI* ai) { return new ThaddiusMovePolarityAction(ai); } static Action* razuvious_target(PlayerbotAI* ai) { return new RazuviousTargetAction(ai); } @@ -83,11 +83,8 @@ class RaidNaxxActionContext : public NamedObjectContext 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 - // SapphironGroundMainTankPositionAction(ai); } static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); } static Action* sapphiron_flight_position(PlayerbotAI* ai) { return new SapphironFlightPositionAction(ai); } - // static Action* sapphiron_avoid_chill(PlayerbotAI* ai) { return new SapphironAvoidChillAction(ai); } static Action* kelthuzad_choose_target(PlayerbotAI* ai) { return new KelthuzadChooseTargetAction(ai); } static Action* kelthuzad_position(PlayerbotAI* ai) { return new KelthuzadPositionAction(ai); } static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new AnubrekhanChooseTargetAction(ai); } @@ -95,6 +92,8 @@ class RaidNaxxActionContext : public NamedObjectContext static Action* faerlina_sacrifice_worshipper(PlayerbotAI* ai) { return new FaerlinaSacrificeWorshipperAction(ai); } static Action* maexxna_attack_web_wrap(PlayerbotAI* ai) { return new MaexxnaAttackWebWrapAction(ai); } static Action* maexxna_tank_spiderlings(PlayerbotAI* ai) { return new MaexxnaTankSpiderlingsAction(ai); } + static Action* gothik_move_to_assigned_side(PlayerbotAI* ai) { return new GothikMoveToAssignedSideAction(ai); } + static Action* gothik_choose_target(PlayerbotAI* ai) { return new GothikChooseTargetAction(ai); } static Action* gluth_choose_target(PlayerbotAI* ai) { return new GluthChooseTargetAction(ai); } static Action* gluth_position(PlayerbotAI* ai) { return new GluthPositionAction(ai); } static Action* gluth_slowdown(PlayerbotAI* ai) { return new GluthSlowdownAction(ai); } diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h index 45160e0249..eccf7159fe 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h @@ -17,6 +17,16 @@ namespace NaxxSpellIds static constexpr uint32 MaexxnaWebWrapEntry = 16486; static constexpr uint32 MaexxnaSpiderlingEntry = 17055; + // Gothik the Harvester + static constexpr float GothikGateY = -3360.78f; + static constexpr uint32 GothikLivingTraineeEntry = 16124; + static constexpr uint32 GothikLivingKnightEntry = 16125; + static constexpr uint32 GothikLivingRiderEntry = 16126; + static constexpr uint32 GothikDeadTraineeEntry = 16127; + static constexpr uint32 GothikDeadKnightEntry = 16148; + static constexpr uint32 GothikDeadHorseEntry = 16149; + static constexpr uint32 GothikDeadRiderEntry = 16150; + // Heigan static constexpr uint32 Eruption10 = 29371; /* diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h index 3fb0b04e58..5295b5fa08 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h @@ -19,6 +19,7 @@ class RaidNaxxTriggerContext : public NamedObjectContext creators["mutating injection ranged"] = &RaidNaxxTriggerContext::mutating_injection_ranged; creators["mutating injection removed"] = &RaidNaxxTriggerContext::mutating_injection_removed; creators["grobbulus cloud"] = &RaidNaxxTriggerContext::grobbulus_cloud; + creators["heigan melee"] = &RaidNaxxTriggerContext::heigan_melee; creators["heigan ranged"] = &RaidNaxxTriggerContext::heigan_ranged; @@ -39,11 +40,17 @@ class RaidNaxxTriggerContext : public NamedObjectContext creators["kel'thuzad"] = &RaidNaxxTriggerContext::kelthuzad; creators["anub'rekhan"] = &RaidNaxxTriggerContext::anubrekhan; + creators["faerlina"] = &RaidNaxxTriggerContext::faerlina; creators["faerlina frenzy"] = &RaidNaxxTriggerContext::faerlina_frenzy; + creators["maexxna"] = &RaidNaxxTriggerContext::maexxna; creators["maexxna web wrap"] = &RaidNaxxTriggerContext::maexxna_web_wrap; creators["maexxna spiderlings"] = &RaidNaxxTriggerContext::maexxna_spiderlings; + + creators["gothik move to assigned side"] = &RaidNaxxTriggerContext::gothik_move_to_assigned_side; + creators["gothik choose target"] = &RaidNaxxTriggerContext::gothik_choose_target; + creators["patchwerk tank"] = &RaidNaxxTriggerContext::patchwerk_tank; creators["patchwerk non-tank"] = &RaidNaxxTriggerContext::patchwerk_non_tank; creators["patchwerk ranged"] = &RaidNaxxTriggerContext::patchwerk_ranged; @@ -82,6 +89,8 @@ class RaidNaxxTriggerContext : public NamedObjectContext static Trigger* maexxna(PlayerbotAI* ai) { return new MaexxnaTrigger(ai); } static Trigger* maexxna_web_wrap(PlayerbotAI* ai) { return new MaexxnaWebWrapTrigger(ai); } static Trigger* maexxna_spiderlings(PlayerbotAI* ai) { return new MaexxnaSpiderlingsTrigger(ai); } + static Trigger* gothik_move_to_assigned_side(PlayerbotAI* ai) { return new GothikMoveToAssignedSideTrigger(ai); } + static Trigger* gothik_choose_target(PlayerbotAI* ai) { return new GothikChooseTargetTrigger(ai); } static Trigger* patchwerk_tank(PlayerbotAI* ai) { return new PatchwerkTankTrigger(ai); } static Trigger* patchwerk_non_tank(PlayerbotAI* ai) { return new PatchwerkNonTankTrigger(ai); } static Trigger* patchwerk_ranged(PlayerbotAI* ai) { return new PatchwerkRangedTrigger(ai); } diff --git a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp index 7e4898bf8b..7021d84441 100644 --- a/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp +++ b/src/Ai/Raid/Naxxramas/Strategy/RaidNaxxStrategy.cpp @@ -54,9 +54,7 @@ void RaidNaxxStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("faerlina frenzy", { - // Hunters: remove the enrage if possible. NextAction("tranquilizing shot", ACTION_RAID + 4), - // Otherwise fall back to the intended encounter mechanic. NextAction("faerlina sacrifice worshipper", ACTION_RAID + 3) } )); @@ -78,6 +76,15 @@ void RaidNaxxStrategy::InitTriggers(std::vector& triggers) }) ); + // Gothik the Harvester + triggers.push_back(new TriggerNode("gothik move to assigned side", + { NextAction("gothik move to assigned side", ACTION_RAID + 4) } + )); + + triggers.push_back(new TriggerNode("gothik choose target", + { NextAction("gothik choose target", ACTION_RAID + 1) } + )); + // Patchwerk triggers.push_back(new TriggerNode("patchwerk tank", { NextAction("tank face", ACTION_RAID + 2) } @@ -179,7 +186,6 @@ void RaidNaxxStrategy::InitMultipliers(std::vector& multipliers) multipliers.push_back(new KelthuzadGenericMultiplier(botAI)); multipliers.push_back(new AnubrekhanGenericMultiplier(botAI)); multipliers.push_back(new FourhorsemanGenericMultiplier(botAI)); - // multipliers.push_back(new GothikGenericMultiplier(botAI)); multipliers.push_back(new GluthGenericMultiplier(botAI)); multipliers.push_back(new NothGenericMultiplier(botAI)); } diff --git a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp index 03cd5d2972..59f0bb4e3d 100644 --- a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp +++ b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.cpp @@ -296,21 +296,13 @@ bool MaexxnaSpiderlingsTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna"); if (!boss) - { return false; - } - // Only relevant during the actual encounter. if (!bot->IsInCombat() && !boss->IsInCombat()) - { return false; - } - // In 25-man we typically have an off-tank; keep the main tank on Maexxna. if (!botAI->IsTank(bot) || botAI->IsMainTank(bot)) - { return false; - } GuidVector attackers = AI_VALUE(GuidVector, "attackers"); for (ObjectGuid const& guid : attackers) @@ -323,6 +315,24 @@ bool MaexxnaSpiderlingsTrigger::IsActive() return false; } +bool GothikMoveToAssignedSideTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); + if (!boss) + return false; + + return bot->GetDistance(boss) <= 160.0f; +} + +bool GothikChooseTargetTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester"); + if (!boss) + return false; + + return boss->IsInCombat() || bot->IsInCombat(); +} + bool PatchwerkTankTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk"); diff --git a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h index 96001ffd2f..f14166edf7 100644 --- a/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h +++ b/src/Ai/Raid/Naxxramas/Triggers/RaidNaxxTriggers.h @@ -142,6 +142,20 @@ class MaexxnaSpiderlingsTrigger : public Trigger bool IsActive() override; }; +class GothikMoveToAssignedSideTrigger : public Trigger +{ +public: + GothikMoveToAssignedSideTrigger(PlayerbotAI* ai) : Trigger(ai, "gothik move to assigned side") {} + bool IsActive() override; +}; + +class GothikChooseTargetTrigger : public Trigger +{ +public: + GothikChooseTargetTrigger(PlayerbotAI* ai) : Trigger(ai, "gothik choose target") {} + bool IsActive() override; +}; + class PatchwerkTankTrigger : public Trigger { public: From 13fff46fa081db1598f1b2a2bd8aace3c2cc6ed9 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:49:37 +0100 Subject: [PATCH 28/42] Improper singletons migration to clean Meyer's singletons (cherry-pick) (#2082) # Pull Request - Applies the clean and corrected singletons, Meyer pattern. (cherry picked from @SmashingQuasar ) Testing by just playing the game in various ways. Been tested by myself @Celandriel and @SmashingQuasar --- ## 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**) --- ## AI Assistance - Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - [x] No - [ ] Yes (**explain below**) --- ## 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 --- ## Notes for Reviewers Anything that significantly improves realism at the cost of stability or performance should be carefully discussed before merging. --------- Co-authored-by: Nicolas Lebacq Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com> --- .../Base/Actions/AcceptInvitationAction.cpp | 6 +- src/Ai/Base/Actions/AreaTriggerAction.cpp | 2 +- src/Ai/Base/Actions/AttackAction.cpp | 6 +- .../AutoMaintenanceOnLevelupAction.cpp | 16 +- .../Base/Actions/BattleGroundJoinAction.cpp | 90 ++-- src/Ai/Base/Actions/BattleGroundTactics.cpp | 34 +- src/Ai/Base/Actions/CastCustomSpellAction.cpp | 8 +- src/Ai/Base/Actions/ChangeStrategyAction.cpp | 6 +- src/Ai/Base/Actions/ChangeTalentsAction.cpp | 40 +- src/Ai/Base/Actions/ChatShortcutActions.cpp | 4 +- src/Ai/Base/Actions/CheatAction.cpp | 2 +- src/Ai/Base/Actions/CheckMailAction.cpp | 4 +- src/Ai/Base/Actions/CheckMountStateAction.cpp | 8 +- src/Ai/Base/Actions/CheckValuesAction.cpp | 2 +- src/Ai/Base/Actions/ChooseRpgTargetAction.cpp | 8 +- src/Ai/Base/Actions/ChooseTargetActions.cpp | 4 +- .../Base/Actions/ChooseTravelTargetAction.cpp | 34 +- src/Ai/Base/Actions/CombatActions.cpp | 4 +- src/Ai/Base/Actions/DebugAction.cpp | 58 +-- src/Ai/Base/Actions/DelayAction.cpp | 2 +- src/Ai/Base/Actions/DropQuestAction.cpp | 2 +- src/Ai/Base/Actions/EmoteAction.cpp | 14 +- src/Ai/Base/Actions/EquipAction.cpp | 2 +- src/Ai/Base/Actions/FishingAction.cpp | 10 +- src/Ai/Base/Actions/FollowActions.cpp | 8 +- src/Ai/Base/Actions/GenericActions.cpp | 6 +- src/Ai/Base/Actions/GenericBuffUtils.cpp | 6 +- src/Ai/Base/Actions/GenericSpellActions.cpp | 6 +- src/Ai/Base/Actions/GiveItemAction.cpp | 6 +- src/Ai/Base/Actions/GoAction.cpp | 22 +- src/Ai/Base/Actions/GreetAction.cpp | 2 +- src/Ai/Base/Actions/GuildCreateActions.cpp | 4 +- .../Base/Actions/GuildManagementActions.cpp | 54 +-- src/Ai/Base/Actions/HireAction.cpp | 6 +- src/Ai/Base/Actions/InviteToGroupAction.cpp | 28 +- src/Ai/Base/Actions/LeaveGroupAction.cpp | 4 +- src/Ai/Base/Actions/LfgActions.cpp | 16 +- src/Ai/Base/Actions/ListQuestsActions.cpp | 10 +- src/Ai/Base/Actions/ListSpellsAction.cpp | 6 +- src/Ai/Base/Actions/LootAction.cpp | 32 +- src/Ai/Base/Actions/LootRollAction.cpp | 4 +- src/Ai/Base/Actions/MoveToRpgTargetAction.cpp | 4 +- .../Base/Actions/MoveToTravelTargetAction.cpp | 8 +- src/Ai/Base/Actions/MovementActions.cpp | 176 ++++---- src/Ai/Base/Actions/MovementActions.h | 16 +- src/Ai/Base/Actions/NonCombatActions.cpp | 2 +- .../Actions/PassLeadershipToMasterAction.cpp | 4 +- src/Ai/Base/Actions/PetsAction.cpp | 56 +-- src/Ai/Base/Actions/PositionAction.cpp | 8 +- src/Ai/Base/Actions/QueryItemUsageAction.cpp | 6 +- src/Ai/Base/Actions/QueryQuestAction.cpp | 2 +- src/Ai/Base/Actions/QuestAction.cpp | 10 +- .../Base/Actions/QuestConfirmAcceptAction.cpp | 2 +- .../Base/Actions/QuestConfirmAcceptAction.h | 2 +- src/Ai/Base/Actions/RandomBotUpdateAction.cpp | 6 +- src/Ai/Base/Actions/ReachTargetActions.cpp | 6 +- src/Ai/Base/Actions/ReachTargetActions.h | 2 +- src/Ai/Base/Actions/ReadyCheckAction.cpp | 6 +- src/Ai/Base/Actions/ReleaseSpiritAction.cpp | 4 +- src/Ai/Base/Actions/ResetAiAction.cpp | 2 +- .../Actions/RevealGatheringItemAction.cpp | 8 +- .../Base/Actions/ReviveFromCorpseAction.cpp | 24 +- src/Ai/Base/Actions/RpgAction.cpp | 2 +- src/Ai/Base/Actions/RpgSubActions.cpp | 10 +- src/Ai/Base/Actions/SayAction.cpp | 52 +-- src/Ai/Base/Actions/SecurityCheckAction.cpp | 2 +- src/Ai/Base/Actions/SeeSpellAction.cpp | 4 +- src/Ai/Base/Actions/SendMailAction.cpp | 2 +- src/Ai/Base/Actions/ShareQuestAction.cpp | 2 +- src/Ai/Base/Actions/StayActions.cpp | 4 +- src/Ai/Base/Actions/SuggestWhatToDoAction.cpp | 10 +- .../Base/Actions/TalkToQuestGiverAction.cpp | 20 +- src/Ai/Base/Actions/TellLosAction.cpp | 2 +- src/Ai/Base/Actions/TradeAction.cpp | 2 +- src/Ai/Base/Actions/TradeStatusAction.cpp | 28 +- src/Ai/Base/Actions/TrainerAction.cpp | 64 +-- src/Ai/Base/Actions/TravelAction.cpp | 8 +- src/Ai/Base/Actions/UseItemAction.cpp | 8 +- src/Ai/Base/Actions/UseMeetingStoneAction.cpp | 27 +- src/Ai/Base/Actions/VehicleActions.cpp | 2 +- src/Ai/Base/Actions/WhoAction.cpp | 4 +- src/Ai/Base/Actions/WorldBuffAction.cpp | 4 +- src/Ai/Base/Actions/WtsAction.cpp | 4 +- src/Ai/Base/Actions/XpGainAction.cpp | 4 +- src/Ai/Base/SharedValueContext.h | 61 +-- src/Ai/Base/Strategy/CastTimeStrategy.cpp | 2 +- src/Ai/Base/Strategy/ConserveManaStrategy.cpp | 14 +- src/Ai/Base/Strategy/EmoteStrategy.cpp | 6 +- src/Ai/Base/Trigger/GenericTriggers.cpp | 31 +- src/Ai/Base/Trigger/GenericTriggers.h | 6 +- src/Ai/Base/Trigger/HealthTriggers.cpp | 2 +- src/Ai/Base/Trigger/HealthTriggers.h | 18 +- src/Ai/Base/Trigger/LootTriggers.cpp | 4 +- src/Ai/Base/Trigger/PvpTriggers.cpp | 8 +- src/Ai/Base/Trigger/RangeTriggers.cpp | 32 +- src/Ai/Base/Trigger/RangeTriggers.h | 2 +- src/Ai/Base/Trigger/TravelTriggers.cpp | 4 +- src/Ai/Base/Value/AoeHealValues.cpp | 10 +- src/Ai/Base/Value/AoeValues.cpp | 6 +- src/Ai/Base/Value/Arrow.cpp | 14 +- src/Ai/Base/Value/AttackerCountValues.cpp | 2 +- src/Ai/Base/Value/AttackersValue.cpp | 14 +- src/Ai/Base/Value/AttackersValue.h | 2 +- src/Ai/Base/Value/AvailableLootValue.cpp | 2 +- src/Ai/Base/Value/CcTargetValue.cpp | 10 +- src/Ai/Base/Value/CollisionValue.cpp | 6 +- src/Ai/Base/Value/DistanceValue.cpp | 12 +- src/Ai/Base/Value/DpsTargetValue.cpp | 6 +- src/Ai/Base/Value/EnemyHealerTargetValue.cpp | 2 +- src/Ai/Base/Value/EnemyPlayerValue.cpp | 4 +- src/Ai/Base/Value/EnemyPlayerValue.h | 2 +- src/Ai/Base/Value/EstimatedLifetimeValue.cpp | 4 +- src/Ai/Base/Value/Formations.cpp | 30 +- src/Ai/Base/Value/Formations.h | 2 +- src/Ai/Base/Value/GrindTargetValue.cpp | 6 +- src/Ai/Base/Value/GroupValues.cpp | 8 +- src/Ai/Base/Value/HasAvailableLootValue.cpp | 2 +- src/Ai/Base/Value/ItemUsageValue.cpp | 22 +- src/Ai/Base/Value/NearestAdsValue.h | 2 +- src/Ai/Base/Value/NearestCorpsesValue.h | 2 +- .../Base/Value/NearestFriendlyPlayersValue.h | 2 +- src/Ai/Base/Value/NearestGameObjects.h | 2 +- src/Ai/Base/Value/NearestNonBotPlayersValue.h | 2 +- src/Ai/Base/Value/NearestNpcsValue.h | 8 +- src/Ai/Base/Value/NearestUnitsValue.h | 2 +- src/Ai/Base/Value/PartyMemberToHeal.cpp | 14 +- src/Ai/Base/Value/PartyMemberValue.cpp | 7 +- .../Base/Value/PartyMemberWithoutAuraValue.h | 2 +- .../Base/Value/PartyMemberWithoutItemValue.h | 2 +- src/Ai/Base/Value/PositionValue.h | 2 +- src/Ai/Base/Value/PossibleRpgTargetsValue.cpp | 2 +- src/Ai/Base/Value/PossibleTargetsValue.h | 4 +- src/Ai/Base/Value/PvpValues.cpp | 8 +- src/Ai/Base/Value/RtiTargetValue.cpp | 8 +- src/Ai/Base/Value/SnareTargetValue.cpp | 2 +- src/Ai/Base/Value/Stances.cpp | 4 +- src/Ai/Base/Value/StatsValues.cpp | 4 +- src/Ai/Base/Value/TargetValue.cpp | 2 +- src/Ai/Base/ValueContext.h | 4 +- src/Ai/Class/Dk/Trigger/DKTriggers.cpp | 2 +- src/Ai/Class/Druid/Action/DruidActions.cpp | 10 +- .../Druid/Action/DruidShapeshiftActions.cpp | 4 +- .../Druid/Strategy/OffhealDruidCatStrategy.h | 2 +- .../Class/Hunter/Trigger/HunterTriggers.cpp | 8 +- src/Ai/Class/Mage/Action/MageActions.cpp | 6 +- .../Class/Paladin/PaladinAiObjectContext.cpp | 2 +- .../Strategy/OffhealRetPaladinStrategy.h | 2 +- src/Ai/Class/Priest/Action/PriestActions.cpp | 12 +- .../Class/Priest/Trigger/PriestTriggers.cpp | 4 +- src/Ai/Class/Rogue/Action/RogueActions.cpp | 2 +- src/Ai/Class/Rogue/RogueAiObjectContext.cpp | 2 +- .../Strategy/AssassinationRogueStrategy.h | 2 +- src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp | 8 +- .../Class/Warlock/Action/WarlockActions.cpp | 10 +- .../Class/Warlock/Trigger/WarlockTriggers.cpp | 2 +- .../Class/Warrior/Trigger/WarriorTriggers.cpp | 2 +- .../Class/Warrior/WarriorAiObjectContext.cpp | 2 +- .../Multiplier/UtgardePinnacleMultipliers.cpp | 2 +- .../Trigger/UtgardePinnacleTriggers.cpp | 2 +- .../BlackwingLair/Strategy/RaidBwlStrategy.h | 2 +- .../Raid/Icecrown/Action/RaidIccActions.cpp | 8 +- src/Ai/Raid/Icecrown/RaidIccScripts.h | 2 +- .../Raid/Icecrown/Trigger/RaidIccTriggers.cpp | 4 +- .../Karazhan/Action/RaidKarazhanActions.cpp | 10 +- .../Karazhan/RaidKarazhanTriggerContext.h | 2 +- .../ObsidianSanctum/Action/RaidOsActions.cpp | 2 +- .../Raid/Ulduar/Action/RaidUlduarActions.cpp | 2 +- .../Multiplier/RaidUlduarMultipliers.cpp | 2 +- .../Ulduar/Multiplier/RaidUlduarMultipliers.h | 2 +- .../Raid/Ulduar/Strategy/RaidUlduarStrategy.h | 2 +- .../Ulduar/Trigger/RaidUlduarTriggers.cpp | 2 +- src/Ai/World/Rpg/Action/NewRpgAction.cpp | 2 +- src/Ai/World/Rpg/Action/NewRpgAction.h | 2 +- src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp | 18 +- src/Ai/World/Rpg/Action/NewRpgBaseAction.h | 2 +- src/Ai/World/Rpg/NewRpgInfo.cpp | 2 +- src/Ai/World/Rpg/NewRpgInfo.h | 2 +- src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp | 2 +- src/Bot/Cmd/PlayerbotCommandServer.cpp | 8 +- src/Bot/Cmd/PlayerbotCommandServer.h | 19 +- src/Bot/Debug/PerfMonitor.cpp | 2 +- src/Bot/Debug/PerfMonitor.h | 28 +- src/Bot/Engine/Engine.cpp | 12 +- src/Bot/Engine/PlayerbotAIBase.cpp | 8 +- src/Bot/Engine/PlayerbotAIBase.h | 2 +- src/Bot/Engine/Value/Value.cpp | 6 +- src/Bot/Engine/Value/Value.h | 10 +- src/Bot/Factory/AiFactory.cpp | 40 +- src/Bot/Factory/PlayerbotFactory.cpp | 282 ++++++------ src/Bot/Factory/RandomPlayerbotFactory.cpp | 56 +-- src/Bot/PlayerbotAI.cpp | 236 +++++----- src/Bot/PlayerbotAI.h | 10 +- src/Bot/PlayerbotMgr.cpp | 158 +++---- src/Bot/PlayerbotMgr.h | 19 +- src/Bot/RandomPlayerbotMgr.cpp | 413 ++++++++---------- src/Bot/RandomPlayerbotMgr.h | 45 +- src/Db/FlightMasterCache.h | 15 +- src/Db/PlayerbotDungeonRepository.cpp | 10 +- src/Db/PlayerbotDungeonRepository.h | 22 +- src/Db/PlayerbotRepository.h | 26 +- src/Db/PlayerbotSpellRepository.cpp | 7 + src/Db/PlayerbotSpellRepository.h | 28 +- src/Mgr/Guild/GuildTaskMgr.cpp | 80 ++-- src/Mgr/Guild/GuildTaskMgr.h | 65 +-- src/Mgr/Guild/PlayerbotGuildMgr.cpp | 17 +- src/Mgr/Guild/PlayerbotGuildMgr.h | 21 +- src/Mgr/Item/ItemVisitors.cpp | 2 +- src/Mgr/Item/RandomItemMgr.cpp | 8 +- src/Mgr/Item/RandomItemMgr.h | 19 +- src/Mgr/Item/StatsCollector.h | 2 +- src/Mgr/Move/FleeManager.cpp | 22 +- src/Mgr/Security/PlayerbotSecurity.cpp | 10 +- src/Mgr/Text/PlayerbotTextMgr.cpp | 8 +- src/Mgr/Text/PlayerbotTextMgr.h | 33 +- src/Mgr/Travel/TravelMgr.cpp | 297 ++++++------- src/Mgr/Travel/TravelMgr.h | 25 +- src/Mgr/Travel/TravelNode.cpp | 196 ++++----- src/Mgr/Travel/TravelNode.h | 17 +- src/PlayerbotAIConfig.cpp | 20 +- src/PlayerbotAIConfig.h | 23 +- src/Script/PlayerbotCommandScript.cpp | 20 +- src/Script/Playerbots.cpp | 221 +++++----- src/Script/Playerbots.h | 12 +- src/Script/PlayerbotsSecureLogin.cpp | 7 +- src/Script/WorldThr/PlayerbotOperations.h | 36 +- .../PlayerbotWorldThreadProcessor.cpp | 21 +- .../WorldThr/PlayerbotWorldThreadProcessor.h | 34 +- src/Script/WorldThr/Queue.cpp | 4 +- src/Script/WorldThr/Queue.h | 2 +- src/Util/BroadcastHelper.cpp | 296 ++++++------- src/Util/BroadcastHelper.h | 2 +- src/Util/ServerFacade.cpp | 6 +- src/Util/ServerFacade.h | 12 +- 233 files changed, 2454 insertions(+), 2348 deletions(-) diff --git a/src/Ai/Base/Actions/AcceptInvitationAction.cpp b/src/Ai/Base/Actions/AcceptInvitationAction.cpp index 5e0bffc47f..1be3c0921a 100644 --- a/src/Ai/Base/Actions/AcceptInvitationAction.cpp +++ b/src/Ai/Base/Actions/AcceptInvitationAction.cpp @@ -46,10 +46,10 @@ bool AcceptInvitationAction::Execute(Event event) if (!bot->GetGroup() || !bot->GetGroup()->IsMember(inviter->GetGUID())) return false; - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sRandomPlayerbotMgr.IsRandomBot(bot)) botAI->SetMaster(inviter); // else - // sPlayerbotRepository->Save(botAI); + // PlayerbotRepository::instance().Save(botAI); botAI->ResetStrategies(); botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT); @@ -57,7 +57,7 @@ bool AcceptInvitationAction::Execute(Event event) botAI->TellMaster("Hello"); - if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance) + if (sPlayerbotAIConfig.summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig.sightDistance) { Teleport(inviter, bot, true); } diff --git a/src/Ai/Base/Actions/AreaTriggerAction.cpp b/src/Ai/Base/Actions/AreaTriggerAction.cpp index f480277825..c610085400 100644 --- a/src/Ai/Base/Actions/AreaTriggerAction.cpp +++ b/src/Ai/Base/Actions/AreaTriggerAction.cpp @@ -50,7 +50,7 @@ bool ReachAreaTriggerAction::Execute(Event event) /*forceDestination*/ false); float distance = bot->GetDistance(at->x, at->y, at->z); - float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay; + float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig.reactDelay; botAI->TellError("Wait for me"); botAI->SetNextCheckDelay(delay); context->GetValue("last area trigger")->Get().lastAreaTrigger = triggerId; diff --git a/src/Ai/Base/Actions/AttackAction.cpp b/src/Ai/Base/Actions/AttackAction.cpp index 2b8c486f8b..87bbd31c6b 100644 --- a/src/Ai/Base/Actions/AttackAction.cpp +++ b/src/Ai/Base/Actions/AttackAction.cpp @@ -87,8 +87,8 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) // Check if bot OR target is in prohibited zone/area (skip for duels) if ((target->IsPlayer() || target->IsPet()) && (!bot->duel || bot->duel->Opponent != target) && - (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) || - sPlayerbotAIConfig->IsPvpProhibited(target->GetZoneId(), target->GetAreaId()))) + (sPlayerbotAIConfig.IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) || + sPlayerbotAIConfig.IsPvpProhibited(target->GetZoneId(), target->GetAreaId()))) { if (verbose) botAI->TellError("I cannot attack other players in PvP prohibited areas."); @@ -160,7 +160,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) } if (botAI->CanMove() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target)) - sServerFacade->SetFacingTo(bot, target); + ServerFacade::instance().SetFacingTo(bot, target); botAI->ChangeEngine(BOT_STATE_COMBAT); diff --git a/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp b/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp index 6939e5c925..de8b5c6fdc 100644 --- a/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp +++ b/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp @@ -19,7 +19,7 @@ bool AutoMaintenanceOnLevelupAction::Execute(Event event) void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel() { - if (!sPlayerbotAIConfig->autoTeleportForLevel || !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig.autoTeleportForLevel || !sRandomPlayerbotMgr.IsRandomBot(bot)) { return; } @@ -27,13 +27,13 @@ void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel() { return; } - sRandomPlayerbotMgr->RandomTeleportForLevel(bot); + sRandomPlayerbotMgr.RandomTeleportForLevel(bot); return; } void AutoMaintenanceOnLevelupAction::AutoPickTalents() { - if (!sPlayerbotAIConfig->autoPickTalents || !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig.autoPickTalents || !sRandomPlayerbotMgr.IsRandomBot(bot)) return; if (bot->GetFreeTalentPoints() <= 0) @@ -65,10 +65,10 @@ void AutoMaintenanceOnLevelupAction::AutoLearnSpell() void AutoMaintenanceOnLevelupAction::LearnSpells(std::ostringstream* out) { BroadcastHelper::BroadcastLevelup(botAI, bot); - if (sPlayerbotAIConfig->autoLearnTrainerSpells && sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sPlayerbotAIConfig.autoLearnTrainerSpells && sRandomPlayerbotMgr.IsRandomBot(bot)) LearnTrainerSpells(out); - if (sPlayerbotAIConfig->autoLearnQuestSpells && sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sPlayerbotAIConfig.autoLearnQuestSpells && sRandomPlayerbotMgr.IsRandomBot(bot)) LearnQuestSpells(out); } @@ -166,7 +166,7 @@ std::string const AutoMaintenanceOnLevelupAction::FormatSpell(SpellInfo const* s void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip() { - if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig.autoUpgradeEquip || !sRandomPlayerbotMgr.IsRandomBot(bot)) return; PlayerbotFactory factory(bot, bot->GetLevel()); @@ -180,9 +180,9 @@ void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip() factory.InitConsumables(); factory.InitPotions(); - if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) + if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel) { - if (sPlayerbotAIConfig->incrementalGearInit) + if (sPlayerbotAIConfig.incrementalGearInit) factory.InitEquipment(true); } } diff --git a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp index 542c7566ff..418b0f1fa7 100644 --- a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp +++ b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp @@ -89,7 +89,7 @@ bool BGJoinAction::gatherArenaTeam(ArenaType type) // continue; if (offline) - sRandomPlayerbotMgr->AddPlayerBot(itr->Guid, 0); + sRandomPlayerbotMgr.AddPlayerBot(itr->Guid, 0); if (member) { @@ -100,7 +100,7 @@ bool BGJoinAction::gatherArenaTeam(ArenaType type) if (member->GetGroup() && memberBotAI->HasRealPlayerMaster()) continue; - if (!sPlayerbotAIConfig->IsInRandomAccountList(member->GetSession()->GetAccountId())) + if (!sPlayerbotAIConfig.IsInRandomAccountList(member->GetSession()->GetAccountId())) continue; if (member->IsInCombat()) @@ -250,13 +250,13 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun TeamSize = (uint32)type; // Check if bots should join Rated Arena (Only captains can queue) - uint32 ratedArenaBotCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount; + uint32 ratedArenaBotCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount; uint32 ratedArenaPlayerCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount; uint32 ratedArenaInstanceCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaInstanceCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaInstanceCount; uint32 activeRatedArenaQueue = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].activeRatedArenaQueue; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].activeRatedArenaQueue; bool isRated = (ratedArenaBotCount + ratedArenaPlayerCount) < (BracketSize * (activeRatedArenaQueue + ratedArenaInstanceCount)); @@ -265,7 +265,7 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun { if (sArenaTeamMgr->GetArenaTeamByCaptain(bot->GetGUID(), type)) { - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount += TeamSize; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount += TeamSize; ratedList.push_back(queueTypeId); return true; } @@ -274,13 +274,13 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun // Check if bots should join Skirmish Arena // We have extra bots queue because same faction can vs each other but can't be in the same group. uint32 skirmishArenaBotCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount; uint32 skirmishArenaPlayerCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].skirmishArenaPlayerCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].skirmishArenaPlayerCount; uint32 skirmishArenaInstanceCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].skirmishArenaInstanceCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].skirmishArenaInstanceCount; uint32 activeSkirmishArenaQueue = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].activeSkirmishArenaQueue; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].activeSkirmishArenaQueue; uint32 maxRequiredSkirmishBots = BracketSize * (activeSkirmishArenaQueue + skirmishArenaInstanceCount); if (maxRequiredSkirmishBots != 0) maxRequiredSkirmishBots = maxRequiredSkirmishBots + TeamSize; @@ -294,12 +294,12 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun } // Check if bots should join Battleground - uint32 bgAllianceBotCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount; - uint32 bgAlliancePlayerCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgAlliancePlayerCount; - uint32 bgHordeBotCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgHordeBotCount; - uint32 bgHordePlayerCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgHordePlayerCount; - uint32 activeBgQueue = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].activeBgQueue; - uint32 bgInstanceCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgInstanceCount; + uint32 bgAllianceBotCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount; + uint32 bgAlliancePlayerCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgAlliancePlayerCount; + uint32 bgHordeBotCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgHordeBotCount; + uint32 bgHordePlayerCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgHordePlayerCount; + uint32 activeBgQueue = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].activeBgQueue; + uint32 bgInstanceCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgInstanceCount; if (teamId == TEAM_ALLIANCE) { @@ -318,7 +318,7 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun bool BGJoinAction::isUseful() { // do not try if BG bots disabled - if (!sPlayerbotAIConfig->randomBotJoinBG) + if (!sPlayerbotAIConfig.randomBotJoinBG) return false; // can't queue while in BG/Arena @@ -440,7 +440,7 @@ bool BGJoinAction::JoinQueue(uint32 type) // get battlemaster // Unit* unit = botAI->GetUnit(AI_VALUE2(CreatureData const*, "bg master", bgTypeId)); - Unit* unit = botAI->GetUnit(sRandomPlayerbotMgr->GetBattleMasterGUID(bot, bgTypeId)); + Unit* unit = botAI->GetUnit(sRandomPlayerbotMgr.GetBattleMasterGUID(bot, bgTypeId)); if (!unit && isArena) { botAI->GetAiObjectContext()->GetValue("bg type")->Set(0); @@ -450,7 +450,7 @@ bool BGJoinAction::JoinQueue(uint32 type) // This breaks groups as refresh includes a remove from group function call. // refresh food/regs - // sRandomPlayerbotMgr->Refresh(bot); + // sRandomPlayerbotMgr.Refresh(bot); bool joinAsGroup = bot->GetGroup() && bot->GetGroup()->GetLeaderGUID() == bot->GetGUID(); @@ -523,23 +523,23 @@ bool BGJoinAction::JoinQueue(uint32 type) { if (!isRated) { - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount++; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount++; } } else if (!joinAsGroup) { if (teamId == TEAM_ALLIANCE) - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount++; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount++; else - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgHordeBotCount++; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgHordeBotCount++; } else { if (teamId == TEAM_ALLIANCE) - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount += + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount += bot->GetGroup()->GetMembersCount(); else - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgHordeBotCount += + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgHordeBotCount += bot->GetGroup()->GetMembersCount(); } @@ -588,13 +588,13 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg TeamSize = (uint32)type; // Check if bots should join Rated Arena (Only captains can queue) - uint32 ratedArenaBotCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount; + uint32 ratedArenaBotCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount; uint32 ratedArenaPlayerCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount; uint32 ratedArenaInstanceCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaInstanceCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaInstanceCount; uint32 activeRatedArenaQueue = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].activeRatedArenaQueue; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].activeRatedArenaQueue; bool isRated = (ratedArenaBotCount + ratedArenaPlayerCount) < (BracketSize * (activeRatedArenaQueue + ratedArenaInstanceCount)); @@ -603,7 +603,7 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg { if (sArenaTeamMgr->GetArenaTeamByCaptain(bot->GetGUID(), type)) { - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount += TeamSize; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount += TeamSize; ratedList.push_back(queueTypeId); return true; } @@ -612,13 +612,13 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg // Check if bots should join Skirmish Arena // We have extra bots queue because same faction can vs each other but can't be in the same group. uint32 skirmishArenaBotCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount; uint32 skirmishArenaPlayerCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].skirmishArenaPlayerCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].skirmishArenaPlayerCount; uint32 skirmishArenaInstanceCount = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].skirmishArenaInstanceCount; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].skirmishArenaInstanceCount; uint32 activeSkirmishArenaQueue = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].activeSkirmishArenaQueue; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].activeSkirmishArenaQueue; uint32 maxRequiredSkirmishBots = BracketSize * (activeSkirmishArenaQueue + skirmishArenaInstanceCount); if (maxRequiredSkirmishBots != 0) maxRequiredSkirmishBots = maxRequiredSkirmishBots + TeamSize; @@ -632,12 +632,12 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg } // Check if bots should join Battleground - uint32 bgAllianceBotCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount; - uint32 bgAlliancePlayerCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgAlliancePlayerCount; - uint32 bgHordeBotCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgHordeBotCount; - uint32 bgHordePlayerCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgHordePlayerCount; - uint32 activeBgQueue = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].activeBgQueue; - uint32 bgInstanceCount = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgInstanceCount; + uint32 bgAllianceBotCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount; + uint32 bgAlliancePlayerCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgAlliancePlayerCount; + uint32 bgHordeBotCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgHordeBotCount; + uint32 bgHordePlayerCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgHordePlayerCount; + uint32 activeBgQueue = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].activeBgQueue; + uint32 bgInstanceCount = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].bgInstanceCount; if (teamId == TEAM_ALLIANCE) { @@ -670,7 +670,7 @@ bool BGLeaveAction::Execute(Event event) uint16 unk = 0x1F90; uint8 unk2 = 0x0; bool isArena = false; - bool IsRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool IsRandomBot = sRandomPlayerbotMgr.IsRandomBot(bot); ArenaType arenaType = ArenaType(BattlegroundMgr::BGArenaType(queueTypeId)); if (arenaType) @@ -709,7 +709,7 @@ bool BGStatusAction::LeaveBG(PlayerbotAI* botAI) if (!bg) return false; bool isArena = bg->isArena(); - bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool isRandomBot = sRandomPlayerbotMgr.IsRandomBot(bot); if (isRandomBot) botAI->SetMaster(nullptr); @@ -805,7 +805,7 @@ bool BGStatusAction::Execute(Event event) break; } - bool IsRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool IsRandomBot = sRandomPlayerbotMgr.IsRandomBot(bot); BattlegroundQueueTypeId queueTypeId = bot->GetBattlegroundQueueTypeId(QueueSlot); BattlegroundTypeId _bgTypeId = BattlegroundMgr::BGTemplateId(queueTypeId); if (!queueTypeId) @@ -958,10 +958,10 @@ bool BGStatusAction::Execute(Event event) //TeamId teamId = bot->GetTeamId(); //not used, line marked for removal. bool realPlayers = false; if (isRated) - realPlayers = sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount > 0; + realPlayers = sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount > 0; else realPlayers = - sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].skirmishArenaPlayerCount > 0; + sRandomPlayerbotMgr.BattlegroundData[queueTypeId][bracketId].skirmishArenaPlayerCount > 0; if (realPlayers) return false; diff --git a/src/Ai/Base/Actions/BattleGroundTactics.cpp b/src/Ai/Base/Actions/BattleGroundTactics.cpp index 827ab01970..1704150a30 100644 --- a/src/Ai/Base/Actions/BattleGroundTactics.cpp +++ b/src/Ai/Base/Actions/BattleGroundTactics.cpp @@ -1276,7 +1276,7 @@ static std::pair IC_AttackObjectives[] = { // useful commands for fixing BG bugs and checking waypoints/paths bool BGTactics::HandleConsoleCommand(ChatHandler* handler, char const* args) { - if (!sPlayerbotAIConfig->enabled) + if (!sPlayerbotAIConfig.enabled) { handler->PSendSysMessage("|cffff0000Playerbot system is currently disabled!"); return true; @@ -2219,7 +2219,7 @@ bool BGTactics::selectObjective(bool reset) if (urand(0, 99) < 20 && teamFC) { target.Relocate(teamFC->GetPositionX(), teamFC->GetPositionY(), teamFC->GetPositionZ()); - if (sServerFacade->GetDistance2d(bot, teamFC) < 33.0f) + if (ServerFacade::instance().GetDistance2d(bot, teamFC) < 33.0f) Follow(teamFC); } else @@ -2263,7 +2263,7 @@ bool BGTactics::selectObjective(bool reset) if (urand(0, 99) < 70) { target.Relocate(teamFC->GetPositionX(), teamFC->GetPositionY(), teamFC->GetPositionZ()); - if (sServerFacade->GetDistance2d(bot, teamFC) < 33.0f) + if (ServerFacade::instance().GetDistance2d(bot, teamFC) < 33.0f) Follow(teamFC); } } @@ -2284,7 +2284,7 @@ bool BGTactics::selectObjective(bool reset) { // Assist own FC if not pursuing enemy FC target.Relocate(teamFC->GetPositionX(), teamFC->GetPositionY(), teamFC->GetPositionZ()); - if (sServerFacade->GetDistance2d(bot, teamFC) < 33.0f) + if (ServerFacade::instance().GetDistance2d(bot, teamFC) < 33.0f) Follow(teamFC); } else if (urand(0, 99) < 5) @@ -3197,11 +3197,11 @@ bool BGTactics::moveToObjective(bool ignoreDist) return true; } - if (!ignoreDist && sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, pos.x, pos.y), 100.0f)) + if (!ignoreDist && ServerFacade::instance().IsDistanceGreaterThan(ServerFacade::instance().GetDistance2d(bot, pos.x, pos.y), 100.0f)) { // std::ostringstream out; // out << "It is too far away! " << pos.x << ", " << pos.y << ", Distance: " << - // sServerFacade->GetDistance2d(bot, pos.x, pos.y); bot->Say(out.str(), LANG_UNIVERSAL); + // ServerFacade::instance().GetDistance2d(bot, pos.x, pos.y); bot->Say(out.str(), LANG_UNIVERSAL); return false; } @@ -3213,7 +3213,7 @@ bool BGTactics::moveToObjective(bool ignoreDist) } // std::ostringstream out; out << "Moving to objective " << pos.x << ", " << pos.y << ", Distance: " << - // sServerFacade->GetDistance2d(bot, pos.x, pos.y); bot->Say(out.str(), LANG_UNIVERSAL); + // ServerFacade::instance().GetDistance2d(bot, pos.x, pos.y); bot->Say(out.str(), LANG_UNIVERSAL); // dont increase from 1.5 will cause bugs with horde capping AV towers return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z, 1.5f); @@ -3433,7 +3433,7 @@ bool BGTactics::moveToObjectiveWp(BattleBotPath* const& currentPath, uint32 curr // out << "WP: "; // reverse ? out << currPoint << " <<< -> " << nPoint : out << currPoint << ">>> ->" << nPoint; // out << ", " << nextPoint.x << ", " << nextPoint.y << " Path Size: " << currentPath->size() << ", Dist: " << - // sServerFacade->GetDistance2d(bot, nextPoint.x, nextPoint.y); bot->Say(out.str(), LANG_UNIVERSAL); + // ServerFacade::instance().GetDistance2d(bot, nextPoint.x, nextPoint.y); bot->Say(out.str(), LANG_UNIVERSAL); return MoveTo(bot->GetMapId(), nextPoint.x + frand(-2, 2), nextPoint.y + frand(-2, 2), nextPoint.z); } @@ -4039,9 +4039,9 @@ bool BGTactics::useBuff() if (closeObjects.empty()) return false; - bool needRegen = bot->GetHealthPct() < sPlayerbotAIConfig->mediumHealth || + bool needRegen = bot->GetHealthPct() < sPlayerbotAIConfig.mediumHealth || (AI_VALUE2(bool, "has mana", "self target") && - AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->mediumMana); + AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.mediumMana); bool needSpeed = (bgType != BATTLEGROUND_WS || bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL)) || !(teamFlagTaken() || flagTaken()); @@ -4057,7 +4057,7 @@ bool BGTactics::useBuff() continue; // use speed buff only if close - if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, go), + if (ServerFacade::instance().IsDistanceGreaterThan(ServerFacade::instance().GetDistance2d(bot, go), go->GetEntry() == Buff_Entries[0] ? 20.0f : 50.0f)) continue; @@ -4107,7 +4107,7 @@ uint32 BGTactics::getPlayersInArea(TeamId teamId, Position point, float range, b if (!combat && player->IsInCombat()) continue; - if (sServerFacade->GetDistance2d(player, point.GetPositionX(), point.GetPositionY()) < range) + if (ServerFacade::instance().GetDistance2d(player, point.GetPositionX(), point.GetPositionY()) < range) ++defCount; } } @@ -4191,9 +4191,9 @@ bool BGTactics::IsLockedInsideKeep() // get closest portal if (bot->GetTeamId() == TEAM_ALLIANCE && go->GetEntry() == GO_TELEPORTER_4) { - float tempDist = sServerFacade->GetDistance2d(bot, go->GetPositionX(), go->GetPositionY()); + float tempDist = ServerFacade::instance().GetDistance2d(bot, go->GetPositionX(), go->GetPositionY()); - if (sServerFacade->IsDistanceLessThan(tempDist, closestDistance)) + if (ServerFacade::instance().IsDistanceLessThan(tempDist, closestDistance)) { closestDistance = tempDist; closestPortal = go; @@ -4204,9 +4204,9 @@ bool BGTactics::IsLockedInsideKeep() // get closest portal if (bot->GetTeamId() == TEAM_HORDE && go->GetEntry() == GO_TELEPORTER_2) { - float tempDist = sServerFacade->GetDistance2d(bot, go->GetPositionX(), go->GetPositionY()); + float tempDist = ServerFacade::instance().GetDistance2d(bot, go->GetPositionX(), go->GetPositionY()); - if (sServerFacade->IsDistanceLessThan(tempDist, closestDistance)) + if (ServerFacade::instance().IsDistanceLessThan(tempDist, closestDistance)) { closestDistance = tempDist; closestPortal = go; @@ -4253,7 +4253,7 @@ bool ArenaTactics::Execute(Event event) { if (!bot->InBattleground()) { - bool IsRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot->GetGUID().GetCounter()); + bool IsRandomBot = sRandomPlayerbotMgr.IsRandomBot(bot->GetGUID().GetCounter()); botAI->ChangeStrategy("-arena", BOT_STATE_COMBAT); botAI->ChangeStrategy("-arena", BOT_STATE_NON_COMBAT); botAI->ResetStrategies(!IsRandomBot); diff --git a/src/Ai/Base/Actions/CastCustomSpellAction.cpp b/src/Ai/Base/Actions/CastCustomSpellAction.cpp index 417ffb50d3..2ec7210d29 100644 --- a/src/Ai/Base/Actions/CastCustomSpellAction.cpp +++ b/src/Ai/Base/Actions/CastCustomSpellAction.cpp @@ -130,10 +130,10 @@ bool CastCustomSpellAction::Execute(Event event) return false; } - if (target != bot && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target, sPlayerbotAIConfig->sightDistance)) + if (target != bot && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target, sPlayerbotAIConfig.sightDistance)) { - sServerFacade->SetFacingTo(bot, target); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + ServerFacade::instance().SetFacingTo(bot, target); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); msg << "cast " << text; botAI->HandleCommand(CHAT_MSG_WHISPER, msg.str(), master); @@ -286,7 +286,7 @@ bool CastRandomSpellAction::Execute(Event event) if (isCast) { - if (MultiCast && ((wo && bot->HasInArc(CAST_ANGLE_IN_FRONT, wo, sPlayerbotAIConfig->sightDistance)))) + if (MultiCast && ((wo && bot->HasInArc(CAST_ANGLE_IN_FRONT, wo, sPlayerbotAIConfig.sightDistance)))) { std::ostringstream cmd; cmd << "castnc " << chat->FormatWorldobject(wo) + " " << spellId << " " << 19; diff --git a/src/Ai/Base/Actions/ChangeStrategyAction.cpp b/src/Ai/Base/Actions/ChangeStrategyAction.cpp index d17cd005d3..b4fe5faaf2 100644 --- a/src/Ai/Base/Actions/ChangeStrategyAction.cpp +++ b/src/Ai/Base/Actions/ChangeStrategyAction.cpp @@ -24,7 +24,7 @@ bool ChangeCombatStrategyAction::Execute(Event event) case '+': case '-': case '~': - sPlayerbotRepository->Save(botAI); + PlayerbotRepository::instance().Save(botAI); break; case '?': break; @@ -40,7 +40,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event) std::string const text = event.getParam(); uint32 account = bot->GetSession()->GetAccountId(); - if (sPlayerbotAIConfig->IsInRandomAccountList(account) && botAI->GetMaster() && + if (sPlayerbotAIConfig.IsInRandomAccountList(account) && botAI->GetMaster() && botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER) { if (text.find("loot") != std::string::npos || text.find("gather") != std::string::npos) @@ -62,7 +62,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event) case '+': case '-': case '~': - sPlayerbotRepository->Save(botAI); + PlayerbotRepository::instance().Save(botAI); break; case '?': break; diff --git a/src/Ai/Base/Actions/ChangeTalentsAction.cpp b/src/Ai/Base/Actions/ChangeTalentsAction.cpp index c50e4a9274..9b57a201a0 100644 --- a/src/Ai/Base/Actions/ChangeTalentsAction.cpp +++ b/src/Ai/Base/Actions/ChangeTalentsAction.cpp @@ -110,20 +110,20 @@ std::string ChangeTalentsAction::SpecList() std::ostringstream out; for (int specNo = 0; specNo < MAX_SPECNO; ++specNo) { - if (sPlayerbotAIConfig->premadeSpecName[cls][specNo].size() == 0) + if (sPlayerbotAIConfig.premadeSpecName[cls][specNo].size() == 0) { break; } specFound++; std::ostringstream out; - std::vector> parsed = sPlayerbotAIConfig->parsedSpecLinkOrder[cls][specNo][80]; + std::vector> parsed = sPlayerbotAIConfig.parsedSpecLinkOrder[cls][specNo][80]; std::unordered_map tabCount; tabCount[0] = tabCount[1] = tabCount[2] = 0; for (auto& item : parsed) { tabCount[item[0]] += item[3]; } - out << specFound << ". " << sPlayerbotAIConfig->premadeSpecName[cls][specNo] << " ("; + out << specFound << ". " << sPlayerbotAIConfig.premadeSpecName[cls][specNo] << " ("; out << tabCount[0] << "-" << tabCount[1] << "-" << tabCount[2] << ")"; botAI->TellMasterNoFacing(out.str()); } @@ -137,11 +137,11 @@ std::string ChangeTalentsAction::SpecPick(std::string param) // int specFound = 0; //not used, line marked for removal. for (int specNo = 0; specNo < MAX_SPECNO; ++specNo) { - if (sPlayerbotAIConfig->premadeSpecName[cls][specNo].size() == 0) + if (sPlayerbotAIConfig.premadeSpecName[cls][specNo].size() == 0) { break; } - if (sPlayerbotAIConfig->premadeSpecName[cls][specNo] == param) + if (sPlayerbotAIConfig.premadeSpecName[cls][specNo] == param) { PlayerbotFactory::InitTalentsBySpecNo(bot, specNo, true); @@ -149,7 +149,7 @@ std::string ChangeTalentsAction::SpecPick(std::string param) factory.InitGlyphs(false); std::ostringstream out; - out << "Picking " << sPlayerbotAIConfig->premadeSpecName[cls][specNo]; + out << "Picking " << sPlayerbotAIConfig.premadeSpecName[cls][specNo]; return out.str(); } } @@ -176,7 +176,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // std::vector ChangeTalentsAction::getPremadePaths(std::string const findName) // { // std::vector ret; -// // for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath) +// // for (auto& path : sPlayerbotAIConfig.classSpecs[bot->getClass()].talentPath) // // { // // if (findName.empty() || path.name.find(findName) != std::string::npos) // // { @@ -191,7 +191,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // { // std::vector ret; -// // for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath) +// // for (auto& path : sPlayerbotAIConfig.classSpecs[bot->getClass()].talentPath) // // { // // TalentSpec newSpec = *GetBestPremadeSpec(path.id); // // newSpec.CropTalents(bot->GetLevel()); @@ -206,7 +206,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // TalentPath* ChangeTalentsAction::getPremadePath(uint32 id) // { -// // for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath) +// // for (auto& path : sPlayerbotAIConfig.classSpecs[bot->getClass()].talentPath) // // { // // if (id == path.id) // // { @@ -214,7 +214,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // // } // // } -// // return &sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath[0]; +// // return &sPlayerbotAIConfig.classSpecs[bot->getClass()].talentPath[0]; // return nullptr; // } @@ -270,9 +270,9 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // return false; // } -// uint32 specNo = sRandomPlayerbotMgr->GetValue(bot->GetGUID().GetCounter(), "specNo"); +// uint32 specNo = sRandomPlayerbotMgr.GetValue(bot->GetGUID().GetCounter(), "specNo"); // uint32 specId = specNo - 1; -// std::string specLink = sRandomPlayerbotMgr->GetData(bot->GetGUID().GetCounter(), "specLink"); +// std::string specLink = sRandomPlayerbotMgr.GetData(bot->GetGUID().GetCounter(), "specLink"); // //Continue the current spec // if (specNo > 0) @@ -319,15 +319,15 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // specId = -1; // // specLink = ""; // } -// else if (paths.size() > 1 && false/*!sPlayerbotAIConfig->autoPickTalents*/ && -// !sRandomPlayerbotMgr->IsRandomBot(bot)) +// else if (paths.size() > 1 && false/*!sPlayerbotAIConfig.autoPickTalents*/ && +// !sRandomPlayerbotMgr.IsRandomBot(bot)) // { // *out << "Found multiple specs: "; // listPremadePaths(paths, out); // } // else // { -// specId = PickPremadePath(paths, sRandomPlayerbotMgr->IsRandomBot(bot))->id; +// specId = PickPremadePath(paths, sRandomPlayerbotMgr.IsRandomBot(bot))->id; // TalentSpec newSpec = *GetBestPremadeSpec(specId); // specLink = newSpec.GetTalentLink(); // newSpec.CropTalents(bot->GetLevel()); @@ -341,12 +341,12 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // } // } -// sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specNo", specId + 1); +// sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", specId + 1); // if (!specLink.empty() && specId == -1) -// sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specLink", 1, specLink); +// sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specLink", 1, specLink); // else -// sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specLink", 0); +// sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specLink", 0); // return (specNo == 0) ? false : true; // } @@ -364,7 +364,7 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // if (path->talentSpec.size()) // return &path->talentSpec.back(); -// // return &sPlayerbotAIConfig->classSpecs[bot->getClassMask()].baseSpec; +// // return &sPlayerbotAIConfig.classSpecs[bot->getClassMask()].baseSpec; // return nullptr; // } @@ -372,7 +372,7 @@ bool AutoSetTalentsAction::Execute(Event event) { std::ostringstream out; - if (!sPlayerbotAIConfig->autoPickTalents || !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig.autoPickTalents || !sRandomPlayerbotMgr.IsRandomBot(bot)) return false; if (bot->GetFreeTalentPoints() <= 0) diff --git a/src/Ai/Base/Actions/ChatShortcutActions.cpp b/src/Ai/Base/Actions/ChatShortcutActions.cpp index 02c306b8f7..85b141d4d1 100644 --- a/src/Ai/Base/Actions/ChatShortcutActions.cpp +++ b/src/Ai/Base/Actions/ChatShortcutActions.cpp @@ -96,7 +96,7 @@ bool FollowChatShortcutAction::Execute(Event event) /* Default mechanics takes care of this now. if (bot->GetMapId() != master->GetMapId() || (master && bot->GetDistance(master) > - sPlayerbotAIConfig->sightDistance)) + sPlayerbotAIConfig.sightDistance)) { if (bot->isDead()) { @@ -161,7 +161,7 @@ bool FleeChatShortcutAction::Execute(Event event) ResetReturnPosition(); ResetStayPosition(); - if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance) + if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig.sightDistance) { botAI->TellError("I will not flee with you - too far away"); return true; diff --git a/src/Ai/Base/Actions/CheatAction.cpp b/src/Ai/Base/Actions/CheatAction.cpp index b3f4ec5cf7..790134e953 100644 --- a/src/Ai/Base/Actions/CheatAction.cpp +++ b/src/Ai/Base/Actions/CheatAction.cpp @@ -86,7 +86,7 @@ void CheatAction::ListCheats() for (int i = 0; i < log2((uint32)BotCheatMask::maxMask); i++) { BotCheatMask cheatMask = BotCheatMask(1 << i); - if ((uint32)cheatMask & (uint32)sPlayerbotAIConfig->botCheatMask) + if ((uint32)cheatMask & (uint32)sPlayerbotAIConfig.botCheatMask) out << "[conf:" << GetCheatName(BotCheatMask(cheatMask)) << "]"; else if (botAI->HasCheat(cheatMask)) out << "[" << GetCheatName(BotCheatMask(cheatMask)) << "]"; diff --git a/src/Ai/Base/Actions/CheckMailAction.cpp b/src/Ai/Base/Actions/CheckMailAction.cpp index 85df168f58..f9c70ff11f 100644 --- a/src/Ai/Base/Actions/CheckMailAction.cpp +++ b/src/Ai/Base/Actions/CheckMailAction.cpp @@ -28,7 +28,7 @@ bool CheckMailAction::Execute(Event event) continue; uint32 account = owner->GetSession()->GetAccountId(); - if (sPlayerbotAIConfig->IsInRandomAccountList(account)) + if (sPlayerbotAIConfig.IsInRandomAccountList(account)) continue; ProcessMail(mail, owner, trans); @@ -80,7 +80,7 @@ void CheckMailAction::ProcessMail(Mail* mail, Player* owner, CharacterDatabaseTr if (!item) continue; - if (!sGuildTaskMgr->CheckItemTask(i->item_template, item->GetCount(), owner, bot, true)) + if (!GuildTaskMgr::instance().CheckItemTask(i->item_template, item->GetCount(), owner, bot, true)) { std::ostringstream body; body << "Hello, " << owner->GetName() << ",\n"; diff --git a/src/Ai/Base/Actions/CheckMountStateAction.cpp b/src/Ai/Base/Actions/CheckMountStateAction.cpp index bf7a3a169a..5ab7cc0f92 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.cpp +++ b/src/Ai/Base/Actions/CheckMountStateAction.cpp @@ -89,7 +89,7 @@ bool CheckMountStateAction::isUseful() return false; // Not useful when level lower than minimum required - if (bot->GetLevel() < sPlayerbotAIConfig->useGroundMountAtMinLevel) + if (bot->GetLevel() < sPlayerbotAIConfig.useGroundMountAtMinLevel) return false; // Allow mounting while transformed only if the form allows it @@ -402,7 +402,7 @@ float CheckMountStateAction::CalculateDismountDistance() const // Warrior bots should dismount far enough to charge (because it's important for generating some initial rage), // a real player would be riding toward enemy mashing the charge key but the bots won't cast charge while mounted. bool isMelee = PlayerbotAI::IsMelee(bot); - float dismountDistance = isMelee ? sPlayerbotAIConfig->meleeDistance + 2.0f : sPlayerbotAIConfig->spellDistance + 2.0f; + float dismountDistance = isMelee ? sPlayerbotAIConfig.meleeDistance + 2.0f : sPlayerbotAIConfig.spellDistance + 2.0f; return bot->getClass() == CLASS_WARRIOR ? std::max(18.0f, dismountDistance) : dismountDistance; } @@ -413,7 +413,7 @@ float CheckMountStateAction::CalculateMountDistance() const // seconds: // 21 / 7 = 21 / 14 + 1.5 = 3 (7 = dismounted speed 14 = epic-mount speed 1.5 = mount-spell cast time) bool isMelee = PlayerbotAI::IsMelee(bot); - float baseDistance = isMelee ? sPlayerbotAIConfig->meleeDistance + 10.0f : sPlayerbotAIConfig->spellDistance + 10.0f; + float baseDistance = isMelee ? sPlayerbotAIConfig.meleeDistance + 10.0f : sPlayerbotAIConfig.spellDistance + 10.0f; return std::max(21.0f, baseDistance); } @@ -440,7 +440,7 @@ int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const Mou int32 ridingSkill = bot->GetPureSkillValue(SKILL_RIDING); int32 botLevel = bot->GetLevel(); - if (ridingSkill <= 75 && botLevel < static_cast(sPlayerbotAIConfig->useFastGroundMountAtMinLevel)) + if (ridingSkill <= 75 && botLevel < static_cast(sPlayerbotAIConfig.useFastGroundMountAtMinLevel)) return 59; // If there is a master and bot not in BG, use master's aura effects. diff --git a/src/Ai/Base/Actions/CheckValuesAction.cpp b/src/Ai/Base/Actions/CheckValuesAction.cpp index 1fc0d4c3a1..7c64b7c513 100644 --- a/src/Ai/Base/Actions/CheckValuesAction.cpp +++ b/src/Ai/Base/Actions/CheckValuesAction.cpp @@ -20,7 +20,7 @@ bool CheckValuesAction::Execute(Event event) if (botAI->HasStrategy("map", BOT_STATE_NON_COMBAT) || botAI->HasStrategy("map full", BOT_STATE_NON_COMBAT)) { - sTravelNodeMap->manageNodes(bot, botAI->HasStrategy("map full", BOT_STATE_NON_COMBAT)); + TravelNodeMap::instance().manageNodes(bot, botAI->HasStrategy("map full", BOT_STATE_NON_COMBAT)); } GuidVector possible_targets = *context->GetValue("possible targets"); diff --git a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp index a888aa14ee..7d83c2b2bc 100644 --- a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp +++ b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp @@ -248,7 +248,7 @@ bool ChooseRpgTargetAction::Execute(Event event) } std::mt19937 gen(time(0)); - sTravelMgr->weighted_shuffle(guidps.begin(), guidps.end(), relevances.begin(), relevances.end(), gen); + TravelMgr::instance().weighted_shuffle(guidps.begin(), guidps.end(), relevances.begin(), relevances.end(), gen); GuidPosition guidP(guidps.front()); if (!guidP) @@ -279,7 +279,7 @@ bool ChooseRpgTargetAction::isUseful() GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); - if (guidP && guidP.distance(bot) < sPlayerbotAIConfig->reactDistance * 2) + if (guidP && guidP.distance(bot) < sPlayerbotAIConfig.reactDistance * 2) return false; // TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target"); //not used, line marked for removal. @@ -330,7 +330,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) return true; - if (bot->GetDistance(groupLeader) > sPlayerbotAIConfig->rpgDistance * 2) + if (bot->GetDistance(groupLeader) > sPlayerbotAIConfig.rpgDistance * 2) return false; Formation* formation = AI_VALUE(Formation*, "formation"); @@ -340,7 +340,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) { Player* player = groupLeader; if (groupLeader && !groupLeader->isMoving() || - PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance) + PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig.reactDistance) return true; } diff --git a/src/Ai/Base/Actions/ChooseTargetActions.cpp b/src/Ai/Base/Actions/ChooseTargetActions.cpp index ab533e7136..200094c900 100644 --- a/src/Ai/Base/Actions/ChooseTargetActions.cpp +++ b/src/Ai/Base/Actions/ChooseTargetActions.cpp @@ -20,13 +20,13 @@ bool AttackEnemyPlayerAction::isUseful() if (PlayerHasFlag::IsCapturingFlag(bot)) return false; - return !sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()); + return !sPlayerbotAIConfig.IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()); } bool AttackEnemyFlagCarrierAction::isUseful() { Unit* target = context->GetValue("enemy flag carrier")->Get(); - return target && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), 100.0f) && + return target && ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(bot, target), 100.0f) && PlayerHasFlag::IsCapturingFlag(bot); } diff --git a/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp b/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp index cf6dddd40c..0f75a30798 100644 --- a/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp +++ b/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp @@ -366,7 +366,7 @@ bool ChooseTravelTargetAction::getBestDestination(std::vector availablePoints = - sTravelMgr->getNextPoint(&botLocation, *activePoints); // Pick a good point. + TravelMgr::instance().getNextPoint(&botLocation, *activePoints); // Pick a good point. if (availablePoints.empty()) // No points available. return false; @@ -488,7 +488,7 @@ bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool onlyCom if (newQuests) { // Prefer new quests near the player at lower levels. - activeDestinations = sTravelMgr->getQuestTravelDestinations(bot, -1, true, false, 400 + bot->GetLevel() * 10); + activeDestinations = TravelMgr::instance().getQuestTravelDestinations(bot, -1, true, false, 400 + bot->GetLevel() * 10); } if (activeQuests || completedQuests) { @@ -510,7 +510,7 @@ bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool onlyCom continue; //Find quest takers or objectives - std::vector questDestinations = sTravelMgr->getQuestTravelDestinations(bot, questId, true, false, 0); + std::vector questDestinations = TravelMgr::instance().getQuestTravelDestinations(bot, questId, true, false, 0); if (onlyClassQuest && activeDestinations.size() && questDestinations.size()) //Only do class quests if we have any. { @@ -525,7 +525,7 @@ bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool onlyCom } } if (newQuests && activeDestinations.empty()) - activeDestinations = sTravelMgr->getQuestTravelDestinations(bot, -1, true, false); //If we really don't find any new quests look futher away. + activeDestinations = TravelMgr::instance().getQuestTravelDestinations(bot, -1, true, false); //If we really don't find any new quests look futher away. if (botAI->HasStrategy("debug travel", BotState::BOT_STATE_NON_COMBAT)) botAI->TellMasterNoFacing(std::to_string(activeDestinations.size()) + " quest destinations found."); @@ -547,7 +547,7 @@ bool ChooseTravelTargetAction::SetNewQuestTarget(TravelTarget* target) // Find quest givers. std::vector TravelDestinations = - sTravelMgr->getQuestTravelDestinations(bot, -1, botAI->HasRealPlayerMaster()); + TravelMgr::instance().getQuestTravelDestinations(bot, -1, botAI->HasRealPlayerMaster()); activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end()); @@ -576,7 +576,7 @@ bool ChooseTravelTargetAction::SetRpgTarget(TravelTarget* target) // Find rpg npcs std::vector TravelDestinations = - sTravelMgr->getRpgTravelDestinations(bot, botAI->HasRealPlayerMaster()); + TravelMgr::instance().getRpgTravelDestinations(bot, botAI->HasRealPlayerMaster()); activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end()); @@ -605,7 +605,7 @@ bool ChooseTravelTargetAction::SetGrindTarget(TravelTarget* target) // Find grind mobs. std::vector TravelDestinations = - sTravelMgr->getGrindTravelDestinations(bot, botAI->HasRealPlayerMaster()); + TravelMgr::instance().getGrindTravelDestinations(bot, botAI->HasRealPlayerMaster()); activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end()); @@ -634,7 +634,7 @@ bool ChooseTravelTargetAction::SetBossTarget(TravelTarget* target) // Find boss mobs. std::vector TravelDestinations = - sTravelMgr->getBossTravelDestinations(bot, botAI->HasRealPlayerMaster()); + TravelMgr::instance().getBossTravelDestinations(bot, botAI->HasRealPlayerMaster()); activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end()); @@ -662,7 +662,7 @@ bool ChooseTravelTargetAction::SetExploreTarget(TravelTarget* target) WorldPosition botLocation(bot); // Find quest givers. - std::vector TravelDestinations = sTravelMgr->getExploreTravelDestinations(bot, true, true); + std::vector TravelDestinations = TravelMgr::instance().getExploreTravelDestinations(bot, true, true); activeDestinations.insert(activeDestinations.end(), TravelDestinations.begin(), TravelDestinations.end()); /* @@ -681,7 +681,7 @@ bool ChooseTravelTargetAction::SetExploreTarget(TravelTarget* target) if (activePoints.empty()) { - TravelDestinations = sTravelMgr->getExploreTravelDestinations(bot, botAI->HasRealPlayerMaster()); + TravelDestinations = TravelMgr::instance().getExploreTravelDestinations(bot, botAI->HasRealPlayerMaster()); for (auto& activeTarget : activeDestinations) { @@ -710,7 +710,7 @@ bool ChooseTravelTargetAction::SetNpcFlagTarget(TravelTarget* target, std::vecto std::vector dests; - for (auto& d : sTravelMgr->getRpgTravelDestinations(bot, true, true)) + for (auto& d : TravelMgr::instance().getRpgTravelDestinations(bot, true, true)) { if (!d->getEntry()) continue; @@ -813,7 +813,7 @@ std::vector TravelMgr::getBossTravelDestinations(Player* bot bool ChooseTravelTargetAction::SetNullTarget(TravelTarget* target) { - target->setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true); + target->setTarget(TravelMgr::instance().nullTravelDestination, TravelMgr::instance().nullWorldPosition, true); return true; } @@ -832,7 +832,7 @@ TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::s //Quests if (quests) { - for (auto& d : sTravelMgr->getQuestTravelDestinations(bot, 0, true, true)) + for (auto& d : TravelMgr::instance().getQuestTravelDestinations(bot, 0, true, true)) { if (strstri(d->getTitle().c_str(), name.c_str())) dests.push_back(d); @@ -842,7 +842,7 @@ TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::s //Zones if (zones) { - for (auto& d : sTravelMgr->getExploreTravelDestinations(bot, true, true)) + for (auto& d : TravelMgr::instance().getExploreTravelDestinations(bot, true, true)) { if (strstri(d->getTitle().c_str(), name.c_str())) dests.push_back(d); @@ -852,7 +852,7 @@ TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::s //Npcs if (npcs) { - for (auto& d : sTravelMgr->getRpgTravelDestinations(bot, true, true)) + for (auto& d : TravelMgr::instance().getRpgTravelDestinations(bot, true, true)) { if (strstri(d->getTitle().c_str(), name.c_str())) dests.push_back(d); @@ -862,7 +862,7 @@ TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::s //Mobs if (mobs) { - for (auto& d : sTravelMgr->getGrindTravelDestinations(bot, true, true, 5000.0f)) + for (auto& d : TravelMgr::instance().getGrindTravelDestinations(bot, true, true, 5000.0f)) { if (strstri(d->getTitle().c_str(), name.c_str())) dests.push_back(d); @@ -872,7 +872,7 @@ TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::s //Bosses if (bosses) { - for (auto& d : sTravelMgr->getBossTravelDestinations(bot, true, true)) + for (auto& d : TravelMgr::instance().getBossTravelDestinations(bot, true, true)) { if (strstri(d->getTitle().c_str(), name.c_str())) dests.push_back(d); diff --git a/src/Ai/Base/Actions/CombatActions.cpp b/src/Ai/Base/Actions/CombatActions.cpp index fe29a11b4f..2f687d5e03 100644 --- a/src/Ai/Base/Actions/CombatActions.cpp +++ b/src/Ai/Base/Actions/CombatActions.cpp @@ -25,7 +25,7 @@ bool SwitchToMeleeAction::isUseful() return botAI->HasStrategy("ranged", BOT_STATE_COMBAT) && ((bot->IsInCombat() && target && (target->GetVictim() == bot && (!bot->GetGroup() || lastFlee) && - sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f))) || + ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f))) || (!bot->IsInCombat())); } @@ -47,7 +47,7 @@ bool SwitchToRangedAction::isUseful() return botAI->HasStrategy("close", BOT_STATE_COMBAT) && hasAmmo && ((bot->IsInCombat() && target && ((target->GetVictim() != bot || target->GetTarget() != bot->GetGUID()) || - sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"), 8.0f))) || + ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"), 8.0f))) || (!bot->IsInCombat())); } diff --git a/src/Ai/Base/Actions/DebugAction.cpp b/src/Ai/Base/Actions/DebugAction.cpp index 11a0bf26a0..578ece17c4 100644 --- a/src/Ai/Base/Actions/DebugAction.cpp +++ b/src/Ai/Base/Actions/DebugAction.cpp @@ -19,7 +19,7 @@ bool DebugAction::Execute(Event event) if (text == "scan") { - sPlayerbotAIConfig->openLog("scan.csv", "w"); + sPlayerbotAIConfig.openLog("scan.csv", "w"); uint32 i = 0; for (auto p : WorldPosition().getCreaturesNear()) @@ -36,15 +36,15 @@ bool DebugAction::Execute(Event event) pos.printWKT(out); - sPlayerbotAIConfig->log("scan.csv", out.str().c_str()); + sPlayerbotAIConfig.log("scan.csv", out.str().c_str()); if (zoneId == 0 && areaId == 0) { - sPlayerbotAIConfig->log("x", out.str().c_str()); + sPlayerbotAIConfig.log("x", out.str().c_str()); } else { - sPlayerbotAIConfig->log("y", out.str().c_str()); + sPlayerbotAIConfig.log("y", out.str().c_str()); } i = zoneId; @@ -53,8 +53,8 @@ bool DebugAction::Execute(Event event) } else if (text.find("printmap") != std::string::npos) { - sTravelNodeMap->printMap(); - sTravelNodeMap->printNodeStore(); + TravelNodeMap::instance().printMap(); + TravelNodeMap::instance().printNodeStore(); return true; } else if (text.find("travel ") != std::string::npos) @@ -72,7 +72,7 @@ bool DebugAction::Execute(Event event) return false; std::vector beginPath, endPath; - TravelNodeRoute route = sTravelNodeMap->getRoute(botPos, *points.front(), beginPath, bot); + TravelNodeRoute route = TravelNodeMap::instance().getRoute(botPos, *points.front(), beginPath, bot); std::ostringstream out; out << "Traveling to " << dest->getTitle() << ": "; @@ -108,7 +108,7 @@ bool DebugAction::Execute(Event event) out << quest->GetTitle() << ": "; - QuestContainer* cont = sTravelMgr->quests[questId]; + QuestContainer* cont = TravelMgr::instance().quests[questId]; for (auto g : cont->questGivers) { @@ -135,11 +135,11 @@ bool DebugAction::Execute(Event event) else if (text.find("quest") != std::string::npos) { std::ostringstream out; - out << sTravelMgr->quests.size() << " quests "; + out << TravelMgr::instance().quests.size() << " quests "; uint32 noT = 0, noG = 0, noO = 0; - for (auto q : sTravelMgr->quests) + for (auto q : TravelMgr::instance().quests) { if (q.second->questGivers.empty()) noG++; @@ -164,7 +164,7 @@ bool DebugAction::Execute(Event event) // uint32 noT = 0, noG = 0, noO = 0; //not used, line marked for removal. - for (auto q : sTravelMgr->quests) + for (auto q : TravelMgr::instance().quests) { Quest const* quest = sObjectMgr->GetQuestTemplate(q.first); @@ -194,16 +194,16 @@ bool DebugAction::Execute(Event event) std::string const name = "USER:" + text.substr(9); - /* TravelNode* startNode = */ sTravelNodeMap->addNode(pos, name, false, false); // startNode not used, but addNode as side effect, fragment marked for removal. + /* TravelNode* startNode = */ TravelNodeMap::instance().addNode(pos, name, false, false); // startNode not used, but addNode as side effect, fragment marked for removal. - for (auto& endNode : sTravelNodeMap->getNodes(pos, 2000)) + for (auto& endNode : TravelNodeMap::instance().getNodes(pos, 2000)) { endNode->setLinked(false); } botAI->TellMasterNoFacing("Node " + name + " created."); - sTravelNodeMap->setHasToGen(); + TravelNodeMap::instance().setHasToGen(); return true; } @@ -211,7 +211,7 @@ bool DebugAction::Execute(Event event) { WorldPosition pos(bot); - TravelNode* startNode = sTravelNodeMap->getNode(pos, nullptr, 50); + TravelNode* startNode = TravelNodeMap::instance().getNode(pos, nullptr, 50); if (!startNode) return false; @@ -221,24 +221,24 @@ bool DebugAction::Execute(Event event) botAI->TellMasterNoFacing("Node can not be removed."); } - sTravelNodeMap->m_nMapMtx.lock(); - sTravelNodeMap->removeNode(startNode); + TravelNodeMap::instance().m_nMapMtx.lock(); + TravelNodeMap::instance().removeNode(startNode); botAI->TellMasterNoFacing("Node removed."); - sTravelNodeMap->m_nMapMtx.unlock(); + TravelNodeMap::instance().m_nMapMtx.unlock(); - sTravelNodeMap->setHasToGen(); + TravelNodeMap::instance().setHasToGen(); return true; } else if (text.find("reset node") != std::string::npos) { - for (auto& node : sTravelNodeMap->getNodes()) + for (auto& node : TravelNodeMap::instance().getNodes()) node->setLinked(false); return true; } else if (text.find("reset path") != std::string::npos) { - for (auto& node : sTravelNodeMap->getNodes()) + for (auto& node : TravelNodeMap::instance().getNodes()) for (auto& path : *node->getLinks()) node->removeLinkTo(path.first, true); return true; @@ -246,23 +246,23 @@ bool DebugAction::Execute(Event event) else if (text.find("gen node") != std::string::npos) { // Pathfinder - sTravelNodeMap->generateNodes(); + TravelNodeMap::instance().generateNodes(); return true; } else if (text.find("gen path") != std::string::npos) { - sTravelNodeMap->generatePaths(); + TravelNodeMap::instance().generatePaths(); return true; } else if (text.find("crop path") != std::string::npos) { - sTravelNodeMap->removeUselessPaths(); + TravelNodeMap::instance().removeUselessPaths(); return true; } else if (text.find("save node") != std::string::npos) { - sTravelNodeMap->printNodeStore(); - sTravelNodeMap->saveNodeStore(); + TravelNodeMap::instance().printNodeStore(); + TravelNodeMap::instance().saveNodeStore(); return true; } else if (text.find("load node") != std::string::npos) @@ -270,8 +270,8 @@ bool DebugAction::Execute(Event event) std::thread t( [] { - sTravelNodeMap->removeNodes(); - sTravelNodeMap->loadNodeStore(); + TravelNodeMap::instance().removeNodes(); + TravelNodeMap::instance().loadNodeStore(); }); t.detach(); @@ -282,7 +282,7 @@ bool DebugAction::Execute(Event event) { WorldPosition pos(bot); - std::vector nodes = sTravelNodeMap->getNodes(pos, 500); + std::vector nodes = TravelNodeMap::instance().getNodes(pos, 500); for (auto& node : nodes) { diff --git a/src/Ai/Base/Actions/DelayAction.cpp b/src/Ai/Base/Actions/DelayAction.cpp index 8d47912fe4..b47b6898d1 100644 --- a/src/Ai/Base/Actions/DelayAction.cpp +++ b/src/Ai/Base/Actions/DelayAction.cpp @@ -10,7 +10,7 @@ bool DelayAction::Execute(Event event) { - uint32 delay = sPlayerbotAIConfig->passiveDelay + sPlayerbotAIConfig->globalCoolDown; + uint32 delay = sPlayerbotAIConfig.passiveDelay + sPlayerbotAIConfig.globalCoolDown; botAI->SetNextCheckDelay(delay); diff --git a/src/Ai/Base/Actions/DropQuestAction.cpp b/src/Ai/Base/Actions/DropQuestAction.cpp index 48e571eb3e..b3cba9c560 100644 --- a/src/Ai/Base/Actions/DropQuestAction.cpp +++ b/src/Ai/Base/Actions/DropQuestAction.cpp @@ -67,7 +67,7 @@ bool CleanQuestLogAction::Execute(Event event) return false; } - if (!sPlayerbotAIConfig->dropObsoleteQuests) + if (!sPlayerbotAIConfig.dropObsoleteQuests) { return false; } diff --git a/src/Ai/Base/Actions/EmoteAction.cpp b/src/Ai/Base/Actions/EmoteAction.cpp index 1770ca2b75..197cc436b1 100644 --- a/src/Ai/Base/Actions/EmoteAction.cpp +++ b/src/Ai/Base/Actions/EmoteAction.cpp @@ -88,7 +88,7 @@ void EmoteActionBase::InitEmotes() bool EmoteActionBase::Emote(Unit* target, uint32 type, bool textEmote) { - if (target && !bot->HasInArc(static_cast(M_PI), target, sPlayerbotAIConfig->sightDistance)) + if (target && !bot->HasInArc(static_cast(M_PI), target, sPlayerbotAIConfig.sightDistance)) bot->SetFacingToObject(target); ObjectGuid oldSelection = bot->GetTarget(); @@ -100,7 +100,7 @@ bool EmoteActionBase::Emote(Unit* target, uint32 type, bool textEmote) if (player) { PlayerbotAI* playerBotAI = GET_PLAYERBOT_AI(player); - if (playerBotAI && !player->HasInArc(static_cast(M_PI), bot, sPlayerbotAIConfig->sightDistance)) + if (playerBotAI && !player->HasInArc(static_cast(M_PI), bot, sPlayerbotAIConfig.sightDistance)) { player->SetFacingToObject(bot); } @@ -133,7 +133,7 @@ Unit* EmoteActionBase::GetTarget() for (GuidVector::iterator i = nfp.begin(); i != nfp.end(); ++i) { Unit* unit = botAI->GetUnit(*i); - if (unit && sServerFacade->GetDistance2d(bot, unit) < sPlayerbotAIConfig->tooCloseDistance) + if (unit && ServerFacade::instance().GetDistance2d(bot, unit) < sPlayerbotAIConfig.tooCloseDistance) targets.push_back(unit); } @@ -618,8 +618,8 @@ bool EmoteActionBase::ReceiveEmote(Player* source, uint32 emote, bool verbal) break; } - if (source && !bot->isMoving() && !bot->HasInArc(static_cast(M_PI), source, sPlayerbotAIConfig->farDistance)) - sServerFacade->SetFacingTo(bot, source); + if (source && !bot->isMoving() && !bot->HasInArc(static_cast(M_PI), source, sPlayerbotAIConfig.farDistance)) + ServerFacade::instance().SetFacingTo(bot, source); if (verbal) { @@ -689,7 +689,7 @@ bool EmoteAction::Execute(Event event) p >> emoteId >> source; pSource = ObjectAccessor::FindPlayer(source); - if (pSource && pSource != bot && sServerFacade->GetDistance2d(bot, pSource) < sPlayerbotAIConfig->farDistance && + if (pSource && pSource != bot && ServerFacade::instance().GetDistance2d(bot, pSource) < sPlayerbotAIConfig.farDistance && emoteId != EMOTE_ONESHOT_NONE) { if ((pSource->GetGUID() != bot->GetGUID()) && @@ -737,7 +737,7 @@ bool EmoteAction::Execute(Event event) // time_t lastEmote = AI_VALUE2(time_t, "last emote", qualifier); //not used, line marked for removal. botAI->GetAiObjectContext() ->GetValue("last emote", qualifier) - ->Set(time(nullptr) + urand(1000, sPlayerbotAIConfig->repeatDelay) / 1000); + ->Set(time(nullptr) + urand(1000, sPlayerbotAIConfig.repeatDelay) / 1000); param = qualifier; } diff --git a/src/Ai/Base/Actions/EquipAction.cpp b/src/Ai/Base/Actions/EquipAction.cpp index 9f4a67ca90..32508ef2e5 100644 --- a/src/Ai/Base/Actions/EquipAction.cpp +++ b/src/Ai/Base/Actions/EquipAction.cpp @@ -330,7 +330,7 @@ void EquipAction::EquipItem(Item* item) bool EquipUpgradesAction::Execute(Event event) { - if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig.autoEquipUpgradeLoot && !sRandomPlayerbotMgr.IsRandomBot(bot)) return false; if (event.GetSource() == "trade status") diff --git a/src/Ai/Base/Actions/FishingAction.cpp b/src/Ai/Base/Actions/FishingAction.cpp index dc431d3425..0fc09c3bce 100644 --- a/src/Ai/Base/Actions/FishingAction.cpp +++ b/src/Ai/Base/Actions/FishingAction.cpp @@ -272,9 +272,9 @@ bool MoveNearWaterAction::isPossible() float fishingSearchWindow; if (master) - fishingSearchWindow = sPlayerbotAIConfig->fishingDistanceFromMaster; + fishingSearchWindow = sPlayerbotAIConfig.fishingDistanceFromMaster; else - fishingSearchWindow = sPlayerbotAIConfig->fishingDistance; + fishingSearchWindow = sPlayerbotAIConfig.fishingDistance; WorldPosition fishingHole = FindFishingHole(botAI); @@ -385,7 +385,7 @@ bool EquipFishingPoleAction::isUseful() } } - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sRandomPlayerbotMgr.IsRandomBot(bot)) { bot->StoreNewItemInBestSlots(FISHING_POLE, 1); // Try to get a fishing pole return true; @@ -396,7 +396,7 @@ bool EquipFishingPoleAction::isUseful() return false; std::string masterName = master->GetName(); - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "no_fishing_pole_error", "I don't have a Fishing Pole",{}); botAI->Whisper(text, masterName); @@ -499,7 +499,7 @@ bool EndMasterFishingAction::isUseful() return false; WorldPosition nearWater = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), - bot->GetMap(), bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, sPlayerbotAIConfig->endFishingWithMaster, 10.0f); + bot->GetMap(), bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, sPlayerbotAIConfig.endFishingWithMaster, 10.0f); return !nearWater.IsValid(); } diff --git a/src/Ai/Base/Actions/FollowActions.cpp b/src/Ai/Base/Actions/FollowActions.cpp index 2593ea28e5..b9623233a9 100644 --- a/src/Ai/Base/Actions/FollowActions.cpp +++ b/src/Ai/Base/Actions/FollowActions.cpp @@ -44,7 +44,7 @@ bool FollowAction::Execute(Event event) // botAI->PetFollow(); // } // if (moved) - // botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + // botAI->SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); return moved; } @@ -98,9 +98,9 @@ bool FollowAction::isUseful() distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ()); } if (botAI->HasStrategy("master fishing", BOT_STATE_NON_COMBAT)) - return sServerFacade->IsDistanceGreaterThan(distance, sPlayerbotAIConfig->fishingDistanceFromMaster); + return ServerFacade::instance().IsDistanceGreaterThan(distance, sPlayerbotAIConfig.fishingDistanceFromMaster); - return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance()); + return ServerFacade::instance().IsDistanceGreaterThan(distance, formation->GetMaxDistance()); } bool FollowAction::CanDeadFollow(Unit* target) @@ -130,7 +130,7 @@ bool FleeToGroupLeaderAction::Execute(Event event) WorldPosition bosPos(bot); float distance = bosPos.fDist(targetPos); - if (distance < sPlayerbotAIConfig->reactDistance * 3) + if (distance < sPlayerbotAIConfig.reactDistance * 3) { if (!urand(0, 3)) botAI->TellMaster("I am close, wait for me!"); diff --git a/src/Ai/Base/Actions/GenericActions.cpp b/src/Ai/Base/Actions/GenericActions.cpp index 184b17715d..453834c097 100644 --- a/src/Ai/Base/Actions/GenericActions.cpp +++ b/src/Ai/Base/Actions/GenericActions.cpp @@ -113,7 +113,7 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) } // Debug message if pet spells have been toggled and debug is enabled - if (toggled && sPlayerbotAIConfig->petChatCommandDebug == 1) + if (toggled && sPlayerbotAIConfig.petChatCommandDebug == 1) botAI->TellMaster("Pet autocast spells have been toggled."); return toggled; @@ -185,7 +185,7 @@ bool SetPetStanceAction::Execute(Event /*event*/) } // Get the default pet stance from the configuration - int32 stance = sPlayerbotAIConfig->defaultPetStance; + int32 stance = sPlayerbotAIConfig.defaultPetStance; ReactStates react = REACT_DEFENSIVE; std::string stanceText = "defensive (from config, fallback)"; @@ -221,7 +221,7 @@ bool SetPetStanceAction::Execute(Event /*event*/) } // If debug is enabled in config, inform the master of the new stance - if (sPlayerbotAIConfig->petChatCommandDebug == 1) + if (sPlayerbotAIConfig.petChatCommandDebug == 1) botAI->TellMaster("Pet stance set to " + stanceText + " (applied to all pets/guardians)."); return true; diff --git a/src/Ai/Base/Actions/GenericBuffUtils.cpp b/src/Ai/Base/Actions/GenericBuffUtils.cpp index 1ac71bad4e..e33c12f180 100644 --- a/src/Ai/Base/Actions/GenericBuffUtils.cpp +++ b/src/Ai/Base/Actions/GenericBuffUtils.cpp @@ -87,7 +87,7 @@ namespace ai::buff { std::string castName = baseName; Group* g = bot->GetGroup(); - if (!g || g->GetMembersCount() < static_cast(sPlayerbotAIConfig->minBotsForGreaterBuff)) + if (!g || g->GetMembersCount() < static_cast(sPlayerbotAIConfig.minBotsForGreaterBuff)) return castName; // Group too small: stay in solo mode if (std::string const groupName = GroupVariantFor(baseName); !groupName.empty()) @@ -114,7 +114,7 @@ namespace ai::buff time_t now = std::time(nullptr); uint32 botLow = static_cast(bot->GetGUID().GetCounter()); time_t& last = s_lastWarn[ std::make_pair(botLow, groupName) ]; - if (!last || now - last >= sPlayerbotAIConfig->rpWarningCooldown) // Configurable anti-spam + if (!last || now - last >= sPlayerbotAIConfig.rpWarningCooldown) // Configurable anti-spam { // DB Key choice in regard of the buff std::string key; @@ -132,7 +132,7 @@ namespace ai::buff placeholders["%group_spell"] = groupName; placeholders["%base_spell"] = baseName; - std::string announceText = sPlayerbotTextMgr->GetBotTextOrDefault(key, + std::string announceText = PlayerbotTextMgr::instance().GetBotTextOrDefault(key, "Out of components for %group_spell. Using %base_spell!", placeholders); announce(announceText); diff --git a/src/Ai/Base/Actions/GenericSpellActions.cpp b/src/Ai/Base/Actions/GenericSpellActions.cpp index af06a457f0..819816f948 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.cpp +++ b/src/Ai/Base/Actions/GenericSpellActions.cpp @@ -82,7 +82,7 @@ bool CastSpellAction::isPossible() { if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "Can cast spell failed. Vehicle. - bot name: {}", bot->GetName()); } @@ -94,7 +94,7 @@ bool CastSpellAction::isPossible() if (spell == "mount" && bot->IsInCombat()) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "Can cast spell failed. Mount. - bot name: {}", bot->GetName()); } @@ -133,7 +133,7 @@ bool CastSpellAction::isUseful() return spellTarget && AI_VALUE2(bool, "spell cast useful", - spell); // && sServerFacade->GetDistance2d(bot, spellTarget) <= (range + combatReach); + spell); // && ServerFacade::instance().GetDistance2d(bot, spellTarget) <= (range + combatReach); } CastMeleeSpellAction::CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) diff --git a/src/Ai/Base/Actions/GiveItemAction.cpp b/src/Ai/Base/Actions/GiveItemAction.cpp index 6e2d6744ec..350b465b83 100644 --- a/src/Ai/Base/Actions/GiveItemAction.cpp +++ b/src/Ai/Base/Actions/GiveItemAction.cpp @@ -64,7 +64,7 @@ Unit* GiveItemAction::GetTarget() { return AI_VALUE2(Unit*, "party member withou bool GiveItemAction::isUseful() { - return GetTarget() && AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig->lowMana; + return GetTarget() && AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.lowMana; } Unit* GiveFoodAction::GetTarget() { return AI_VALUE(Unit*, "party member without food"); } @@ -74,7 +74,7 @@ bool GiveFoodAction::isUseful() if (!GetTarget()) return false; - bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr->IsRandomBot((Player*)GetTarget()); + bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr.IsRandomBot((Player*)GetTarget()); return !isRandomBot || (isRandomBot && !botAI->HasCheat(BotCheatMask::food)); } @@ -86,7 +86,7 @@ bool GiveWaterAction::isUseful() if (!GetTarget()) return false; - bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr->IsRandomBot((Player*)GetTarget()); + bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr.IsRandomBot((Player*)GetTarget()); return !isRandomBot || (isRandomBot && !botAI->HasCheat(BotCheatMask::food)); } diff --git a/src/Ai/Base/Actions/GoAction.cpp b/src/Ai/Base/Actions/GoAction.cpp index 1b8cd62a2a..61a7550c2b 100644 --- a/src/Ai/Base/Actions/GoAction.cpp +++ b/src/Ai/Base/Actions/GoAction.cpp @@ -61,7 +61,7 @@ bool GoAction::Execute(Event event) else { botAI->TellMasterNoFacing("Clearing travel target"); - target->setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition); + target->setTarget(TravelMgr::instance().nullTravelDestination, TravelMgr::instance().nullWorldPosition); target->setForced(false); return true; } @@ -75,8 +75,8 @@ bool GoAction::Execute(Event event) if (GameObject* go = botAI->GetGameObject(guid)) if (go->isSpawned()) { - if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, go), - sPlayerbotAIConfig->reactDistance)) + if (ServerFacade::instance().IsDistanceGreaterThan(ServerFacade::instance().GetDistance2d(bot, go), + sPlayerbotAIConfig.reactDistance)) { botAI->TellError("It is too far away"); return false; @@ -86,7 +86,7 @@ bool GoAction::Execute(Event event) out << "Moving to " << ChatHelper::FormatGameobject(go); botAI->TellMasterNoFacing(out.str()); return MoveNear(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ() + 0.5f, - sPlayerbotAIConfig->followDistance); + sPlayerbotAIConfig.followDistance); } } return false; @@ -106,7 +106,7 @@ bool GoAction::Execute(Event event) out << "Moving to " << unit->GetName(); botAI->TellMasterNoFacing(out.str()); return MoveNear(bot->GetMapId(), unit->GetPositionX(), unit->GetPositionY(), - unit->GetPositionZ() + 0.5f, sPlayerbotAIConfig->followDistance); + unit->GetPositionZ() + 0.5f, sPlayerbotAIConfig.followDistance); } } @@ -176,8 +176,8 @@ bool GoAction::Execute(Event event) float z = bot->GetPositionZ(); bot->UpdateAllowedPositionZ(x, y, z); - if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, x, y), - sPlayerbotAIConfig->reactDistance)) + if (ServerFacade::instance().IsDistanceGreaterThan(ServerFacade::instance().GetDistance2d(bot, x, y), + sPlayerbotAIConfig.reactDistance)) { botAI->TellMaster("It is too far away"); return false; @@ -203,14 +203,14 @@ bool GoAction::Execute(Event event) out << "Moving to " << x1 << "," << y1; botAI->TellMasterNoFacing(out.str()); - return MoveNear(bot->GetMapId(), x, y, z + 0.5f, sPlayerbotAIConfig->followDistance); + return MoveNear(bot->GetMapId(), x, y, z + 0.5f, sPlayerbotAIConfig.followDistance); } PositionInfo pos = context->GetValue("position")->Get()[param]; if (pos.isSet()) { - if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, pos.x, pos.y), - sPlayerbotAIConfig->reactDistance)) + if (ServerFacade::instance().IsDistanceGreaterThan(ServerFacade::instance().GetDistance2d(bot, pos.x, pos.y), + sPlayerbotAIConfig.reactDistance)) { botAI->TellError("It is too far away"); return false; @@ -219,7 +219,7 @@ bool GoAction::Execute(Event event) std::ostringstream out; out << "Moving to position " << param; botAI->TellMasterNoFacing(out.str()); - return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z + 0.5f, sPlayerbotAIConfig->followDistance); + return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z + 0.5f, sPlayerbotAIConfig.followDistance); } botAI->TellMaster("Whisper 'go x,y', 'go [game object]', 'go unit' or 'go position' and I will go there"); diff --git a/src/Ai/Base/Actions/GreetAction.cpp b/src/Ai/Base/Actions/GreetAction.cpp index 87bf2c5ffb..a9fda122ec 100644 --- a/src/Ai/Base/Actions/GreetAction.cpp +++ b/src/Ai/Base/Actions/GreetAction.cpp @@ -20,7 +20,7 @@ bool GreetAction::Execute(Event event) if (!player) return false; - if (!bot->HasInArc(CAST_ANGLE_IN_FRONT, player, sPlayerbotAIConfig->sightDistance)) + if (!bot->HasInArc(CAST_ANGLE_IN_FRONT, player, sPlayerbotAIConfig.sightDistance)) bot->SetFacingToObject(player); ObjectGuid oldSel = bot->GetTarget(); diff --git a/src/Ai/Base/Actions/GuildCreateActions.cpp b/src/Ai/Base/Actions/GuildCreateActions.cpp index 075b28cac3..c9f9e689eb 100644 --- a/src/Ai/Base/Actions/GuildCreateActions.cpp +++ b/src/Ai/Base/Actions/GuildCreateActions.cpp @@ -184,11 +184,11 @@ bool PetitionOfferNearbyAction::Execute(Event event) } else { - if (!sPlayerbotAIConfig->randomBotGroupNearby) + if (!sPlayerbotAIConfig.randomBotGroupNearby) return false; } - if (sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance) + if (ServerFacade::instance().GetDistance2d(bot, player) > sPlayerbotAIConfig.sightDistance) continue; // Parse rpg target to quest action. diff --git a/src/Ai/Base/Actions/GuildManagementActions.cpp b/src/Ai/Base/Actions/GuildManagementActions.cpp index f00a955e7c..be94b6ae62 100644 --- a/src/Ai/Base/Actions/GuildManagementActions.cpp +++ b/src/Ai/Base/Actions/GuildManagementActions.cpp @@ -171,7 +171,7 @@ bool GuildManageNearbyAction::Execute(Event event) continue; } - if (!sPlayerbotAIConfig->randomBotGuildNearby) + if (!sPlayerbotAIConfig.randomBotGuildNearby) return false; if (guild->GetMemberSize() > 1000) @@ -185,7 +185,7 @@ bool GuildManageNearbyAction::Execute(Event event) PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (!sPlayerbotAIConfig->randomBotInvitePlayer && botAi && botAi->IsRealPlayer()) + if (!sPlayerbotAIConfig.randomBotInvitePlayer && botAi && botAi->IsRealPlayer()) continue; if (botAi) @@ -193,16 +193,16 @@ bool GuildManageNearbyAction::Execute(Event event) if (botAi->GetGuilderType() == GuilderType::SOLO && !botAi->HasRealPlayerMaster()) //Do not invite solo players. continue; - if (botAi->HasActivePlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(player)) //Do not invite alts of active players. + if (botAi->HasActivePlayerMaster() && !sRandomPlayerbotMgr.IsRandomBot(player)) //Do not invite alts of active players. continue; } bool sameGroup = bot->GetGroup() && bot->GetGroup()->IsMember(player->GetGUID()); - if (!sameGroup && sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->spellDistance) + if (!sameGroup && ServerFacade::instance().GetDistance2d(bot, player) > sPlayerbotAIConfig.spellDistance) continue; - if (sPlayerbotAIConfig->inviteChat && (sRandomPlayerbotMgr->IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) + if (sPlayerbotAIConfig.inviteChat && (sRandomPlayerbotMgr.IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) { /* std::map placeholders; placeholders["%name"] = player->GetName(); @@ -210,8 +210,8 @@ bool GuildManageNearbyAction::Execute(Event event) placeholders["%guildname"] = guild->GetName(); AreaTableEntry const* current_area = botAI->GetCurrentArea(); AreaTableEntry const* current_zone = botAI->GetCurrentZone(); - placeholders["%area_name"] = current_area ? current_area->area_name[BroadcastHelper::GetLocale()] : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? current_zone->area_name[BroadcastHelper::GetLocale()] : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? current_area->area_name[BroadcastHelper::GetLocale()] : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? current_zone->area_name[BroadcastHelper::GetLocale()] : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); std::vector lines; @@ -219,45 +219,45 @@ bool GuildManageNearbyAction::Execute(Event event) switch ((urand(0, 10) * urand(0, 10)) / 10) { case 0: - lines.push_back(BOT_TEXT2("Hey %name do you want to join my guild?", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("Hey %name do you want to join my guild?", placeholders)); break; case 1: - lines.push_back(BOT_TEXT2("Hey man you wanna join my guild %name?", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("Hey man you wanna join my guild %name?", placeholders)); break; case 2: - lines.push_back(BOT_TEXT2("I think you would be a good contribution to %guildname. Would you like to join %name?", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("I think you would be a good contribution to %guildname. Would you like to join %name?", placeholders)); break; case 3: - lines.push_back(BOT_TEXT2("My guild %guildname has %members quality members. Would you like to make it 1 more %name?", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("My guild %guildname has %members quality members. Would you like to make it 1 more %name?", placeholders)); break; case 4: - lines.push_back(BOT_TEXT2("Hey %name do you want to join %guildname? We have %members members and looking to become number 1 of the server.", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("Hey %name do you want to join %guildname? We have %members members and looking to become number 1 of the server.", placeholders)); break; case 5: - lines.push_back(BOT_TEXT2("I'm not really good at smalltalk. Do you wanna join my guild %name/r?", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("I'm not really good at smalltalk. Do you wanna join my guild %name/r?", placeholders)); break; case 6: - lines.push_back(BOT_TEXT2("Welcome to %zone_name.... do you want to join my guild %name?", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("Welcome to %zone_name.... do you want to join my guild %name?", placeholders)); break; case 7: - lines.push_back(BOT_TEXT2("%name, you should join my guild!", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("%name, you should join my guild!", placeholders)); break; case 8: - lines.push_back(BOT_TEXT2("%name, I got this guild....", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("%name, I got this guild....", placeholders)); break; case 9: - lines.push_back(BOT_TEXT2("You are actually going to join my guild %name?", placeholders)); - lines.push_back(BOT_TEXT2("Haha.. you are the man! We are going to raid Molten...", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("You are actually going to join my guild %name?", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("Haha.. you are the man! We are going to raid Molten...", placeholders)); break; case 10: - lines.push_back(BOT_TEXT2("Hey Hey! do you guys wanna join my gild????", placeholders)); - lines.push_back(BOT_TEXT2("We've got a bunch of high levels and we are really super friendly..", placeholders)); - lines.push_back(BOT_TEXT2("..and watch your dog and do your homework...", placeholders)); - lines.push_back(BOT_TEXT2("..and we raid once a week and are working on MC raids...", placeholders)); - lines.push_back(BOT_TEXT2("..and we have more members than just me...", placeholders)); - lines.push_back(BOT_TEXT2("..and please stop I'm lonenly and we can get a ride the whole time...", placeholders)); - lines.push_back(BOT_TEXT2("..and it's really beautifull and I feel like crying...", placeholders)); - lines.push_back(BOT_TEXT2("So what do you guys say are you going to join are you going to join?", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("Hey Hey! do you guys wanna join my gild????", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("We've got a bunch of high levels and we are really super friendly..", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("..and watch your dog and do your homework...", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("..and we raid once a week and are working on MC raids...", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("..and we have more members than just me...", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("..and please stop I'm lonenly and we can get a ride the whole time...", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("..and it's really beautifull and I feel like crying...", placeholders)); + lines.push_back(PlayerbotTextMgr::instance().GetBotText("So what do you guys say are you going to join are you going to join?", placeholders)); break; } @@ -274,7 +274,7 @@ bool GuildManageNearbyAction::Execute(Event event) if (botAI->DoSpecificAction("guild invite", Event("guild management", guid), true)) { - if (sPlayerbotAIConfig->inviteChat) + if (sPlayerbotAIConfig.inviteChat) return true; found++; } diff --git a/src/Ai/Base/Actions/HireAction.cpp b/src/Ai/Base/Actions/HireAction.cpp index f8b3653964..eba607d2e5 100644 --- a/src/Ai/Base/Actions/HireAction.cpp +++ b/src/Ai/Base/Actions/HireAction.cpp @@ -14,7 +14,7 @@ bool HireAction::Execute(Event event) if (!master) return false; - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return false; uint32 account = master->GetSession()->GetAccountId(); @@ -39,7 +39,7 @@ bool HireAction::Execute(Event event) return false; } - uint32 discount = sRandomPlayerbotMgr->GetTradeDiscount(bot, master); + uint32 discount = sRandomPlayerbotMgr.GetTradeDiscount(bot, master); uint32 m = 1 + (bot->GetLevel() / 10); uint32 moneyReq = m * 5000 * bot->GetLevel(); if (discount < moneyReq) @@ -54,7 +54,7 @@ bool HireAction::Execute(Event event) botAI->TellMaster("I will join you at your next relogin"); bot->SetMoney(moneyReq); - sRandomPlayerbotMgr->Remove(bot); + sRandomPlayerbotMgr.Remove(bot); CharacterDatabase.Execute("UPDATE characters SET account = {} WHERE guid = {}", account, bot->GetGUID().GetCounter()); diff --git a/src/Ai/Base/Actions/InviteToGroupAction.cpp b/src/Ai/Base/Actions/InviteToGroupAction.cpp index bec515fefb..14f943f76c 100644 --- a/src/Ai/Base/Actions/InviteToGroupAction.cpp +++ b/src/Ai/Base/Actions/InviteToGroupAction.cpp @@ -31,7 +31,7 @@ bool InviteToGroupAction::Invite(Player* inviter, Player* player) if (!group->isRaidGroup() && group->GetMembersCount() > 4) { auto convertOp = std::make_unique(inviter->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(convertOp)); } } @@ -62,7 +62,7 @@ bool InviteNearbyToGroupAction::Execute(Event event) if (player->GetGroup()) continue; - if (!sPlayerbotAIConfig->randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer()) + if (!sPlayerbotAIConfig.randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer()) continue; Group* group = bot->GetGroup(); @@ -88,7 +88,7 @@ bool InviteNearbyToGroupAction::Execute(Event event) if (abs(int32(player->GetLevel() - bot->GetLevel())) > 2) continue; - if (sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance) + if (ServerFacade::instance().GetDistance2d(bot, player) > sPlayerbotAIConfig.sightDistance) continue; // When inviting the 5th member of the group convert to raid for future invites. @@ -96,19 +96,19 @@ bool InviteNearbyToGroupAction::Execute(Event event) bot->GetGroup()->GetMembersCount() > 3) { auto convertOp = std::make_unique(bot->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(convertOp)); } - if (sPlayerbotAIConfig->inviteChat && sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sPlayerbotAIConfig.inviteChat && sRandomPlayerbotMgr.IsRandomBot(bot)) { std::map placeholders; placeholders["%player"] = player->GetName(); if (group && group->isRaidGroup()) - bot->Say(BOT_TEXT2("join_raid", placeholders), + bot->Say(PlayerbotTextMgr::instance().GetBotText("join_raid", placeholders), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); else - bot->Say(BOT_TEXT2("join_group", placeholders), + bot->Say(PlayerbotTextMgr::instance().GetBotText("join_group", placeholders), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); } @@ -120,7 +120,7 @@ bool InviteNearbyToGroupAction::Execute(Event event) bool InviteNearbyToGroupAction::isUseful() { - if (!sPlayerbotAIConfig->randomBotGroupNearby) + if (!sPlayerbotAIConfig.randomBotGroupNearby) return false; if (bot->InBattleground()) @@ -186,7 +186,7 @@ bool InviteGuildToGroupAction::Execute(Event event) if (player->isDND()) continue; - if (!sPlayerbotAIConfig->randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer()) + if (!sPlayerbotAIConfig.randomBotInvitePlayer && GET_PLAYERBOT_AI(player)->IsRealPlayer()) continue; if (player->IsBeingTeleported()) @@ -221,7 +221,7 @@ bool InviteGuildToGroupAction::Execute(Event event) player->GetLevel() + 5) // Do not invite members that too low level or risk dragging them to deadly places. continue; - if (!playerAi && sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->sightDistance) + if (!playerAi && ServerFacade::instance().GetDistance2d(bot, player) > sPlayerbotAIConfig.sightDistance) continue; Group* group = bot->GetGroup(); @@ -230,11 +230,11 @@ bool InviteGuildToGroupAction::Execute(Event event) bot->GetGroup()->GetMembersCount() > 3) { auto convertOp = std::make_unique(bot->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(convertOp)); } - if (sPlayerbotAIConfig->inviteChat && - (sRandomPlayerbotMgr->IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) + if (sPlayerbotAIConfig.inviteChat && + (sRandomPlayerbotMgr.IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) { BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group); } @@ -373,7 +373,7 @@ bool LfgAction::Execute(Event event) else { auto convertOp = std::make_unique(requester->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(convertOp)); } } diff --git a/src/Ai/Base/Actions/LeaveGroupAction.cpp b/src/Ai/Base/Actions/LeaveGroupAction.cpp index a279c9426c..03a24bd199 100644 --- a/src/Ai/Base/Actions/LeaveGroupAction.cpp +++ b/src/Ai/Base/Actions/LeaveGroupAction.cpp @@ -33,7 +33,7 @@ bool PartyCommandAction::Execute(Event event) Player* master = GetMaster(); if (master && member == master->GetName()) { - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sRandomPlayerbotMgr.IsRandomBot(bot)) { Player* newMaster = botAI->FindNewMaster(); if (newMaster || bot->InBattleground()) @@ -150,7 +150,7 @@ bool LeaveFarAwayAction::isUseful() if (abs(int32(groupLeader->GetLevel() - bot->GetLevel())) > 4) return true; - if (bot->GetMapId() != groupLeader->GetMapId() || bot->GetDistance2d(groupLeader) >= 2 * sPlayerbotAIConfig->rpgDistance) + if (bot->GetMapId() != groupLeader->GetMapId() || bot->GetDistance2d(groupLeader) >= 2 * sPlayerbotAIConfig.rpgDistance) { return true; } diff --git a/src/Ai/Base/Actions/LfgActions.cpp b/src/Ai/Base/Actions/LfgActions.cpp index 15f8b92f25..a34c3efc63 100644 --- a/src/Ai/Base/Actions/LfgActions.cpp +++ b/src/Ai/Base/Actions/LfgActions.cpp @@ -20,7 +20,7 @@ bool LfgJoinAction::Execute(Event event) { return JoinLFG(); } uint32 LfgJoinAction::GetRoles() { - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) { if (botAI->IsTank(bot)) return PLAYER_ROLE_TANK; @@ -101,7 +101,7 @@ bool LfgJoinAction::JoinLFG() LfgDungeonSet list; std::vector selected; - std::vector dungeons = sRandomPlayerbotMgr->LfgDungeons[bot->GetTeamId()]; + std::vector dungeons = sRandomPlayerbotMgr.LfgDungeons[bot->GetTeamId()]; if (!dungeons.size()) return false; @@ -216,9 +216,9 @@ bool LfgAcceptAction::Execute(Event event) *packet << id << true; bot->GetSession()->QueuePacket(packet); - if (sRandomPlayerbotMgr->IsRandomBot(bot) && !bot->GetGroup()) + if (sRandomPlayerbotMgr.IsRandomBot(bot) && !bot->GetGroup()) { - sRandomPlayerbotMgr->Refresh(bot); + sRandomPlayerbotMgr.Refresh(bot); botAI->ResetStrategies(); } @@ -251,9 +251,9 @@ bool LfgAcceptAction::Execute(Event event) *packet << id << true; bot->GetSession()->QueuePacket(packet); - if (sRandomPlayerbotMgr->IsRandomBot(bot) && !bot->GetGroup()) + if (sRandomPlayerbotMgr.IsRandomBot(bot) && !bot->GetGroup()) { - sRandomPlayerbotMgr->Refresh(bot); + sRandomPlayerbotMgr.Refresh(bot); botAI->ResetStrategies(); } @@ -306,7 +306,7 @@ bool LfgTeleportAction::Execute(Event event) bool LfgJoinAction::isUseful() { - if (!sPlayerbotAIConfig->randomBotJoinLfg) + if (!sPlayerbotAIConfig.randomBotJoinLfg) { // botAI->ChangeStrategy("-lfg", BOT_STATE_NON_COMBAT); return false; @@ -337,7 +337,7 @@ bool LfgJoinAction::isUseful() if (bot->isDead()) return false; - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return false; Map* map = bot->GetMap(); diff --git a/src/Ai/Base/Actions/ListQuestsActions.cpp b/src/Ai/Base/Actions/ListQuestsActions.cpp index 7968417059..0071b113fe 100644 --- a/src/Ai/Base/Actions/ListQuestsActions.cpp +++ b/src/Ai/Base/Actions/ListQuestsActions.cpp @@ -107,14 +107,14 @@ uint32 ListQuestsAction::ListQuests(bool completed, bool silent, QuestTravelDeta if (travelDetail == QUEST_TRAVEL_DETAIL_SUMMARY) { std::vector allDestinations = - sTravelMgr->getQuestTravelDestinations(bot, questId, true, true, -1); + TravelMgr::instance().getQuestTravelDestinations(bot, questId, true, true, -1); std::vector availDestinations = - sTravelMgr->getQuestTravelDestinations(bot, questId, botAI->GetMaster(), false, -1); + TravelMgr::instance().getQuestTravelDestinations(bot, questId, botAI->GetMaster(), false, -1); uint32 desTot = allDestinations.size(); uint32 desAvail = availDestinations.size(); - uint32 desFull = desAvail - sTravelMgr->getQuestTravelDestinations(bot, questId, false, false, -1).size(); - uint32 desRange = desAvail - sTravelMgr->getQuestTravelDestinations(bot, questId, false, false).size(); + uint32 desFull = desAvail - TravelMgr::instance().getQuestTravelDestinations(bot, questId, false, false, -1).size(); + uint32 desRange = desAvail - TravelMgr::instance().getQuestTravelDestinations(bot, questId, false, false).size(); uint32 tpoints = 0; uint32 apoints = 0; @@ -140,7 +140,7 @@ uint32 ListQuestsAction::ListQuests(bool completed, bool silent, QuestTravelDeta { uint32 limit = 0; std::vector allDestinations = - sTravelMgr->getQuestTravelDestinations(bot, questId, true, true, -1); + TravelMgr::instance().getQuestTravelDestinations(bot, questId, true, true, -1); std::sort(allDestinations.begin(), allDestinations.end(), [botPos](TravelDestination* i, TravelDestination* j) { diff --git a/src/Ai/Base/Actions/ListSpellsAction.cpp b/src/Ai/Base/Actions/ListSpellsAction.cpp index 6f67aef1aa..720e2f9835 100644 --- a/src/Ai/Base/Actions/ListSpellsAction.cpp +++ b/src/Ai/Base/Actions/ListSpellsAction.cpp @@ -136,7 +136,7 @@ std::vector> ListSpellsAction::GetSpellList(std:: if (spellInfo->IsPassive()) continue; - SkillLineAbilityEntry const* skillLine = sPlayerbotSpellRepository->GetSkillLine(itr->first); + SkillLineAbilityEntry const* skillLine = PlayerbotSpellRepository::Instance().GetSkillLine(itr->first); if (skill != SKILL_NONE && (!skillLine || skillLine->SkillLine != skill)) continue; @@ -175,7 +175,7 @@ std::vector> ListSpellsAction::GetSpellList(std:: FindItemByIdVisitor visitor(itemid); uint32 reagentsInInventory = InventoryAction::GetItemCount(&visitor); - bool buyable = sPlayerbotSpellRepository->IsItemBuyable(itemid); + bool buyable = PlayerbotSpellRepository::Instance().IsItemBuyable(itemid); if (!buyable) { uint32 craftable = reagentsInInventory / reagentsRequired; @@ -303,4 +303,4 @@ bool ListSpellsAction::Execute(Event event) botAI->TellMasterNoFacing(i->second); return true; -} \ No newline at end of file +} diff --git a/src/Ai/Base/Actions/LootAction.cpp b/src/Ai/Base/Actions/LootAction.cpp index 2e0656e711..93368f5244 100644 --- a/src/Ai/Base/Actions/LootAction.cpp +++ b/src/Ai/Base/Actions/LootAction.cpp @@ -25,7 +25,7 @@ bool LootAction::Execute(Event /*event*/) LootObject prevLoot = AI_VALUE(LootObject, "loot target"); LootObject const& lootObject = - AI_VALUE(LootObjectStack*, "available loot")->GetLoot(sPlayerbotAIConfig->lootDistance); + AI_VALUE(LootObjectStack*, "available loot")->GetLoot(sPlayerbotAIConfig.lootDistance); if (!prevLoot.IsEmpty() && prevLoot.guid != lootObject.guid) { @@ -37,7 +37,7 @@ bool LootAction::Execute(Event /*event*/) // Provide a system to check if the game object id is disallowed in the user configurable list or not. // Check if the game object id is disallowed in the user configurable list or not. - if (sPlayerbotAIConfig->disallowedGameObjects.find(lootObject.guid.GetEntry()) != sPlayerbotAIConfig->disallowedGameObjects.end()) + if (sPlayerbotAIConfig.disallowedGameObjects.find(lootObject.guid.GetEntry()) != sPlayerbotAIConfig.disallowedGameObjects.end()) { return false; // Game object ID is disallowed, so do not proceed } @@ -50,7 +50,7 @@ bool LootAction::Execute(Event /*event*/) bool LootAction::isUseful() { - return sPlayerbotAIConfig->freeMethodLoot || !bot->GetGroup() || bot->GetGroup()->GetLootMethod() != FREE_FOR_ALL; + return sPlayerbotAIConfig.freeMethodLoot || !bot->GetGroup() || bot->GetGroup()->GetLootMethod() != FREE_FOR_ALL; } enum ProfessionSpells @@ -95,7 +95,7 @@ bool OpenLootAction::DoLoot(LootObject& lootObject) if (bot->IsMounted()) { bot->Dismount(); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->lootDelay); // Small delay to avoid animation issues + botAI->SetNextCheckDelay(sPlayerbotAIConfig.lootDelay); // Small delay to avoid animation issues } if (creature && creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE)) @@ -104,7 +104,7 @@ bool OpenLootAction::DoLoot(LootObject& lootObject) *packet << lootObject.guid; bot->GetSession()->QueuePacket(packet); // bot->GetSession()->HandleLootOpcode(packet); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->lootDelay); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.lootDelay); return true; } @@ -202,7 +202,7 @@ uint32 OpenLootAction::GetOpeningSpell(LootObject& lootObject, GameObject* go) return spellId; } - return sPlayerbotAIConfig->openGoSpell; + return sPlayerbotAIConfig.openGoSpell; } bool OpenLootAction::CanOpenLock(LootObject& /*lootObject*/, SpellInfo const* spellInfo, GameObject* go) @@ -294,7 +294,7 @@ bool StoreLootAction::AuctionItem(uint32 itemId) AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(ahEntry); - uint32 price = oldItem->GetCount() * proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot); + uint32 price = oldItem->GetCount() * proto->BuyPrice * sRandomPlayerbotMgr.GetBuyMultiplier(bot); uint32 stackCount = urand(1, proto->GetMaxStackSize()); if (!price || !stackCount) @@ -426,28 +426,28 @@ bool StoreLootAction::Execute(Event event) } Player* master = botAI->GetMaster(); - if (sRandomPlayerbotMgr->IsRandomBot(bot) && master) + if (sRandomPlayerbotMgr.IsRandomBot(bot) && master) { - uint32 price = itemcount * proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot) + gold; + uint32 price = itemcount * proto->BuyPrice * sRandomPlayerbotMgr.GetBuyMultiplier(bot) + gold; if (price) - sRandomPlayerbotMgr->AddTradeDiscount(bot, master, price); + sRandomPlayerbotMgr.AddTradeDiscount(bot, master, price); if (Group* group = bot->GetGroup()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) if (ref->GetSource() != bot) - sGuildTaskMgr->CheckItemTask(itemid, itemcount, ref->GetSource(), bot); + GuildTaskMgr::instance().CheckItemTask(itemid, itemcount, ref->GetSource(), bot); } WorldPacket* packet = new WorldPacket(CMSG_AUTOSTORE_LOOT_ITEM, 1); *packet << itemindex; bot->GetSession()->QueuePacket(packet); // bot->GetSession()->HandleAutostoreLootItemOpcode(packet); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->lootDelay); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.lootDelay); - if (proto->Quality > ITEM_QUALITY_NORMAL && !urand(0, 50) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT) && sPlayerbotAIConfig->randomBotEmote) + if (proto->Quality > ITEM_QUALITY_NORMAL && !urand(0, 50) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT) && sPlayerbotAIConfig.randomBotEmote) botAI->PlayEmote(TEXT_EMOTE_CHEER); - if (proto->Quality >= ITEM_QUALITY_RARE && !urand(0, 1) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT) && sPlayerbotAIConfig->randomBotEmote) + if (proto->Quality >= ITEM_QUALITY_RARE && !urand(0, 1) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT) && sPlayerbotAIConfig.randomBotEmote) botAI->PlayEmote(TEXT_EMOTE_CHEER); BroadcastHelper::BroadcastLootingItem(botAI, bot, proto); @@ -498,7 +498,7 @@ bool StoreLootAction::IsLootAllowed(uint32 itemid, PlayerbotAI* botAI) { // if (AI_VALUE2(uint32, "item count", proto->Name1) < quest->RequiredItemCount[i]) // { - // if (botAI->GetMaster() && sPlayerbotAIConfig->syncQuestWithPlayer) + // if (botAI->GetMaster() && sPlayerbotAIConfig.syncQuestWithPlayer) // return false; //Quest is autocomplete for the bot so no item needed. // } @@ -514,7 +514,7 @@ bool StoreLootAction::IsLootAllowed(uint32 itemid, PlayerbotAI* botAI) bool canLoot = lootStrategy->CanLoot(proto, context); // if (canLoot && proto->Bonding == BIND_WHEN_PICKED_UP && botAI->HasActivePlayerMaster()) - // canLoot = sPlayerbotAIConfig->IsInRandomAccountList(botAI->GetBot()->GetSession()->GetAccountId()); + // canLoot = sPlayerbotAIConfig.IsInRandomAccountList(botAI->GetBot()->GetSession()->GetAccountId()); return canLoot; } diff --git a/src/Ai/Base/Actions/LootRollAction.cpp b/src/Ai/Base/Actions/LootRollAction.cpp index 912f75fddf..e600f1f204 100644 --- a/src/Ai/Base/Actions/LootRollAction.cpp +++ b/src/Ai/Base/Actions/LootRollAction.cpp @@ -84,11 +84,11 @@ bool LootRollAction::Execute(Event event) break; } } - if (sPlayerbotAIConfig->lootRollLevel == 0) + if (sPlayerbotAIConfig.lootRollLevel == 0) { vote = PASS; } - else if (sPlayerbotAIConfig->lootRollLevel == 1) + else if (sPlayerbotAIConfig.lootRollLevel == 1) { if (vote == NEED) { diff --git a/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp b/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp index 618af10af1..4e898f30ef 100644 --- a/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp +++ b/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp @@ -61,7 +61,7 @@ bool MoveToRpgTargetAction::Execute(Event event) } if ((unit && unit->isMoving() && !urand(0, 20)) || !ChooseRpgTargetAction::isFollowValid(bot, wo) || - guidP.distance(bot) > sPlayerbotAIConfig->reactDistance * 2 || !urand(0, 50)) + guidP.distance(bot) > sPlayerbotAIConfig.reactDistance * 2 || !urand(0, 50)) { AI_VALUE(GuidSet&, "ignore rpg target").insert(AI_VALUE(GuidPosition, "rpg target")); RESET_AI_VALUE(GuidPosition, "rpg target"); @@ -73,7 +73,7 @@ bool MoveToRpgTargetAction::Execute(Event event) float z = wo->GetPositionZ(); float mapId = wo->GetMapId(); - if (sPlayerbotAIConfig->randombotsWalkingRPG) + if (sPlayerbotAIConfig.randombotsWalkingRPG) if (!bot->IsOutdoors()) bot->m_movementInfo.AddMovementFlag(MOVEMENTFLAG_WALKING); diff --git a/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp b/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp index ed60339b82..958dfe7391 100644 --- a/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp +++ b/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp @@ -43,7 +43,7 @@ bool MoveToTravelTargetAction::Execute(Event event) if (memberDistance < 50.0f) continue; - if (memberDistance > sPlayerbotAIConfig->reactDistance * 20) + if (memberDistance > sPlayerbotAIConfig.reactDistance * 20) continue; // float memberAngle = botLocation.getAngleBetween(targetPos, memberPos); @@ -65,9 +65,9 @@ bool MoveToTravelTargetAction::Execute(Event event) botAI->TellMasterNoFacing(out); } - target->setExpireIn(target->getTimeLeft() + sPlayerbotAIConfig->maxWaitForMove); + target->setExpireIn(target->getTimeLeft() + sPlayerbotAIConfig.maxWaitForMove); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->maxWaitForMove); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.maxWaitForMove); return true; } @@ -80,7 +80,7 @@ bool MoveToTravelTargetAction::Execute(Event event) if (target->getMaxTravelTime() > target->getTimeLeft()) // The bot is late. Speed it up. { - // distance = sPlayerbotAIConfig->fleeDistance; + // distance = sPlayerbotAIConfig.fleeDistance; // angle = bot->GetAngle(location.GetPositionX(), location.GetPositionY()); // location = botLocation.getLocation(); } diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index 69e5c278ba..c595dff6e4 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -51,7 +51,7 @@ MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Act void MovementAction::CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important) { float dist = wpOwner->GetDistance(x, y, z); - float delay = 1000.0f * dist / wpOwner->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay; + float delay = 1000.0f * dist / wpOwner->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig.reactDelay; // if (!important) // delay *= 0.25; @@ -195,8 +195,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, bool generatePath = !bot->IsFlying() && !bot->isSwimming(); bool disableMoveSplinePath = - sPlayerbotAIConfig->disableMoveSplinePath >= 2 || - (sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground()); + sPlayerbotAIConfig.disableMoveSplinePath >= 2 || + (sPlayerbotAIConfig.disableMoveSplinePath == 1 && bot->InBattleground()); if (Vehicle* vehicle = bot->GetVehicle()) { VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot); @@ -216,7 +216,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, delay -= botAI->GetReactDelay(); } delay = std::max(.0f, delay); - delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay); + delay = std::min((float)sPlayerbotAIConfig.maxWaitForMove, delay); AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority); return true; } @@ -241,7 +241,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, delay -= botAI->GetReactDelay(); } delay = std::max(.0f, delay); - delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay); + delay = std::min((float)sPlayerbotAIConfig.maxWaitForMove, delay); AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority); return true; } @@ -250,7 +250,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, { float modifiedZ; Movement::PointsArray path = - SearchForBestPath(x, y, z, modifiedZ, sPlayerbotAIConfig->maxMovementSearchTime, normal_only); + SearchForBestPath(x, y, z, modifiedZ, sPlayerbotAIConfig.maxMovementSearchTime, normal_only); if (modifiedZ == INVALID_HEIGHT) return false; float distance = bot->GetExactDist(x, y, modifiedZ); @@ -272,7 +272,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, delay -= botAI->GetReactDelay(); } delay = std::max(.0f, delay); - delay = std::min((float)sPlayerbotAIConfig->maxWaitForMove, delay); + delay = std::min((float)sPlayerbotAIConfig.maxWaitForMove, delay); AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), delay, priority); return true; } @@ -308,8 +308,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } // } - // float minDist = sPlayerbotAIConfig->targetPosRecalcDistance; //Minium distance a bot should move. - // float maxDist = sPlayerbotAIConfig->reactDistance; //Maxium distance a bot can move in one single + // float minDist = sPlayerbotAIConfig.targetPosRecalcDistance; //Minium distance a bot should move. + // float maxDist = sPlayerbotAIConfig.reactDistance; //Maxium distance a bot can move in one single // action. float originalZ = z; // save original destination height to check // if bot needs to fly up @@ -372,9 +372,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // if (startPosition.getMapId() != endPosition.getMapId() || totalDistance > maxDist) // { - // if (!sTravelNodeMap->getNodes().empty() && !bot->InBattleground()) + // if (!TravelNodeMap::instance().getNodes().empty() && !bot->InBattleground()) // { - // if (sPlayerbotAIConfig->tweakValue) + // if (sPlayerbotAIConfig.tweakValue) // { // if (lastMove.future.valid()) // { @@ -388,12 +388,12 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } // } // else - // movePath = sTravelNodeMap->getFullPath(startPosition, endPosition, bot); + // movePath = TravelNodeMap::instance().getFullPath(startPosition, endPosition, bot); // if (movePath.empty()) // { // //We have no path. Beyond 450yd the standard PathGenerator will probably move the wrong way. - // if (sServerFacade->IsDistanceGreaterThan(totalDistance, maxDist * 3)) + // if (ServerFacade::instance().IsDistanceGreaterThan(totalDistance, maxDist * 3)) // { // movePath.clear(); // movePath.addPoint(endPosition); @@ -402,7 +402,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // bot->StopMoving(); // if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) // botAI->TellMasterNoFacing("I have no path"); - // LOG_DEBUG("playerbots", "sServerFacade->IsDistanceGreaterThan(totalDistance, maxDist * 3)"); + // LOG_DEBUG("playerbots", "ServerFacade::instance().IsDistanceGreaterThan(totalDistance, maxDist * 3)"); // return false; // } @@ -450,7 +450,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // if (pathType == TravelNodePathType::portal) // && !botAI->isRealPlayer()) // { // //Log bot movement - // if (sPlayerbotAIConfig->hasLog("bot_movement.csv")) + // if (sPlayerbotAIConfig.hasLog("bot_movement.csv")) // { // WorldPosition telePos; // if (entry) @@ -463,7 +463,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // telePos = movePosition; // std::ostringstream out; - // out << sPlayerbotAIConfig->GetTimestampStr() << "+00,"; + // out << sPlayerbotAIConfig.GetTimestampStr() << "+00,"; // out << bot->GetName() << ","; // if (telePos && telePos.GetExactDist(movePosition) > 0.001) // startPosition.printWKT({ startPosition, movePosition, telePos }, out, 1); @@ -475,7 +475,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // out << bot->GetLevel() << ","; // out << (entry ? -1 : entry); - // sPlayerbotAIConfig->log("bot_movement.csv", out.str().c_str()); + // sPlayerbotAIConfig.log("bot_movement.csv", out.str().c_str()); // } // if (entry) @@ -621,10 +621,10 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } // //Log bot movement - // if (sPlayerbotAIConfig->hasLog("bot_movement.csv") && lastMove.lastMoveShort.GetExactDist(movePosition) > 0.001) + // if (sPlayerbotAIConfig.hasLog("bot_movement.csv") && lastMove.lastMoveShort.GetExactDist(movePosition) > 0.001) // { // std::ostringstream out; - // out << sPlayerbotAIConfig->GetTimestampStr() << "+00,"; + // out << sPlayerbotAIConfig.GetTimestampStr() << "+00,"; // out << bot->GetName() << ","; // startPosition.printWKT({ startPosition, movePosition }, out, 1); // out << std::to_string(bot->getRace()) << ","; @@ -632,7 +632,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // out << bot->GetLevel(); // out << 0; - // sPlayerbotAIConfig->log("bot_movement.csv", out.str().c_str()); + // sPlayerbotAIConfig.log("bot_movement.csv", out.str().c_str()); // } // // LOG_DEBUG("playerbots", "({}, {}) -> ({}, {})", startPosition.getX(), startPosition.getY(), // movePosition.getX(), movePosition.getY()); if (!react) @@ -694,7 +694,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // if (botAI->GetMaster()) // { // if (botAI->GetMaster()->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING) && - // sServerFacade->GetDistance2d(bot, botAI->GetMaster()) < 20.0f) + // ServerFacade::instance().GetDistance2d(bot, botAI->GetMaster()) < 20.0f) // masterWalking = true; // } @@ -787,7 +787,7 @@ bool MovementAction::MoveTo(WorldObject* target, float distance, MovementPriorit float angle = bot->GetAngle(target); float needToGo = distanceToTarget - distance; - float maxDistance = sPlayerbotAIConfig->spellDistance; + float maxDistance = sPlayerbotAIConfig.spellDistance; if (needToGo > 0 && needToGo > maxDistance) needToGo = maxDistance; else if (needToGo < 0 && needToGo < -maxDistance) @@ -898,8 +898,8 @@ bool MovementAction::IsMovingAllowed(WorldObject* target) if (bot->GetMapId() != target->GetMapId()) return false; - // float distance = sServerFacade->GetDistance2d(bot, target); - // if (!bot->InBattleground() && distance > sPlayerbotAIConfig->reactDistance) + // float distance = ServerFacade::instance().GetDistance2d(bot, target); + // if (!bot->InBattleground() && distance > sPlayerbotAIConfig.reactDistance) // return false; return IsMovingAllowed(); @@ -923,7 +923,7 @@ bool MovementAction::IsDuplicateMove(uint32 mapId, float x, float y, float z) LastMovement& lastMove = *context->GetValue("last movement"); // heuristic 5s - if (lastMove.msTime + sPlayerbotAIConfig->maxWaitForMove < getMSTime() || + if (lastMove.msTime + sPlayerbotAIConfig.maxWaitForMove < getMSTime() || lastMove.lastMoveShort.GetExactDist(x, y, z) > 0.01f) return false; @@ -1046,7 +1046,7 @@ void MovementAction::UpdateMovementState() // if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE && bot->CanNotReachTarget() && // !bot->InBattleground()) // { - // if (Unit* pTarget = sServerFacade->GetChaseTarget(bot)) + // if (Unit* pTarget = ServerFacade::instance().GetChaseTarget(bot)) // { // if (!bot->IsWithinMeleeRange(pTarget) && pTarget->IsCreature()) // { @@ -1080,7 +1080,7 @@ void MovementAction::UpdateMovementState() // bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) && bot->CanNotReachTarget() // && !bot->InBattleground()) // { - // if (Unit* pTarget = sServerFacade->GetChaseTarget(bot)) + // if (Unit* pTarget = ServerFacade::instance().GetChaseTarget(bot)) // { // if (pTarget != botAI->GetGroupLeader()) // return; @@ -1117,8 +1117,8 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (!target) return false; - if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), - sPlayerbotAIConfig->followDistance)) + if (!bot->InBattleground() && ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(bot, target), + sPlayerbotAIConfig.followDistance)) { // botAI->TellError("No need to follow"); return false; @@ -1126,9 +1126,9 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) /* if (!bot->InBattleground() - && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target->GetPositionX(), - target->GetPositionY()), sPlayerbotAIConfig->sightDistance) - && abs(bot->GetPositionZ() - target->GetPositionZ()) >= sPlayerbotAIConfig->spellDistance && + && ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(bot, target->GetPositionX(), + target->GetPositionY()), sPlayerbotAIConfig.sightDistance) + && abs(bot->GetPositionZ() - target->GetPositionZ()) >= sPlayerbotAIConfig.spellDistance && botAI->HasRealPlayerMaster() && (target->GetMapId() && bot->GetMapId() != target->GetMapId())) { @@ -1189,13 +1189,13 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) WorldPosition botPos(bot); WorldPosition cPos(corpse); - if (botPos.fDist(cPos) > sPlayerbotAIConfig->spellDistance) + if (botPos.fDist(cPos) > sPlayerbotAIConfig.spellDistance) return MoveTo(cPos.getMapId(), cPos.getX(), cPos.getY(), cPos.getZ()); } } - if (sServerFacade->IsDistanceGreaterOrEqualThan(sServerFacade->GetDistance2d(bot, target), - sPlayerbotAIConfig->sightDistance)) + if (ServerFacade::instance().IsDistanceGreaterOrEqualThan(ServerFacade::instance().GetDistance2d(bot, target), + sPlayerbotAIConfig.sightDistance)) { if (target->GetGUID().IsPlayer()) { @@ -1238,11 +1238,11 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) } if (!target->HasUnitState(UNIT_STATE_IN_FLIGHT)) - return MoveTo(target, sPlayerbotAIConfig->followDistance); + return MoveTo(target, sPlayerbotAIConfig.followDistance); } - if (sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), - sPlayerbotAIConfig->followDistance)) + if (ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(bot, target), + sPlayerbotAIConfig.followDistance)) { // botAI->TellError("No need to follow"); return false; @@ -1251,8 +1251,8 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (target->IsFriendlyTo(bot) && bot->IsMounted() && AI_VALUE(GuidVector, "all targets").empty()) distance += angle; - if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), - sPlayerbotAIConfig->followDistance)) + if (!bot->InBattleground() && ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(bot, target), + sPlayerbotAIConfig.followDistance)) { // botAI->TellError("No need to follow"); return false; @@ -1274,7 +1274,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) { - Unit* currentTarget = sServerFacade->GetChaseTarget(bot); + Unit* currentTarget = ServerFacade::instance().GetChaseTarget(bot); if (currentTarget && currentTarget->GetGUID() == target->GetGUID()) return false; } @@ -1347,13 +1347,13 @@ void MovementAction::WaitForReach(float distance) { float delay = 1000.0f * MoveDelay(distance); - if (delay > sPlayerbotAIConfig->maxWaitForMove) - delay = sPlayerbotAIConfig->maxWaitForMove; + if (delay > sPlayerbotAIConfig.maxWaitForMove) + delay = sPlayerbotAIConfig.maxWaitForMove; Unit* target = *botAI->GetAiObjectContext()->GetValue("current target"); Unit* player = *botAI->GetAiObjectContext()->GetValue("enemy player target"); - if ((player || target) && delay > sPlayerbotAIConfig->globalCoolDown) - delay = sPlayerbotAIConfig->globalCoolDown; + if ((player || target) && delay > sPlayerbotAIConfig.globalCoolDown) + delay = sPlayerbotAIConfig.globalCoolDown; if (delay < 0) delay = 0; @@ -1378,7 +1378,7 @@ bool MovementAction::Flee(Unit* target) if (!target) return false; - if (!sPlayerbotAIConfig->fleeingEnabled) + if (!sPlayerbotAIConfig.fleeingEnabled) return false; if (!IsMovingAllowed()) @@ -1390,7 +1390,7 @@ bool MovementAction::Flee(Unit* target) bool foundFlee = false; time_t lastFlee = AI_VALUE(LastMovement&, "last movement").lastFlee; time_t now = time(0); - uint32 fleeDelay = urand(2, sPlayerbotAIConfig->returnDelay / 1000); + uint32 fleeDelay = urand(2, sPlayerbotAIConfig.returnDelay / 1000); if (lastFlee) { @@ -1406,7 +1406,7 @@ bool MovementAction::Flee(Unit* target) if (Group* group = bot->GetGroup()) { Unit* fleeTarget = nullptr; - float fleeDistance = sPlayerbotAIConfig->sightDistance; + float fleeDistance = sPlayerbotAIConfig.sightDistance; for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { @@ -1416,8 +1416,8 @@ bool MovementAction::Flee(Unit* target) if (botAI->IsTank(player)) { - float distanceToTank = sServerFacade->GetDistance2d(bot, player); - float distanceToTarget = sServerFacade->GetDistance2d(bot, target); + float distanceToTank = ServerFacade::instance().GetDistance2d(bot, player); + float distanceToTarget = ServerFacade::instance().GetDistance2d(bot, target); if (distanceToTank < fleeDistance) { fleeTarget = player; @@ -1460,10 +1460,10 @@ bool MovementAction::Flee(Unit* target) if ((isHealer && botAI->IsHeal(player)) || needHealer) { - float distanceToHealer = sServerFacade->GetDistance2d(bot, player); - float distanceToTarget = sServerFacade->GetDistance2d(player, target); + float distanceToHealer = ServerFacade::instance().GetDistance2d(bot, player); + float distanceToTarget = ServerFacade::instance().GetDistance2d(player, target); if (distanceToHealer < fleeDistance && - distanceToTarget > (botAI->GetRange("shoot") / 2 + sPlayerbotAIConfig->followDistance) && + distanceToTarget > (botAI->GetRange("shoot") / 2 + sPlayerbotAIConfig.followDistance) && (needHealer || player->IsWithinLOSInMap(target))) { fleeTarget = player; @@ -1473,10 +1473,10 @@ bool MovementAction::Flee(Unit* target) } else if (isRanged && botAI->IsRanged(player)) { - float distanceToRanged = sServerFacade->GetDistance2d(bot, player); - float distanceToTarget = sServerFacade->GetDistance2d(player, target); + float distanceToRanged = ServerFacade::instance().GetDistance2d(bot, player); + float distanceToTarget = ServerFacade::instance().GetDistance2d(player, target); if (distanceToRanged < fleeDistance && - distanceToTarget > (botAI->GetRange("shoot") / 2 + sPlayerbotAIConfig->followDistance) && + distanceToTarget > (botAI->GetRange("shoot") / 2 + sPlayerbotAIConfig.followDistance) && player->IsWithinLOSInMap(target)) { fleeTarget = player; @@ -1485,10 +1485,10 @@ bool MovementAction::Flee(Unit* target) } } // remember any group member in case no one else found - float distanceToFlee = sServerFacade->GetDistance2d(bot, player); - float distanceToTarget = sServerFacade->GetDistance2d(player, target); + float distanceToFlee = ServerFacade::instance().GetDistance2d(bot, player); + float distanceToTarget = ServerFacade::instance().GetDistance2d(player, target); if (distanceToFlee < spareDistance && - distanceToTarget > (botAI->GetRange("shoot") / 2 + sPlayerbotAIConfig->followDistance) && + distanceToTarget > (botAI->GetRange("shoot") / 2 + sPlayerbotAIConfig.followDistance) && player->IsWithinLOSInMap(target)) { spareTarget = player; @@ -1508,8 +1508,8 @@ bool MovementAction::Flee(Unit* target) if ((!fleeTarget || !foundFlee) && master && master->IsAlive() && master->IsWithinLOSInMap(target)) { - float distanceToTarget = sServerFacade->GetDistance2d(master, target); - if (distanceToTarget > (botAI->GetRange("shoot") / 2 + sPlayerbotAIConfig->followDistance)) + float distanceToTarget = ServerFacade::instance().GetDistance2d(master, target); + if (distanceToTarget > (botAI->GetRange("shoot") / 2 + sPlayerbotAIConfig.followDistance)) foundFlee = MoveNear(master); } } @@ -1846,7 +1846,7 @@ void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool gen bool FleeAction::Execute(Event event) { - return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig->fleeDistance, true); + return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig.fleeDistance, true); } bool FleeAction::isUseful() @@ -1924,8 +1924,8 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() { return false; } - if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellInfo->Id) != - sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + if (sPlayerbotAIConfig.aoeAvoidSpellWhitelist.find(spellInfo->Id) != + sPlayerbotAIConfig.aoeAvoidSpellWhitelist.end()) return false; DynamicObject* dynOwner = aura->GetDynobjOwner(); @@ -1934,7 +1934,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() return false; } float radius = dynOwner->GetRadius(); - if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius) + if (!radius || radius > sPlayerbotAIConfig.maxAoeAvoidRadius) return false; if (bot->GetDistance(dynOwner) > radius) { @@ -1944,7 +1944,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() name << spellInfo->SpellName[LOCALE_enUS]; // << "] (aura)"; if (FleePosition(dynOwner->GetPosition(), radius)) { - if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) + if (sPlayerbotAIConfig.tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); lastMoveTimer = getMSTime(); @@ -1990,8 +1990,8 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() continue; } - if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(spellId) != - sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + if (sPlayerbotAIConfig.aoeAvoidSpellWhitelist.find(spellId) != + sPlayerbotAIConfig.aoeAvoidSpellWhitelist.end()) continue; const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); @@ -2001,7 +2001,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() } float radius = (float)goInfo->trap.diameter / 2 + go->GetCombatReach(); - if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius) + if (!radius || radius > sPlayerbotAIConfig.maxAoeAvoidRadius) continue; if (bot->GetDistance(go) > radius) @@ -2012,7 +2012,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() name << spellInfo->SpellName[LOCALE_enUS]; // << "] (object)"; if (FleePosition(go->GetPosition(), radius)) { - if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) + if (sPlayerbotAIConfig.tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); lastMoveTimer = getMSTime(); @@ -2061,8 +2061,8 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() sSpellMgr->GetSpellInfo(spellInfo->Effects[aurEff->GetEffIndex()].TriggerSpell); if (!triggerSpellInfo) continue; - if (sPlayerbotAIConfig->aoeAvoidSpellWhitelist.find(triggerSpellInfo->Id) != - sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) + if (sPlayerbotAIConfig.aoeAvoidSpellWhitelist.find(triggerSpellInfo->Id) != + sPlayerbotAIConfig.aoeAvoidSpellWhitelist.end()) return false; for (int j = 0; j < MAX_SPELL_EFFECTS; j++) { @@ -2073,13 +2073,13 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() { break; } - if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius) + if (!radius || radius > sPlayerbotAIConfig.maxAoeAvoidRadius) continue; std::ostringstream name; name << triggerSpellInfo->SpellName[LOCALE_enUS]; //<< "] (unit)"; if (FleePosition(unit->GetPosition(), radius)) { - if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) + if (sPlayerbotAIConfig.tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); lastMoveTimer = getMSTime(); @@ -2134,7 +2134,7 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) continue; } bool strict = checkAngle.strict; - float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance); + float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig.fleeDistance); float dx = bot->GetPositionX() + cos(angle) * fleeDis; float dy = bot->GetPositionY() + sin(angle) * fleeDis; float dz = bot->GetPositionZ(); @@ -2146,7 +2146,7 @@ Position MovementAction::BestPositionForMeleeToFlee(Position pos, float radius) Position fleePos{dx, dy, dz}; if (strict && currentTarget && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > - sPlayerbotAIConfig->tooCloseDistance && + sPlayerbotAIConfig.tooCloseDistance && bot->IsWithinMeleeRange(currentTarget)) { continue; @@ -2197,7 +2197,7 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius) continue; } bool strict = checkAngle.strict; - float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig->fleeDistance); + float fleeDis = std::min(radius + 1.0f, sPlayerbotAIConfig.fleeDistance); float dx = bot->GetPositionX() + cos(angle) * fleeDis; float dy = bot->GetPositionY() + sin(angle) * fleeDis; float dz = bot->GetPositionZ(); @@ -2208,13 +2208,13 @@ Position MovementAction::BestPositionForRangedToFlee(Position pos, float radius) } Position fleePos{dx, dy, dz}; if (strict && currentTarget && - fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->spellDistance) + fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig.spellDistance) { continue; } if (strict && currentTarget && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() < - (sPlayerbotAIConfig->tooCloseDistance)) + (sPlayerbotAIConfig.tooCloseDistance)) { continue; } @@ -2357,7 +2357,7 @@ Position CombatFormationMoveAction::AverageGroupPos(float dis, bool ranged, bool continue; if (!member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || - sServerFacade->GetDistance2d(bot, member) > dis) + ServerFacade::instance().GetDistance2d(bot, member) > dis) continue; averageX += member->GetPositionX(); @@ -2395,7 +2395,7 @@ float CombatFormationMoveAction::AverageGroupAngle(Unit* from, bool ranged, bool continue; if (!member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || - sServerFacade->GetDistance2d(bot, member) > sPlayerbotAIConfig->sightDistance) + ServerFacade::instance().GetDistance2d(bot, member) > sPlayerbotAIConfig.sightDistance) continue; cnt++; @@ -2437,7 +2437,7 @@ Player* CombatFormationMoveAction::NearestGroupMember(float dis) { Player* member = ObjectAccessor::FindPlayer(itr->guid); if (!member || !member->IsAlive() || member == bot || member->GetMapId() != bot->GetMapId() || - member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis) + member->IsCharmed() || ServerFacade::instance().GetDistance2d(bot, member) > dis) continue; if (nearestDis > bot->GetExactDist(member)) { @@ -2651,7 +2651,7 @@ bool MoveToLootAction::Execute(Event event) if (!loot.IsLootPossible(bot)) return false; - return MoveNear(loot.GetWorldObject(bot), sPlayerbotAIConfig->contactDistance); + return MoveNear(loot.GetWorldObject(bot), sPlayerbotAIConfig.contactDistance); } bool MoveOutOfEnemyContactAction::Execute(Event event) @@ -2660,7 +2660,7 @@ bool MoveOutOfEnemyContactAction::Execute(Event event) if (!target) return false; - return MoveTo(target, sPlayerbotAIConfig->contactDistance); + return MoveTo(target, sPlayerbotAIConfig.contactDistance); } bool MoveOutOfEnemyContactAction::isUseful() { return AI_VALUE2(bool, "inside target", "current target"); } @@ -2674,8 +2674,8 @@ bool SetFacingTargetAction::Execute(Event event) if (bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) return true; - sServerFacade->SetFacingTo(bot, target); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + ServerFacade::instance().SetFacingTo(bot, target); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); return true; } @@ -2754,7 +2754,7 @@ bool SetBehindTargetAction::Execute(Event event) bool MoveOutOfCollisionAction::Execute(Event event) { float angle = M_PI * 2000 / frand(1.f, 1000.f); - float distance = sPlayerbotAIConfig->followDistance; + float distance = sPlayerbotAIConfig.followDistance; return MoveTo(bot->GetMapId(), bot->GetPositionX() + cos(angle) * distance, bot->GetPositionY() + sin(angle) * distance, bot->GetPositionZ()); } @@ -2771,7 +2771,7 @@ bool MoveOutOfCollisionAction::isUseful() bool MoveRandomAction::Execute(Event event) { - float distance = sPlayerbotAIConfig->tooCloseDistance + urand(10, 30); + float distance = sPlayerbotAIConfig.tooCloseDistance + urand(10, 30); Map* map = bot->GetMap(); for (int i = 0; i < 3; ++i) diff --git a/src/Ai/Base/Actions/MovementActions.h b/src/Ai/Base/Actions/MovementActions.h index d12bcd555c..7db911baa4 100644 --- a/src/Ai/Base/Actions/MovementActions.h +++ b/src/Ai/Base/Actions/MovementActions.h @@ -29,7 +29,7 @@ class MovementAction : public Action protected: bool JumpTo(uint32 mapId, float x, float y, float z, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); - bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->contactDistance, + bool MoveNear(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig.contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); bool MoveToLOS(WorldObject* target, bool ranged = false); bool MoveTo(uint32 mapId, float x, float y, float z, bool idle = false, bool react = false, @@ -38,10 +38,10 @@ class MovementAction : public Action bool backwards = false); bool MoveTo(WorldObject* target, float distance = 0.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); - bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig->contactDistance, + bool MoveNear(WorldObject* target, float distance = sPlayerbotAIConfig.contactDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); float GetFollowAngle(); - bool Follow(Unit* target, float distance = sPlayerbotAIConfig->followDistance); + bool Follow(Unit* target, float distance = sPlayerbotAIConfig.followDistance); bool Follow(Unit* target, float distance, float angle); bool ChaseTo(WorldObject* obj, float distance = 0.0f, float angle = 0.0f); bool ReachCombatTo(Unit* target, float distance = 0.0f); @@ -56,10 +56,10 @@ class MovementAction : public Action bool Flee(Unit* target); void ClearIdleState(); void UpdateMovementState(); - bool MoveAway(Unit* target, float distance = sPlayerbotAIConfig->fleeDistance, bool backwards = false); + bool MoveAway(Unit* target, float distance = sPlayerbotAIConfig.fleeDistance, bool backwards = false); bool MoveFromGroup(float distance); bool Move(float angle, float distance); - bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig->followDistance, + bool MoveInside(uint32 mapId, float x, float y, float z, float distance = sPlayerbotAIConfig.followDistance, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL); void CreateWp(Player* wpOwner, float x, float y, float z, float o, uint32 entry, bool important = false); Position BestPositionForMeleeToFlee(Position pos, float radius); @@ -86,7 +86,7 @@ class MovementAction : public Action class FleeAction : public MovementAction { public: - FleeAction(PlayerbotAI* botAI, float distance = sPlayerbotAIConfig->spellDistance) + FleeAction(PlayerbotAI* botAI, float distance = sPlayerbotAIConfig.spellDistance) : MovementAction(botAI, "flee"), distance(distance) { } @@ -138,8 +138,8 @@ class CombatFormationMoveAction : public MovementAction bool Execute(Event event) override; protected: - Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance, bool ranged = false, bool self = false); - Player* NearestGroupMember(float dis = sPlayerbotAIConfig->sightDistance); + Position AverageGroupPos(float dis = sPlayerbotAIConfig.sightDistance, bool ranged = false, bool self = false); + Player* NearestGroupMember(float dis = sPlayerbotAIConfig.sightDistance); float AverageGroupAngle(Unit* from, bool ranged = false, bool self = false); Position GetNearestPosition(const std::vector& positions); int lastMoveTimer = 0; diff --git a/src/Ai/Base/Actions/NonCombatActions.cpp b/src/Ai/Base/Actions/NonCombatActions.cpp index f87aa891dd..492da8ae6b 100644 --- a/src/Ai/Base/Actions/NonCombatActions.cpp +++ b/src/Ai/Base/Actions/NonCombatActions.cpp @@ -76,7 +76,7 @@ bool EatAction::Execute(Event event) if (bot->isMoving()) { bot->StopMoving(); - // botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); + // botAI->SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); // return false; } diff --git a/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp b/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp index 87890c1c57..4ecfdc3bc1 100644 --- a/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp +++ b/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp @@ -16,12 +16,12 @@ bool PassLeadershipToMasterAction::Execute(Event event) if (master && master != bot && bot->GetGroup() && bot->GetGroup()->IsMember(master->GetGUID())) { auto setLeaderOp = std::make_unique(bot->GetGUID(), master->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(setLeaderOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(setLeaderOp)); if (!message.empty()) botAI->TellMasterNoFacing(message); - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sRandomPlayerbotMgr.IsRandomBot(bot)) { botAI->ResetStrategies(); botAI->Reset(); diff --git a/src/Ai/Base/Actions/PetsAction.cpp b/src/Ai/Base/Actions/PetsAction.cpp index cb9dc93957..d98740ee0b 100644 --- a/src/Ai/Base/Actions/PetsAction.cpp +++ b/src/Ai/Base/Actions/PetsAction.cpp @@ -23,7 +23,7 @@ bool PetsAction::Execute(Event event) if (param.empty()) { // If no parameter is provided, show usage instructions and return. - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_usage_error", "Usage: pet ", {}); botAI->TellError(text); return false; @@ -52,7 +52,7 @@ bool PetsAction::Execute(Event event) // If no pets or guardians are found, notify and return. if (targets.empty()) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_no_pet_error", "You have no pet or guardian pet.", {}); botAI->TellError(text); return false; @@ -65,19 +65,19 @@ bool PetsAction::Execute(Event event) if (param == "aggressive") { react = REACT_AGGRESSIVE; - stanceText = sPlayerbotTextMgr->GetBotTextOrDefault( + stanceText = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_aggressive", "aggressive", {}); } else if (param == "defensive") { react = REACT_DEFENSIVE; - stanceText = sPlayerbotTextMgr->GetBotTextOrDefault( + stanceText = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_defensive", "defensive", {}); } else if (param == "passive") { react = REACT_PASSIVE; - stanceText = sPlayerbotTextMgr->GetBotTextOrDefault( + stanceText = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_passive", "passive", {}); } // The "stance" command simply reports the current stance of each pet/guardian. @@ -86,30 +86,30 @@ bool PetsAction::Execute(Event event) for (Creature* target : targets) { std::string type = target->IsPet() ? - sPlayerbotTextMgr->GetBotTextOrDefault("pet_type_pet", "pet", {}) : - sPlayerbotTextMgr->GetBotTextOrDefault("pet_type_guardian", "guardian", {}); + PlayerbotTextMgr::instance().GetBotTextOrDefault("pet_type_pet", "pet", {}) : + PlayerbotTextMgr::instance().GetBotTextOrDefault("pet_type_guardian", "guardian", {}); std::string name = target->GetName(); std::string stance; switch (target->GetReactState()) { case REACT_AGGRESSIVE: - stance = sPlayerbotTextMgr->GetBotTextOrDefault( + stance = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_aggressive", "aggressive", {}); break; case REACT_DEFENSIVE: - stance = sPlayerbotTextMgr->GetBotTextOrDefault( + stance = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_defensive", "defensive", {}); break; case REACT_PASSIVE: - stance = sPlayerbotTextMgr->GetBotTextOrDefault( + stance = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_passive", "passive", {}); break; default: - stance = sPlayerbotTextMgr->GetBotTextOrDefault( + stance = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_unknown", "unknown", {}); break; } - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_report", "Current stance of %type \"%name\": %stance.", {{"type", type}, {"name", name}, {"stance", stance}}); botAI->TellMaster(text); @@ -133,30 +133,30 @@ bool PetsAction::Execute(Event event) // If no valid target is selected, show an error and return. if (!targetUnit) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_no_target_error", "No valid target selected by master.", {}); botAI->TellError(text); return false; } if (!targetUnit->IsAlive()) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_target_dead_error", "Target is not alive.", {}); botAI->TellError(text); return false; } if (!bot->IsValidAttackTarget(targetUnit)) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_invalid_target_error", "Target is not a valid attack target for the bot.", {}); botAI->TellError(text); return false; } - if (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) && + if (sPlayerbotAIConfig.IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) && (targetUnit->IsPlayer() || targetUnit->IsPet()) && (!bot->duel || bot->duel->Opponent != targetUnit)) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_pvp_prohibited_error", "I cannot command my pet to attack players in PvP prohibited areas.", {}); botAI->TellError(text); return false; @@ -208,15 +208,15 @@ bool PetsAction::Execute(Event event) } } // Inform the master if the command succeeded or failed. - if (didAttack && sPlayerbotAIConfig->petChatCommandDebug == 1) + if (didAttack && sPlayerbotAIConfig.petChatCommandDebug == 1) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_attack_success", "Pet commanded to attack your target.", {}); botAI->TellMaster(text); } else if (!didAttack) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_attack_failed", "Pet did not attack. (Already attacking or unable to attack target)", {}); botAI->TellError(text); } @@ -226,9 +226,9 @@ bool PetsAction::Execute(Event event) else if (param == "follow") { botAI->PetFollow(); - if (sPlayerbotAIConfig->petChatCommandDebug == 1) + if (sPlayerbotAIConfig.petChatCommandDebug == 1) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_follow_success", "Pet commanded to follow.", {}); botAI->TellMaster(text); } @@ -267,9 +267,9 @@ bool PetsAction::Execute(Event event) charmInfo->SetForcedTargetGUID(); } } - if (sPlayerbotAIConfig->petChatCommandDebug == 1) + if (sPlayerbotAIConfig.petChatCommandDebug == 1) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stay_success", "Pet commanded to stay.", {}); botAI->TellMaster(text); } @@ -278,7 +278,7 @@ bool PetsAction::Execute(Event event) // Unknown command: show usage instructions and return. else { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_unknown_command_error", "Unknown pet command: %param. Use: pet ", {{"param", param}}); botAI->TellError(text); @@ -295,13 +295,13 @@ bool PetsAction::Execute(Event event) } // Inform the master of the new stance if debug is enabled. - if (sPlayerbotAIConfig->petChatCommandDebug == 1) + if (sPlayerbotAIConfig.petChatCommandDebug == 1) { - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "pet_stance_set_success", "Pet stance set to %stance.", {{"stance", stanceText}}); botAI->TellMaster(text); } return true; -} \ No newline at end of file +} diff --git a/src/Ai/Base/Actions/PositionAction.cpp b/src/Ai/Base/Actions/PositionAction.cpp index 2de27f0414..7d8f4264ca 100644 --- a/src/Ai/Base/Actions/PositionAction.cpp +++ b/src/Ai/Base/Actions/PositionAction.cpp @@ -120,7 +120,7 @@ bool MoveToPositionAction::isUseful() { PositionInfo pos = context->GetValue("position")->Get()[qualifier]; float distance = AI_VALUE2(float, "distance", std::string("position_") + qualifier); - return pos.isSet() && distance > sPlayerbotAIConfig->followDistance && distance < sPlayerbotAIConfig->reactDistance; + return pos.isSet() && distance > sPlayerbotAIConfig.followDistance && distance < sPlayerbotAIConfig.reactDistance; } bool SetReturnPositionAction::Execute(Event event) @@ -131,7 +131,7 @@ bool SetReturnPositionAction::Execute(Event event) if (returnPos.isSet() && !randomPos.isSet()) { float angle = 2 * M_PI * urand(0, 1000) / 100.0f; - float dist = sPlayerbotAIConfig->followDistance * urand(0, 1000) / 1000.0f; + float dist = sPlayerbotAIConfig.followDistance * urand(0, 1000) / 1000.0f; float x = returnPos.x + cos(angle) * dist; float y = returnPos.y + sin(angle) * dist; float z = bot->GetPositionZ(); @@ -157,7 +157,7 @@ bool SetReturnPositionAction::isUseful() bool ReturnAction::isUseful() { PositionInfo pos = context->GetValue("position")->Get()[qualifier]; - return pos.isSet() && AI_VALUE2(float, "distance", "position_random") > sPlayerbotAIConfig->followDistance; + return pos.isSet() && AI_VALUE2(float, "distance", "position_random") > sPlayerbotAIConfig.followDistance; } bool ReturnToStayPositionAction::isPossible() @@ -167,7 +167,7 @@ bool ReturnToStayPositionAction::isPossible() if (stayPosition.isSet()) { const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); - if (distance > sPlayerbotAIConfig->reactDistance) + if (distance > sPlayerbotAIConfig.reactDistance) { botAI->TellMaster("The stay position is too far to return. I am going to stay where I am now"); diff --git a/src/Ai/Base/Actions/QueryItemUsageAction.cpp b/src/Ai/Base/Actions/QueryItemUsageAction.cpp index 2fbc75af8f..34e2cc831b 100644 --- a/src/Ai/Base/Actions/QueryItemUsageAction.cpp +++ b/src/Ai/Base/Actions/QueryItemUsageAction.cpp @@ -118,7 +118,7 @@ std::string const QueryItemUsageAction::QueryItemUsage(ItemTemplate const* item) std::string const QueryItemUsageAction::QueryItemPrice(ItemTemplate const* item) { - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return ""; if (item->Bonding == BIND_WHEN_PICKED_UP) @@ -133,7 +133,7 @@ std::string const QueryItemUsageAction::QueryItemPrice(ItemTemplate const* item) { Item* sell = *i; int32 price = - sell->GetCount() * sell->GetTemplate()->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot); + sell->GetCount() * sell->GetTemplate()->SellPrice * sRandomPlayerbotMgr.GetSellMultiplier(bot); if (!sellPrice || sellPrice > price) sellPrice = price; } @@ -147,7 +147,7 @@ std::string const QueryItemUsageAction::QueryItemPrice(ItemTemplate const* item) if (usage == ITEM_USAGE_NONE) return msg.str(); - int32 buyPrice = item->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot); + int32 buyPrice = item->BuyPrice * sRandomPlayerbotMgr.GetBuyMultiplier(bot); if (buyPrice) { if (sellPrice) diff --git a/src/Ai/Base/Actions/QueryQuestAction.cpp b/src/Ai/Base/Actions/QueryQuestAction.cpp index 6ceccad975..5f5d63e08c 100644 --- a/src/Ai/Base/Actions/QueryQuestAction.cpp +++ b/src/Ai/Base/Actions/QueryQuestAction.cpp @@ -73,7 +73,7 @@ bool QueryQuestAction::Execute(Event event) { uint32 limit = 0; std::vector allDestinations = - sTravelMgr->getQuestTravelDestinations(bot, questId, true, true, -1); + TravelMgr::instance().getQuestTravelDestinations(bot, questId, true, true, -1); std::sort(allDestinations.begin(), allDestinations.end(), [ptr_botpos](TravelDestination* i, TravelDestination* j) {return i->distanceTo(ptr_botpos) < j->distanceTo(ptr_botpos); }); for (auto dest : allDestinations) diff --git a/src/Ai/Base/Actions/QuestAction.cpp b/src/Ai/Base/Actions/QuestAction.cpp index b96c539475..dfe7bb9f1e 100644 --- a/src/Ai/Base/Actions/QuestAction.cpp +++ b/src/Ai/Base/Actions/QuestAction.cpp @@ -182,7 +182,7 @@ bool QuestAction::ProcessQuests(WorldObject* questGiver) { ObjectGuid guid = questGiver->GetGUID(); - if (bot->GetDistance(questGiver) > INTERACTION_DISTANCE && !sPlayerbotAIConfig->syncQuestWithPlayer) + if (bot->GetDistance(questGiver) > INTERACTION_DISTANCE && !sPlayerbotAIConfig.syncQuestWithPlayer) { //if (botAI->HasStrategy("debug", BotState::BOT_STATE_COMBAT) || botAI->HasStrategy("debug", BotState::BOT_STATE_NON_COMBAT)) @@ -190,7 +190,7 @@ bool QuestAction::ProcessQuests(WorldObject* questGiver) return false; } - if (!bot->HasInArc(CAST_ANGLE_IN_FRONT, questGiver, sPlayerbotAIConfig->sightDistance)) + if (!bot->HasInArc(CAST_ANGLE_IN_FRONT, questGiver, sPlayerbotAIConfig.sightDistance)) bot->SetFacingToObject(questGiver); bot->SetTarget(guid); @@ -238,7 +238,7 @@ bool QuestAction::AcceptQuest(Quest const* quest, ObjectGuid questGiver) p.rpos(0); bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p); - if (bot->GetQuestStatus(questId) == QUEST_STATUS_NONE && sPlayerbotAIConfig->syncQuestWithPlayer) + if (bot->GetQuestStatus(questId) == QUEST_STATUS_NONE && sPlayerbotAIConfig.syncQuestWithPlayer) { Object* pObject = ObjectAccessor::GetObjectByTypeMask(*bot, questGiver, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM); @@ -366,7 +366,7 @@ bool QuestUpdateAddItemAction::Execute(Event event) placeholders["%quest_obj_required"] = std::to_string(requiredItemsCount); if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_COMBAT) || botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT)) { - const auto text = BOT_TEXT2("%quest_link - %item_link %quest_obj_available/%quest_obj_required", placeholders); + const auto text = PlayerbotTextMgr::instance().GetBotText("%quest_link - %item_link %quest_obj_available/%quest_obj_required", placeholders); botAI->Say(text); LOG_INFO("playerbots", "{} => {}", bot->GetName(), text); } @@ -454,7 +454,7 @@ bool QuestUpdateFailedTimerAction::Execute(Event event) { std::map placeholders; placeholders["%quest_link"] = botAI->GetChatHelper()->FormatQuest(qInfo); - botAI->TellMaster(BOT_TEXT2("Failed timer for %quest_link, abandoning", placeholders)); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotText("Failed timer for %quest_link, abandoning", placeholders)); BroadcastHelper::BroadcastQuestUpdateFailedTimer(botAI, bot, qInfo); } else diff --git a/src/Ai/Base/Actions/QuestConfirmAcceptAction.cpp b/src/Ai/Base/Actions/QuestConfirmAcceptAction.cpp index cc23fdaf0a..9c47af37b2 100644 --- a/src/Ai/Base/Actions/QuestConfirmAcceptAction.cpp +++ b/src/Ai/Base/Actions/QuestConfirmAcceptAction.cpp @@ -20,4 +20,4 @@ bool QuestConfirmAcceptAction::Execute(Event event) botAI->TellMaster(out); bot->GetSession()->HandleQuestConfirmAccept(sendPacket); return true; -} \ No newline at end of file +} diff --git a/src/Ai/Base/Actions/QuestConfirmAcceptAction.h b/src/Ai/Base/Actions/QuestConfirmAcceptAction.h index 029c6eeff2..1f13a0abfa 100644 --- a/src/Ai/Base/Actions/QuestConfirmAcceptAction.h +++ b/src/Ai/Base/Actions/QuestConfirmAcceptAction.h @@ -24,4 +24,4 @@ class QuestConfirmAcceptAction : public Action bool Execute(Event event) override; }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/Base/Actions/RandomBotUpdateAction.cpp b/src/Ai/Base/Actions/RandomBotUpdateAction.cpp index 61a8360e59..76ed46c901 100644 --- a/src/Ai/Base/Actions/RandomBotUpdateAction.cpp +++ b/src/Ai/Base/Actions/RandomBotUpdateAction.cpp @@ -10,7 +10,7 @@ bool RandomBotUpdateAction::Execute(Event event) { - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return false; if (bot->GetGroup() && botAI->GetGroupLeader()) @@ -20,10 +20,10 @@ bool RandomBotUpdateAction::Execute(Event event) return true; } - if (botAI->HasPlayerNearby(sPlayerbotAIConfig->grindDistance)) + if (botAI->HasPlayerNearby(sPlayerbotAIConfig.grindDistance)) return true; - return sRandomPlayerbotMgr->ProcessBot(bot); + return sRandomPlayerbotMgr.ProcessBot(bot); } bool RandomBotUpdateAction::isUseful() { return AI_VALUE(bool, "random bot update"); } diff --git a/src/Ai/Base/Actions/ReachTargetActions.cpp b/src/Ai/Base/Actions/ReachTargetActions.cpp index 2be0da6beb..a58e9b96ea 100644 --- a/src/Ai/Base/Actions/ReachTargetActions.cpp +++ b/src/Ai/Base/Actions/ReachTargetActions.cpp @@ -28,7 +28,7 @@ bool ReachTargetAction::isUseful() Unit* target = GetTarget(); // float dis = distance + CONTACT_DISTANCE; return target && - !bot->IsWithinCombatRange(target, distance); // sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, + !bot->IsWithinCombatRange(target, distance); // ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, // "distance", GetTargetName()), distance); } @@ -42,8 +42,8 @@ bool CastReachTargetSpellAction::isUseful() return false; } - return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"), - (distance + sPlayerbotAIConfig->contactDistance)); + return ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"), + (distance + sPlayerbotAIConfig.contactDistance)); } ReachSpellAction::ReachSpellAction(PlayerbotAI* botAI) diff --git a/src/Ai/Base/Actions/ReachTargetActions.h b/src/Ai/Base/Actions/ReachTargetActions.h index 40559753f9..1a8ce0c9cc 100644 --- a/src/Ai/Base/Actions/ReachTargetActions.h +++ b/src/Ai/Base/Actions/ReachTargetActions.h @@ -44,7 +44,7 @@ class CastReachTargetSpellAction : public CastSpellAction class ReachMeleeAction : public ReachTargetAction { public: - ReachMeleeAction(PlayerbotAI* botAI) : ReachTargetAction(botAI, "reach melee", sPlayerbotAIConfig->meleeDistance) {} + ReachMeleeAction(PlayerbotAI* botAI) : ReachTargetAction(botAI, "reach melee", sPlayerbotAIConfig.meleeDistance) {} }; class ReachSpellAction : public ReachTargetAction diff --git a/src/Ai/Base/Actions/ReadyCheckAction.cpp b/src/Ai/Base/Actions/ReadyCheckAction.cpp index 2fc25e6dcc..1c510d69d4 100644 --- a/src/Ai/Base/Actions/ReadyCheckAction.cpp +++ b/src/Ai/Base/Actions/ReadyCheckAction.cpp @@ -47,7 +47,7 @@ class HealthChecker : public ReadyChecker public: bool Check(PlayerbotAI* botAI, AiObjectContext* context) override { - return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->almostFullHealth; + return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig.almostFullHealth; } std::string const getName() override { return "HP"; } @@ -59,7 +59,7 @@ class ManaChecker : public ReadyChecker bool Check(PlayerbotAI* botAI, AiObjectContext* context) override { return !AI_VALUE2(bool, "has mana", "self target") || - AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig->mediumHealth; + AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.mediumHealth; } std::string const getName() override { return "MP"; } @@ -73,7 +73,7 @@ class DistanceChecker : public ReadyChecker Player* bot = botAI->GetBot(); if (Player* master = botAI->GetMaster()) { - bool distance = bot->GetDistance(master) <= sPlayerbotAIConfig->sightDistance; + bool distance = bot->GetDistance(master) <= sPlayerbotAIConfig.sightDistance; if (!distance) { return false; diff --git a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp index 17fcc42a0b..9e3ec445d2 100644 --- a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp +++ b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp @@ -183,9 +183,9 @@ bool AutoReleaseSpiritAction::ShouldAutoRelease() const return false; } - return sServerFacade->IsDistanceGreaterThan( + return ServerFacade::instance().IsDistanceGreaterThan( AI_VALUE2(float, "distance", "group leader"), - sPlayerbotAIConfig->sightDistance); + sPlayerbotAIConfig.sightDistance); } bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const diff --git a/src/Ai/Base/Actions/ResetAiAction.cpp b/src/Ai/Base/Actions/ResetAiAction.cpp index 3df6bbcd2a..19017c9161 100644 --- a/src/Ai/Base/Actions/ResetAiAction.cpp +++ b/src/Ai/Base/Actions/ResetAiAction.cpp @@ -44,7 +44,7 @@ bool ResetAiAction::Execute(Event event) } } } - sPlayerbotRepository->Reset(botAI); + PlayerbotRepository::instance().Reset(botAI); botAI->ResetStrategies(false); botAI->TellMaster("AI was reset to defaults"); return true; diff --git a/src/Ai/Base/Actions/RevealGatheringItemAction.cpp b/src/Ai/Base/Actions/RevealGatheringItemAction.cpp index 9725dce5ce..35cd7f8f04 100644 --- a/src/Ai/Base/Actions/RevealGatheringItemAction.cpp +++ b/src/Ai/Base/Actions/RevealGatheringItemAction.cpp @@ -20,16 +20,16 @@ bool RevealGatheringItemAction::Execute(Event event) return false; std::list targets; - AnyGameObjectInObjectRangeCheck u_check(bot, sPlayerbotAIConfig->grindDistance); + AnyGameObjectInObjectRangeCheck u_check(bot, sPlayerbotAIConfig.grindDistance); Acore::GameObjectListSearcher searcher(bot, targets, u_check); - Cell::VisitObjects(bot, searcher, sPlayerbotAIConfig->reactDistance); + Cell::VisitObjects(bot, searcher, sPlayerbotAIConfig.reactDistance); std::vector result; for (GameObject* go : targets) { if (!go || !go->isSpawned() || - sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, go), - sPlayerbotAIConfig->lootDistance)) + ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(bot, go), + sPlayerbotAIConfig.lootDistance)) continue; if (LockEntry const* lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->GetLockId())) diff --git a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp index ce0b1fa05d..4af96fc698 100644 --- a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp +++ b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp @@ -24,8 +24,8 @@ bool ReviveFromCorpseAction::Execute(Event event) WorldPacket& p = event.getPacket(); if (!p.empty() && p.GetOpcode() == CMSG_RECLAIM_CORPSE && groupLeader && !corpse && bot->IsAlive()) { - if (sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), - sPlayerbotAIConfig->farDistance)) + if (ServerFacade::instance().IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), + sPlayerbotAIConfig.farDistance)) { if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) { @@ -46,8 +46,8 @@ bool ReviveFromCorpseAction::Execute(Event event) if (groupLeader) { if (!GET_PLAYERBOT_AI(groupLeader) && groupLeader->isDead() && groupLeader->GetCorpse() && - sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), - sPlayerbotAIConfig->farDistance)) + ServerFacade::instance().IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), + sPlayerbotAIConfig.farDistance)) return false; } @@ -87,8 +87,8 @@ bool FindCorpseAction::Execute(Event event) // if (groupLeader) // { // if (!GET_PLAYERBOT_AI(groupLeader) && - // sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), - // sPlayerbotAIConfig->farDistance)) return false; + // ServerFacade::instance().IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"), + // sPlayerbotAIConfig.farDistance)) return false; // } uint32 dCount = AI_VALUE(uint32, "death count"); @@ -101,8 +101,8 @@ bool FindCorpseAction::Execute(Event event) // bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), // bot->GetName().c_str()); context->GetValue("death count")->Set(0); - // sRandomPlayerbotMgr->RandomTeleportForLevel(bot); - sRandomPlayerbotMgr->Revive(bot); + // sRandomPlayerbotMgr.RandomTeleportForLevel(bot); + sRandomPlayerbotMgr.Revive(bot); return true; } } @@ -123,7 +123,7 @@ bool FindCorpseAction::Execute(Event event) { if (moveToLeader) // We are near group leader. { - if (botPos.fDist(leaderPos) < sPlayerbotAIConfig->spellDistance) + if (botPos.fDist(leaderPos) < sPlayerbotAIConfig.spellDistance) return false; } else if (deadTime > 8 * MINUTE) // We have walked too long already. @@ -138,7 +138,7 @@ bool FindCorpseAction::Execute(Event event) } // If we are getting close move to a save ressurrection spot instead of just the corpse. - if (corpseDist < sPlayerbotAIConfig->reactDistance) + if (corpseDist < sPlayerbotAIConfig.reactDistance) { if (moveToLeader) moveToPos = leaderPos; @@ -162,7 +162,7 @@ bool FindCorpseAction::Execute(Event event) if (!botAI->AllowActivity(ALL_ACTIVITY)) { - uint32 delay = sServerFacade->GetDistance2d(bot, corpse) / + uint32 delay = ServerFacade::instance().GetDistance2d(bot, corpse) / bot->GetSpeed(MOVE_RUN); // Time a bot would take to travel to it's corpse. delay = std::min(delay, uint32(10 * MINUTE)); // Cap time to get to corpse at 10 minutes. @@ -308,7 +308,7 @@ bool SpiritHealerAction::Execute(Event event) GraveyardStruct const* ClosestGrave = GetGrave(dCount > 10 || deadTime > 15 * MINUTE || AI_VALUE(uint8, "durability") < 10); - if (bot->GetDistance2d(ClosestGrave->x, ClosestGrave->y) < sPlayerbotAIConfig->sightDistance) + if (bot->GetDistance2d(ClosestGrave->x, ClosestGrave->y) < sPlayerbotAIConfig.sightDistance) { GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++) diff --git a/src/Ai/Base/Actions/RpgAction.cpp b/src/Ai/Base/Actions/RpgAction.cpp index 6002eaf63a..919e25c58e 100644 --- a/src/Ai/Base/Actions/RpgAction.cpp +++ b/src/Ai/Base/Actions/RpgAction.cpp @@ -130,7 +130,7 @@ bool RpgAction::SetNextRpgAction() std::mt19937 gen(time(0)); - sTravelMgr->weighted_shuffle(actions.begin(), actions.end(), relevances.begin(), relevances.end(), gen); + TravelMgr::instance().weighted_shuffle(actions.begin(), actions.end(), relevances.begin(), relevances.end(), gen); Action* action = actions.front(); diff --git a/src/Ai/Base/Actions/RpgSubActions.cpp b/src/Ai/Base/Actions/RpgSubActions.cpp index aa4269fa92..43bff6ab83 100644 --- a/src/Ai/Base/Actions/RpgSubActions.cpp +++ b/src/Ai/Base/Actions/RpgSubActions.cpp @@ -77,9 +77,9 @@ void RpgHelper::setFacing(GuidPosition guidPosition) void RpgHelper::setDelay(bool waitForGroup) { if (!botAI->HasRealPlayerMaster() || (waitForGroup && botAI->GetGroupLeader() == bot && bot->GetGroup())) - botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.rpgDelay); else - botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay / 5); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.rpgDelay / 5); } bool RpgSubAction::isPossible() { return rpg->guidP() && rpg->guidP().GetWorldObject(); } @@ -392,7 +392,7 @@ bool RpgTradeUsefulAction::Execute(Event event) bot->Say("Start trade with" + chat->FormatWorldobject(player), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.rpgDelay); return true; } @@ -402,7 +402,7 @@ bool RpgTradeUsefulAction::Execute(Event event) bool RpgDuelAction::isUseful() { // do not offer duel in non pvp areas - if (sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId())) + if (sPlayerbotAIConfig.IsInPvpProhibitedZone(bot->GetZoneId())) return false; // Players can only fight a duel with each other outside (=not inside dungeons and not in capital cities) @@ -440,4 +440,4 @@ bool RpgMountAnimAction::Execute(Event event) bot->GetSession()->HandleMountSpecialAnimOpcode(p); return true; -} \ No newline at end of file +} diff --git a/src/Ai/Base/Actions/SayAction.cpp b/src/Ai/Base/Actions/SayAction.cpp index 00fae65293..050d7ddbba 100644 --- a/src/Ai/Base/Actions/SayAction.cpp +++ b/src/Ai/Base/Actions/SayAction.cpp @@ -134,7 +134,7 @@ bool SayAction::Execute(Event event) } // load text based on chance - if (!sPlayerbotTextMgr->GetBotText(qualifier, text, placeholders)) + if (!PlayerbotTextMgr::instance().GetBotText(qualifier, text, placeholders)) return false; if (text.find("/y ") == 0) @@ -206,7 +206,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint } //toxic links - if (msg.starts_with(sPlayerbotAIConfig->toxicLinksPrefix) + if (msg.starts_with(sPlayerbotAIConfig.toxicLinksPrefix) && (GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllItemIds(msg).size() > 0 || GET_PLAYERBOT_AI(bot)->GetChatHelper()->ExtractAllQuestIds(msg).size() > 0)) { HandleToxicLinksReply(bot, chatChannelSource, msg, name); @@ -230,7 +230,7 @@ bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chat const auto thunderfury = sObjectMgr->GetItemTemplate(19019); placeholders["%thunderfury_link"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(thunderfury); - std::string responseMessage = BOT_TEXT2("thunderfury_spam", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("thunderfury_spam", placeholders); switch (chatChannelSource) { @@ -271,8 +271,8 @@ bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatC std::vector botItems = GET_PLAYERBOT_AI(bot)->GetInventoryAndEquippedItems(); std::map placeholders; - placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : BOT_TEXT1("string_empty_link"); - placeholders["%prefix"] = sPlayerbotAIConfig->toxicLinksPrefix; + placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : PlayerbotTextMgr::instance().GetBotText("string_empty_link"); + placeholders["%prefix"] = sPlayerbotAIConfig.toxicLinksPrefix; if (incompleteQuests.size() > 0) { @@ -287,8 +287,8 @@ bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatC placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); AreaTableEntry const* current_area = GET_PLAYERBOT_AI(bot)->GetCurrentArea(); AreaTableEntry const* current_zone = GET_PLAYERBOT_AI(bot)->GetCurrentZone(); - placeholders["%area_name"] = current_area ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); @@ -297,17 +297,17 @@ bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatC { case ChatChannelSource::SRC_WORLD: { - GET_PLAYERBOT_AI(bot)->SayToWorld(BOT_TEXT2("suggest_toxic_links", placeholders)); + GET_PLAYERBOT_AI(bot)->SayToWorld(PlayerbotTextMgr::instance().GetBotText("suggest_toxic_links", placeholders)); break; } case ChatChannelSource::SRC_GENERAL: { - GET_PLAYERBOT_AI(bot)->SayToChannel(BOT_TEXT2("suggest_toxic_links", placeholders), ChatChannelId::GENERAL); + GET_PLAYERBOT_AI(bot)->SayToChannel(PlayerbotTextMgr::instance().GetBotText("suggest_toxic_links", placeholders), ChatChannelId::GENERAL); break; } case ChatChannelSource::SRC_GUILD: { - GET_PLAYERBOT_AI(bot)->SayToGuild(BOT_TEXT2("suggest_toxic_links", placeholders)); + GET_PLAYERBOT_AI(bot)->SayToGuild(PlayerbotTextMgr::instance().GetBotText("suggest_toxic_links", placeholders)); break; } default: @@ -343,8 +343,8 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha placeholders["%other_name"] = name; AreaTableEntry const* current_area = GET_PLAYERBOT_AI(bot)->GetCurrentArea(); AreaTableEntry const* current_zone = GET_PLAYERBOT_AI(bot)->GetCurrentZone(); - placeholders["%area_name"] = current_area ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); @@ -365,12 +365,12 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha //may reply to the same channel or whisper if (urand(0, 1)) { - std::string responseMessage = BOT_TEXT2("response_wtb_items_channel", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_wtb_items_channel", placeholders); GET_PLAYERBOT_AI(bot)->SayToWorld(responseMessage); } else { - std::string responseMessage = BOT_TEXT2("response_wtb_items_whisper", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_wtb_items_whisper", placeholders); GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); } break; @@ -380,12 +380,12 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha //may reply to the same channel or whisper if (urand(0, 1)) { - std::string responseMessage = BOT_TEXT2("response_wtb_items_channel", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_wtb_items_channel", placeholders); GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL); } else { - std::string responseMessage = BOT_TEXT2("response_wtb_items_whisper", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_wtb_items_whisper", placeholders); GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); } break; @@ -395,12 +395,12 @@ bool ChatReplyAction::HandleWTBItemsReply(Player* bot, ChatChannelSource chatCha //may reply to the same channel or whisper if (urand(0, 1)) { - std::string responseMessage = BOT_TEXT2("response_wtb_items_channel", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_wtb_items_channel", placeholders); GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::TRADE); } else { - std::string responseMessage = BOT_TEXT2("response_wtb_items_whisper", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_wtb_items_whisper", placeholders); GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); } break; @@ -438,8 +438,8 @@ bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatCh placeholders["%other_name"] = name; AreaTableEntry const* current_area = GET_PLAYERBOT_AI(bot)->GetCurrentArea(); AreaTableEntry const* current_zone = GET_PLAYERBOT_AI(bot)->GetCurrentZone(); - placeholders["%area_name"] = current_area ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); @@ -458,12 +458,12 @@ bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatCh //may reply to the same channel or whisper if (urand(0, 1)) { - std::string responseMessage = BOT_TEXT2("response_lfg_quests_channel", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_lfg_quests_channel", placeholders); GET_PLAYERBOT_AI(bot)->SayToWorld(responseMessage); } else { - std::string responseMessage = BOT_TEXT2("response_lfg_quests_whisper", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_lfg_quests_whisper", placeholders); GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); } break; @@ -473,12 +473,12 @@ bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatCh //may reply to the same channel or whisper if (urand(0, 1)) { - std::string responseMessage = BOT_TEXT2("response_lfg_quests_channel", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_lfg_quests_channel", placeholders); GET_PLAYERBOT_AI(bot)->SayToChannel(responseMessage, ChatChannelId::GENERAL); } else { - std::string responseMessage = BOT_TEXT2("response_lfg_quests_whisper", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_lfg_quests_whisper", placeholders); GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); } break; @@ -487,7 +487,7 @@ bool ChatReplyAction::HandleLFGQuestsReply(Player* bot, ChatChannelSource chatCh { //do not reply to the chat //may whisper - std::string responseMessage = BOT_TEXT2("response_lfg_quests_whisper", placeholders); + std::string responseMessage = PlayerbotTextMgr::instance().GetBotText("response_lfg_quests_whisper", placeholders); GET_PLAYERBOT_AI(bot)->Whisper(responseMessage, name); break; } @@ -1042,7 +1042,7 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco // load text if needed if (respondsText.empty()) { - respondsText = BOT_TEXT2(replyType, name); + respondsText = PlayerbotTextMgr::instance().GetBotText(replyType, name); } if (respondsText.size() > 255) diff --git a/src/Ai/Base/Actions/SecurityCheckAction.cpp b/src/Ai/Base/Actions/SecurityCheckAction.cpp index c47a6e52cb..ede59440e3 100644 --- a/src/Ai/Base/Actions/SecurityCheckAction.cpp +++ b/src/Ai/Base/Actions/SecurityCheckAction.cpp @@ -10,7 +10,7 @@ bool SecurityCheckAction::isUseful() { - return sRandomPlayerbotMgr->IsRandomBot(bot) && botAI->GetMaster() && + return sRandomPlayerbotMgr.IsRandomBot(bot) && botAI->GetMaster() && botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER && !GET_PLAYERBOT_AI(botAI->GetMaster()); } diff --git a/src/Ai/Base/Actions/SeeSpellAction.cpp b/src/Ai/Base/Actions/SeeSpellAction.cpp index 88848ca81f..bec9577cf6 100644 --- a/src/Ai/Base/Actions/SeeSpellAction.cpp +++ b/src/Ai/Base/Actions/SeeSpellAction.cpp @@ -20,7 +20,7 @@ Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, bool important) { float dist = wpOwner->GetDistance(x, y, z); - float delay = 1000.0f * dist / wpOwner->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay; + float delay = 1000.0f * dist / wpOwner->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig.reactDelay; if (!important) delay *= 0.25; @@ -61,7 +61,7 @@ bool SeeSpellAction::Execute(Event event) if (FISHING_SPELLS.find(spellId) != FISHING_SPELLS.end()) { - if (AI_VALUE(bool, "can fish") && sPlayerbotAIConfig->enableFishingWithMaster) + if (AI_VALUE(bool, "can fish") && sPlayerbotAIConfig.enableFishingWithMaster) { botAI->ChangeStrategy("+master fishing", BOT_STATE_NON_COMBAT); return true; diff --git a/src/Ai/Base/Actions/SendMailAction.cpp b/src/Ai/Base/Actions/SendMailAction.cpp index c6184c4b98..9be9d553ab 100644 --- a/src/Ai/Base/Actions/SendMailAction.cpp +++ b/src/Ai/Base/Actions/SendMailAction.cpp @@ -14,7 +14,7 @@ bool SendMailAction::Execute(Event event) { uint32 account = bot->GetSession()->GetAccountId(); - bool randomBot = sPlayerbotAIConfig->IsInRandomAccountList(account); + bool randomBot = sPlayerbotAIConfig.IsInRandomAccountList(account); GuidVector gos = *context->GetValue("nearest game objects"); bool mailboxFound = false; diff --git a/src/Ai/Base/Actions/ShareQuestAction.cpp b/src/Ai/Base/Actions/ShareQuestAction.cpp index d4b470b4c1..a9fe32c973 100644 --- a/src/Ai/Base/Actions/ShareQuestAction.cpp +++ b/src/Ai/Base/Actions/ShareQuestAction.cpp @@ -109,4 +109,4 @@ bool AutoShareQuestAction::Execute(Event event) bool AutoShareQuestAction::isUseful() { return bot->GetGroup() && !botAI->HasActivePlayerMaster(); -} \ No newline at end of file +} diff --git a/src/Ai/Base/Actions/StayActions.cpp b/src/Ai/Base/Actions/StayActions.cpp index 525ff8d53e..dbc7abd090 100644 --- a/src/Ai/Base/Actions/StayActions.cpp +++ b/src/Ai/Base/Actions/StayActions.cpp @@ -19,7 +19,7 @@ bool StayActionBase::Stay() if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) return false; - uint32 sitDelay = sPlayerbotAIConfig->sitDelay / 1000; + uint32 sitDelay = sPlayerbotAIConfig.sitDelay / 1000; time_t stayTime = AI_VALUE(time_t, "stay time"); time_t now = time(nullptr); if (!stayTime) @@ -48,7 +48,7 @@ bool StayAction::isUseful() if (stayPosition.isSet()) { const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); - if (sPlayerbotAIConfig->followDistance) + if (sPlayerbotAIConfig.followDistance) { return false; } diff --git a/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp b/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp index 159bfd4ebb..9012443beb 100644 --- a/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp +++ b/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp @@ -54,7 +54,7 @@ SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string con bool SuggestWhatToDoAction::isUseful() { - if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId() || bot->GetBattleground()) + if (!sRandomPlayerbotMgr.IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId() || bot->GetBattleground()) return false; std::string qualifier = "suggest what to do"; @@ -140,7 +140,7 @@ void SuggestWhatToDoAction::grindMaterials() placeholders["%role"] = chat->formatClass(bot, AiFactory::GetPlayerSpecTab(bot)); placeholders["%category"] = item; - spam(BOT_TEXT2("suggest_trade", placeholders), urand(0, 1) ? 0x3C : 0x18, !urand(0, 2), !urand(0, + spam(PlayerbotTextMgr::instance().GetBotText("suggest_trade", placeholders), urand(0, 1) ? 0x3C : 0x18, !urand(0, 2), !urand(0, 3)); return; } } @@ -260,9 +260,9 @@ SuggestDungeonAction::SuggestDungeonAction(PlayerbotAI* botAI) : SuggestWhatToDo bool SuggestDungeonAction::Execute(Event event) { - // TODO: use sPlayerbotDungeonRepository + // TODO: use PlayerbotDungeonRepository::instance() - if (!sPlayerbotAIConfig->randomBotSuggestDungeons || bot->GetGroup()) + if (!sPlayerbotAIConfig.randomBotSuggestDungeons || bot->GetGroup()) return false; if (instances.empty()) @@ -373,7 +373,7 @@ bool SuggestTradeAction::Execute(Event event) if (!proto) return false; - uint32 price = proto->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot) * count; + uint32 price = proto->SellPrice * sRandomPlayerbotMgr.GetSellMultiplier(bot) * count; if (!price) return false; diff --git a/src/Ai/Base/Actions/TalkToQuestGiverAction.cpp b/src/Ai/Base/Actions/TalkToQuestGiverAction.cpp index 7f791d0d63..8fd120d605 100644 --- a/src/Ai/Base/Actions/TalkToQuestGiverAction.cpp +++ b/src/Ai/Base/Actions/TalkToQuestGiverAction.cpp @@ -24,7 +24,7 @@ bool TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver QuestStatus status = bot->GetQuestStatus(quest->GetQuestId()); Player* master = GetMaster(); - if (sPlayerbotAIConfig->syncQuestForPlayer && master) + if (sPlayerbotAIConfig.syncQuestForPlayer && master) { PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master); if (!masterBotAI || masterBotAI->IsRealPlayer()) @@ -35,7 +35,7 @@ bool TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver } } - if (sPlayerbotAIConfig->syncQuestWithPlayer) + if (sPlayerbotAIConfig.syncQuestWithPlayer) { if (master && master->GetQuestStatus(quest->GetQuestId()) == QUEST_STATUS_COMPLETE && (status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_FAILED)) @@ -105,14 +105,14 @@ void TalkToQuestGiverAction::RewardNoItem(Quest const* quest, Object* questGiver if (bot->CanRewardQuest(quest, false)) { - out << BOT_TEXT2("quest_status_completed", args); + out << PlayerbotTextMgr::instance().GetBotText("quest_status_completed", args); BroadcastHelper::BroadcastQuestTurnedIn(botAI, bot, quest); bot->RewardQuest(quest, 0, questGiver, false); } else { - out << BOT_TEXT2("quest_status_unable_to_complete", args); + out << PlayerbotTextMgr::instance().GetBotText("quest_status_unable_to_complete", args); } } @@ -126,13 +126,13 @@ void TalkToQuestGiverAction::RewardSingleItem(Quest const* quest, Object* questG if (bot->CanRewardQuest(quest, index, false)) { - out << BOT_TEXT2("quest_status_complete_single_reward", args); + out << PlayerbotTextMgr::instance().GetBotText("quest_status_complete_single_reward", args); BroadcastHelper::BroadcastQuestTurnedIn(botAI, bot, quest); bot->RewardQuest(quest, index, questGiver, true); } else { - out << BOT_TEXT2("quest_status_unable_to_complete", args); + out << PlayerbotTextMgr::instance().GetBotText("quest_status_unable_to_complete", args); } } @@ -171,7 +171,7 @@ void TalkToQuestGiverAction::RewardMultipleItem(Quest const* quest, Object* ques std::set bestIds; std::ostringstream outid; - if (!botAI->IsAlt() || sPlayerbotAIConfig->autoPickReward == "yes") + if (!botAI->IsAlt() || sPlayerbotAIConfig.autoPickReward == "yes") { bestIds = BestRewards(quest); if (!bestIds.empty()) @@ -198,7 +198,7 @@ void TalkToQuestGiverAction::RewardMultipleItem(Quest const* quest, Object* ques AskToSelectReward(quest, out, true); } } - else if (sPlayerbotAIConfig->autoPickReward == "no") + else if (sPlayerbotAIConfig.autoPickReward == "no") { // Old functionality, list rewards. AskToSelectReward(quest, out, false); @@ -260,7 +260,7 @@ bool TurnInQueryQuestAction::Execute(Event event) QuestStatus status = bot->GetQuestStatus(quest->GetQuestId()); Player* master = GetMaster(); - if (sPlayerbotAIConfig->syncQuestForPlayer && master) + if (sPlayerbotAIConfig.syncQuestForPlayer && master) { PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master); if (!masterBotAI || masterBotAI->IsRealPlayer()) @@ -271,7 +271,7 @@ bool TurnInQueryQuestAction::Execute(Event event) } } - if (sPlayerbotAIConfig->syncQuestWithPlayer) + if (sPlayerbotAIConfig.syncQuestWithPlayer) { if (status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_FAILED) { diff --git a/src/Ai/Base/Actions/TellLosAction.cpp b/src/Ai/Base/Actions/TellLosAction.cpp index 9cd12b02e3..13852bdc0a 100644 --- a/src/Ai/Base/Actions/TellLosAction.cpp +++ b/src/Ai/Base/Actions/TellLosAction.cpp @@ -152,4 +152,4 @@ bool TellCalculateItemAction::Execute(Event event) out << "Calculated score of " << chat->FormatItem(proto) << " : " << score; botAI->TellMasterNoFacing(out.str()); return true; -} \ No newline at end of file +} diff --git a/src/Ai/Base/Actions/TradeAction.cpp b/src/Ai/Base/Actions/TradeAction.cpp index ad81d9f93b..b6f6912be2 100644 --- a/src/Ai/Base/Actions/TradeAction.cpp +++ b/src/Ai/Base/Actions/TradeAction.cpp @@ -15,7 +15,7 @@ bool TradeAction::Execute(Event event) std::string const text = event.getParam(); // If text starts with any excluded prefix, don't process it further. - for (auto const& prefix : sPlayerbotAIConfig->tradeActionExcludedPrefixes) + for (auto const& prefix : sPlayerbotAIConfig.tradeActionExcludedPrefixes) { if (text.find(prefix) == 0) return false; diff --git a/src/Ai/Base/Actions/TradeStatusAction.cpp b/src/Ai/Base/Actions/TradeStatusAction.cpp index 16ad2ca79d..3057fff097 100644 --- a/src/Ai/Base/Actions/TradeStatusAction.cpp +++ b/src/Ai/Base/Actions/TradeStatusAction.cpp @@ -32,7 +32,7 @@ bool TradeStatusAction::Execute(Event event) return false; } - if (sPlayerbotAIConfig->enableRandomBotTrading == 0 && (sRandomPlayerbotMgr->IsRandomBot(bot)|| sRandomPlayerbotMgr->IsAddclassBot(bot))) + if (sPlayerbotAIConfig.enableRandomBotTrading == 0 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot))) { bot->Whisper("Trading is disabled", LANG_UNIVERSAL, trader); return false; @@ -61,7 +61,7 @@ bool TradeStatusAction::Execute(Event event) uint32 status = 0; p << status; - uint32 discount = sRandomPlayerbotMgr->GetTradeDiscount(bot, trader); + uint32 discount = sRandomPlayerbotMgr.GetTradeDiscount(bot, trader); if (CheckTrade()) { int32 botMoney = CalculateCost(bot, true); @@ -81,7 +81,7 @@ bool TradeStatusAction::Execute(Event event) bot->GetSession()->HandleAcceptTradeOpcode(p); if (bot->GetTradeData()) { - sRandomPlayerbotMgr->SetTradeDiscount(bot, trader, discount); + sRandomPlayerbotMgr.SetTradeDiscount(bot, trader, discount); return false; } @@ -96,7 +96,7 @@ bool TradeStatusAction::Execute(Event event) craftData.AddObtained(itemId, count); } - sGuildTaskMgr->CheckItemTask(itemId, count, trader, bot); + GuildTaskMgr::instance().CheckItemTask(itemId, count, trader, bot); } for (std::map::iterator i = takenItemIds.begin(); i != takenItemIds.end(); ++i) @@ -116,7 +116,7 @@ bool TradeStatusAction::Execute(Event event) } else if (status == TRADE_STATUS_BEGIN_TRADE) { - if (!bot->HasInArc(CAST_ANGLE_IN_FRONT, trader, sPlayerbotAIConfig->sightDistance)) + if (!bot->HasInArc(CAST_ANGLE_IN_FRONT, trader, sPlayerbotAIConfig.sightDistance)) bot->SetFacingToObject(trader); BeginTrade(); @@ -141,9 +141,9 @@ void TradeStatusAction::BeginTrade() botAI->TellMaster("=== Inventory ==="); TellItems(visitor.items, visitor.soulbound); - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sRandomPlayerbotMgr.IsRandomBot(bot)) { - uint32 discount = sRandomPlayerbotMgr->GetTradeDiscount(bot, botAI->GetMaster()); + uint32 discount = sRandomPlayerbotMgr.GetTradeDiscount(bot, botAI->GetMaster()); if (discount) { std::ostringstream out; @@ -198,7 +198,7 @@ bool TradeStatusAction::CheckTrade() return false; } uint32 accountId = bot->GetSession()->GetAccountId(); - if (!sPlayerbotAIConfig->IsInRandomAccountList(accountId)) + if (!sPlayerbotAIConfig.IsInRandomAccountList(accountId)) { int32 botItemsMoney = CalculateCost(bot, true); int32 botMoney = bot->GetTradeData()->GetMoney() + botItemsMoney; @@ -214,12 +214,12 @@ bool TradeStatusAction::CheckTrade() int32 botMoney = bot->GetTradeData()->GetMoney() + botItemsMoney; int32 playerItemsMoney = CalculateCost(trader, false); int32 playerMoney = trader->GetTradeData()->GetMoney() + playerItemsMoney; - if (botItemsMoney > 0 && sPlayerbotAIConfig->enableRandomBotTrading == 2 && (sRandomPlayerbotMgr->IsRandomBot(bot)|| sRandomPlayerbotMgr->IsAddclassBot(bot))) + if (botItemsMoney > 0 && sPlayerbotAIConfig.enableRandomBotTrading == 2 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot))) { bot->Whisper("Selling is disabled.", LANG_UNIVERSAL, trader); return false; } - if (playerItemsMoney && sPlayerbotAIConfig->enableRandomBotTrading == 3 && (sRandomPlayerbotMgr->IsRandomBot(bot)|| sRandomPlayerbotMgr->IsAddclassBot(bot))) + if (playerItemsMoney && sPlayerbotAIConfig.enableRandomBotTrading == 3 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot))) { bot->Whisper("Buying is disabled.", LANG_UNIVERSAL, trader); return false; @@ -262,7 +262,7 @@ bool TradeStatusAction::CheckTrade() return false; } - int32 discount = (int32)sRandomPlayerbotMgr->GetTradeDiscount(bot, trader); + int32 discount = (int32)sRandomPlayerbotMgr.GetTradeDiscount(bot, trader); int32 delta = playerMoney - botMoney; int32 moneyDelta = (int32)trader->GetTradeData()->GetMoney() - (int32)bot->GetTradeData()->GetMoney(); bool success = false; @@ -287,7 +287,7 @@ bool TradeStatusAction::CheckTrade() if (success) { - sRandomPlayerbotMgr->AddTradeDiscount(bot, trader, delta); + sRandomPlayerbotMgr.AddTradeDiscount(bot, trader, delta); switch (urand(0, 4)) { case 0: @@ -353,11 +353,11 @@ int32 TradeStatusAction::CalculateCost(Player* player, bool sell) if (sell) { - sum += item->GetCount() * proto->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot); + sum += item->GetCount() * proto->SellPrice * sRandomPlayerbotMgr.GetSellMultiplier(bot); } else { - sum += item->GetCount() * proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot); + sum += item->GetCount() * proto->BuyPrice * sRandomPlayerbotMgr.GetBuyMultiplier(bot); } } diff --git a/src/Ai/Base/Actions/TrainerAction.cpp b/src/Ai/Base/Actions/TrainerAction.cpp index bca7406012..e377fa3eb2 100644 --- a/src/Ai/Base/Actions/TrainerAction.cpp +++ b/src/Ai/Base/Actions/TrainerAction.cpp @@ -12,7 +12,7 @@ void TrainerAction::Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg) { - if (sPlayerbotAIConfig->autoTrainSpells != "free" && !botAI->HasCheat(BotCheatMask::gold)) + if (sPlayerbotAIConfig.autoTrainSpells != "free" && !botAI->HasCheat(BotCheatMask::gold)) { if (AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) < cost) { @@ -126,8 +126,8 @@ bool TrainerAction::Execute(Event event) if (spell) spells.insert(spell); - if (text.find("learn") != std::string::npos || sRandomPlayerbotMgr->IsRandomBot(bot) || - (sPlayerbotAIConfig->autoTrainSpells != "no" && + if (text.find("learn") != std::string::npos || sRandomPlayerbotMgr.IsRandomBot(bot) || + (sPlayerbotAIConfig.autoTrainSpells != "no" && (trainer->GetTrainerType() != Trainer::Type::Tradeskill || !botAI->HasActivePlayerMaster()))) // Todo rewrite to only exclude start primary profession skills and make // config dependent. @@ -157,7 +157,7 @@ void TrainerAction::TellFooter(uint32 totalCost) bool MaintenanceAction::Execute(Event event) { - if (!sPlayerbotAIConfig->maintenanceCommand) + if (!sPlayerbotAIConfig.maintenanceCommand) { botAI->TellError("maintenance command is not allowed, please check the configuration."); return false; @@ -186,66 +186,66 @@ bool MaintenanceAction::Execute(Event event) factory.InitMounts(); factory.InitGlyphs(false); factory.InitKeyring(); - if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) factory.ApplyEnchantAndGemsNew(); } else { - if (sPlayerbotAIConfig->altMaintenanceAttunementQs) + if (sPlayerbotAIConfig.altMaintenanceAttunementQs) factory.InitAttunementQuests(); - if (sPlayerbotAIConfig->altMaintenanceBags) + if (sPlayerbotAIConfig.altMaintenanceBags) factory.InitBags(false); - if (sPlayerbotAIConfig->altMaintenanceAmmo) + if (sPlayerbotAIConfig.altMaintenanceAmmo) factory.InitAmmo(); - if (sPlayerbotAIConfig->altMaintenanceFood) + if (sPlayerbotAIConfig.altMaintenanceFood) factory.InitFood(); - if (sPlayerbotAIConfig->altMaintenanceReagents) + if (sPlayerbotAIConfig.altMaintenanceReagents) factory.InitReagents(); - if (sPlayerbotAIConfig->altMaintenanceConsumables) + if (sPlayerbotAIConfig.altMaintenanceConsumables) factory.InitConsumables(); - if (sPlayerbotAIConfig->altMaintenancePotions) + if (sPlayerbotAIConfig.altMaintenancePotions) factory.InitPotions(); - if (sPlayerbotAIConfig->altMaintenanceTalentTree) + if (sPlayerbotAIConfig.altMaintenanceTalentTree) factory.InitTalentsTree(true); - if (sPlayerbotAIConfig->altMaintenancePet) + if (sPlayerbotAIConfig.altMaintenancePet) factory.InitPet(); - if (sPlayerbotAIConfig->altMaintenancePetTalents) + if (sPlayerbotAIConfig.altMaintenancePetTalents) factory.InitPetTalents(); - if (sPlayerbotAIConfig->altMaintenanceSkills) + if (sPlayerbotAIConfig.altMaintenanceSkills) factory.InitSkills(); - if (sPlayerbotAIConfig->altMaintenanceClassSpells) + if (sPlayerbotAIConfig.altMaintenanceClassSpells) factory.InitClassSpells(); - if (sPlayerbotAIConfig->altMaintenanceAvailableSpells) + if (sPlayerbotAIConfig.altMaintenanceAvailableSpells) factory.InitAvailableSpells(); - if (sPlayerbotAIConfig->altMaintenanceReputation) + if (sPlayerbotAIConfig.altMaintenanceReputation) factory.InitReputation(); - if (sPlayerbotAIConfig->altMaintenanceSpecialSpells) + if (sPlayerbotAIConfig.altMaintenanceSpecialSpells) factory.InitSpecialSpells(); - if (sPlayerbotAIConfig->altMaintenanceMounts) + if (sPlayerbotAIConfig.altMaintenanceMounts) factory.InitMounts(); - if (sPlayerbotAIConfig->altMaintenanceGlyphs) + if (sPlayerbotAIConfig.altMaintenanceGlyphs) factory.InitGlyphs(false); - if (sPlayerbotAIConfig->altMaintenanceKeyring) + if (sPlayerbotAIConfig.altMaintenanceKeyring) factory.InitKeyring(); - if (sPlayerbotAIConfig->altMaintenanceGemsEnchants && bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) + if (sPlayerbotAIConfig.altMaintenanceGemsEnchants && bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) factory.ApplyEnchantAndGemsNew(); } @@ -267,28 +267,28 @@ bool RemoveGlyphAction::Execute(Event event) bool AutoGearAction::Execute(Event event) { - if (!sPlayerbotAIConfig->autoGearCommand) + if (!sPlayerbotAIConfig.autoGearCommand) { botAI->TellError("autogear command is not allowed, please check the configuration."); return false; } - if (!sPlayerbotAIConfig->autoGearCommandAltBots && - !sPlayerbotAIConfig->IsInRandomAccountList(bot->GetSession()->GetAccountId())) + if (!sPlayerbotAIConfig.autoGearCommandAltBots && + !sPlayerbotAIConfig.IsInRandomAccountList(bot->GetSession()->GetAccountId())) { botAI->TellError("You cannot use autogear on alt bots."); return false; } botAI->TellMaster("I'm auto gearing"); - uint32 gs = sPlayerbotAIConfig->autoGearScoreLimit == 0 + uint32 gs = sPlayerbotAIConfig.autoGearScoreLimit == 0 ? 0 - : PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig->autoGearScoreLimit, - sPlayerbotAIConfig->autoGearQualityLimit); - PlayerbotFactory factory(bot, bot->GetLevel(), sPlayerbotAIConfig->autoGearQualityLimit, gs); + : PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig.autoGearScoreLimit, + sPlayerbotAIConfig.autoGearQualityLimit); + PlayerbotFactory factory(bot, bot->GetLevel(), sPlayerbotAIConfig.autoGearQualityLimit, gs); factory.InitEquipment(true); factory.InitAmmo(); - if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) { factory.ApplyEnchantAndGemsNew(); } diff --git a/src/Ai/Base/Actions/TravelAction.cpp b/src/Ai/Base/Actions/TravelAction.cpp index a290605718..f0afef01bf 100644 --- a/src/Ai/Base/Actions/TravelAction.cpp +++ b/src/Ai/Base/Actions/TravelAction.cpp @@ -21,9 +21,9 @@ bool TravelAction::Execute(Event event) Unit* newTarget = nullptr; std::list targets; - Acore::AnyUnitInObjectRangeCheck u_check(bot, sPlayerbotAIConfig->sightDistance * 2); + Acore::AnyUnitInObjectRangeCheck u_check(bot, sPlayerbotAIConfig.sightDistance * 2); Acore::UnitListSearcher searcher(bot, targets, u_check); - Cell::VisitObjects(bot, searcher, sPlayerbotAIConfig->sightDistance); + Cell::VisitObjects(bot, searcher, sPlayerbotAIConfig.sightDistance); for (Unit* unit : targets) { @@ -77,7 +77,7 @@ bool MoveToDarkPortalAction::Execute(Event event) if (bot->GetTeamId() == TEAM_ALLIANCE) { Quest const* quest = sObjectMgr->GetQuestTemplate(10119); - CreatureData const* creatureData = sRandomPlayerbotMgr->GetCreatureDataByEntry(16841); + CreatureData const* creatureData = sRandomPlayerbotMgr.GetCreatureDataByEntry(16841); if (quest && creatureData) { auto creatureBounds = @@ -89,7 +89,7 @@ bool MoveToDarkPortalAction::Execute(Event event) else { Quest const* quest = sObjectMgr->GetQuestTemplate(9407); - CreatureData const* creatureData = sRandomPlayerbotMgr->GetCreatureDataByEntry(19254); + CreatureData const* creatureData = sRandomPlayerbotMgr.GetCreatureDataByEntry(19254); if (quest && creatureData) { auto creatureBounds = diff --git a/src/Ai/Base/Actions/UseItemAction.cpp b/src/Ai/Base/Actions/UseItemAction.cpp index bfb86ef0bd..473816e3ec 100644 --- a/src/Ai/Base/Actions/UseItemAction.cpp +++ b/src/Ai/Base/Actions/UseItemAction.cpp @@ -187,7 +187,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni if (bot->isMoving()) { bot->StopMoving(); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); return false; } @@ -229,7 +229,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni out << " on " << chat->FormatItem(itemForSpell->GetTemplate()); } uint32 castTime = spellInfo->CalcCastTime(); - botAI->SetNextCheckDelay(castTime + sPlayerbotAIConfig->reactDelay); + botAI->SetNextCheckDelay(castTime + sPlayerbotAIConfig.reactDelay); } break; @@ -307,7 +307,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni if (!spellId) return false; - // botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); + // botAI->SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); botAI->TellMasterNoFacing(out.str()); bot->GetSession()->HandleUseItemOpcode(packet); return true; @@ -486,7 +486,7 @@ bool UseRandomQuestItem::Execute(Event event) bool used = UseItem(item, goTarget, nullptr, unitTarget); if (used) - botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); + botAI->SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); return used; } diff --git a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp index ec6a060ab1..b334429189 100644 --- a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp +++ b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp @@ -91,9 +91,9 @@ bool SummonAction::Execute(Event event) bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserveAuras) { std::list targets; - AnyGameObjectInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance); + AnyGameObjectInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig.sightDistance); Acore::GameObjectListSearcher searcher(summoner, targets, u_check); - Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance); + Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig.sightDistance); for (GameObject* go : targets) { @@ -107,13 +107,13 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserv bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras) { - if (!sPlayerbotAIConfig->summonAtInnkeepersEnabled) + if (!sPlayerbotAIConfig.summonAtInnkeepersEnabled) return false; std::list targets; - Acore::AnyUnitInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance); + Acore::AnyUnitInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig.sightDistance); Acore::UnitListSearcher searcher(summoner, targets, u_check); - Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig->sightDistance); + Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig.sightDistance); for (Unit* unit : targets) { @@ -165,38 +165,37 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras for (float angle = followAngle - M_PI; angle <= followAngle + M_PI; angle += M_PI / 4) { uint32 mapId = summoner->GetMapId(); - float x = summoner->GetPositionX() + cos(angle) * sPlayerbotAIConfig->followDistance; - float y = summoner->GetPositionY() + sin(angle) * sPlayerbotAIConfig->followDistance; + float x = summoner->GetPositionX() + cos(angle) * sPlayerbotAIConfig.followDistance; + float y = summoner->GetPositionY() + sin(angle) * sPlayerbotAIConfig.followDistance; float z = summoner->GetPositionZ(); if (summoner->IsWithinLOS(x, y, z)) { - if (sPlayerbotAIConfig - ->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on + if (sPlayerbotAIConfig.botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on bot->DurabilityRepairAll(false, 1.0f, false); - if (summoner->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat) + if (summoner->IsInCombat() && !sPlayerbotAIConfig.allowSummonInCombat) { botAI->TellError("You cannot summon me while you're in combat"); return false; } - if (!summoner->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead) + if (!summoner->IsAlive() && !sPlayerbotAIConfig.allowSummonWhenMasterIsDead) { botAI->TellError("You cannot summon me while you're dead"); return false; } if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) && - !sPlayerbotAIConfig->allowSummonWhenBotIsDead) + !sPlayerbotAIConfig.allowSummonWhenBotIsDead) { botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first"); return false; } bool revive = - sPlayerbotAIConfig->reviveBotWhenSummoned == 2 || - (sPlayerbotAIConfig->reviveBotWhenSummoned == 1 && !summoner->IsInCombat() && summoner->IsAlive()); + sPlayerbotAIConfig.reviveBotWhenSummoned == 2 || + (sPlayerbotAIConfig.reviveBotWhenSummoned == 1 && !summoner->IsInCombat() && summoner->IsAlive()); if (bot->isDead() && revive) { diff --git a/src/Ai/Base/Actions/VehicleActions.cpp b/src/Ai/Base/Actions/VehicleActions.cpp index ee1c6e2dd8..ac925fbcb1 100644 --- a/src/Ai/Base/Actions/VehicleActions.cpp +++ b/src/Ai/Base/Actions/VehicleActions.cpp @@ -74,7 +74,7 @@ bool EnterVehicleAction::Execute(Event event) bool EnterVehicleAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) { - float dist = sServerFacade->GetDistance2d(bot, vehicleBase); + float dist = ServerFacade::instance().GetDistance2d(bot, vehicleBase); if (dist > 40.0f) return false; diff --git a/src/Ai/Base/Actions/WhoAction.cpp b/src/Ai/Base/Actions/WhoAction.cpp index edafdfcb27..8f83b6e489 100644 --- a/src/Ai/Base/Actions/WhoAction.cpp +++ b/src/Ai/Base/Actions/WhoAction.cpp @@ -33,7 +33,7 @@ bool WhoAction::Execute(Event event) { out << QuerySkill(text); - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sRandomPlayerbotMgr.IsRandomBot(bot)) out << QueryTrade(text); } else @@ -74,7 +74,7 @@ std::string const WhoAction::QueryTrade(std::string const text) for (Item* sell : items) { int32 sellPrice = - sell->GetTemplate()->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot) * sell->GetCount(); + sell->GetTemplate()->SellPrice * sRandomPlayerbotMgr.GetSellMultiplier(bot) * sell->GetCount(); if (!sellPrice) continue; diff --git a/src/Ai/Base/Actions/WorldBuffAction.cpp b/src/Ai/Base/Actions/WorldBuffAction.cpp index 3cd770e4d0..b2bf4e096f 100644 --- a/src/Ai/Base/Actions/WorldBuffAction.cpp +++ b/src/Ai/Base/Actions/WorldBuffAction.cpp @@ -25,7 +25,7 @@ std::vector WorldBuffAction::NeedWorldBuffs(Unit* unit) { std::vector retVec; - if (sPlayerbotAIConfig->worldBuffs.empty()) + if (sPlayerbotAIConfig.worldBuffs.empty()) return retVec; FactionTemplateEntry const* humanFaction = sFactionTemplateStore.LookupEntry(1); @@ -70,7 +70,7 @@ std::vector WorldBuffAction::NeedWorldBuffs(Unit* unit) // If tank, effectiveSpec remains unchanged } - for (auto const& wb : sPlayerbotAIConfig->worldBuffs) + for (auto const& wb : sPlayerbotAIConfig.worldBuffs) { // Faction check if (wb.factionId != 0 && wb.factionId != factionId) diff --git a/src/Ai/Base/Actions/WtsAction.cpp b/src/Ai/Base/Actions/WtsAction.cpp index a3c77c27f9..950ab7d05f 100644 --- a/src/Ai/Base/Actions/WtsAction.cpp +++ b/src/Ai/Base/Actions/WtsAction.cpp @@ -20,7 +20,7 @@ bool WtsAction::Execute(Event event) std::ostringstream out; std::string const text = event.getParam(); - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return false; std::string const link = event.getParam(); @@ -42,7 +42,7 @@ bool WtsAction::Execute(Event event) if (usage == ITEM_USAGE_NONE) continue; - int32 buyPrice = proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot); + int32 buyPrice = proto->BuyPrice * sRandomPlayerbotMgr.GetBuyMultiplier(bot); if (!buyPrice) continue; diff --git a/src/Ai/Base/Actions/XpGainAction.cpp b/src/Ai/Base/Actions/XpGainAction.cpp index 4048ce3af8..fba57180f2 100644 --- a/src/Ai/Base/Actions/XpGainAction.cpp +++ b/src/Ai/Base/Actions/XpGainAction.cpp @@ -40,14 +40,14 @@ bool XpGainAction::Execute(Event event) } // randomBotXPRate is now implemented in OnPlayerGiveXP script - // if (!sRandomPlayerbotMgr->IsRandomBot(bot) || sPlayerbotAIConfig->randomBotXPRate == 1) + // if (!sRandomPlayerbotMgr.IsRandomBot(bot) || sPlayerbotAIConfig.randomBotXPRate == 1) // return true; // Unit* victim = nullptr; // if (guid) // victim = botAI->GetUnit(guid); - // xpgain = xpgain * (sPlayerbotAIConfig->randomBotXPRate - 1); + // xpgain = xpgain * (sPlayerbotAIConfig.randomBotXPRate - 1); // GiveXP(xpgain, victim); return true; diff --git a/src/Ai/Base/SharedValueContext.h b/src/Ai/Base/SharedValueContext.h index 23b8266bdd..94653aeabf 100644 --- a/src/Ai/Base/SharedValueContext.h +++ b/src/Ai/Base/SharedValueContext.h @@ -12,39 +12,14 @@ #include "PvpValues.h" #include "QuestValues.h" -class PlayerbotAI; - class SharedValueContext : public NamedObjectContext { public: - SharedValueContext() : NamedObjectContext(true) - { - creators["bg masters"] = &SharedValueContext::bg_masters; - creators["drop map"] = &SharedValueContext::drop_map; - creators["item drop list"] = &SharedValueContext::item_drop_list; - creators["entry loot list"] = &SharedValueContext::entry_loot_list; - - creators["entry quest relation"] = &SharedValueContext::entry_quest_relation; - creators["quest guidp map"] = &SharedValueContext::quest_guidp_map; - creators["quest givers"] = &SharedValueContext::quest_givers; - } - -private: - static UntypedValue* bg_masters(PlayerbotAI* botAI) { return new BgMastersValue(botAI); } - static UntypedValue* drop_map(PlayerbotAI* botAI) { return new DropMapValue(botAI); } - static UntypedValue* item_drop_list(PlayerbotAI* botAI) { return new ItemDropListValue(botAI); } - static UntypedValue* entry_loot_list(PlayerbotAI* botAI) { return new EntryLootListValue(botAI); } - - static UntypedValue* entry_quest_relation(PlayerbotAI* botAI) { return new EntryQuestRelationMapValue(botAI); } - static UntypedValue* quest_guidp_map(PlayerbotAI* botAI) { return new QuestGuidpMapValue(botAI); } - static UntypedValue* quest_givers(PlayerbotAI* botAI) { return new QuestGiversValue(botAI); } - - // Global acess functions -public: - static SharedValueContext* instance() + static SharedValueContext& instance() { static SharedValueContext instance; - return &instance; + + return instance; } template @@ -74,6 +49,36 @@ class SharedValueContext : public NamedObjectContext out << param; return getGlobalValue(name, out.str()); } + +private: + SharedValueContext() : NamedObjectContext(true) + { + creators["bg masters"] = &SharedValueContext::bg_masters; + creators["drop map"] = &SharedValueContext::drop_map; + creators["item drop list"] = &SharedValueContext::item_drop_list; + creators["entry loot list"] = &SharedValueContext::entry_loot_list; + + creators["entry quest relation"] = &SharedValueContext::entry_quest_relation; + creators["quest guidp map"] = &SharedValueContext::quest_guidp_map; + creators["quest givers"] = &SharedValueContext::quest_givers; + } + ~SharedValueContext() = default; + + SharedValueContext(const SharedValueContext&) = delete; + SharedValueContext& operator=(const SharedValueContext&) = delete; + + SharedValueContext(SharedValueContext&&) = delete; + SharedValueContext& operator=(SharedValueContext&&) = delete; + + static UntypedValue* bg_masters(PlayerbotAI* botAI) { return new BgMastersValue(botAI); } + static UntypedValue* drop_map(PlayerbotAI* botAI) { return new DropMapValue(botAI); } + static UntypedValue* item_drop_list(PlayerbotAI* botAI) { return new ItemDropListValue(botAI); } + static UntypedValue* entry_loot_list(PlayerbotAI* botAI) { return new EntryLootListValue(botAI); } + + static UntypedValue* entry_quest_relation(PlayerbotAI* botAI) { return new EntryQuestRelationMapValue(botAI); } + static UntypedValue* quest_guidp_map(PlayerbotAI* botAI) { return new QuestGuidpMapValue(botAI); } + static UntypedValue* quest_givers(PlayerbotAI* botAI) { return new QuestGiversValue(botAI); } + }; #define sSharedValueContext SharedValueContext::instance() diff --git a/src/Ai/Base/Strategy/CastTimeStrategy.cpp b/src/Ai/Base/Strategy/CastTimeStrategy.cpp index 042d1c9e41..7c70923d40 100644 --- a/src/Ai/Base/Strategy/CastTimeStrategy.cpp +++ b/src/Ai/Base/Strategy/CastTimeStrategy.cpp @@ -16,7 +16,7 @@ float CastTimeMultiplier::GetValue(Action* action) if (!action->GetTarget() || action->GetTarget() != AI_VALUE(Unit*, "current target")) return 1.0f; - if (/*targetHealth < sPlayerbotAIConfig->criticalHealth && */ dynamic_cast(action)) + if (/*targetHealth < sPlayerbotAIConfig.criticalHealth && */ dynamic_cast(action)) { CastSpellAction* spellAction = dynamic_cast(action); uint32 spellId = AI_VALUE2(uint32, "spell id", spellAction->getSpell()); diff --git a/src/Ai/Base/Strategy/ConserveManaStrategy.cpp b/src/Ai/Base/Strategy/ConserveManaStrategy.cpp index adc3cec884..3acabcd4de 100644 --- a/src/Ai/Base/Strategy/ConserveManaStrategy.cpp +++ b/src/Ai/Base/Strategy/ConserveManaStrategy.cpp @@ -19,9 +19,9 @@ // uint8 targetHealth = AI_VALUE2(uint8, "health", "current target"); // uint8 mana = AI_VALUE2(uint8, "mana", "self target"); // bool hasMana = AI_VALUE2(bool, "has mana", "self target"); -// bool mediumMana = hasMana && mana < sPlayerbotAIConfig->mediumMana; +// bool mediumMana = hasMana && mana < sPlayerbotAIConfig.mediumMana; -// if (health < sPlayerbotAIConfig->lowHealth) +// if (health < sPlayerbotAIConfig.lowHealth) // return 1.0f; // Unit* target = AI_VALUE(Unit*, "current target"); @@ -92,7 +92,7 @@ float HealerAutoSaveManaMultiplier::GetValue(Action* action) { uint8 mana = bot->GetPowerPct(Powers::POWER_MANA); - if (mana > sPlayerbotAIConfig->saveManaThreshold) + if (mana > sPlayerbotAIConfig.saveManaThreshold) return 1.0f; CastHealingSpellAction* healingAction = dynamic_cast(action); @@ -110,16 +110,16 @@ float HealerAutoSaveManaMultiplier::GetValue(Action* action) if (isTank) { estAmount /= 1.5; // tanks have more health - if (health >= sPlayerbotAIConfig->mediumHealth && + if (health >= sPlayerbotAIConfig.mediumHealth && (lossAmount < estAmount || manaEfficiency <= HealingManaEfficiency::MEDIUM)) return 0.0f; - if (health >= sPlayerbotAIConfig->lowHealth && + if (health >= sPlayerbotAIConfig.lowHealth && (lossAmount < estAmount || manaEfficiency <= HealingManaEfficiency::LOW)) return 0.0f; } else { - if (health >= sPlayerbotAIConfig->mediumHealth && + if (health >= sPlayerbotAIConfig.mediumHealth && (lossAmount < estAmount || manaEfficiency <= HealingManaEfficiency::MEDIUM)) return 0.0f; if (lossAmount < estAmount || manaEfficiency <= HealingManaEfficiency::LOW) @@ -131,4 +131,4 @@ float HealerAutoSaveManaMultiplier::GetValue(Action* action) void HealerAutoSaveManaStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new HealerAutoSaveManaMultiplier(botAI)); -} \ No newline at end of file +} diff --git a/src/Ai/Base/Strategy/EmoteStrategy.cpp b/src/Ai/Base/Strategy/EmoteStrategy.cpp index 9419e0c386..f2e002e8d7 100644 --- a/src/Ai/Base/Strategy/EmoteStrategy.cpp +++ b/src/Ai/Base/Strategy/EmoteStrategy.cpp @@ -9,7 +9,7 @@ void EmoteStrategy::InitTriggers(std::vector& triggers) { - if (sPlayerbotAIConfig->randomBotEmote) + if (sPlayerbotAIConfig.randomBotEmote) { triggers.push_back(new TriggerNode("often", { NextAction("talk", 1.0f) })); triggers.push_back(new TriggerNode("seldom", { NextAction("emote", 1.0f) })); @@ -19,7 +19,7 @@ void EmoteStrategy::InitTriggers(std::vector& triggers) new TriggerNode("receive emote", { NextAction("emote", 10.0f) })); } - if (sPlayerbotAIConfig->randomBotTalk) + if (sPlayerbotAIConfig.randomBotTalk) { triggers.push_back(new TriggerNode( "often", @@ -27,7 +27,7 @@ void EmoteStrategy::InitTriggers(std::vector& triggers) NextAction("suggest trade", 3.0f) })); } - if (sPlayerbotAIConfig->enableGreet) + if (sPlayerbotAIConfig.enableGreet) triggers.push_back( new TriggerNode("new player nearby", { NextAction("greet", 1.0f) })); diff --git a/src/Ai/Base/Trigger/GenericTriggers.cpp b/src/Ai/Base/Trigger/GenericTriggers.cpp index 988413b2f3..a933bc232d 100644 --- a/src/Ai/Base/Trigger/GenericTriggers.cpp +++ b/src/Ai/Base/Trigger/GenericTriggers.cpp @@ -22,17 +22,18 @@ #include "Timer.h" #include "PlayerbotAI.h" #include "Player.h" +#include "Corpse.h" bool LowManaTrigger::IsActive() { return AI_VALUE2(bool, "has mana", "self target") && - AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->lowMana; + AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.lowMana; } bool MediumManaTrigger::IsActive() { return AI_VALUE2(bool, "has mana", "self target") && - AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->mediumMana; + AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.mediumMana; } bool NoPetTrigger::IsActive() @@ -72,7 +73,7 @@ bool PetAttackTrigger::IsActive() bool HighManaTrigger::IsActive() { - return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->highMana; + return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.highMana; } bool AlmostFullManaTrigger::IsActive() @@ -82,7 +83,7 @@ bool AlmostFullManaTrigger::IsActive() bool EnoughManaTrigger::IsActive() { - return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig->highMana; + return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.highMana; } bool RageAvailable::IsActive() { return AI_VALUE2(uint8, "rage", "self target") >= amount; } @@ -110,9 +111,9 @@ bool HasAggroTrigger::IsActive() { return AI_VALUE2(bool, "has aggro", "current bool PanicTrigger::IsActive() { - return AI_VALUE2(uint8, "health", "self target") < sPlayerbotAIConfig->criticalHealth && + return AI_VALUE2(uint8, "health", "self target") < sPlayerbotAIConfig.criticalHealth && (!AI_VALUE2(bool, "has mana", "self target") || - AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->lowMana); + AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.lowMana); } bool OutNumberedTrigger::IsActive() @@ -248,7 +249,7 @@ bool AoeTrigger::IsActive() bool NoFoodTrigger::IsActive() { - bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool isRandomBot = sRandomPlayerbotMgr.IsRandomBot(bot); if (isRandomBot && botAI->HasCheat(BotCheatMask::food)) return false; @@ -257,7 +258,7 @@ bool NoFoodTrigger::IsActive() bool NoDrinkTrigger::IsActive() { - bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool isRandomBot = sRandomPlayerbotMgr.IsRandomBot(bot); if (isRandomBot && botAI->HasCheat(BotCheatMask::food)) return false; @@ -319,11 +320,11 @@ RandomTrigger::RandomTrigger(PlayerbotAI* botAI, std::string const name, int32 p bool RandomTrigger::IsActive() { - if (getMSTime() - lastCheck < sPlayerbotAIConfig->repeatDelay) + if (getMSTime() - lastCheck < sPlayerbotAIConfig.repeatDelay) return false; lastCheck = getMSTime(); - int32 k = (int32)(probability / sPlayerbotAIConfig->randomChangeMultiplier); + int32 k = (int32)(probability / sPlayerbotAIConfig.randomChangeMultiplier); if (k < 1) k = 1; return (rand() % k) == 0; @@ -381,10 +382,10 @@ bool GenericBoostTrigger::IsActive() bool HealerShouldAttackTrigger::IsActive() { // nobody can help me - if (botAI->GetNearGroupMemberCount(sPlayerbotAIConfig->sightDistance) <= 1) + if (botAI->GetNearGroupMemberCount(sPlayerbotAIConfig.sightDistance) <= 1) return true; - if (AI_VALUE2(uint8, "health", "party member to heal") < sPlayerbotAIConfig->almostFullHealth) + if (AI_VALUE2(uint8, "health", "party member to heal") < sPlayerbotAIConfig.almostFullHealth) return false; // special check for resto druid (dont remove tree of life frequently) @@ -401,9 +402,9 @@ bool HealerShouldAttackTrigger::IsActive() if (balance <= 50) manaThreshold = 85; else if (balance <= 100) - manaThreshold = sPlayerbotAIConfig->highMana; + manaThreshold = sPlayerbotAIConfig.highMana; else - manaThreshold = sPlayerbotAIConfig->mediumMana; + manaThreshold = sPlayerbotAIConfig.mediumMana; if (AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < manaThreshold) return false; @@ -632,7 +633,7 @@ bool ReturnToStayPositionTrigger::IsActive() if (stayPosition.isSet()) { const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); - return distance > sPlayerbotAIConfig->followDistance; + return distance > sPlayerbotAIConfig.followDistance; } return false; diff --git a/src/Ai/Base/Trigger/GenericTriggers.h b/src/Ai/Base/Trigger/GenericTriggers.h index 943e80b742..7b884fbf08 100644 --- a/src/Ai/Base/Trigger/GenericTriggers.h +++ b/src/Ai/Base/Trigger/GenericTriggers.h @@ -216,7 +216,7 @@ class DeflectSpellTrigger : public SpellTrigger class AttackerCountTrigger : public Trigger { public: - AttackerCountTrigger(PlayerbotAI* botAI, int32 amount, float distance = sPlayerbotAIConfig->sightDistance) + AttackerCountTrigger(PlayerbotAI* botAI, int32 amount, float distance = sPlayerbotAIConfig.sightDistance) : Trigger(botAI), amount(amount), distance(distance) { } @@ -836,7 +836,7 @@ class StayTimeTrigger : public Trigger class SitTrigger : public StayTimeTrigger { public: - SitTrigger(PlayerbotAI* botAI) : StayTimeTrigger(botAI, sPlayerbotAIConfig->sitDelay, "sit") {} + SitTrigger(PlayerbotAI* botAI) : StayTimeTrigger(botAI, sPlayerbotAIConfig.sitDelay, "sit") {} }; class ReturnToStayPositionTrigger : public Trigger @@ -850,7 +850,7 @@ class ReturnToStayPositionTrigger : public Trigger class ReturnTrigger : public StayTimeTrigger { public: - ReturnTrigger(PlayerbotAI* botAI) : StayTimeTrigger(botAI, sPlayerbotAIConfig->returnDelay, "return") {} + ReturnTrigger(PlayerbotAI* botAI) : StayTimeTrigger(botAI, sPlayerbotAIConfig.returnDelay, "return") {} }; class GiveItemTrigger : public Trigger diff --git a/src/Ai/Base/Trigger/HealthTriggers.cpp b/src/Ai/Base/Trigger/HealthTriggers.cpp index 9f0485b779..a2b36962d5 100644 --- a/src/Ai/Base/Trigger/HealthTriggers.cpp +++ b/src/Ai/Base/Trigger/HealthTriggers.cpp @@ -38,4 +38,4 @@ bool AoeInGroupTrigger::IsActive() threshold = std::min(threshold, 15); return AI_VALUE2(uint8, "aoe heal", type) >= threshold; -} \ No newline at end of file +} diff --git a/src/Ai/Base/Trigger/HealthTriggers.h b/src/Ai/Base/Trigger/HealthTriggers.h index d8149bffdb..0fbd403d72 100644 --- a/src/Ai/Base/Trigger/HealthTriggers.h +++ b/src/Ai/Base/Trigger/HealthTriggers.h @@ -48,7 +48,7 @@ class LowHealthTrigger : public HealthInRangeTrigger { public: LowHealthTrigger(PlayerbotAI* botAI, std::string const name = "low health", - float value = sPlayerbotAIConfig->lowHealth, float minValue = 0) + float value = sPlayerbotAIConfig.lowHealth, float minValue = 0) : HealthInRangeTrigger(botAI, name, value, minValue) { } @@ -60,7 +60,7 @@ class CriticalHealthTrigger : public LowHealthTrigger { public: CriticalHealthTrigger(PlayerbotAI* botAI) - : LowHealthTrigger(botAI, "critical health", sPlayerbotAIConfig->criticalHealth, 0) + : LowHealthTrigger(botAI, "critical health", sPlayerbotAIConfig.criticalHealth, 0) { } }; @@ -69,7 +69,7 @@ class MediumHealthTrigger : public LowHealthTrigger { public: MediumHealthTrigger(PlayerbotAI* botAI) - : LowHealthTrigger(botAI, "medium health", sPlayerbotAIConfig->mediumHealth, 0) + : LowHealthTrigger(botAI, "medium health", sPlayerbotAIConfig.mediumHealth, 0) { } }; @@ -78,8 +78,8 @@ class AlmostFullHealthTrigger : public LowHealthTrigger { public: AlmostFullHealthTrigger(PlayerbotAI* botAI) - : LowHealthTrigger(botAI, "almost full health", sPlayerbotAIConfig->almostFullHealth, - sPlayerbotAIConfig->mediumHealth) + : LowHealthTrigger(botAI, "almost full health", sPlayerbotAIConfig.almostFullHealth, + sPlayerbotAIConfig.mediumHealth) { } }; @@ -88,7 +88,7 @@ class PartyMemberLowHealthTrigger : public HealthInRangeTrigger { public: PartyMemberLowHealthTrigger(PlayerbotAI* botAI, std::string const name = "party member low health", - float value = sPlayerbotAIConfig->lowHealth, + float value = sPlayerbotAIConfig.lowHealth, float minValue = 0) : HealthInRangeTrigger(botAI, name, value, minValue) { @@ -101,7 +101,7 @@ class PartyMemberCriticalHealthTrigger : public PartyMemberLowHealthTrigger { public: PartyMemberCriticalHealthTrigger(PlayerbotAI* botAI) - : PartyMemberLowHealthTrigger(botAI, "party member critical health", sPlayerbotAIConfig->criticalHealth, 0) + : PartyMemberLowHealthTrigger(botAI, "party member critical health", sPlayerbotAIConfig.criticalHealth, 0) { } }; @@ -110,7 +110,7 @@ class PartyMemberMediumHealthTrigger : public PartyMemberLowHealthTrigger { public: PartyMemberMediumHealthTrigger(PlayerbotAI* botAI) - : PartyMemberLowHealthTrigger(botAI, "party member medium health", sPlayerbotAIConfig->mediumHealth, + : PartyMemberLowHealthTrigger(botAI, "party member medium health", sPlayerbotAIConfig.mediumHealth, 0) { } @@ -120,7 +120,7 @@ class PartyMemberAlmostFullHealthTrigger : public PartyMemberLowHealthTrigger { public: PartyMemberAlmostFullHealthTrigger(PlayerbotAI* botAI) - : PartyMemberLowHealthTrigger(botAI, "party member almost full health", sPlayerbotAIConfig->almostFullHealth, + : PartyMemberLowHealthTrigger(botAI, "party member almost full health", sPlayerbotAIConfig.almostFullHealth, 0) { } diff --git a/src/Ai/Base/Trigger/LootTriggers.cpp b/src/Ai/Base/Trigger/LootTriggers.cpp index f422f386ee..0a4b7e4117 100644 --- a/src/Ai/Base/Trigger/LootTriggers.cpp +++ b/src/Ai/Base/Trigger/LootTriggers.cpp @@ -15,11 +15,11 @@ bool LootAvailableTrigger::IsActive() if (botAI->HasStrategy("stay", BOT_STATE_NON_COMBAT)) { distanceCheck = - sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), CONTACT_DISTANCE); + ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), CONTACT_DISTANCE); } else { - distanceCheck = sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), + distanceCheck = ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), INTERACTION_DISTANCE - 2.0f); } diff --git a/src/Ai/Base/Trigger/PvpTriggers.cpp b/src/Ai/Base/Trigger/PvpTriggers.cpp index c837d8b16c..31fcd0357e 100644 --- a/src/Ai/Base/Trigger/PvpTriggers.cpp +++ b/src/Ai/Base/Trigger/PvpTriggers.cpp @@ -247,7 +247,7 @@ bool EnemyFlagCarrierNear::IsActive() { Unit* carrier = AI_VALUE(Unit*, "enemy flag carrier"); - if (!carrier || !sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, carrier), 100.f)) + if (!carrier || !ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(bot, carrier), 100.f)) return false; // Check if there is another enemy player target closer than the FC @@ -255,8 +255,8 @@ bool EnemyFlagCarrierNear::IsActive() if (nearbyEnemy) { - float distToFC = sServerFacade->GetDistance2d(bot, carrier); - float distToEnemy = sServerFacade->GetDistance2d(bot, nearbyEnemy); + float distToFC = ServerFacade::instance().GetDistance2d(bot, carrier); + float distToEnemy = ServerFacade::instance().GetDistance2d(bot, nearbyEnemy); // If the other enemy is significantly closer, don't pursue FC if (distToEnemy + 15.0f < distToFC) // Add small buffer @@ -283,7 +283,7 @@ bool TeamFlagCarrierNear::IsActive() } Unit* carrier = AI_VALUE(Unit*, "team flag carrier"); - return carrier && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, carrier), 200.f); + return carrier && ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(bot, carrier), 200.f); } bool PlayerWantsInBattlegroundTrigger::IsActive() diff --git a/src/Ai/Base/Trigger/RangeTriggers.cpp b/src/Ai/Base/Trigger/RangeTriggers.cpp index af29f984d1..2acc2aec85 100644 --- a/src/Ai/Base/Trigger/RangeTriggers.cpp +++ b/src/Ai/Base/Trigger/RangeTriggers.cpp @@ -34,7 +34,7 @@ bool EnemyTooCloseForSpellTrigger::IsActive() // bool isBoss = false; // bool isRaid = false; // float combatReach = bot->GetCombatReach() + target->GetCombatReach(); - // float targetDistance = sServerFacade->GetDistance2d(bot, target) + combatReach; + // float targetDistance = ServerFacade::instance().GetDistance2d(bot, target) + combatReach; // if (target->IsCreature()) // { // Creature* creature = botAI->GetCreature(target->GetGUID()); @@ -48,10 +48,10 @@ bool EnemyTooCloseForSpellTrigger::IsActive() // isRaid = true; // // if (isBoss || isRaid) - // // return sServerFacade->IsDistanceLessThan(targetDistance, (sPlayerbotAIConfig->tooCloseDistance + + // // return ServerFacade::instance().IsDistanceLessThan(targetDistance, (sPlayerbotAIConfig.tooCloseDistance + // combatReach) / 2); - // return sServerFacade->IsDistanceLessOrEqualThan(targetDistance, (sPlayerbotAIConfig->tooCloseDistance + + // return ServerFacade::instance().IsDistanceLessOrEqualThan(targetDistance, (sPlayerbotAIConfig.tooCloseDistance + // combatReach / 2)); } @@ -80,7 +80,7 @@ bool EnemyTooCloseForAutoShotTrigger::IsActive() // bool isBoss = false; // bool isRaid = false; // float combatReach = bot->GetCombatReach() + target->GetCombatReach(); - // float targetDistance = sServerFacade->GetDistance2d(bot, target) + combatReach; + // float targetDistance = ServerFacade::instance().GetDistance2d(bot, target) + combatReach; // if (target->IsCreature()) // { // Creature* creature = botAI->GetCreature(target->GetGUID()); @@ -93,7 +93,7 @@ bool EnemyTooCloseForAutoShotTrigger::IsActive() // if (bot->GetMap() && bot->GetMap()->IsRaid()) // isRaid = true; - // return sServerFacade->IsDistanceLessOrEqualThan(targetDistance, 5.0f); + // return ServerFacade::instance().IsDistanceLessOrEqualThan(targetDistance, 5.0f); } bool EnemyTooCloseForShootTrigger::IsActive() @@ -115,7 +115,7 @@ bool EnemyTooCloseForShootTrigger::IsActive() // bool isBoss = false; // bool isRaid = false; // float combatReach = bot->GetCombatReach() + target->GetCombatReach(); - // float targetDistance = sServerFacade->GetDistance2d(bot, target) + combatReach; + // float targetDistance = ServerFacade::instance().GetDistance2d(bot, target) + combatReach; // if (target->IsCreature()) // { // Creature* creature = botAI->GetCreature(target->GetGUID()); @@ -129,9 +129,9 @@ bool EnemyTooCloseForShootTrigger::IsActive() // isRaid = true; // // if (isBoss || isRaid) - // // return sServerFacade->IsDistanceLessThan(targetDistance, botAI->GetRange("shoot") + combatReach); + // // return ServerFacade::instance().IsDistanceLessThan(targetDistance, botAI->GetRange("shoot") + combatReach); - // return sServerFacade->IsDistanceLessOrEqualThan(targetDistance, (botAI->GetRange("shoot") + combatReach / + // return ServerFacade::instance().IsDistanceLessOrEqualThan(targetDistance, (botAI->GetRange("shoot") + combatReach / // 2)); } @@ -147,8 +147,8 @@ bool EnemyTooCloseForMeleeTrigger::IsActive() bool EnemyIsCloseTrigger::IsActive() { Unit* target = AI_VALUE(Unit*, "current target"); - return target && sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), - sPlayerbotAIConfig->tooCloseDistance); + return target && ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), + sPlayerbotAIConfig.tooCloseDistance); } bool EnemyWithinMeleeTrigger::IsActive() @@ -165,7 +165,7 @@ bool OutOfRangeTrigger::IsActive() return target && !bot->IsWithinCombatRange( target, - dis); // sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", GetTargetName()), distance); + dis); // ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, "distance", GetTargetName()), distance); } EnemyOutOfSpellRangeTrigger::EnemyOutOfSpellRangeTrigger(PlayerbotAI* botAI) @@ -180,8 +180,8 @@ EnemyOutOfSpellRangeTrigger::EnemyOutOfSpellRangeTrigger(PlayerbotAI* botAI) // return false; // float combatReach = bot->GetCombatReach() + target->GetCombatReach(); -// return target && (sServerFacade->GetDistance2d(bot, target) > (distance + combatReach + -// sPlayerbotAIConfig->contactDistance) || !bot->IsWithinLOSInMap(target)); +// return target && (ServerFacade::instance().GetDistance2d(bot, target) > (distance + combatReach + +// sPlayerbotAIConfig.contactDistance) || !bot->IsWithinLOSInMap(target)); // } // bool EnemyOutOfMeleeTrigger::IsActive() @@ -190,7 +190,7 @@ EnemyOutOfSpellRangeTrigger::EnemyOutOfSpellRangeTrigger(PlayerbotAI* botAI) // if (!target) // return false; -// float targetDistance = sServerFacade->GetDistance2d(bot, target); +// float targetDistance = ServerFacade::instance().GetDistance2d(bot, target); // return target && (targetDistance > std::max(5.0f, bot->GetCombatReach() + target->GetCombatReach()) || // (!bot->IsWithinLOSInMap(target) && targetDistance > 5.0f)); // } @@ -202,7 +202,7 @@ bool PartyMemberToHealOutOfSpellRangeTrigger::IsActive() return false; float combatReach = bot->GetCombatReach() + target->GetCombatReach(); - return target && (sServerFacade->GetDistance2d(bot, target) > (distance + sPlayerbotAIConfig->contactDistance) || + return target && (ServerFacade::instance().GetDistance2d(bot, target) > (distance + sPlayerbotAIConfig.contactDistance) || !bot->IsWithinLOSInMap(target)); } @@ -213,7 +213,7 @@ PartyMemberToHealOutOfSpellRangeTrigger::PartyMemberToHealOutOfSpellRangeTrigger bool FarFromMasterTrigger::IsActive() { - return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), distance); + return ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), distance); } bool TooCloseToCreatureTrigger::TooCloseToCreature(uint32 creatureId, float range, bool alive) diff --git a/src/Ai/Base/Trigger/RangeTriggers.h b/src/Ai/Base/Trigger/RangeTriggers.h index cc320ad027..62e1ae6e03 100644 --- a/src/Ai/Base/Trigger/RangeTriggers.h +++ b/src/Ai/Base/Trigger/RangeTriggers.h @@ -78,7 +78,7 @@ class EnemyOutOfMeleeTrigger : public OutOfRangeTrigger { public: EnemyOutOfMeleeTrigger(PlayerbotAI* botAI) - : OutOfRangeTrigger(botAI, "enemy out of melee range", sPlayerbotAIConfig->meleeDistance) + : OutOfRangeTrigger(botAI, "enemy out of melee range", sPlayerbotAIConfig.meleeDistance) { } diff --git a/src/Ai/Base/Trigger/TravelTriggers.cpp b/src/Ai/Base/Trigger/TravelTriggers.cpp index 88dfbb2b4b..ce036027c1 100644 --- a/src/Ai/Base/Trigger/TravelTriggers.cpp +++ b/src/Ai/Base/Trigger/TravelTriggers.cpp @@ -21,7 +21,7 @@ bool AtDarkPortalAzerothTrigger::IsActive() { if (bot->GetAreaId() == 72) { - if (sServerFacade->GetDistance2d(bot, -11906.9f, -3208.53f) < 20.0f) + if (ServerFacade::instance().GetDistance2d(bot, -11906.9f, -3208.53f) < 20.0f) { return true; } @@ -34,7 +34,7 @@ bool AtDarkPortalOutlandTrigger::IsActive() { if (bot->GetAreaId() == 3539) { - if (sServerFacade->GetDistance2d(bot, -248.1939f, 921.919f) < 10.0f) + if (ServerFacade::instance().GetDistance2d(bot, -248.1939f, 921.919f) < 10.0f) { return true; } diff --git a/src/Ai/Base/Value/AoeHealValues.cpp b/src/Ai/Base/Value/AoeHealValues.cpp index 06f0002056..2c56986678 100644 --- a/src/Ai/Base/Value/AoeHealValues.cpp +++ b/src/Ai/Base/Value/AoeHealValues.cpp @@ -16,13 +16,13 @@ uint8 AoeHealValue::Calculate() float range = 0; if (qualifier == "low") - range = sPlayerbotAIConfig->lowHealth; + range = sPlayerbotAIConfig.lowHealth; else if (qualifier == "medium") - range = sPlayerbotAIConfig->mediumHealth; + range = sPlayerbotAIConfig.mediumHealth; else if (qualifier == "critical") - range = sPlayerbotAIConfig->criticalHealth; + range = sPlayerbotAIConfig.criticalHealth; else if (qualifier == "almost full") - range = sPlayerbotAIConfig->almostFullHealth; + range = sPlayerbotAIConfig.almostFullHealth; uint8 count = 0; Group::MemberSlotList const& groupSlot = group->GetMemberSlots(); @@ -32,7 +32,7 @@ uint8 AoeHealValue::Calculate() if (!player || !player->IsAlive()) continue; - if (player->GetDistance(bot) >= sPlayerbotAIConfig->sightDistance) + if (player->GetDistance(bot) >= sPlayerbotAIConfig.sightDistance) continue; float percent = (static_cast(player->GetHealth()) / player->GetMaxHealth()) * 100; diff --git a/src/Ai/Base/Value/AoeValues.cpp b/src/Ai/Base/Value/AoeValues.cpp index d46dcb816f..a51a2f4d3b 100644 --- a/src/Ai/Base/Value/AoeValues.cpp +++ b/src/Ai/Base/Value/AoeValues.cpp @@ -29,8 +29,8 @@ GuidVector FindMaxDensity(Player* bot) if (!other) continue; - float d = sServerFacade->GetDistance2d(unit, other); - if (sServerFacade->IsDistanceLessOrEqualThan(d, sPlayerbotAIConfig->aoeRadius * 2)) + float d = ServerFacade::instance().GetDistance2d(unit, other); + if (ServerFacade::instance().IsDistanceLessOrEqualThan(d, sPlayerbotAIConfig.aoeRadius * 2)) groups[*i].push_back(*j); } @@ -157,4 +157,4 @@ Aura* AreaDebuffValue::Calculate() } } return nullptr; -} \ No newline at end of file +} diff --git a/src/Ai/Base/Value/Arrow.cpp b/src/Ai/Base/Value/Arrow.cpp index 15fc2e2959..5fd710220c 100644 --- a/src/Ai/Base/Value/Arrow.cpp +++ b/src/Ai/Base/Value/Arrow.cpp @@ -31,15 +31,15 @@ WorldLocation ArrowFormation::GetLocationInternal() tanks.PlaceUnits(&placer); tanks.Move(-cos(orientation) * offset, -sin(orientation) * offset); - offset += tankLines * sPlayerbotAIConfig->followDistance + sPlayerbotAIConfig->tooCloseDistance / 2; + offset += tankLines * sPlayerbotAIConfig.followDistance + sPlayerbotAIConfig.tooCloseDistance / 2; melee.PlaceUnits(&placer); melee.Move(-cos(orientation) * offset, -sin(orientation) * offset); - offset += meleeLines * sPlayerbotAIConfig->followDistance + sPlayerbotAIConfig->tooCloseDistance / 2; + offset += meleeLines * sPlayerbotAIConfig.followDistance + sPlayerbotAIConfig.tooCloseDistance / 2; ranged.PlaceUnits(&placer); ranged.Move(-cos(orientation) * offset, -sin(orientation) * offset); - offset += rangedLines * sPlayerbotAIConfig->followDistance; + offset += rangedLines * sPlayerbotAIConfig.followDistance; healers.PlaceUnits(&placer); healers.Move(-cos(orientation) * offset, -sin(orientation) * offset); @@ -143,16 +143,16 @@ UnitPosition MultiLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint3 uint32 lineNo = index / 6; uint32 indexInLine = index % 6; uint32 lineSize = std::max(count - lineNo * 6, uint32(6)); - float x = cos(orientation) * sPlayerbotAIConfig->followDistance * lineNo; - float y = sin(orientation) * sPlayerbotAIConfig->followDistance * lineNo; + float x = cos(orientation) * sPlayerbotAIConfig.followDistance * lineNo; + float y = sin(orientation) * sPlayerbotAIConfig.followDistance * lineNo; return placer.Place(unit, indexInLine, lineSize); } UnitPosition SingleLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint32 count) { float angle = orientation - M_PI / 2.0f; - float x = cos(angle) * sPlayerbotAIConfig->followDistance * ((float)index - (float)count / 2); - float y = sin(angle) * sPlayerbotAIConfig->followDistance * ((float)index - (float)count / 2); + float x = cos(angle) * sPlayerbotAIConfig.followDistance * ((float)index - (float)count / 2); + float y = sin(angle) * sPlayerbotAIConfig.followDistance * ((float)index - (float)count / 2); return UnitPosition(x, y); } diff --git a/src/Ai/Base/Value/AttackerCountValues.cpp b/src/Ai/Base/Value/AttackerCountValues.cpp index 637fd72864..88c3cd1fb6 100644 --- a/src/Ai/Base/Value/AttackerCountValues.cpp +++ b/src/Ai/Base/Value/AttackerCountValues.cpp @@ -34,7 +34,7 @@ bool HasAggroValue::Calculate() uint8 AttackerCountValue::Calculate() { uint32 count = 0; - float range = sPlayerbotAIConfig->sightDistance; + float range = sPlayerbotAIConfig.sightDistance; GuidVector attackers = context->GetValue("attackers")->Get(); for (ObjectGuid const guid : attackers) diff --git a/src/Ai/Base/Value/AttackersValue.cpp b/src/Ai/Base/Value/AttackersValue.cpp index f6da0aa7a6..dbde7ab8df 100644 --- a/src/Ai/Base/Value/AttackersValue.cpp +++ b/src/Ai/Base/Value/AttackersValue.cpp @@ -71,7 +71,7 @@ void AttackersValue::AddAttackersOf(Group* group, std::unordered_set& tar { Player* member = ObjectAccessor::FindPlayer(itr->guid); if (!member || !member->IsAlive() || member == bot || member->GetMapId() != bot->GetMapId() || - sServerFacade->GetDistance2d(bot, member) > sPlayerbotAIConfig->sightDistance) + ServerFacade::instance().GetDistance2d(bot, member) > sPlayerbotAIConfig.sightDistance) continue; AddAttackersOf(member, targets); @@ -103,7 +103,7 @@ void AttackersValue::AddAttackersOf(Player* player, std::unordered_set& t Unit* attacker = threatMgr->GetOwner(); if (player->IsValidAttackTarget(attacker) && - player->GetDistance2d(attacker) < sPlayerbotAIConfig->sightDistance) + player->GetDistance2d(attacker) < sPlayerbotAIConfig.sightDistance) targets.insert(attacker); ref = ref->next(); @@ -176,8 +176,8 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float /*range // PvP prohibition checks (skip for duels) if ((attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet()) && (!bot->duel || bot->duel->Opponent != attacker) && - (sPlayerbotAIConfig->IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId()) || - sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()))) + (sPlayerbotAIConfig.IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId()) || + sPlayerbotAIConfig.IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()))) { // This will stop aggresive pets from starting an attack. // This will stop currently attacking pets from continuing their attack. @@ -269,11 +269,11 @@ bool PossibleAddsValue::Calculate() if (!attacker) continue; - float dist = sServerFacade->GetDistance2d(attacker, add); - if (sServerFacade->IsDistanceLessOrEqualThan(dist, sPlayerbotAIConfig->aoeRadius * 1.5f)) + float dist = ServerFacade::instance().GetDistance2d(attacker, add); + if (ServerFacade::instance().IsDistanceLessOrEqualThan(dist, sPlayerbotAIConfig.aoeRadius * 1.5f)) continue; - if (sServerFacade->IsDistanceLessOrEqualThan(dist, sPlayerbotAIConfig->aggroDistance)) + if (ServerFacade::instance().IsDistanceLessOrEqualThan(dist, sPlayerbotAIConfig.aggroDistance)) return true; } } diff --git a/src/Ai/Base/Value/AttackersValue.h b/src/Ai/Base/Value/AttackersValue.h index 906db92a88..7e9397c86c 100644 --- a/src/Ai/Base/Value/AttackersValue.h +++ b/src/Ai/Base/Value/AttackersValue.h @@ -20,7 +20,7 @@ class AttackersValue : public ObjectGuidListCalculatedValue AttackersValue(PlayerbotAI* botAI) : ObjectGuidListCalculatedValue(botAI, "attackers", 1 * 1000) {} GuidVector Calculate(); - static bool IsPossibleTarget(Unit* attacker, Player* bot, float range = sPlayerbotAIConfig->sightDistance); + static bool IsPossibleTarget(Unit* attacker, Player* bot, float range = sPlayerbotAIConfig.sightDistance); static bool IsValidTarget(Unit* attacker, Player* bot); private: diff --git a/src/Ai/Base/Value/AvailableLootValue.cpp b/src/Ai/Base/Value/AvailableLootValue.cpp index 7f945d7e1e..75b4b599a4 100644 --- a/src/Ai/Base/Value/AvailableLootValue.cpp +++ b/src/Ai/Base/Value/AvailableLootValue.cpp @@ -26,5 +26,5 @@ bool CanLootValue::Calculate() { LootObject loot = AI_VALUE(LootObject, "loot target"); return !loot.IsEmpty() && loot.GetWorldObject(bot) && loot.IsLootPossible(bot) && - sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), INTERACTION_DISTANCE - 2); + ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), INTERACTION_DISTANCE - 2); } diff --git a/src/Ai/Base/Value/CcTargetValue.cpp b/src/Ai/Base/Value/CcTargetValue.cpp index c47151ddfd..1c305aec3c 100644 --- a/src/Ai/Base/Value/CcTargetValue.cpp +++ b/src/Ai/Base/Value/CcTargetValue.cpp @@ -34,7 +34,7 @@ class FindTargetForCcStrategy : public FindTargetStrategy return; uint8 health = static_cast(creature->GetHealthPct()); - if (health < sPlayerbotAIConfig->mediumHealth) + if (health < sPlayerbotAIConfig.mediumHealth) return; float minDistance = botAI->GetRange("spell"); @@ -45,9 +45,9 @@ class FindTargetForCcStrategy : public FindTargetStrategy if (*botAI->GetAiObjectContext()->GetValue("aoe count") > 2) { WorldLocation aoe = *botAI->GetAiObjectContext()->GetValue("aoe position"); - if (sServerFacade->IsDistanceLessOrEqualThan( - sServerFacade->GetDistance2d(creature, aoe.GetPositionX(), aoe.GetPositionY()), - sPlayerbotAIConfig->aoeRadius)) + if (ServerFacade::instance().IsDistanceLessOrEqualThan( + ServerFacade::instance().GetDistance2d(creature, aoe.GetPositionX(), aoe.GetPositionY()), + sPlayerbotAIConfig.aoeRadius)) return; } @@ -70,7 +70,7 @@ class FindTargetForCcStrategy : public FindTargetStrategy if (!botAI->IsTank(member)) continue; - float distance = sServerFacade->GetDistance2d(member, creature); + float distance = ServerFacade::instance().GetDistance2d(member, creature); if (distance < minDistance) minDistance = distance; } diff --git a/src/Ai/Base/Value/CollisionValue.cpp b/src/Ai/Base/Value/CollisionValue.cpp index 7657742590..172a909b17 100644 --- a/src/Ai/Base/Value/CollisionValue.cpp +++ b/src/Ai/Base/Value/CollisionValue.cpp @@ -18,7 +18,7 @@ bool CollisionValue::Calculate() return false; std::list targets; - float range = sPlayerbotAIConfig->contactDistance; + float range = sPlayerbotAIConfig.contactDistance; Acore::AnyUnitInObjectRangeCheck u_check(bot, range); Acore::UnitListSearcher searcher(bot, targets, u_check); Cell::VisitObjects(bot, searcher, range); @@ -28,8 +28,8 @@ bool CollisionValue::Calculate() if (bot == target) continue; - float dist = sServerFacade->GetDistance2d(bot, target->GetPositionX(), target->GetPositionY()); - if (sServerFacade->IsDistanceLessThan(dist, target->GetCombatReach())) + float dist = ServerFacade::instance().GetDistance2d(bot, target->GetPositionX(), target->GetPositionY()); + if (ServerFacade::instance().IsDistanceLessThan(dist, target->GetCombatReach())) return true; } diff --git a/src/Ai/Base/Value/DistanceValue.cpp b/src/Ai/Base/Value/DistanceValue.cpp index 2c1e818b54..a5c6fe857a 100644 --- a/src/Ai/Base/Value/DistanceValue.cpp +++ b/src/Ai/Base/Value/DistanceValue.cpp @@ -24,7 +24,7 @@ float DistanceValue::Calculate() if (!obj || !obj->IsInWorld()) return 0.0f; - return sServerFacade->GetDistance2d(botAI->GetBot(), obj); + return ServerFacade::instance().GetDistance2d(botAI->GetBot(), obj); } if (qualifier.find("position_") == 0) @@ -37,7 +37,7 @@ float DistanceValue::Calculate() if (botAI->GetBot()->GetMapId() != pos.mapId) return 0.0f; - return sServerFacade->GetDistance2d(botAI->GetBot(), pos.x, pos.y); + return ServerFacade::instance().GetDistance2d(botAI->GetBot(), pos.x, pos.y); } Unit* target = nullptr; @@ -76,7 +76,7 @@ float DistanceValue::Calculate() { Formation* formation = AI_VALUE(Formation*, "formation"); WorldLocation loc = formation->GetLocation(); - return sServerFacade->GetDistance2d(botAI->GetBot(), loc.GetPositionX(), loc.GetPositionY()); + return ServerFacade::instance().GetDistance2d(botAI->GetBot(), loc.GetPositionX(), loc.GetPositionY()); } } @@ -86,7 +86,7 @@ float DistanceValue::Calculate() if (target == botAI->GetBot()) return 0.0f; - return sServerFacade->GetDistance2d(botAI->GetBot(), target); + return ServerFacade::instance().GetDistance2d(botAI->GetBot(), target); } bool InsideTargetValue::Calculate() @@ -95,6 +95,6 @@ bool InsideTargetValue::Calculate() if (!target || !target->IsInWorld() || target == botAI->GetBot()) return false; - float dist = sServerFacade->GetDistance2d(botAI->GetBot(), target->GetPositionX(), target->GetPositionY()); - return sServerFacade->IsDistanceLessThan(dist, target->GetCombatReach()); + float dist = ServerFacade::instance().GetDistance2d(botAI->GetBot(), target->GetPositionX(), target->GetPositionY()); + return ServerFacade::instance().IsDistanceLessThan(dist, target->GetCombatReach()); } diff --git a/src/Ai/Base/Value/DpsTargetValue.cpp b/src/Ai/Base/Value/DpsTargetValue.cpp index 55b47d7c01..1a813c9940 100644 --- a/src/Ai/Base/Value/DpsTargetValue.cpp +++ b/src/Ai/Base/Value/DpsTargetValue.cpp @@ -116,7 +116,7 @@ class CasterFindTargetSmartStrategy : public FindTargetStrategy float time = unit->GetHealth() / dps_; float dis = unit->GetDistance(botAI->GetBot()); float attackRange = - botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; + botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig.spellDistance : sPlayerbotAIConfig.meleeDistance; attackRange += 5.0f; int level = dis < attackRange ? 10 : 0; if (time >= 5 && time <= 30) @@ -198,7 +198,7 @@ class GeneralFindTargetSmartStrategy : public FindTargetStrategy float time = unit->GetHealth() / dps_; float dis = unit->GetDistance(botAI->GetBot()); float attackRange = - botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; + botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig.spellDistance : sPlayerbotAIConfig.meleeDistance; attackRange += 5.0f; int level = dis < attackRange ? 10 : 0; return level; @@ -279,7 +279,7 @@ class ComboFindTargetSmartStrategy : public FindTargetStrategy float time = unit->GetHealth() / dps_; float dis = unit->GetDistance(botAI->GetBot()); float attackRange = - botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; + botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig.spellDistance : sPlayerbotAIConfig.meleeDistance; attackRange += 5.0f; int level = dis < attackRange ? 10 : 0; return level; diff --git a/src/Ai/Base/Value/EnemyHealerTargetValue.cpp b/src/Ai/Base/Value/EnemyHealerTargetValue.cpp index 6040c4fcb5..85559f7827 100644 --- a/src/Ai/Base/Value/EnemyHealerTargetValue.cpp +++ b/src/Ai/Base/Value/EnemyHealerTargetValue.cpp @@ -20,7 +20,7 @@ Unit* EnemyHealerTargetValue::Calculate() if (!unit || unit == target) continue; - if (sServerFacade->GetDistance2d(bot, unit) > botAI->GetRange("spell")) + if (ServerFacade::instance().GetDistance2d(bot, unit) > botAI->GetRange("spell")) continue; if (!botAI->IsInterruptableSpellCasting(unit, spell)) diff --git a/src/Ai/Base/Value/EnemyPlayerValue.cpp b/src/Ai/Base/Value/EnemyPlayerValue.cpp index 2325c9c09b..7de0cd670a 100644 --- a/src/Ai/Base/Value/EnemyPlayerValue.cpp +++ b/src/Ai/Base/Value/EnemyPlayerValue.cpp @@ -14,7 +14,7 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit) bool inCannon = botAI->IsInVehicle(false, true); Player* enemy = dynamic_cast(unit); if (enemy && botAI->IsOpposing(enemy) && enemy->IsPvP() && - !sPlayerbotAIConfig->IsPvpProhibited(enemy->GetZoneId(), enemy->GetAreaId()) && + !sPlayerbotAIConfig.IsPvpProhibited(enemy->GetZoneId(), enemy->GetAreaId()) && !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2) && ((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) && /*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) && @@ -131,7 +131,7 @@ Unit* EnemyPlayerValue::Calculate() if (pMember == bot) continue; - if (sServerFacade->GetDistance2d(bot, pMember) > 30.0f) + if (ServerFacade::instance().GetDistance2d(bot, pMember) > 30.0f) continue; if (Unit* pAttacker = pMember->getAttackerForHelper()) diff --git a/src/Ai/Base/Value/EnemyPlayerValue.h b/src/Ai/Base/Value/EnemyPlayerValue.h index f94f20265f..2ff56f4eb2 100644 --- a/src/Ai/Base/Value/EnemyPlayerValue.h +++ b/src/Ai/Base/Value/EnemyPlayerValue.h @@ -16,7 +16,7 @@ class Unit; class NearestEnemyPlayersValue : public PossibleTargetsValue { public: - NearestEnemyPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->grindDistance) + NearestEnemyPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.grindDistance) : PossibleTargetsValue(botAI, "nearest enemy players", range) { } diff --git a/src/Ai/Base/Value/EstimatedLifetimeValue.cpp b/src/Ai/Base/Value/EstimatedLifetimeValue.cpp index dd79207130..8b7e5163c6 100644 --- a/src/Ai/Base/Value/EstimatedLifetimeValue.cpp +++ b/src/Ai/Base/Value/EstimatedLifetimeValue.cpp @@ -46,7 +46,7 @@ float EstimatedGroupDpsValue::Calculate() if (member->GetMapId() != bot->GetMapId()) continue; - if (member->GetExactDist(bot) > sPlayerbotAIConfig->sightDistance) + if (member->GetExactDist(bot) > sPlayerbotAIConfig.sightDistance) continue; groupPlayer.push_back(member); @@ -146,4 +146,4 @@ float EstimatedGroupDpsValue::GetBasicGs(uint32 level) basic_gs = PlayerbotFactory::CalcMixedGearScore(155 + (level - 70) * 4, ITEM_QUALITY_RARE); } return basic_gs; -} \ No newline at end of file +} diff --git a/src/Ai/Base/Value/Formations.cpp b/src/Ai/Base/Value/Formations.cpp index 69869889d7..1394b9fbe0 100644 --- a/src/Ai/Base/Value/Formations.cpp +++ b/src/Ai/Base/Value/Formations.cpp @@ -64,8 +64,8 @@ WorldLocation MoveAheadFormation::GetLocation() // if (master->isMoving()) // { // float ori = master->GetOrientation(); - // float x1 = x + sPlayerbotAIConfig->tooCloseDistance * cos(ori); - // float y1 = y + sPlayerbotAIConfig->tooCloseDistance * sin(ori); + // float x1 = x + sPlayerbotAIConfig.tooCloseDistance * cos(ori); + // float y1 = y + sPlayerbotAIConfig.tooCloseDistance * sin(ori); // float ground = map->GetHeight(x1, y1, z); // if (ground > INVALID_HEIGHT) // { @@ -111,7 +111,7 @@ class NearFormation : public MoveAheadFormation if (!ValidateTargetContext(master, bot, map)) return Formation::NullLocation; - float range = sPlayerbotAIConfig->followDistance; + float range = sPlayerbotAIConfig.followDistance; float angle = GetFollowAngle(); float x = master->GetPositionX() + cos(angle) * range; float y = master->GetPositionY() + sin(angle) * range; @@ -127,7 +127,7 @@ class NearFormation : public MoveAheadFormation return WorldLocation(master->GetMapId(), x, y, z); } - float GetMaxDistance() override { return sPlayerbotAIConfig->followDistance; } + float GetMaxDistance() override { return sPlayerbotAIConfig.followDistance; } }; class ChaosFormation : public MoveAheadFormation @@ -142,7 +142,7 @@ class ChaosFormation : public MoveAheadFormation if (!ValidateTargetContext(master, bot, map)) return Formation::NullLocation; - float range = sPlayerbotAIConfig->followDistance; + float range = sPlayerbotAIConfig.followDistance; float angle = GetFollowAngle(); time_t now = time(nullptr); @@ -150,8 +150,8 @@ class ChaosFormation : public MoveAheadFormation { lastChangeTime = now; - dx = (urand(0, 10) / 10.0f - 0.5f) * sPlayerbotAIConfig->tooCloseDistance; - dy = (urand(0, 10) / 10.0f - 0.5f) * sPlayerbotAIConfig->tooCloseDistance; + dx = (urand(0, 10) / 10.0f - 0.5f) * sPlayerbotAIConfig.tooCloseDistance; + dy = (urand(0, 10) / 10.0f - 0.5f) * sPlayerbotAIConfig.tooCloseDistance; dr = std::sqrt(dx * dx + dy * dy); } @@ -173,7 +173,7 @@ class ChaosFormation : public MoveAheadFormation return WorldLocation(master->GetMapId(), x, y, z); } - float GetMaxDistance() override { return sPlayerbotAIConfig->followDistance + dr; } + float GetMaxDistance() override { return sPlayerbotAIConfig.followDistance + dr; } private: time_t lastChangeTime; @@ -287,7 +287,7 @@ class ShieldFormation : public MoveFormation if (!group) return Formation::NullLocation; - float range = sPlayerbotAIConfig->followDistance; + float range = sPlayerbotAIConfig.followDistance; Player* master = GetMaster(); if (!master) @@ -326,14 +326,14 @@ class ShieldFormation : public MoveFormation if (botAI->IsTank(bot) && !botAI->IsTank(master)) { - float diff = (tanks.size() % 2 == 0) ? -sPlayerbotAIConfig->tooCloseDistance / 2.0f : 0.0f; + float diff = (tanks.size() % 2 == 0) ? -sPlayerbotAIConfig.tooCloseDistance / 2.0f : 0.0f; return MoveLine(tanks, diff, x + cos(orientation) * range, y + sin(orientation) * range, z, orientation, range); } if (!botAI->IsTank(bot) && botAI->IsTank(master)) { - float diff = (dps.size() % 2 == 0) ? -sPlayerbotAIConfig->tooCloseDistance / 2.0f : 0.0f; + float diff = (dps.size() % 2 == 0) ? -sPlayerbotAIConfig.tooCloseDistance / 2.0f : 0.0f; return MoveLine(dps, diff, x - cos(orientation) * range, y - sin(orientation) * range, z, orientation, range); } @@ -354,10 +354,10 @@ class FarFormation : public FollowFormation if (!ValidateTargetContext(master, bot, map)) return Formation::NullLocation; - float range = sPlayerbotAIConfig->farDistance; - float followRange = sPlayerbotAIConfig->followDistance; + float range = sPlayerbotAIConfig.farDistance; + float followRange = sPlayerbotAIConfig.followDistance; - if (sServerFacade->GetDistance2d(bot, master) <= range) + if (ServerFacade::instance().GetDistance2d(bot, master) <= range) return Formation::NullLocation; float angleToBot = master->GetAngle(bot); @@ -378,7 +378,7 @@ class FarFormation : public FollowFormation float tx = master->GetPositionX() + cos(a) * range + cos(followAngle) * followRange; float ty = master->GetPositionY() + sin(a) * range + sin(followAngle) * followRange; - float dist = sServerFacade->GetDistance2d(bot, tx, ty); + float dist = ServerFacade::instance().GetDistance2d(bot, tx, ty); float tg = master->GetMapHeight(tx, ty, z + 30.0f); if (tg > INVALID_HEIGHT && (!minDist || dist < minDist)) diff --git a/src/Ai/Base/Value/Formations.h b/src/Ai/Base/Value/Formations.h index c44e1d0f42..3e5decf7f1 100644 --- a/src/Ai/Base/Value/Formations.h +++ b/src/Ai/Base/Value/Formations.h @@ -21,7 +21,7 @@ class Formation : public AiNamedObject virtual ~Formation() = default; virtual std::string const GetTargetName() { return ""; } virtual WorldLocation GetLocation() { return NullLocation; } - virtual float GetMaxDistance() { return sPlayerbotAIConfig->followDistance; } + virtual float GetMaxDistance() { return sPlayerbotAIConfig.followDistance; } static WorldLocation NullLocation; static bool IsNullLocation(WorldLocation const& loc); diff --git a/src/Ai/Base/Value/GrindTargetValue.cpp b/src/Ai/Base/Value/GrindTargetValue.cpp index 7e6168c776..ce021a2d9a 100644 --- a/src/Ai/Base/Value/GrindTargetValue.cpp +++ b/src/Ai/Base/Value/GrindTargetValue.cpp @@ -82,12 +82,12 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) if (!bot->InBattleground() && GetTargetingPlayerCount(unit) > assistCount) continue; - // if (!bot->InBattleground() && master && master->GetDistance(unit) >= sPlayerbotAIConfig->grindDistance && - // !sRandomPlayerbotMgr->IsRandomBot(bot)) continue; + // if (!bot->InBattleground() && master && master->GetDistance(unit) >= sPlayerbotAIConfig.grindDistance && + // !sRandomPlayerbotMgr.IsRandomBot(bot)) continue; // Bots in bot-groups no have a more limited range to look for grind target if (!bot->InBattleground() && master && botAI->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT) && - sServerFacade->GetDistance2d(master, unit) > sPlayerbotAIConfig->lootDistance) + ServerFacade::instance().GetDistance2d(master, unit) > sPlayerbotAIConfig.lootDistance) { if (botAI->HasStrategy("debug grind", BotState::BOT_STATE_NON_COMBAT)) botAI->TellMaster(chat->FormatWorldobject(unit) + " ignored (far from master)."); diff --git a/src/Ai/Base/Value/GroupValues.cpp b/src/Ai/Base/Value/GroupValues.cpp index 01049a7969..460b9f334d 100644 --- a/src/Ai/Base/Value/GroupValues.cpp +++ b/src/Ai/Base/Value/GroupValues.cpp @@ -47,7 +47,7 @@ bool IsNearLeaderValue::Calculate() if (groupLeader == bot) return true; - return sServerFacade->GetDistance2d(bot, botAI->GetGroupLeader()) < sPlayerbotAIConfig->sightDistance; + return ServerFacade::instance().GetDistance2d(bot, botAI->GetGroupLeader()) < sPlayerbotAIConfig.sightDistance; } bool BoolANDValue::Calculate() @@ -155,10 +155,10 @@ bool GroupReadyValue::Calculate() // We only wait for members that are in range otherwise we might be waiting for bots stuck in dead loops // forever. if (botAI->GetGroupLeader() && - sServerFacade->GetDistance2d(member, botAI->GetGroupLeader()) > sPlayerbotAIConfig->sightDistance) + ServerFacade::instance().GetDistance2d(member, botAI->GetGroupLeader()) > sPlayerbotAIConfig.sightDistance) continue; - if (member->GetHealthPct() < sPlayerbotAIConfig->almostFullHealth) + if (member->GetHealthPct() < sPlayerbotAIConfig.almostFullHealth) return false; if (!member->GetPower(POWER_MANA)) @@ -166,7 +166,7 @@ bool GroupReadyValue::Calculate() float mana = (static_cast(member->GetPower(POWER_MANA)) / member->GetMaxPower(POWER_MANA)) * 100; - if (mana < sPlayerbotAIConfig->mediumMana) + if (mana < sPlayerbotAIConfig.mediumMana) return false; } diff --git a/src/Ai/Base/Value/HasAvailableLootValue.cpp b/src/Ai/Base/Value/HasAvailableLootValue.cpp index d687e1fc41..532e06afcd 100644 --- a/src/Ai/Base/Value/HasAvailableLootValue.cpp +++ b/src/Ai/Base/Value/HasAvailableLootValue.cpp @@ -11,5 +11,5 @@ bool HasAvailableLootValue::Calculate() { return !AI_VALUE(bool, "can loot") && - AI_VALUE(LootObjectStack*, "available loot")->CanLoot(sPlayerbotAIConfig->lootDistance); + AI_VALUE(LootObjectStack*, "available loot")->CanLoot(sPlayerbotAIConfig.lootDistance); } diff --git a/src/Ai/Base/Value/ItemUsageValue.cpp b/src/Ai/Base/Value/ItemUsageValue.cpp index 25866c8059..6f48fa9739 100644 --- a/src/Ai/Base/Value/ItemUsageValue.cpp +++ b/src/Ai/Base/Value/ItemUsageValue.cpp @@ -99,7 +99,7 @@ ItemUsage ItemUsageValue::Calculate() } } - if (bot->GetGuildId() && sGuildTaskMgr->IsGuildTaskItem(itemId, bot->GetGuildId())) + if (bot->GetGuildId() && GuildTaskMgr::instance().IsGuildTaskItem(itemId, bot->GetGuildId())) return ITEM_USAGE_GUILD_TASK; ItemUsage equip = QueryItemUsageForEquip(proto, randomPropertyId); @@ -129,7 +129,7 @@ ItemUsage ItemUsageValue::Calculate() Player* master = botAI->GetMaster(); bool isSelfBot = (master == bot); bool botNeedsItemForQuest = IsItemUsefulForQuest(bot, proto); - bool masterNeedsItemForQuest = master && sPlayerbotAIConfig->syncQuestWithPlayer && IsItemUsefulForQuest(master, proto); + bool masterNeedsItemForQuest = master && sPlayerbotAIConfig.syncQuestWithPlayer && IsItemUsefulForQuest(master, proto); // Identify the source of loot LootObject lootObject = AI_VALUE(LootObject, "loot target"); @@ -304,7 +304,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, } bool shouldEquip = false; - // uint32 statWeight = sRandomItemMgr->GetLiveStatWeight(bot, itemProto->ItemId); + // uint32 statWeight = sRandomItemMgr.GetLiveStatWeight(bot, itemProto->ItemId); StatsWeightCalculator calculator(bot); calculator.SetItemSetBonus(false); calculator.SetOverflowPenalty(false); @@ -314,10 +314,10 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, if (itemScore) shouldEquip = true; - if (itemProto->Class == ITEM_CLASS_WEAPON && !sRandomItemMgr->CanEquipWeapon(bot->getClass(), itemProto)) + if (itemProto->Class == ITEM_CLASS_WEAPON && !sRandomItemMgr.CanEquipWeapon(bot->getClass(), itemProto)) shouldEquip = false; if (itemProto->Class == ITEM_CLASS_ARMOR && - !sRandomItemMgr->CanEquipArmor(bot->getClass(), bot->GetLevel(), itemProto)) + !sRandomItemMgr.CanEquipArmor(bot->getClass(), bot->GetLevel(), itemProto)) shouldEquip = false; uint8 possibleSlots = 1; @@ -396,10 +396,10 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, float oldScore = calculator.CalculateItem(oldItemProto->ItemId, oldItem->GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID)); if (oldItem) { - // uint32 oldStatWeight = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId); + // uint32 oldStatWeight = sRandomItemMgr.GetLiveStatWeight(bot, oldItemProto->ItemId); if (itemScore || oldScore) { - shouldEquipInSlot = itemScore > oldScore * sPlayerbotAIConfig->equipUpgradeThreshold; + shouldEquipInSlot = itemScore > oldScore * sPlayerbotAIConfig.equipUpgradeThreshold; } } @@ -417,15 +417,15 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, } bool existingShouldEquip = true; - if (oldItemProto->Class == ITEM_CLASS_WEAPON && !sRandomItemMgr->CanEquipWeapon(bot->getClass(), oldItemProto)) + if (oldItemProto->Class == ITEM_CLASS_WEAPON && !sRandomItemMgr.CanEquipWeapon(bot->getClass(), oldItemProto)) existingShouldEquip = false; if (oldItemProto->Class == ITEM_CLASS_ARMOR && - !sRandomItemMgr->CanEquipArmor(bot->getClass(), bot->GetLevel(), oldItemProto)) + !sRandomItemMgr.CanEquipArmor(bot->getClass(), bot->GetLevel(), oldItemProto)) existingShouldEquip = false; - // uint32 oldItemPower = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId); - // uint32 newItemPower = sRandomItemMgr->GetLiveStatWeight(bot, itemProto->ItemId); + // uint32 oldItemPower = sRandomItemMgr.GetLiveStatWeight(bot, oldItemProto->ItemId); + // uint32 newItemPower = sRandomItemMgr.GetLiveStatWeight(bot, itemProto->ItemId); // Compare items based on item level, quality or itemId. bool isBetter = false; diff --git a/src/Ai/Base/Value/NearestAdsValue.h b/src/Ai/Base/Value/NearestAdsValue.h index 312d5596eb..b9e3fbe316 100644 --- a/src/Ai/Base/Value/NearestAdsValue.h +++ b/src/Ai/Base/Value/NearestAdsValue.h @@ -14,7 +14,7 @@ class PlayerbotAI; class NearestAddsValue : public PossibleTargetsValue { public: - NearestAddsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->tooCloseDistance) + NearestAddsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.tooCloseDistance) : PossibleTargetsValue(botAI, "nearest adds", range, true) { } diff --git a/src/Ai/Base/Value/NearestCorpsesValue.h b/src/Ai/Base/Value/NearestCorpsesValue.h index 4c0716e299..f6a067c3fe 100644 --- a/src/Ai/Base/Value/NearestCorpsesValue.h +++ b/src/Ai/Base/Value/NearestCorpsesValue.h @@ -14,7 +14,7 @@ class PlayerbotAI; class NearestCorpsesValue : public NearestUnitsValue { public: - NearestCorpsesValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) + NearestCorpsesValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.sightDistance) : NearestUnitsValue(botAI, "nearest corpses", range, true) { } diff --git a/src/Ai/Base/Value/NearestFriendlyPlayersValue.h b/src/Ai/Base/Value/NearestFriendlyPlayersValue.h index 9876316929..2588409044 100644 --- a/src/Ai/Base/Value/NearestFriendlyPlayersValue.h +++ b/src/Ai/Base/Value/NearestFriendlyPlayersValue.h @@ -14,7 +14,7 @@ class PlayerbotAI; class NearestFriendlyPlayersValue : public NearestUnitsValue { public: - NearestFriendlyPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) + NearestFriendlyPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.sightDistance) : NearestUnitsValue(botAI, "nearest friendly players", range) { } diff --git a/src/Ai/Base/Value/NearestGameObjects.h b/src/Ai/Base/Value/NearestGameObjects.h index 2018e93eb1..9d1153aade 100644 --- a/src/Ai/Base/Value/NearestGameObjects.h +++ b/src/Ai/Base/Value/NearestGameObjects.h @@ -33,7 +33,7 @@ class AnyGameObjectInObjectRangeCheck class NearestGameObjects : public ObjectGuidListCalculatedValue { public: - NearestGameObjects(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance, bool ignoreLos = false, + NearestGameObjects(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.sightDistance, bool ignoreLos = false, std::string const name = "nearest game objects") : ObjectGuidListCalculatedValue(botAI, name, 1 * 1000), range(range), ignoreLos(ignoreLos) { diff --git a/src/Ai/Base/Value/NearestNonBotPlayersValue.h b/src/Ai/Base/Value/NearestNonBotPlayersValue.h index 73d522c5fb..1980bcf2ba 100644 --- a/src/Ai/Base/Value/NearestNonBotPlayersValue.h +++ b/src/Ai/Base/Value/NearestNonBotPlayersValue.h @@ -14,7 +14,7 @@ class PlayerbotAI; class NearestNonBotPlayersValue : public NearestUnitsValue { public: - NearestNonBotPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->grindDistance) + NearestNonBotPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.grindDistance) : NearestUnitsValue(botAI, "nearest non bot players", range, true) { } diff --git a/src/Ai/Base/Value/NearestNpcsValue.h b/src/Ai/Base/Value/NearestNpcsValue.h index 6935026147..71e1d9de45 100644 --- a/src/Ai/Base/Value/NearestNpcsValue.h +++ b/src/Ai/Base/Value/NearestNpcsValue.h @@ -14,7 +14,7 @@ class PlayerbotAI; class NearestNpcsValue : public NearestUnitsValue { public: - NearestNpcsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) + NearestNpcsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.sightDistance) : NearestUnitsValue(botAI, "nearest npcs", range) { } @@ -27,7 +27,7 @@ class NearestNpcsValue : public NearestUnitsValue class NearestHostileNpcsValue : public NearestUnitsValue { public: - NearestHostileNpcsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) + NearestHostileNpcsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.sightDistance) : NearestUnitsValue(botAI, "nearest hostile npcs", range) { } @@ -40,7 +40,7 @@ class NearestHostileNpcsValue : public NearestUnitsValue class NearestVehiclesValue : public NearestUnitsValue { public: - NearestVehiclesValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) + NearestVehiclesValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.sightDistance) : NearestUnitsValue(botAI, "nearest vehicles", range) { } @@ -53,7 +53,7 @@ class NearestVehiclesValue : public NearestUnitsValue class NearestTriggersValue : public NearestUnitsValue { public: - NearestTriggersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) + NearestTriggersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.sightDistance) : NearestUnitsValue(botAI, "nearest triggers", range) { } diff --git a/src/Ai/Base/Value/NearestUnitsValue.h b/src/Ai/Base/Value/NearestUnitsValue.h index 527925a0d7..5e00cb6d09 100644 --- a/src/Ai/Base/Value/NearestUnitsValue.h +++ b/src/Ai/Base/Value/NearestUnitsValue.h @@ -16,7 +16,7 @@ class NearestUnitsValue : public ObjectGuidListCalculatedValue { public: NearestUnitsValue(PlayerbotAI* botAI, std::string const name = "nearest units", - float range = sPlayerbotAIConfig->sightDistance, bool ignoreLos = false, uint32 checkInterval = 1) + float range = sPlayerbotAIConfig.sightDistance, bool ignoreLos = false, uint32 checkInterval = 1) : ObjectGuidListCalculatedValue(botAI, name, checkInterval), range(range), ignoreLos(ignoreLos) { } diff --git a/src/Ai/Base/Value/PartyMemberToHeal.cpp b/src/Ai/Base/Value/PartyMemberToHeal.cpp index 47554c7c2b..b49c1cd419 100644 --- a/src/Ai/Base/Value/PartyMemberToHeal.cpp +++ b/src/Ai/Base/Value/PartyMemberToHeal.cpp @@ -46,10 +46,10 @@ Unit* PartyMemberToHeal::Calculate() if (player && player->IsAlive()) { uint8 health = player->GetHealthPct(); - if (isRaid || health < sPlayerbotAIConfig->mediumHealth || !IsTargetOfSpellCast(player, predicate)) + if (isRaid || health < sPlayerbotAIConfig.mediumHealth || !IsTargetOfSpellCast(player, predicate)) { uint32 probeValue = 100; - if (player->GetDistance2d(bot) > sPlayerbotAIConfig->healDistance) + if (player->GetDistance2d(bot) > sPlayerbotAIConfig.healDistance) { probeValue = health + 30; } @@ -70,7 +70,7 @@ Unit* PartyMemberToHeal::Calculate() { uint8 health = ((Unit*)pet)->GetHealthPct(); uint32 probeValue = 100; - if (isRaid || health < sPlayerbotAIConfig->mediumHealth) + if (isRaid || health < sPlayerbotAIConfig.mediumHealth) probeValue = health + 30; // delay Check pet to here for better performance if (probeValue < calc.minValue && Check(pet)) @@ -84,7 +84,7 @@ Unit* PartyMemberToHeal::Calculate() { uint8 health = charm->GetHealthPct(); uint32 probeValue = 100; - if (isRaid || health < sPlayerbotAIConfig->mediumHealth) + if (isRaid || health < sPlayerbotAIConfig.mediumHealth) probeValue = health + 30; // delay Check charm to here for better performance if (probeValue < calc.minValue && Check(charm)) @@ -99,10 +99,10 @@ Unit* PartyMemberToHeal::Calculate() bool PartyMemberToHeal::Check(Unit* player) { // return player && player != bot && player->GetMapId() == bot->GetMapId() && player->IsInWorld() && - // sServerFacade->GetDistance2d(bot, player) < (player->IsPlayer() && botAI->IsTank((Player*)player) ? 50.0f + // ServerFacade::instance().GetDistance2d(bot, player) < (player->IsPlayer() && botAI->IsTank((Player*)player) ? 50.0f // : 40.0f); return player->GetMapId() == bot->GetMapId() && !player->IsCharmed() && - bot->GetDistance2d(player) < sPlayerbotAIConfig->healDistance * 2 && bot->IsWithinLOSInMap(player); + bot->GetDistance2d(player) < sPlayerbotAIConfig.healDistance * 2 && bot->IsWithinLOSInMap(player); } Unit* PartyMemberToProtect::Calculate() @@ -129,7 +129,7 @@ Unit* PartyMemberToProtect::Calculate() continue; float attackDistance = 30.0f; - if (sServerFacade->GetDistance2d(pVictim, unit) > attackDistance) + if (ServerFacade::instance().GetDistance2d(pVictim, unit) > attackDistance) continue; if (botAI->IsTank((Player*)pVictim) && pVictim->GetHealthPct() > 10) diff --git a/src/Ai/Base/Value/PartyMemberValue.cpp b/src/Ai/Base/Value/PartyMemberValue.cpp index 4ae9be08ad..7477429094 100644 --- a/src/Ai/Base/Value/PartyMemberValue.cpp +++ b/src/Ai/Base/Value/PartyMemberValue.cpp @@ -4,6 +4,7 @@ */ #include "PartyMemberValue.h" +#include "Corpse.h" #include "Playerbots.h" #include "ServerFacade.h" @@ -102,10 +103,10 @@ Unit* PartyMemberValue::FindPartyMember(FindPlayerPredicate& predicate, bool ign bool PartyMemberValue::Check(Unit* player) { // return player && player != bot && player->GetMapId() == bot->GetMapId() && bot->IsWithinDistInMap(player, - // sPlayerbotAIConfig->sightDistance, false); + // sPlayerbotAIConfig.sightDistance, false); bool isGM = player->ToPlayer() && player->ToPlayer()->IsGameMaster(); return player && player->GetMapId() == bot->GetMapId() && !isGM && - bot->GetDistance(player) < sPlayerbotAIConfig->spellDistance * 2 && + bot->GetDistance(player) < sPlayerbotAIConfig.spellDistance * 2 && bot->IsWithinLOS(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); } @@ -170,4 +171,4 @@ Unit* PartyMemberMainTankValue::Calculate() { FindMainTankPlayer findMainTankPlayer(botAI); return FindPartyMember(findMainTankPlayer); -} \ No newline at end of file +} diff --git a/src/Ai/Base/Value/PartyMemberWithoutAuraValue.h b/src/Ai/Base/Value/PartyMemberWithoutAuraValue.h index 57bb958c76..4ba9558ffe 100644 --- a/src/Ai/Base/Value/PartyMemberWithoutAuraValue.h +++ b/src/Ai/Base/Value/PartyMemberWithoutAuraValue.h @@ -17,7 +17,7 @@ class PartyMemberWithoutAuraValue : public PartyMemberValue, public Qualified { public: PartyMemberWithoutAuraValue(PlayerbotAI* botAI, std::string const name = "party member without aura", - float range = sPlayerbotAIConfig->sightDistance) + float range = sPlayerbotAIConfig.sightDistance) : PartyMemberValue(botAI, name) { } diff --git a/src/Ai/Base/Value/PartyMemberWithoutItemValue.h b/src/Ai/Base/Value/PartyMemberWithoutItemValue.h index 9ca128ff8d..2918ea4c57 100644 --- a/src/Ai/Base/Value/PartyMemberWithoutItemValue.h +++ b/src/Ai/Base/Value/PartyMemberWithoutItemValue.h @@ -17,7 +17,7 @@ class PartyMemberWithoutItemValue : public PartyMemberValue, public Qualified { public: PartyMemberWithoutItemValue(PlayerbotAI* botAI, std::string const name = "party member without item", - float range = sPlayerbotAIConfig->farDistance) + float range = sPlayerbotAIConfig.farDistance) : PartyMemberValue(botAI, name) { } diff --git a/src/Ai/Base/Value/PositionValue.h b/src/Ai/Base/Value/PositionValue.h index 69e8e35943..a0123dafc8 100644 --- a/src/Ai/Base/Value/PositionValue.h +++ b/src/Ai/Base/Value/PositionValue.h @@ -71,7 +71,7 @@ class CurrentPositionValue : public LogCalculatedValue bool EqualToLast(WorldPosition value) override { - return value.fDist(lastValue) < sPlayerbotAIConfig->tooCloseDistance; + return value.fDist(lastValue) < sPlayerbotAIConfig.tooCloseDistance; } WorldPosition Calculate() override; diff --git a/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp b/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp index e9b79debf8..f2b6aef105 100644 --- a/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp +++ b/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp @@ -60,7 +60,7 @@ bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit) if (unit->IsHostileTo(bot) || unit->IsPlayer()) return false; - if (sServerFacade->GetDistance2d(bot, unit) <= sPlayerbotAIConfig->tooCloseDistance) + if (ServerFacade::instance().GetDistance2d(bot, unit) <= sPlayerbotAIConfig.tooCloseDistance) return false; if (unit->HasNpcFlag(UNIT_NPC_FLAG_SPIRITHEALER)) diff --git a/src/Ai/Base/Value/PossibleTargetsValue.h b/src/Ai/Base/Value/PossibleTargetsValue.h index 13023b3bcb..c710c2676b 100644 --- a/src/Ai/Base/Value/PossibleTargetsValue.h +++ b/src/Ai/Base/Value/PossibleTargetsValue.h @@ -15,7 +15,7 @@ class PossibleTargetsValue : public NearestUnitsValue { public: PossibleTargetsValue(PlayerbotAI* botAI, std::string const name = "possible targets", - float range = sPlayerbotAIConfig->sightDistance, bool ignoreLos = false) + float range = sPlayerbotAIConfig.sightDistance, bool ignoreLos = false) : NearestUnitsValue(botAI, name, range, ignoreLos) { } @@ -28,7 +28,7 @@ class PossibleTargetsValue : public NearestUnitsValue class AllTargetsValue : public PossibleTargetsValue { public: - AllTargetsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) + AllTargetsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig.sightDistance) : PossibleTargetsValue(botAI, "all targets", range, true) { } diff --git a/src/Ai/Base/Value/PvpValues.cpp b/src/Ai/Base/Value/PvpValues.cpp index e01b56ef2c..c8caf1441f 100644 --- a/src/Ai/Base/Value/PvpValues.cpp +++ b/src/Ai/Base/Value/PvpValues.cpp @@ -34,7 +34,7 @@ Unit* FlagCarrierValue::Calculate() if (carrier) { - if (ignoreRange || bot->IsWithinDistInMap(carrier, sPlayerbotAIConfig->sightDistance)) + if (ignoreRange || bot->IsWithinDistInMap(carrier, sPlayerbotAIConfig.sightDistance)) { return carrier; } @@ -65,7 +65,7 @@ Unit* FlagCarrierValue::Calculate() if (carrier) { - if (ignoreRange || bot->IsWithinDistInMap(carrier, sPlayerbotAIConfig->sightDistance)) + if (ignoreRange || bot->IsWithinDistInMap(carrier, sPlayerbotAIConfig.sightDistance)) { return carrier; } @@ -84,7 +84,7 @@ std::vector BgMastersValue::Calculate() std::vector entries; std::map>> battleMastersCache = - sRandomPlayerbotMgr->getBattleMastersCache(); + sRandomPlayerbotMgr.getBattleMastersCache(); entries.insert(entries.end(), battleMastersCache[TEAM_NEUTRAL][bgTypeId].begin(), battleMastersCache[TEAM_NEUTRAL][bgTypeId].end()); entries.insert(entries.end(), battleMastersCache[TEAM_ALLIANCE][bgTypeId].begin(), @@ -199,7 +199,7 @@ BattlegroundTypeId RpgBgTypeValue::Calculate() continue; std::map>> battleMastersCache = - sRandomPlayerbotMgr->getBattleMastersCache(); + sRandomPlayerbotMgr.getBattleMastersCache(); for (auto& entry : battleMastersCache[TEAM_NEUTRAL][bgTypeId]) if (entry == guidPosition.GetEntry()) diff --git a/src/Ai/Base/Value/RtiTargetValue.cpp b/src/Ai/Base/Value/RtiTargetValue.cpp index 205bcc3db0..4240e91c76 100644 --- a/src/Ai/Base/Value/RtiTargetValue.cpp +++ b/src/Ai/Base/Value/RtiTargetValue.cpp @@ -60,8 +60,8 @@ Unit* RtiTargetValue::Calculate() Unit* unit = botAI->GetUnit(guid); if (!unit || unit->isDead() || !bot->IsWithinLOSInMap(unit) || !AttackersValue::IsValidTarget(unit, bot) || - sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, unit), - sPlayerbotAIConfig->sightDistance)) + ServerFacade::instance().IsDistanceGreaterThan(ServerFacade::instance().GetDistance2d(bot, unit), + sPlayerbotAIConfig.sightDistance)) return nullptr; // Also prevent chasing raid icon targets that are too far away from the master, @@ -69,8 +69,8 @@ Unit* RtiTargetValue::Calculate() if (Player* master = botAI->GetMaster()) { if (master->IsInWorld() && master->GetMapId() == unit->GetMapId() && - sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(master, unit), - sPlayerbotAIConfig->sightDistance)) + ServerFacade::instance().IsDistanceGreaterThan(ServerFacade::instance().GetDistance2d(master, unit), + sPlayerbotAIConfig.sightDistance)) return nullptr; } diff --git a/src/Ai/Base/Value/SnareTargetValue.cpp b/src/Ai/Base/Value/SnareTargetValue.cpp index 96e958c2d8..7965198aa4 100644 --- a/src/Ai/Base/Value/SnareTargetValue.cpp +++ b/src/Ai/Base/Value/SnareTargetValue.cpp @@ -30,7 +30,7 @@ Unit* SnareTargetValue::Calculate() return unit; case CHASE_MOTION_TYPE: { - chaseTarget = sServerFacade->GetChaseTarget(unit); + chaseTarget = ServerFacade::instance().GetChaseTarget(unit); if (!chaseTarget) continue; Player* chaseTargetPlayer = ObjectAccessor::FindPlayer(chaseTarget->GetGUID()); diff --git a/src/Ai/Base/Value/Stances.cpp b/src/Ai/Base/Value/Stances.cpp index 2e7a18d2b9..81d9b285c5 100644 --- a/src/Ai/Base/Value/Stances.cpp +++ b/src/Ai/Base/Value/Stances.cpp @@ -48,7 +48,7 @@ WorldLocation Stance::GetNearLocation(float angle, float distance) WorldLocation MoveStance::GetLocationInternal() { Unit* target = GetTarget(); - float distance = std::max(sPlayerbotAIConfig->meleeDistance, target->GetCombatReach()); + float distance = std::max(sPlayerbotAIConfig.meleeDistance, target->GetCombatReach()); float angle = GetAngle(); return GetNearLocation(angle, distance); @@ -56,7 +56,7 @@ WorldLocation MoveStance::GetLocationInternal() std::string const Stance::GetTargetName() { return "current target"; } -float Stance::GetMaxDistance() { return sPlayerbotAIConfig->contactDistance; } +float Stance::GetMaxDistance() { return sPlayerbotAIConfig.contactDistance; } StanceValue::~StanceValue() { diff --git a/src/Ai/Base/Value/StatsValues.cpp b/src/Ai/Base/Value/StatsValues.cpp index dc3499c683..615f39f019 100644 --- a/src/Ai/Base/Value/StatsValues.cpp +++ b/src/Ai/Base/Value/StatsValues.cpp @@ -183,8 +183,8 @@ bool IsInCombatValue::Calculate() continue; if (member->IsInCombat() && - sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(member, bot), - sPlayerbotAIConfig->reactDistance)) + ServerFacade::instance().IsDistanceLessOrEqualThan(ServerFacade::instance().GetDistance2d(member, bot), + sPlayerbotAIConfig.reactDistance)) return true; } } diff --git a/src/Ai/Base/Value/TargetValue.cpp b/src/Ai/Base/Value/TargetValue.cpp index 598b78441b..21621545ea 100644 --- a/src/Ai/Base/Value/TargetValue.cpp +++ b/src/Ai/Base/Value/TargetValue.cpp @@ -175,4 +175,4 @@ Unit* BossTargetValue::Calculate() { FindBossTargetStrategy strategy(botAI); return FindTarget(&strategy); -} \ No newline at end of file +} diff --git a/src/Ai/Base/ValueContext.h b/src/Ai/Base/ValueContext.h index 7349e9d614..ab93bcea47 100644 --- a/src/Ai/Base/ValueContext.h +++ b/src/Ai/Base/ValueContext.h @@ -396,7 +396,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* nearest_game_objects(PlayerbotAI* botAI) { return new NearestGameObjects(botAI); } static UntypedValue* nearest_game_objects_no_los(PlayerbotAI* botAI) { - return new NearestGameObjects(botAI, sPlayerbotAIConfig->sightDistance, true); + return new NearestGameObjects(botAI, sPlayerbotAIConfig.sightDistance, true); } static UntypedValue* closest_game_objects(PlayerbotAI* botAI) { @@ -422,7 +422,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* possible_triggers(PlayerbotAI* botAI) { return new PossibleTriggersValue(botAI); } static UntypedValue* possible_targets_no_los(PlayerbotAI* botAI) { - return new PossibleTargetsValue(botAI, "possible targets", sPlayerbotAIConfig->sightDistance, true); + return new PossibleTargetsValue(botAI, "possible targets", sPlayerbotAIConfig.sightDistance, true); } static UntypedValue* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsValue(botAI); } static UntypedValue* prioritized_targets(PlayerbotAI* botAI) { return new PrioritizedTargetsValue(botAI); } diff --git a/src/Ai/Class/Dk/Trigger/DKTriggers.cpp b/src/Ai/Class/Dk/Trigger/DKTriggers.cpp index 90c1620953..4cad0c047c 100644 --- a/src/Ai/Class/Dk/Trigger/DKTriggers.cpp +++ b/src/Ai/Class/Dk/Trigger/DKTriggers.cpp @@ -75,4 +75,4 @@ bool DeathAndDecayCooldownTrigger::IsActive() return true; return bot->GetSpellCooldownDelay(spellId) >= 2000; -} \ No newline at end of file +} diff --git a/src/Ai/Class/Druid/Action/DruidActions.cpp b/src/Ai/Class/Druid/Action/DruidActions.cpp index 13336a674a..33b7826bf8 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidActions.cpp @@ -43,8 +43,8 @@ bool CastStarfallAction::isUseful() Unit* ccTarget = context->GetValue("current cc target")->Get(); if (ccTarget && ccTarget->IsAlive()) { - float dist2d = sServerFacade->GetDistance2d(ccTarget, aoePos.GetPositionX(), aoePos.GetPositionY()); - if (sServerFacade->IsDistanceLessOrEqualThan(dist2d, sPlayerbotAIConfig->aoeRadius)) + float dist2d = ServerFacade::instance().GetDistance2d(ccTarget, aoePos.GetPositionX(), aoePos.GetPositionY()); + if (ServerFacade::instance().IsDistanceLessOrEqualThan(dist2d, sPlayerbotAIConfig.aoeRadius)) return false; } @@ -75,7 +75,7 @@ std::vector CastRebirthAction::getPrerequisites() bool CastRebirthAction::isUseful() { return CastSpellAction::isUseful() && - AI_VALUE2(float, "distance", GetTargetName()) <= sPlayerbotAIConfig->spellDistance; + AI_VALUE2(float, "distance", GetTargetName()) <= sPlayerbotAIConfig.spellDistance; } Unit* CastRejuvenationOnNotFullAction::GetTarget() @@ -91,7 +91,7 @@ Unit* CastRejuvenationOnNotFullAction::GetTarget() { continue; } - if (player->GetDistance2d(bot) > sPlayerbotAIConfig->spellDistance) + if (player->GetDistance2d(bot) > sPlayerbotAIConfig.spellDistance) { continue; } @@ -107,4 +107,4 @@ Unit* CastRejuvenationOnNotFullAction::GetTarget() bool CastRejuvenationOnNotFullAction::isUseful() { return GetTarget(); -} \ No newline at end of file +} diff --git a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp index 4f4a4e5968..1f066dc349 100644 --- a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp @@ -36,7 +36,7 @@ bool CastCasterFormAction::isUseful() { return botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form", "flight form", "swift flight form", "moonkin form", nullptr) && - AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig->mediumHealth; + AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig.mediumHealth; } bool CastCasterFormAction::Execute(Event event) @@ -59,4 +59,4 @@ bool CastCancelTreeFormAction::Execute(Event event) bool CastTreeFormAction::isUseful() { return GetTarget() && CastSpellAction::isUseful() && !botAI->HasAura(33891, bot); -} \ No newline at end of file +} diff --git a/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.h b/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.h index 736de37cda..83775ef98c 100644 --- a/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.h +++ b/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.h @@ -24,4 +24,4 @@ } }; - #endif \ No newline at end of file + #endif diff --git a/src/Ai/Class/Hunter/Trigger/HunterTriggers.cpp b/src/Ai/Class/Hunter/Trigger/HunterTriggers.cpp index 091b8d5f72..84ece04b76 100644 --- a/src/Ai/Class/Hunter/Trigger/HunterTriggers.cpp +++ b/src/Ai/Class/Hunter/Trigger/HunterTriggers.cpp @@ -62,7 +62,7 @@ bool HuntersPetLowHealthTrigger::IsActive() bool HuntersPetMediumHealthTrigger::IsActive() { Unit* pet = AI_VALUE(Unit*, "pet target"); - return pet && AI_VALUE2(uint8, "health", "pet target") < sPlayerbotAIConfig->mediumHealth && + return pet && AI_VALUE2(uint8, "health", "pet target") < sPlayerbotAIConfig.mediumHealth && !AI_VALUE2(bool, "dead", "pet target") && !AI_VALUE2(bool, "mounted", "self target"); } @@ -74,7 +74,7 @@ bool HunterPetNotHappy::IsActive() bool HunterAspectOfTheViperTrigger::IsActive() { return SpellTrigger::IsActive() && !botAI->HasAura(spell, GetTarget()) && - AI_VALUE2(uint8, "mana", "self target") < (sPlayerbotAIConfig->lowMana / 2); + AI_VALUE2(uint8, "mana", "self target") < (sPlayerbotAIConfig.lowMana / 2); ; } @@ -96,7 +96,7 @@ bool SwitchToRangedTrigger::IsActive() Unit* target = AI_VALUE(Unit*, "current target"); return botAI->HasStrategy("close", BOT_STATE_COMBAT) && target && (target->GetVictim() != bot && - sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"), 8.0f)); + ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, "distance", "current target"), 8.0f)); } bool SwitchToMeleeTrigger::IsActive() @@ -104,7 +104,7 @@ bool SwitchToMeleeTrigger::IsActive() Unit* target = AI_VALUE(Unit*, "current target"); return botAI->HasStrategy("ranged", BOT_STATE_COMBAT) && target && (target->GetVictim() == bot && - sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f)); + ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "current target"), 8.0f)); } bool NoTrackTrigger::IsActive() diff --git a/src/Ai/Class/Mage/Action/MageActions.cpp b/src/Ai/Class/Mage/Action/MageActions.cpp index 67587e3213..5eb63ea866 100644 --- a/src/Ai/Class/Mage/Action/MageActions.cpp +++ b/src/Ai/Class/Mage/Action/MageActions.cpp @@ -61,13 +61,13 @@ bool CastFrostNovaAction::isUseful() if (target->isFrozen()) return false; - return sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); + return ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); } bool CastConeOfColdAction::isUseful() { bool facingTarget = AI_VALUE2(bool, "facing", "current target"); - bool targetClose = sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); + bool targetClose = ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); return facingTarget && targetClose; } @@ -105,7 +105,7 @@ Unit* CastFocusMagicOnPartyAction::GetTarget() if (!member || member == bot || !member->IsAlive()) continue; - if (member->GetMap() != bot->GetMap() || bot->GetDistance(member) > sPlayerbotAIConfig->spellDistance) + if (member->GetMap() != bot->GetMap() || bot->GetDistance(member) > sPlayerbotAIConfig.spellDistance) continue; if (member->HasAura(54646)) diff --git a/src/Ai/Class/Paladin/PaladinAiObjectContext.cpp b/src/Ai/Class/Paladin/PaladinAiObjectContext.cpp index 4929eb9df0..45f676ec9f 100644 --- a/src/Ai/Class/Paladin/PaladinAiObjectContext.cpp +++ b/src/Ai/Class/Paladin/PaladinAiObjectContext.cpp @@ -454,4 +454,4 @@ void PaladinAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContext void PaladinAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList& valueContexts) { AiObjectContext::BuildSharedValueContexts(valueContexts); -} \ No newline at end of file +} diff --git a/src/Ai/Class/Paladin/Strategy/OffhealRetPaladinStrategy.h b/src/Ai/Class/Paladin/Strategy/OffhealRetPaladinStrategy.h index 3dfd24aa81..eeb9672ede 100644 --- a/src/Ai/Class/Paladin/Strategy/OffhealRetPaladinStrategy.h +++ b/src/Ai/Class/Paladin/Strategy/OffhealRetPaladinStrategy.h @@ -24,4 +24,4 @@ class OffhealRetPaladinStrategy : public GenericPaladinStrategy } }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/Class/Priest/Action/PriestActions.cpp b/src/Ai/Class/Priest/Action/PriestActions.cpp index e1c61033a1..ae55b104dc 100644 --- a/src/Ai/Class/Priest/Action/PriestActions.cpp +++ b/src/Ai/Class/Priest/Action/PriestActions.cpp @@ -30,11 +30,11 @@ Unit* CastPowerWordShieldOnAlmostFullHealthBelowAction::GetTarget() { continue; } - if (player->GetHealthPct() > sPlayerbotAIConfig->almostFullHealth) + if (player->GetHealthPct() > sPlayerbotAIConfig.almostFullHealth) { continue; } - if (player->GetDistance2d(bot) > sPlayerbotAIConfig->spellDistance) + if (player->GetDistance2d(bot) > sPlayerbotAIConfig.spellDistance) { continue; } @@ -59,11 +59,11 @@ bool CastPowerWordShieldOnAlmostFullHealthBelowAction::isUseful() { continue; } - if (player->GetHealthPct() > sPlayerbotAIConfig->almostFullHealth) + if (player->GetHealthPct() > sPlayerbotAIConfig.almostFullHealth) { continue; } - if (player->GetDistance2d(bot) > sPlayerbotAIConfig->spellDistance) + if (player->GetDistance2d(bot) > sPlayerbotAIConfig.spellDistance) { continue; } @@ -89,7 +89,7 @@ Unit* CastPowerWordShieldOnNotFullAction::GetTarget() { continue; } - if (player->GetDistance2d(bot) > sPlayerbotAIConfig->spellDistance) + if (player->GetDistance2d(bot) > sPlayerbotAIConfig.spellDistance) { continue; } @@ -105,4 +105,4 @@ Unit* CastPowerWordShieldOnNotFullAction::GetTarget() bool CastPowerWordShieldOnNotFullAction::isUseful() { return GetTarget(); -} \ No newline at end of file +} diff --git a/src/Ai/Class/Priest/Trigger/PriestTriggers.cpp b/src/Ai/Class/Priest/Trigger/PriestTriggers.cpp index 894780ad45..7f61ba6eec 100644 --- a/src/Ai/Class/Priest/Trigger/PriestTriggers.cpp +++ b/src/Ai/Class/Priest/Trigger/PriestTriggers.cpp @@ -68,14 +68,14 @@ bool ShadowformTrigger::IsActive() { return !botAI->HasAura("shadowform", bot); bool ShadowfiendTrigger::IsActive() { return BoostTrigger::IsActive() && !bot->HasSpellCooldown(34433); } BindingHealTrigger::BindingHealTrigger(PlayerbotAI* botAI) - : PartyMemberLowHealthTrigger(botAI, "binding heal", sPlayerbotAIConfig->lowHealth, 0) + : PartyMemberLowHealthTrigger(botAI, "binding heal", sPlayerbotAIConfig.lowHealth, 0) { } bool BindingHealTrigger::IsActive() { return PartyMemberLowHealthTrigger::IsActive() && - AI_VALUE2(uint8, "health", "self target") < sPlayerbotAIConfig->mediumHealth; + AI_VALUE2(uint8, "health", "self target") < sPlayerbotAIConfig.mediumHealth; } const std::set MindSearChannelCheckTrigger::MIND_SEAR_SPELL_IDS = { diff --git a/src/Ai/Class/Rogue/Action/RogueActions.cpp b/src/Ai/Class/Rogue/Action/RogueActions.cpp index 3019e0ec86..fb4975ab33 100644 --- a/src/Ai/Class/Rogue/Action/RogueActions.cpp +++ b/src/Ai/Class/Rogue/Action/RogueActions.cpp @@ -14,7 +14,7 @@ bool CastStealthAction::isUseful() { Unit* target = AI_VALUE(Unit*, "current target"); - if (target && bot->GetDistance(target) >= sPlayerbotAIConfig->spellDistance) + if (target && bot->GetDistance(target) >= sPlayerbotAIConfig.spellDistance) return false; return true; } diff --git a/src/Ai/Class/Rogue/RogueAiObjectContext.cpp b/src/Ai/Class/Rogue/RogueAiObjectContext.cpp index 9875f23dec..8586d93d14 100644 --- a/src/Ai/Class/Rogue/RogueAiObjectContext.cpp +++ b/src/Ai/Class/Rogue/RogueAiObjectContext.cpp @@ -227,4 +227,4 @@ void RogueAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextLi void RogueAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList& valueContexts) { AiObjectContext::BuildSharedValueContexts(valueContexts); -} \ No newline at end of file +} diff --git a/src/Ai/Class/Rogue/Strategy/AssassinationRogueStrategy.h b/src/Ai/Class/Rogue/Strategy/AssassinationRogueStrategy.h index 5692528a0e..3a6ac209d1 100644 --- a/src/Ai/Class/Rogue/Strategy/AssassinationRogueStrategy.h +++ b/src/Ai/Class/Rogue/Strategy/AssassinationRogueStrategy.h @@ -16,4 +16,4 @@ class AssassinationRogueStrategy : public MeleeCombatStrategy uint32 GetType() const override { return MeleeCombatStrategy::GetType() | STRATEGY_TYPE_DPS; } }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp b/src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp index fd9901552e..c33dbb7fe4 100644 --- a/src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp +++ b/src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp @@ -22,7 +22,7 @@ bool UnstealthTrigger::IsActive() return botAI->HasAura("stealth", bot) && !AI_VALUE(uint8, "attacker count") && (AI_VALUE2(bool, "moving", "self target") && ((botAI->GetMaster() && - sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), 10.0f) && + ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), 10.0f) && AI_VALUE2(bool, "moving", "group leader")) || !AI_VALUE(uint8, "attacker count"))); } @@ -60,7 +60,7 @@ bool StealthTrigger::IsActive() if (bot->InArena()) distance += 15; - return target && sServerFacade->GetDistance2d(bot, target) < distance; + return target && ServerFacade::instance().GetDistance2d(bot, target) < distance; } bool SapTrigger::IsPossible() { return bot->GetLevel() > 10 && bot->HasSpell(6770) && !bot->IsInCombat(); } @@ -99,8 +99,8 @@ bool SprintTrigger::IsActive() return AI_VALUE2(bool, "moving", "self target") && (AI_VALUE2(bool, "moving", "dps target") || AI_VALUE2(bool, "moving", "enemy player target")) && targeted && - (sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "dps target"), distance) || - sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "enemy player target"), distance)); + (ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, "distance", "dps target"), distance) || + ServerFacade::instance().IsDistanceGreaterThan(AI_VALUE2(float, "distance", "enemy player target"), distance)); } bool ExposeArmorTrigger::IsActive() diff --git a/src/Ai/Class/Warlock/Action/WarlockActions.cpp b/src/Ai/Class/Warlock/Action/WarlockActions.cpp index e899c7a2f6..4e09eb6523 100644 --- a/src/Ai/Class/Warlock/Action/WarlockActions.cpp +++ b/src/Ai/Class/Warlock/Action/WarlockActions.cpp @@ -25,7 +25,7 @@ const int ITEM_SOUL_SHARD = 6265; bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 26; } // Checks if the bot's health is above a certain threshold, and if so, allows casting Life Tap -bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->lowHealth; } +bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig.lowHealth; } // Checks if the target marked with the moon icon can be banished bool CastBanishOnCcAction::isPossible() @@ -288,7 +288,7 @@ bool UseSoulstoneMasterAction::Execute(Event event) soulstoneReservations[master->GetGUID()] = now + 2500; // Reserve for 2.5 seconds } - float distance = sServerFacade->GetDistance2d(bot, master); + float distance = ServerFacade::instance().GetDistance2d(bot, master); if (distance >= 30.0f) return false; @@ -325,7 +325,7 @@ bool UseSoulstoneTankAction::Execute(Event event) if (soulstoneReservations.count(member->GetGUID()) && soulstoneReservations[member->GetGUID()] > now) continue; // Already being soulstoned - float distance = sServerFacade->GetDistance2d(bot, member); + float distance = ServerFacade::instance().GetDistance2d(bot, member); if (distance < 30.0f && bot->IsWithinLOSInMap(member)) { chosenTank = member; @@ -348,7 +348,7 @@ bool UseSoulstoneTankAction::Execute(Event event) soulstoneReservations[member->GetGUID()] > now) continue; // Already being soulstoned - float distance = sServerFacade->GetDistance2d(bot, member); + float distance = ServerFacade::instance().GetDistance2d(bot, member); if (distance < 30.0f && bot->IsWithinLOSInMap(member)) { chosenTank = member; @@ -392,7 +392,7 @@ bool UseSoulstoneHealerAction::Execute(Event event) soulstoneReservations[member->GetGUID()] > now) continue; // Already being soulstoned - float distance = sServerFacade->GetDistance2d(bot, member); + float distance = ServerFacade::instance().GetDistance2d(bot, member); if (distance < 30.0f && bot->IsWithinLOSInMap(member)) { healer = member; diff --git a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp index 04592c9b41..53e77669b2 100644 --- a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp +++ b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp @@ -96,7 +96,7 @@ bool DecimationTrigger::IsActive() // Checks if the bot's mana is below 85% and health is above a low health threshold bool LifeTapTrigger::IsActive() { - if (AI_VALUE2(uint8, "health", "self target") <= sPlayerbotAIConfig->lowHealth) + if (AI_VALUE2(uint8, "health", "self target") <= sPlayerbotAIConfig.lowHealth) return false; if (!AI_VALUE2(bool, "has mana", "self target")) diff --git a/src/Ai/Class/Warrior/Trigger/WarriorTriggers.cpp b/src/Ai/Class/Warrior/Trigger/WarriorTriggers.cpp index b561c4815d..5aa419c927 100644 --- a/src/Ai/Class/Warrior/Trigger/WarriorTriggers.cpp +++ b/src/Ai/Class/Warrior/Trigger/WarriorTriggers.cpp @@ -9,7 +9,7 @@ bool BloodrageBuffTrigger::IsActive() { - return AI_VALUE2(uint8, "health", "self target") >= sPlayerbotAIConfig->mediumHealth && + return AI_VALUE2(uint8, "health", "self target") >= sPlayerbotAIConfig.mediumHealth && AI_VALUE2(uint8, "rage", "self target") < 20; } diff --git a/src/Ai/Class/Warrior/WarriorAiObjectContext.cpp b/src/Ai/Class/Warrior/WarriorAiObjectContext.cpp index 3a9af55172..22754beab4 100644 --- a/src/Ai/Class/Warrior/WarriorAiObjectContext.cpp +++ b/src/Ai/Class/Warrior/WarriorAiObjectContext.cpp @@ -358,4 +358,4 @@ void WarriorAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContext void WarriorAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList& valueContexts) { AiObjectContext::BuildSharedValueContexts(valueContexts); -} \ No newline at end of file +} diff --git a/src/Ai/Dungeon/UtgardePinnacle/Multiplier/UtgardePinnacleMultipliers.cpp b/src/Ai/Dungeon/UtgardePinnacle/Multiplier/UtgardePinnacleMultipliers.cpp index 9a950933e5..cc34cb6a4e 100644 --- a/src/Ai/Dungeon/UtgardePinnacle/Multiplier/UtgardePinnacleMultipliers.cpp +++ b/src/Ai/Dungeon/UtgardePinnacle/Multiplier/UtgardePinnacleMultipliers.cpp @@ -96,4 +96,4 @@ float YmironMultiplier::GetValue(Action* action) } } return 1.0f; -} \ No newline at end of file +} diff --git a/src/Ai/Dungeon/UtgardePinnacle/Trigger/UtgardePinnacleTriggers.cpp b/src/Ai/Dungeon/UtgardePinnacle/Trigger/UtgardePinnacleTriggers.cpp index b9b21dc846..dc35e8be08 100644 --- a/src/Ai/Dungeon/UtgardePinnacle/Trigger/UtgardePinnacleTriggers.cpp +++ b/src/Ai/Dungeon/UtgardePinnacle/Trigger/UtgardePinnacleTriggers.cpp @@ -53,4 +53,4 @@ bool YmironBaneTrigger::IsActive() if (!boss) { return false; } return boss->FindCurrentSpellBySpellId(SPELL_BANE) || boss->HasAura(SPELL_BANE); -} \ No newline at end of file +} diff --git a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h index 27bced8ea4..4308871c85 100644 --- a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h +++ b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h @@ -15,4 +15,4 @@ class RaidBwlStrategy : public Strategy // virtual void InitMultipliers(std::vector &multipliers) override; }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp index 14c8ada9ac..d6a91d0477 100644 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp +++ b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp @@ -6810,7 +6810,7 @@ bool IccLichKingShadowTrapAction::Execute(Event event) Difficulty diff = bot->GetRaidDifficulty(); - if (sPlayerbotAIConfig->EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { //-------CHEAT------- if (!bot->HasAura(SPELL_EXPERIENCED)) @@ -7006,7 +7006,7 @@ bool IccLichKingWinterAction::Execute(Event event) Difficulty diff = bot->GetRaidDifficulty(); - if (sPlayerbotAIConfig->EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { //------CHEAT------- if (!bot->HasAura(SPELL_EXPERIENCED)) @@ -7792,7 +7792,7 @@ bool IccLichKingAddsAction::Execute(Event event) Difficulty diff = bot->GetRaidDifficulty(); - if (sPlayerbotAIConfig->EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { //------CHEAT------- if (!bot->HasAura(SPELL_EXPERIENCED)) @@ -9192,7 +9192,7 @@ void IccLichKingAddsAction::HandleValkyrAssignment(const std::vector& gra bot->SetFacingToObject(myValkyr); Difficulty diff = bot->GetRaidDifficulty(); - if (sPlayerbotAIConfig->EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { //---------CHEAT--------- if (!myValkyr->HasAura(SPELL_HAMMER_OF_JUSTICE)) diff --git a/src/Ai/Raid/Icecrown/RaidIccScripts.h b/src/Ai/Raid/Icecrown/RaidIccScripts.h index 98c7d1ba3b..e2fc782cad 100644 --- a/src/Ai/Raid/Icecrown/RaidIccScripts.h +++ b/src/Ai/Raid/Icecrown/RaidIccScripts.h @@ -3,4 +3,4 @@ #include "../../../../src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h" -#endif \ No newline at end of file +#endif diff --git a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp index b9ebb8ca8d..b34fe003fc 100644 --- a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp +++ b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp @@ -287,7 +287,7 @@ bool IccPutricideGrowingOozePuddleTrigger::IsActive() Difficulty diff = bot->GetRaidDifficulty(); - if (sPlayerbotAIConfig->EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { //-------CHEAT------- if (!bot->HasAura(SPELL_EXPERIENCED)) @@ -834,7 +834,7 @@ bool IccSindragosaGroupPositionTrigger::IsActive() Difficulty diff = bot->GetRaidDifficulty(); - if (sPlayerbotAIConfig->EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { //-------CHEAT------- if (!bot->HasAura(SPELL_EXPERIENCED)) diff --git a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp index 2e5d7abcfd..adf0eac488 100644 --- a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp +++ b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp @@ -617,7 +617,7 @@ bool NetherspiteBlockRedBeamAction::Execute(Event event) if (!wasBlocking) { std::map placeholders{{"%player", bot->GetName()}}; - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "netherspite_beam_blocking_red", "%player is moving to block the red beam!", placeholders); bot->Yell(text, LANG_UNIVERSAL); } @@ -700,7 +700,7 @@ bool NetherspiteBlockBlueBeamAction::Execute(Event event) if (wasBlocking && !isBlockingNow) { std::map placeholders{{"%player", bot->GetName()}}; - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "netherspite_beam_leaving_blue", "%player is leaving the blue beam--next blocker up!", placeholders); bot->Yell(text, LANG_UNIVERSAL); _wasBlockingBlueBeam[botGuid] = false; @@ -711,7 +711,7 @@ bool NetherspiteBlockBlueBeamAction::Execute(Event event) if (!wasBlocking) { std::map placeholders{{"%player", bot->GetName()}}; - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "netherspite_beam_blocking_blue", "%player is moving to block the blue beam!", placeholders); bot->Yell(text, LANG_UNIVERSAL); } @@ -793,7 +793,7 @@ bool NetherspiteBlockGreenBeamAction::Execute(Event event) if (wasBlocking && !isBlockingNow) { std::map placeholders{{"%player", bot->GetName()}}; - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "netherspite_beam_leaving_green", "%player is leaving the green beam--next blocker up!", placeholders); bot->Yell(text, LANG_UNIVERSAL); _wasBlockingGreenBeam[botGuid] = false; @@ -804,7 +804,7 @@ bool NetherspiteBlockGreenBeamAction::Execute(Event event) if (!wasBlocking) { std::map placeholders{{"%player", bot->GetName()}}; - std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( "netherspite_beam_blocking_green", "%player is moving to block the green beam!", placeholders); bot->Yell(text, LANG_UNIVERSAL); } diff --git a/src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h b/src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h index fa2b7d37a5..e3f606c949 100644 --- a/src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h +++ b/src/Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h @@ -260,4 +260,4 @@ class RaidKarazhanTriggerContext : public NamedObjectContext PlayerbotAI* botAI) { return new NightbaneNeedToManageTimersAndTrackersTrigger(botAI); } }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp b/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp index 724b91902f..2ee68bca4b 100644 --- a/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp +++ b/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp @@ -243,4 +243,4 @@ bool ExitTwilightPortalAction::Execute(Event event) bot->GetSession()->HandleGameObjectUseOpcode(data1); return true; -} \ No newline at end of file +} diff --git a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp index b7c3723bb3..b20425d224 100644 --- a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp +++ b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp @@ -2964,7 +2964,7 @@ bool YoggSaronMoveToEnterPortalAction::Execute(Event event) { return MoveNear(bot->GetMapId(), assignedPortalPosition.GetPositionX(), assignedPortalPosition.GetPositionY(), - assignedPortalPosition.GetPositionZ(), sPlayerbotAIConfig->contactDistance, + assignedPortalPosition.GetPositionZ(), sPlayerbotAIConfig.contactDistance, MovementPriority::MOVEMENT_FORCED); } } diff --git a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp b/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp index 16df9c9af2..0a51ca407b 100644 --- a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp +++ b/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp @@ -25,4 +25,4 @@ float FlameLeviathanMultiplier::GetValue(Action* action) // if (dynamic_cast(action)) // return 0.0f; return 1.0f; -} \ No newline at end of file +} diff --git a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.h b/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.h index 736d9483b2..6c9a468fcd 100644 --- a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.h +++ b/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.h @@ -14,4 +14,4 @@ class FlameLeviathanMultiplier : public Multiplier virtual float GetValue(Action* action); }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h b/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h index 6538845c2e..81bb93c3a5 100644 --- a/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h +++ b/src/Ai/Raid/Ulduar/Strategy/RaidUlduarStrategy.h @@ -15,4 +15,4 @@ class RaidUlduarStrategy : public Strategy virtual void InitMultipliers(std::vector& multipliers) override; }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp index f883917ed0..a4bc2cf9a2 100644 --- a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp +++ b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp @@ -1621,7 +1621,7 @@ bool VezaxCheatTrigger::IsActive() return false; } - return AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->lowMana; + return AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.lowMana; } bool VezaxShadowCrashTrigger::IsActive() diff --git a/src/Ai/World/Rpg/Action/NewRpgAction.cpp b/src/Ai/World/Rpg/Action/NewRpgAction.cpp index 01595edf79..41fec2291c 100644 --- a/src/Ai/World/Rpg/Action/NewRpgAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgAction.cpp @@ -440,4 +440,4 @@ bool NewRpgTravelFlightAction::Execute(Event event) botAI->rpgInfo.ChangeToIdle(); } return true; -} \ No newline at end of file +} diff --git a/src/Ai/World/Rpg/Action/NewRpgAction.h b/src/Ai/World/Rpg/Action/NewRpgAction.h index 7669253743..0e621fc406 100644 --- a/src/Ai/World/Rpg/Action/NewRpgAction.h +++ b/src/Ai/World/Rpg/Action/NewRpgAction.h @@ -103,4 +103,4 @@ class NewRpgTravelFlightAction : public NewRpgBaseAction bool Execute(Event event) override; }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp index 4898d7e36c..0204a21331 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp @@ -866,7 +866,7 @@ bool NewRpgBaseAction::GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot) { - const std::vector& locs = sRandomPlayerbotMgr->locsPerLevelCache[bot->GetLevel()]; + const std::vector& locs = sRandomPlayerbotMgr.locsPerLevelCache[bot->GetLevel()]; float hiRange = 500.0f; float loRange = 2500.0f; if (bot->GetLevel() < 5) @@ -925,8 +925,8 @@ WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot) WorldPosition NewRpgBaseAction::SelectRandomCampPos(Player* bot) { const std::vector& locs = IsAlliance(bot->getRace()) - ? sRandomPlayerbotMgr->allianceStarterPerLevelCache[bot->GetLevel()] - : sRandomPlayerbotMgr->hordeStarterPerLevelCache[bot->GetLevel()]; + ? sRandomPlayerbotMgr.allianceStarterPerLevelCache[bot->GetLevel()] + : sRandomPlayerbotMgr.hordeStarterPerLevelCache[bot->GetLevel()]; bool inCity = false; @@ -969,7 +969,7 @@ WorldPosition NewRpgBaseAction::SelectRandomCampPos(Player* bot) bool NewRpgBaseAction::SelectRandomFlightTaxiNode(ObjectGuid& flightMaster, uint32& fromNode, uint32& toNode) { - Creature* nearestFlightMaster = sFlightMasterCache->GetNearestFlightMaster(bot); + Creature* nearestFlightMaster = FlightMasterCache::Instance().GetNearestFlightMaster(bot); if (!nearestFlightMaster || bot->GetDistance(nearestFlightMaster) > 500.0f) return false; @@ -1015,8 +1015,8 @@ bool NewRpgBaseAction::SelectRandomFlightTaxiNode(ObjectGuid& flightMaster, uint capital = zone->flags & AREA_FLAG_CAPITAL; } - auto itr = sRandomPlayerbotMgr->zone2LevelBracket.find(nodeZoneId); - if (!capital && itr == sRandomPlayerbotMgr->zone2LevelBracket.end()) + auto itr = sRandomPlayerbotMgr.zone2LevelBracket.find(nodeZoneId); + if (!capital && itr == sRandomPlayerbotMgr.zone2LevelBracket.end()) continue; if (!capital && (bot->GetLevel() < itr->second.low || bot->GetLevel() > itr->second.high)) @@ -1040,13 +1040,13 @@ bool NewRpgBaseAction::RandomChangeStatus(std::vector candidateSta uint32 probSum = 0; for (NewRpgStatus status : candidateStatus) { - if (sPlayerbotAIConfig->RpgStatusProbWeight[status] == 0) + if (sPlayerbotAIConfig.RpgStatusProbWeight[status] == 0) continue; if (CheckRpgStatusAvailable(status)) { availableStatus.push_back(status); - probSum += sPlayerbotAIConfig->RpgStatusProbWeight[status]; + probSum += sPlayerbotAIConfig.RpgStatusProbWeight[status]; } } // Safety check. Default to "rest" if all RPG weights = 0 @@ -1061,7 +1061,7 @@ bool NewRpgBaseAction::RandomChangeStatus(std::vector candidateSta NewRpgStatus chosenStatus = RPG_STATUS_END; for (NewRpgStatus status : availableStatus) { - accumulate += sPlayerbotAIConfig->RpgStatusProbWeight[status]; + accumulate += sPlayerbotAIConfig.RpgStatusProbWeight[status]; if (accumulate >= rand) { chosenStatus = status; diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.h b/src/Ai/World/Rpg/Action/NewRpgBaseAction.h index 0d2a96fbca..910e5f4942 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.h +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.h @@ -64,4 +64,4 @@ class NewRpgBaseAction : public MovementAction const uint32 stuckTime = 5 * 60 * 1000; }; -#endif \ No newline at end of file +#endif diff --git a/src/Ai/World/Rpg/NewRpgInfo.cpp b/src/Ai/World/Rpg/NewRpgInfo.cpp index ff830464a5..889fb1ff92 100644 --- a/src/Ai/World/Rpg/NewRpgInfo.cpp +++ b/src/Ai/World/Rpg/NewRpgInfo.cpp @@ -136,4 +136,4 @@ std::string NewRpgInfo::ToString() out << "UNKNOWN"; } return out.str(); -} \ No newline at end of file +} diff --git a/src/Ai/World/Rpg/NewRpgInfo.h b/src/Ai/World/Rpg/NewRpgInfo.h index 5c14f70d17..20f801bf73 100644 --- a/src/Ai/World/Rpg/NewRpgInfo.h +++ b/src/Ai/World/Rpg/NewRpgInfo.h @@ -130,4 +130,4 @@ struct NewRpgStatistic // not sure is it necessary but keep it for now #define RPG_INFO(x, y) botAI->rpgInfo.x.y -#endif \ No newline at end of file +#endif diff --git a/src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp b/src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp index 6635409229..0804320ad9 100644 --- a/src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp +++ b/src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp @@ -1,4 +1,4 @@ #include "NewRpgTriggers.h" #include "PlayerbotAI.h" -bool NewRpgStatusTrigger::IsActive() { return status == botAI->rpgInfo.status; } \ No newline at end of file +bool NewRpgStatusTrigger::IsActive() { return status == botAI->rpgInfo.status; } diff --git a/src/Bot/Cmd/PlayerbotCommandServer.cpp b/src/Bot/Cmd/PlayerbotCommandServer.cpp index 83931229a0..1532cc0543 100644 --- a/src/Bot/Cmd/PlayerbotCommandServer.cpp +++ b/src/Bot/Cmd/PlayerbotCommandServer.cpp @@ -48,7 +48,7 @@ void session(socket_ptr sock) std::string buffer, request; while (ReadLine(sock, &buffer, &request)) { - std::string const response = sRandomPlayerbotMgr->HandleRemoteCommand(request) + "\n"; + std::string const response = sRandomPlayerbotMgr.HandleRemoteCommand(request) + "\n"; boost::asio::write(*sock, boost::asio::buffer(response.c_str(), response.size())); request = ""; } @@ -72,19 +72,19 @@ void server(Acore::Asio::IoContext& io_service, short port) void Run() { - if (!sPlayerbotAIConfig->commandServerPort) + if (!sPlayerbotAIConfig.commandServerPort) { return; } std::ostringstream s; - s << "Starting Playerbots Command Server on port " << sPlayerbotAIConfig->commandServerPort; + s << "Starting Playerbots Command Server on port " << sPlayerbotAIConfig.commandServerPort; LOG_INFO("playerbots", "{}", s.str().c_str()); try { Acore::Asio::IoContext io_service; - server(io_service, sPlayerbotAIConfig->commandServerPort); + server(io_service, sPlayerbotAIConfig.commandServerPort); } catch (std::exception& e) diff --git a/src/Bot/Cmd/PlayerbotCommandServer.h b/src/Bot/Cmd/PlayerbotCommandServer.h index e1b93a338e..eb410c644c 100644 --- a/src/Bot/Cmd/PlayerbotCommandServer.h +++ b/src/Bot/Cmd/PlayerbotCommandServer.h @@ -9,17 +9,24 @@ class PlayerbotCommandServer { public: - PlayerbotCommandServer() {} - virtual ~PlayerbotCommandServer() {} - static PlayerbotCommandServer* instance() + static PlayerbotCommandServer& instance() { static PlayerbotCommandServer instance; - return &instance; + + return instance; } void Start(); -}; -#define sPlayerbotCommandServer PlayerbotCommandServer::instance() +private: + PlayerbotCommandServer() = default; + ~PlayerbotCommandServer() = default; + + PlayerbotCommandServer(const PlayerbotCommandServer&) = delete; + PlayerbotCommandServer& operator=(const PlayerbotCommandServer&) = delete; + + PlayerbotCommandServer(PlayerbotCommandServer&&) = delete; + PlayerbotCommandServer& operator=(PlayerbotCommandServer&&) = delete; +}; #endif diff --git a/src/Bot/Debug/PerfMonitor.cpp b/src/Bot/Debug/PerfMonitor.cpp index c6d134edb9..82eece916f 100644 --- a/src/Bot/Debug/PerfMonitor.cpp +++ b/src/Bot/Debug/PerfMonitor.cpp @@ -10,7 +10,7 @@ PerfMonitorOperation* PerfMonitor::start(PerformanceMetric metric, std::string const name, PerformanceStack* stack) { - if (!sPlayerbotAIConfig->perfMonEnabled) + if (!sPlayerbotAIConfig.perfMonEnabled) return nullptr; std::string stackName = name; diff --git a/src/Bot/Debug/PerfMonitor.h b/src/Bot/Debug/PerfMonitor.h index 0d3eb8aad1..156392ee21 100644 --- a/src/Bot/Debug/PerfMonitor.h +++ b/src/Bot/Debug/PerfMonitor.h @@ -11,17 +11,16 @@ #include #include #include - -#include "Common.h" +#include typedef std::vector PerformanceStack; struct PerformanceData { - uint64 minTime; - uint64 maxTime; - uint64 totalTime; - uint32 count; + uint64_t minTime; + uint64_t maxTime; + uint64_t totalTime; + uint32_t count; std::mutex lock; }; @@ -50,21 +49,28 @@ class PerfMonitorOperation class PerfMonitor { public: - PerfMonitor(){}; - virtual ~PerfMonitor(){}; - static PerfMonitor* instance() + static PerfMonitor& instance() { static PerfMonitor instance; - return &instance; + + return instance; } -public: PerfMonitorOperation* start(PerformanceMetric metric, std::string const name, PerformanceStack* stack = nullptr); void PrintStats(bool perTick = false, bool fullStack = false); void Reset(); private: + PerfMonitor() = default; + virtual ~PerfMonitor() = default; + + PerfMonitor(const PerfMonitor&) = delete; + PerfMonitor& operator=(const PerfMonitor&) = delete; + + PerfMonitor(PerfMonitor&&) = delete; + PerfMonitor& operator=(PerfMonitor&&) = delete; + std::map > data; std::mutex lock; }; diff --git a/src/Bot/Engine/Engine.cpp b/src/Bot/Engine/Engine.cpp index 6ed61a1a4b..bc24baf560 100644 --- a/src/Bot/Engine/Engine.cpp +++ b/src/Bot/Engine/Engine.cpp @@ -142,7 +142,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) { LogAction("--- AI Tick ---"); - if (sPlayerbotAIConfig->logValuesPerTick) + if (sPlayerbotAIConfig.logValuesPerTick) LogValues(); bool actionExecuted = false; @@ -154,7 +154,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) PushDefaultActions(); uint32 iterations = 0; - uint32 iterationsPerTick = queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick); + uint32 iterationsPerTick = queue.Size() * (minimal ? 2 : sPlayerbotAIConfig.iterationsPerTick); while (++iterations <= iterationsPerTick) { @@ -204,7 +204,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) } } - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_ACTION, action->getName(), &aiObjectContext->performanceStack); + PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_ACTION, action->getName(), &aiObjectContext->performanceStack); actionExecuted = ListenAndExecute(action, event); if (pmo) pmo->finish(); @@ -457,7 +457,7 @@ void Engine::ProcessTriggers(bool minimal) continue; PerfMonitorOperation* pmo = - sPerfMonitor->start(PERF_MON_TRIGGER, trigger->getName(), &aiObjectContext->performanceStack); + sPerfMonitor.start(PERF_MON_TRIGGER, trigger->getName(), &aiObjectContext->performanceStack); Event event = trigger->Check(); if (pmo) pmo->finish(); @@ -599,7 +599,7 @@ bool Engine::ListenAndExecute(Action* action, Event event) void Engine::LogAction(char const* format, ...) { Player* bot = botAI->GetBot(); - if (sPlayerbotAIConfig->logInGroupOnly && (!bot->GetGroup() || !botAI->HasRealPlayerMaster()) && !testMode) + if (sPlayerbotAIConfig.logInGroupOnly && (!bot->GetGroup() || !botAI->HasRealPlayerMaster()) && !testMode) return; char buf[1024]; @@ -661,7 +661,7 @@ void Engine::LogValues() return; Player* bot = botAI->GetBot(); - if (sPlayerbotAIConfig->logInGroupOnly && (!bot->GetGroup() || !botAI->HasRealPlayerMaster())) + if (sPlayerbotAIConfig.logInGroupOnly && (!bot->GetGroup() || !botAI->HasRealPlayerMaster())) return; std::string const text = botAI->GetAiObjectContext()->FormatValues(); diff --git a/src/Bot/Engine/PlayerbotAIBase.cpp b/src/Bot/Engine/PlayerbotAIBase.cpp index c034979340..cf4ad172cc 100644 --- a/src/Bot/Engine/PlayerbotAIBase.cpp +++ b/src/Bot/Engine/PlayerbotAIBase.cpp @@ -14,7 +14,7 @@ void PlayerbotAIBase::UpdateAI(uint32 elapsed, bool minimal) if (totalPmo) totalPmo->finish(); - totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "PlayerbotAIBase::FullTick"); + totalPmo = sPerfMonitor.start(PERF_MON_TOTAL, "PlayerbotAIBase::FullTick"); if (nextAICheckDelay > elapsed) nextAICheckDelay -= elapsed; @@ -35,7 +35,7 @@ void PlayerbotAIBase::SetNextCheckDelay(uint32 const delay) nextAICheckDelay = delay; - // if (nextAICheckDelay > sPlayerbotAIConfig->globalCoolDown) + // if (nextAICheckDelay > sPlayerbotAIConfig.globalCoolDown) // LOG_DEBUG("playerbots", "std::set next check delay: {}", nextAICheckDelay); } @@ -43,7 +43,7 @@ void PlayerbotAIBase::IncreaseNextCheckDelay(uint32 delay) { nextAICheckDelay += delay; - // if (nextAICheckDelay > sPlayerbotAIConfig->globalCoolDown) + // if (nextAICheckDelay > sPlayerbotAIConfig.globalCoolDown) // LOG_DEBUG("playerbots", "increase next check delay: {}", nextAICheckDelay); } @@ -55,6 +55,6 @@ void PlayerbotAIBase::YieldThread(uint32 delay) nextAICheckDelay = delay; } -bool PlayerbotAIBase::IsActive() { return nextAICheckDelay < sPlayerbotAIConfig->maxWaitForMove; } +bool PlayerbotAIBase::IsActive() { return nextAICheckDelay < sPlayerbotAIConfig.maxWaitForMove; } bool PlayerbotAIBase::IsBotAI() const { return _isBotAI; } diff --git a/src/Bot/Engine/PlayerbotAIBase.h b/src/Bot/Engine/PlayerbotAIBase.h index 03c762a58f..d0e0b775b2 100644 --- a/src/Bot/Engine/PlayerbotAIBase.h +++ b/src/Bot/Engine/PlayerbotAIBase.h @@ -17,7 +17,7 @@ class PlayerbotAIBase bool CanUpdateAI(); void SetNextCheckDelay(uint32 const delay); void IncreaseNextCheckDelay(uint32 delay); - void YieldThread(uint32 delay = sPlayerbotAIConfig->reactDelay); + void YieldThread(uint32 delay = sPlayerbotAIConfig.reactDelay); virtual void UpdateAI(uint32 elapsed, bool minimal = false); virtual void UpdateAIInternal(uint32 elapsed, bool minimal = false) = 0; bool IsActive(); diff --git a/src/Bot/Engine/Value/Value.cpp b/src/Bot/Engine/Value/Value.cpp index c559e03816..a9235a8e9f 100644 --- a/src/Bot/Engine/Value/Value.cpp +++ b/src/Bot/Engine/Value/Value.cpp @@ -123,7 +123,7 @@ Unit* UnitCalculatedValue::Get() { if (checkInterval < 2) { - PerfMonitorOperation* pmo = sPerfMonitor->start( + PerfMonitorOperation* pmo = sPerfMonitor.start( PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); value = Calculate(); if (pmo) @@ -135,7 +135,7 @@ Unit* UnitCalculatedValue::Get() if (!lastCheckTime || now - lastCheckTime >= checkInterval) { lastCheckTime = now; - PerfMonitorOperation* pmo = sPerfMonitor->start( + PerfMonitorOperation* pmo = sPerfMonitor.start( PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); value = Calculate(); if (pmo) @@ -154,4 +154,4 @@ Unit* UnitManualSetValue::Get() if (value && value->IsInWorld()) return value; return nullptr; -} \ No newline at end of file +} diff --git a/src/Bot/Engine/Value/Value.h b/src/Bot/Engine/Value/Value.h index b37e162050..fe84bb4d79 100644 --- a/src/Bot/Engine/Value/Value.h +++ b/src/Bot/Engine/Value/Value.h @@ -72,7 +72,7 @@ class CalculatedValue : public UntypedValue, public Value { if (checkInterval < 2) { - // PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(), + // PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_VALUE, this->getName(), // this->context ? &this->context->performanceStack : nullptr); value = Calculate(); // if (pmo) @@ -84,7 +84,7 @@ class CalculatedValue : public UntypedValue, public Value if (!lastCheckTime || now - lastCheckTime >= checkInterval) { lastCheckTime = now; - // PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(), + // PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_VALUE, this->getName(), // this->context ? &this->context->performanceStack : nullptr); value = Calculate(); // if (pmo) @@ -105,7 +105,7 @@ class CalculatedValue : public UntypedValue, public Value { if (checkInterval < 2) { - // PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(), + // PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_VALUE, this->getName(), // this->context ? &this->context->performanceStack : nullptr); value = Calculate(); // if (pmo) @@ -117,7 +117,7 @@ class CalculatedValue : public UntypedValue, public Value if (!lastCheckTime || now - lastCheckTime >= checkInterval) { lastCheckTime = now; - // PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(), + // PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_VALUE, this->getName(), // this->context ? &this->context->performanceStack : nullptr); value = Calculate(); // if (pmo) @@ -154,7 +154,7 @@ class SingleCalculatedValue : public CalculatedValue { this->lastCheckTime = now; - PerfMonitorOperation* pmo = sPerfMonitor->start( + PerfMonitorOperation* pmo = sPerfMonitor.start( PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); this->value = this->Calculate(); if (pmo) diff --git a/src/Bot/Factory/AiFactory.cpp b/src/Bot/Factory/AiFactory.cpp index 8ca3534b1d..84b3a7dc53 100644 --- a/src/Bot/Factory/AiFactory.cpp +++ b/src/Bot/Factory/AiFactory.cpp @@ -281,7 +281,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (!player->InBattleground()) engine->addStrategiesNoInit("racials", "chat", "default", "cast time", "potions", "duel", "boost", nullptr); - if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster()) + if (sPlayerbotAIConfig.autoAvoidAoe && facade->HasRealPlayerMaster()) engine->addStrategy("avoid aoe", false); engine->addStrategy("formation", false); @@ -399,13 +399,13 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (PlayerbotAI::IsHeal(player, true)) { - if (sPlayerbotAIConfig->autoSaveMana) + if (sPlayerbotAIConfig.autoSaveMana) engine->addStrategy("save mana", false); - if (!sPlayerbotAIConfig->IsRestrictedHealerDPSMap(player->GetMapId())) + if (!sPlayerbotAIConfig.IsRestrictedHealerDPSMap(player->GetMapId())) engine->addStrategy("healer dps", false); } - if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player)) + if (facade->IsRealPlayer() || sRandomPlayerbotMgr.IsRandomBot(player)) { if (!player->GetGroup()) { @@ -448,10 +448,10 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa } } } - if (sRandomPlayerbotMgr->IsRandomBot(player)) - engine->ChangeStrategy(sPlayerbotAIConfig->randomBotCombatStrategies); + if (sRandomPlayerbotMgr.IsRandomBot(player)) + engine->ChangeStrategy(sPlayerbotAIConfig.randomBotCombatStrategies); else - engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies); + engine->ChangeStrategy(sPlayerbotAIConfig.combatStrategies); // Battleground switch if (player->InBattleground() && player->GetBattleground()) @@ -586,10 +586,10 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const "gather", "duel", "pvp", "buff", "mount", "emote", nullptr); } - if (sPlayerbotAIConfig->autoSaveMana && PlayerbotAI::IsHeal(player, true)) + if (sPlayerbotAIConfig.autoSaveMana && PlayerbotAI::IsHeal(player, true)) nonCombatEngine->addStrategy("save mana", false); - if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground()) + if ((sRandomPlayerbotMgr.IsRandomBot(player)) && !player->InBattleground()) { Player* master = facade->GetMaster(); @@ -597,7 +597,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const if (!urand(0, 3)) nonCombatEngine->addStrategy("start duel", false); - if (sPlayerbotAIConfig->randomBotJoinLfg) + if (sPlayerbotAIConfig.randomBotJoinLfg) nonCombatEngine->addStrategy("lfg", false); if (!player->GetGroup() || player->GetGroup()->GetLeaderGUID() == player->GetGUID()) @@ -612,9 +612,9 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const // nonCombatEngine->addStrategy("guild"); nonCombatEngine->addStrategy("grind", false); - if (sPlayerbotAIConfig->enableNewRpgStrategy) + if (sPlayerbotAIConfig.enableNewRpgStrategy) nonCombatEngine->addStrategy("new rpg", false); - else if (sPlayerbotAIConfig->autoDoQuests) + else if (sPlayerbotAIConfig.autoDoQuests) { // nonCombatEngine->addStrategy("travel"); nonCombatEngine->addStrategy("rpg", false); @@ -622,13 +622,13 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const else nonCombatEngine->addStrategy("move random", false); - if (sPlayerbotAIConfig->randomBotJoinBG) + if (sPlayerbotAIConfig.randomBotJoinBG) nonCombatEngine->addStrategy("bg", false); // if (!master || GET_PLAYERBOT_AI(master)) // nonCombatEngine->addStrategy("maintenance"); - nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->randomBotNonCombatStrategies); + nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig.randomBotNonCombatStrategies); } else { @@ -637,14 +637,14 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const if (master) { PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master); - if (masterBotAI || sRandomPlayerbotMgr->IsRandomBot(player)) + if (masterBotAI || sRandomPlayerbotMgr.IsRandomBot(player)) { // nonCombatEngine->addStrategy("pvp", false); // nonCombatEngine->addStrategy("collision"); // nonCombatEngine->addStrategy("group"); // nonCombatEngine->addStrategy("guild"); - // if (sPlayerbotAIConfig->autoDoQuests) + // if (sPlayerbotAIConfig.autoDoQuests) // { // // nonCombatEngine->addStrategy("travel"); // nonCombatEngine->addStrategy("rpg"); @@ -657,19 +657,19 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const // if (masterBotAI) // nonCombatEngine->addStrategy("maintenance"); - nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->randomBotNonCombatStrategies); + nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig.randomBotNonCombatStrategies); } else { // nonCombatEngine->addStrategy("pvp", false); - nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies); + nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig.nonCombatStrategies); } } } } } else - nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies); + nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig.nonCombatStrategies); // Battleground switch if (player->InBattleground() && player->GetBattleground()) @@ -726,7 +726,7 @@ void AiFactory::AddDefaultDeadStrategies(Player* player, PlayerbotAI* const faca (void)facade; // unused and remove warning deadEngine->addStrategiesNoInit("dead", "stay", "chat", "default", "follow", nullptr); - if (sRandomPlayerbotMgr->IsRandomBot(player) && !player->GetGroup()) + if (sRandomPlayerbotMgr.IsRandomBot(player) && !player->GetGroup()) deadEngine->removeStrategy("follow", false); } diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index 67f5909c11..dfefd323c2 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -68,18 +68,18 @@ PlayerbotFactory::PlayerbotFactory(Player* bot, uint32 level, uint32 itemQuality botAI = GET_PLAYERBOT_AI(bot); if (!this->itemQuality) { - uint32 gs = sPlayerbotAIConfig->randomGearScoreLimit == 0 + uint32 gs = sPlayerbotAIConfig.randomGearScoreLimit == 0 ? 0 - : PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig->randomGearScoreLimit, - sPlayerbotAIConfig->randomGearQualityLimit); - this->itemQuality = sPlayerbotAIConfig->randomGearQualityLimit; + : PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig.randomGearScoreLimit, + sPlayerbotAIConfig.randomGearQualityLimit); + this->itemQuality = sPlayerbotAIConfig.randomGearQualityLimit; this->gearScoreLimit = gs; } } void PlayerbotFactory::Init() { - if (sPlayerbotAIConfig->randomBotPreQuests) + if (sPlayerbotAIConfig.randomBotPreQuests) { ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates(); for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i) @@ -111,8 +111,8 @@ void PlayerbotFactory::Init() } } - for (std::vector::iterator i = sPlayerbotAIConfig->randomBotQuestIds.begin(); - i != sPlayerbotAIConfig->randomBotQuestIds.end(); ++i) + for (std::vector::iterator i = sPlayerbotAIConfig.randomBotQuestIds.begin(); + i != sPlayerbotAIConfig.randomBotQuestIds.end(); ++i) { uint32 questId = *i; AddPrevQuests(questId, specialQuestIds); @@ -190,7 +190,7 @@ void PlayerbotFactory::Init() continue; } - if (sRandomItemMgr->IsTestItem(gemId)) + if (sRandomItemMgr.IsTestItem(gemId)) { continue; } @@ -218,12 +218,12 @@ void PlayerbotFactory::Prepare() { bot->SetUInt32Value(PLAYER_XP, 0); } - if (!sPlayerbotAIConfig->randomBotShowHelmet || !urand(0, 4)) + if (!sPlayerbotAIConfig.randomBotShowHelmet || !urand(0, 4)) { bot->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM); } - if (!sPlayerbotAIConfig->randomBotShowCloak || !urand(0, 4)) + if (!sPlayerbotAIConfig.randomBotShowCloak || !urand(0, 4)) { bot->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK); } @@ -231,7 +231,7 @@ void PlayerbotFactory::Prepare() void PlayerbotFactory::Randomize(bool incremental) { - // if (sPlayerbotAIConfig->disableRandomLevels) + // if (sPlayerbotAIConfig.disableRandomLevels) // { // return; // } @@ -240,8 +240,8 @@ void PlayerbotFactory::Randomize(bool incremental) // LOG_DEBUG("playerbots", "Preparing to {} randomize...", (incremental ? "incremental" : "full")); Prepare(); LOG_DEBUG("playerbots", "Resetting player..."); - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reset"); - if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel) + PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Reset"); + if (!sPlayerbotAIConfig.equipmentPersistence || level < sPlayerbotAIConfig.equipmentPersistenceLevel) { bot->resetTalents(true); } @@ -250,7 +250,7 @@ void PlayerbotFactory::Randomize(bool incremental) ClearSkills(); ClearSpells(); ResetQuests(); - if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel) + if (!sPlayerbotAIConfig.equipmentPersistence || level < sPlayerbotAIConfig.equipmentPersistenceLevel) { ClearAllItems(); } @@ -266,15 +266,15 @@ void PlayerbotFactory::Randomize(bool incremental) if (pmo) pmo->finish(); - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Immersive"); + // pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Immersive"); // LOG_INFO("playerbots", "Initializing immersive..."); // InitImmersive(); // if (pmo) // pmo->finish(); - if (sPlayerbotAIConfig->randomBotPreQuests) + if (sPlayerbotAIConfig.randomBotPreQuests) { - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); InitInstanceQuests(); InitAttunementQuests(); if (pmo) @@ -282,157 +282,157 @@ void PlayerbotFactory::Randomize(bool incremental) } else { - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); InitAttunementQuests(); if (pmo) pmo->finish(); } LOG_DEBUG("playerbots", "Initializing skills (step 1)..."); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1"); bot->LearnDefaultSkills(); InitSkills(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1"); LOG_DEBUG("playerbots", "Initializing spells (step 1)..."); InitClassSpells(); InitAvailableSpells(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Talents"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Talents"); LOG_DEBUG("playerbots", "Initializing talents..."); - if (!incremental || !sPlayerbotAIConfig->equipmentPersistence || - bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) + if (!incremental || !sPlayerbotAIConfig.equipmentPersistence || + bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel) { InitTalentsTree(); } - sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specNo", 0); + sRandomPlayerbotMgr.SetValue(bot->GetGUID().GetCounter(), "specNo", 0); if (botAI) { - sPlayerbotRepository->Reset(botAI); + PlayerbotRepository::instance().Reset(botAI); // botAI->DoSpecificAction("auto talents"); botAI->ResetStrategies(false); // fix wrong stored strategy } if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells2"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells2"); LOG_DEBUG("playerbots", "Initializing spells (step 2)..."); InitAvailableSpells(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reputation"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Reputation"); LOG_DEBUG("playerbots", "Initializing reputation..."); InitReputation(); if (pmo) pmo->finish(); LOG_DEBUG("playerbots", "Initializing special spells..."); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells3"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells3"); InitSpecialSpells(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Mounts"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Mounts"); LOG_DEBUG("playerbots", "Initializing mounts..."); InitMounts(); // bot->SaveToDB(false, false); if (pmo) pmo->finish(); - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills2"); + // pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills2"); // LOG_INFO("playerbots", "Initializing skills (step 2)..."); // UpdateTradeSkills(); // if (pmo) // pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Equip"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Equip"); LOG_DEBUG("playerbots", "Initializing equipmemt..."); - if (!incremental || !sPlayerbotAIConfig->equipmentPersistence || - bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) + if (!incremental || !sPlayerbotAIConfig.equipmentPersistence || + bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel) { - if (sPlayerbotAIConfig->incrementalGearInit || !incremental) - InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit); + if (sPlayerbotAIConfig.incrementalGearInit || !incremental) + InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig.twoRoundsGearInit); } // bot->SaveToDB(false, false); if (pmo) pmo->finish(); - // if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) + // if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) // { - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Enchant"); + // pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Enchant"); // LOG_INFO("playerbots", "Initializing enchant templates..."); // LoadEnchantContainer(); // if (pmo) // pmo->finish(); // } - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Bags"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Bags"); LOG_DEBUG("playerbots", "Initializing bags..."); InitBags(); // bot->SaveToDB(false, false); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Ammo"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Ammo"); LOG_DEBUG("playerbots", "Initializing ammo..."); InitAmmo(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Food"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Food"); LOG_DEBUG("playerbots", "Initializing food..."); InitFood(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Potions"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Potions"); LOG_DEBUG("playerbots", "Initializing potions..."); InitPotions(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reagents"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Reagents"); LOG_DEBUG("playerbots", "Initializing reagents..."); InitReagents(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Keys"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Keys"); LOG_DEBUG("playerbots", "Initializing keys..."); InitKeyring(); if (pmo) pmo->finish(); - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_EqSets"); + // pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_EqSets"); // LOG_DEBUG("playerbots", "Initializing second equipment set..."); // InitSecondEquipmentSet(); // if (pmo) // pmo->finish(); - if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) { ApplyEnchantAndGemsNew(); } // { - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_EnchantTemplate"); + // pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_EnchantTemplate"); // LOG_INFO("playerbots", "Initializing enchant templates..."); // ApplyEnchantTemplate(); // if (pmo) // pmo->finish(); // } - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Inventory"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Inventory"); LOG_DEBUG("playerbots", "Initializing inventory..."); // InitInventory(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Consumable"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Consumable"); LOG_DEBUG("playerbots", "Initializing consumables..."); InitConsumables(); if (pmo) @@ -442,9 +442,9 @@ void PlayerbotFactory::Randomize(bool incremental) InitGlyphs(); // bot->SaveToDB(false, false); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Guilds"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Guilds"); // bot->SaveToDB(false, false); - if (sPlayerbotAIConfig->randomBotGuildCount > 0) + if (sPlayerbotAIConfig.randomBotGuildCount > 0) { LOG_DEBUG("playerbots", "Initializing guilds..."); InitGuild(); @@ -455,7 +455,7 @@ void PlayerbotFactory::Randomize(bool incremental) if (bot->GetLevel() >= 70) { - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Arenas"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Arenas"); // LOG_INFO("playerbots", "Initializing arena teams..."); InitArenaTeam(); if (pmo) @@ -470,7 +470,7 @@ void PlayerbotFactory::Randomize(bool incremental) } if (bot->GetLevel() >= 10) { - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Pet"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Pet"); LOG_DEBUG("playerbots", "Initializing pet..."); InitPet(); // bot->SaveToDB(false, false); @@ -479,7 +479,7 @@ void PlayerbotFactory::Randomize(bool incremental) pmo->finish(); } - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Save"); + pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "PlayerbotFactory_Save"); LOG_DEBUG("playerbots", "Saving to DB..."); bot->SetMoney(urand(level * 100000, level * 5 * 100000)); bot->SetHealth(bot->GetMaxHealth()); @@ -493,7 +493,7 @@ void PlayerbotFactory::Randomize(bool incremental) void PlayerbotFactory::Refresh() { // Prepare(); - // if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) + // if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel) // { // InitEquipment(true); // } @@ -513,11 +513,11 @@ void PlayerbotFactory::Refresh() InitSpecialSpells(); InitMounts(); InitKeyring(); - if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) + if (!sPlayerbotAIConfig.equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig.equipmentPersistenceLevel) { InitTalentsTree(true, true, true); } - if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) { ApplyEnchantAndGemsNew(); } @@ -746,7 +746,7 @@ void PlayerbotFactory::InitConsumables() void PlayerbotFactory::InitPetTalents() { - if (bot->GetLevel() <= 70 && sPlayerbotAIConfig->limitTalentsExpansion) + if (bot->GetLevel() <= 70 && sPlayerbotAIConfig.limitTalentsExpansion) return; Pet* pet = bot->GetPet(); @@ -794,7 +794,7 @@ void PlayerbotFactory::InitPetTalents() } std::vector> order = - sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20]; + sPlayerbotAIConfig.parsedHunterPetLinkOrder[pet_family->petTalentType][20]; uint32 maxTalentPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); if (order.empty()) @@ -842,16 +842,16 @@ void PlayerbotFactory::InitPetTalents() uint32 spec = pet_family->petTalentType; uint32 startPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel()); while (startPoints > 1 && startPoints < 20 && - sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0) + sPlayerbotAIConfig.parsedHunterPetLinkOrder[spec][startPoints].size() == 0) { startPoints--; } for (uint32 points = startPoints; points <= 20; points++) { - if (sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points].size() == 0) + if (sPlayerbotAIConfig.parsedHunterPetLinkOrder[spec][points].size() == 0) continue; - for (std::vector& p : sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points]) + for (std::vector& p : sPlayerbotAIConfig.parsedHunterPetLinkOrder[spec][points]) { uint32 row = p[0], col = p[1], lvl = p[2]; uint32 talentID = 0; @@ -924,17 +924,17 @@ void PlayerbotFactory::InitPet() if (itr->second.minlevel > bot->GetLevel()) continue; - bool onlyWolf = sPlayerbotAIConfig->hunterWolfPet == 2 || - (sPlayerbotAIConfig->hunterWolfPet == 1 && + bool onlyWolf = sPlayerbotAIConfig.hunterWolfPet == 2 || + (sPlayerbotAIConfig.hunterWolfPet == 1 && bot->GetLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)); // Wolf only (for higher dps) if (onlyWolf && itr->second.family != CREATURE_FAMILY_WOLF) continue; // Exclude configured pet families - if (std::find(sPlayerbotAIConfig->excludedHunterPetFamilies.begin(), - sPlayerbotAIConfig->excludedHunterPetFamilies.end(), - itr->second.family) != sPlayerbotAIConfig->excludedHunterPetFamilies.end()) + if (std::find(sPlayerbotAIConfig.excludedHunterPetFamilies.begin(), + sPlayerbotAIConfig.excludedHunterPetFamilies.end(), + itr->second.family) != sPlayerbotAIConfig.excludedHunterPetFamilies.end()) continue; ids.push_back(itr->first); @@ -1123,8 +1123,8 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa bool isCat = !bot->HasAura(16931); if (!isCat && bot->GetLevel() == 20) { - uint32 bearP = sPlayerbotAIConfig->randomClassSpecProb[cls][1]; - uint32 catP = sPlayerbotAIConfig->randomClassSpecProb[cls][3]; + uint32 bearP = sPlayerbotAIConfig.randomClassSpecProb[cls][1]; + uint32 catP = sPlayerbotAIConfig.randomClassSpecProb[cls][3]; if (urand(1, bearP + catP) <= catP) isCat = true; } @@ -1139,14 +1139,14 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa uint32 pointSum = 0; for (int i = 0; i < MAX_SPECNO; i++) { - pointSum += sPlayerbotAIConfig->randomClassSpecProb[cls][i]; + pointSum += sPlayerbotAIConfig.randomClassSpecProb[cls][i]; } uint32 point = urand(1, pointSum); uint32 currentP = 0; int i; for (i = 0; i < MAX_SPECNO; i++) { - currentP += sPlayerbotAIConfig->randomClassSpecProb[cls][i]; + currentP += sPlayerbotAIConfig.randomClassSpecProb[cls][i]; if (point <= currentP) { specTab = i; @@ -1204,17 +1204,17 @@ void PlayerbotFactory::InitTalentsBySpecNo(Player* bot, int specNo, bool reset) spells_row[talentInfo->Row].push_back(talentInfo); } while (startLevel > 1 && startLevel < 80 && - sPlayerbotAIConfig->parsedSpecLinkOrder[cls][specNo][startLevel].size() == 0) + sPlayerbotAIConfig.parsedSpecLinkOrder[cls][specNo][startLevel].size() == 0) { startLevel--; } for (int level = startLevel; level <= 80; level++) { - if (sPlayerbotAIConfig->parsedSpecLinkOrder[cls][specNo][level].size() == 0) + if (sPlayerbotAIConfig.parsedSpecLinkOrder[cls][specNo][level].size() == 0) { continue; } - for (std::vector& p : sPlayerbotAIConfig->parsedSpecLinkOrder[cls][specNo][level]) + for (std::vector& p : sPlayerbotAIConfig.parsedSpecLinkOrder[cls][specNo][level]) { uint32 tab = p[0], row = p[1], col = p[2], lvl = p[3]; uint32 talentID = -1; @@ -1341,7 +1341,7 @@ class DestroyItemsVisitor : public IterateItemsVisitor if (keep.find(id) != keep.end()) return false; - if (sPlayerbotAIConfig->IsInRandomQuestItemList(id)) + if (sPlayerbotAIConfig.IsInRandomQuestItemList(id)) return true; return false; @@ -1603,7 +1603,7 @@ void Shuffle(std::vector& items) // bool noItem = false; // uint32 quality = urand(ITEM_QUALITY_UNCOMMON, ITEM_QUALITY_EPIC); // uint32 attempts = 10; -// if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && quality > ITEM_QUALITY_NORMAL) +// if (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance && quality > ITEM_QUALITY_NORMAL) // { // quality--; // } @@ -1614,11 +1614,11 @@ void Shuffle(std::vector& items) // uint32 itemInSlot = isUpgrade ? oldItem->GetTemplate()->ItemId : 0; -// uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; +// uint32 maxLevel = sPlayerbotAIConfig.randomBotMaxLevel; // if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) // maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); -// uint32 minLevel = sPlayerbotAIConfig->randomBotMinLevel; +// uint32 minLevel = sPlayerbotAIConfig.randomBotMinLevel; // if (minLevel < sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL)) // minLevel = sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL); @@ -1627,7 +1627,7 @@ void Shuffle(std::vector& items) // { // if (isUpgrade) // { -// std::vector ids = sRandomItemMgr->GetUpgradeList(bot, specName, slot, 0, itemInSlot); +// std::vector ids = sRandomItemMgr.GetUpgradeList(bot, specName, slot, 0, itemInSlot); // if (!ids.empty()) // Shuffle(ids); @@ -1667,7 +1667,7 @@ void Shuffle(std::vector& items) // } // else // { -// std::vector ids = sRandomItemMgr->GetUpgradeList(bot, specName, slot, quality, itemInSlot); +// std::vector ids = sRandomItemMgr.GetUpgradeList(bot, specName, slot, quality, itemInSlot); // if (!ids.empty()) // Shuffle(ids); @@ -1706,7 +1706,7 @@ void Shuffle(std::vector& items) void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { - if (incremental && !sPlayerbotAIConfig->incrementalGearInit) + if (incremental && !sPlayerbotAIConfig.incrementalGearInit) return; if (level < 5) @@ -1786,7 +1786,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); int32 desiredQuality = itemQuality; - if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) + if (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { desiredQuality--; } @@ -1797,7 +1797,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { for (InventoryType inventoryType : GetPossibleInventoryTypeListBySlot((EquipmentSlots)slot)) { - for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType)) + for (uint32 itemId : sRandomItemMgr.GetCachedEquipments(requiredLevel, inventoryType)) { if (itemId == 46978) // shaman earth ring totem { @@ -1808,10 +1808,10 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) continue; // disable next expansion gear - if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728) + if (sPlayerbotAIConfig.limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728) continue; - if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 70 && itemId >= 35570 && + if (sPlayerbotAIConfig.limitGearExpansion && bot->GetLevel() <= 70 && itemId >= 35570 && itemId != 36737 && itemId != 37739 && itemId != 37740) // transition point from TBC -> WOTLK isn't as clear, and there are other // wearable TBC items above 35570 but nothing of significance @@ -2009,7 +2009,7 @@ bool PlayerbotFactory::IsDesiredReplacement(Item* item) } // if (!requiredLevel) // { - // requiredLevel = sRandomItemMgr->GetMinLevelFromCache(proto->ItemId); + // requiredLevel = sRandomItemMgr.GetMinLevelFromCache(proto->ItemId); // } uint32 delta = 1 + (80 - bot->GetLevel()) / 10; @@ -2039,7 +2039,7 @@ inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint3 // std::map> items; // uint32 desiredQuality = itemQuality; -// while (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > +// while (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance && desiredQuality > // ITEM_QUALITY_NORMAL) // { // desiredQuality--; @@ -2061,9 +2061,9 @@ inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint3 // //if (!CanEquipWeapon(proto)) // // continue; -// if (sRandomItemMgr->HasStatWeight(proto->ItemId)) +// if (sRandomItemMgr.HasStatWeight(proto->ItemId)) // { -// if (!sRandomItemMgr->GetLiveStatWeight(bot, proto->ItemId)) +// if (!sRandomItemMgr.GetLiveStatWeight(bot, proto->ItemId)) // continue; // } @@ -2096,9 +2096,9 @@ inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint3 // //if (!CanEquipArmor(proto)) // // continue; -// if (sRandomItemMgr->HasStatWeight(proto->ItemId)) +// if (sRandomItemMgr.HasStatWeight(proto->ItemId)) // { -// if (!sRandomItemMgr->GetLiveStatWeight(bot, proto->ItemId)) +// if (!sRandomItemMgr.GetLiveStatWeight(bot, proto->ItemId)) // continue; // } @@ -2173,10 +2173,10 @@ void PlayerbotFactory::InitBags(bool destroyOld) void PlayerbotFactory::EnchantItem(Item* item) { - if (bot->GetLevel() < sPlayerbotAIConfig->minEnchantingBotLevel) + if (bot->GetLevel() < sPlayerbotAIConfig.minEnchantingBotLevel) return; - if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance) + if (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance) return; ItemTemplate const* proto = item->GetTemplate(); @@ -2279,8 +2279,8 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) void PlayerbotFactory::InitTradeSkills() { - uint16 firstSkill = sRandomPlayerbotMgr->GetValue(bot, "firstSkill"); - uint16 secondSkill = sRandomPlayerbotMgr->GetValue(bot, "secondSkill"); + uint16 firstSkill = sRandomPlayerbotMgr.GetValue(bot, "firstSkill"); + uint16 secondSkill = sRandomPlayerbotMgr.GetValue(bot, "secondSkill"); if (!firstSkill || !secondSkill) { std::vector firstSkills; @@ -2332,8 +2332,8 @@ void PlayerbotFactory::InitTradeSkills() break; } - sRandomPlayerbotMgr->SetValue(bot, "firstSkill", firstSkill); - sRandomPlayerbotMgr->SetValue(bot, "secondSkill", secondSkill); + sRandomPlayerbotMgr.SetValue(bot, "firstSkill", firstSkill); + sRandomPlayerbotMgr.SetValue(bot, "secondSkill", secondSkill); } SetRandomSkill(SKILL_FIRST_AID); @@ -2359,13 +2359,13 @@ void PlayerbotFactory::InitSkills() bot->UpdateSkillsForLevel(); bot->SetSkill(SKILL_RIDING, 0, 0, 0); - if (bot->GetLevel() >= sPlayerbotAIConfig->useGroundMountAtMinLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.useGroundMountAtMinLevel) bot->learnSpell(33388); - if (bot->GetLevel() >= sPlayerbotAIConfig->useFastGroundMountAtMinLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.useFastGroundMountAtMinLevel) bot->learnSpell(33391); - if (bot->GetLevel() >= sPlayerbotAIConfig->useFlyMountAtMinLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.useFlyMountAtMinLevel) bot->learnSpell(34090); - if (bot->GetLevel() >= sPlayerbotAIConfig->useFastFlyMountAtMinLevel) + if (bot->GetLevel() >= sPlayerbotAIConfig.useFastFlyMountAtMinLevel) bot->learnSpell(34091); uint32 skillLevel = bot->GetLevel() < 40 ? 0 : 1; @@ -2680,8 +2680,8 @@ void PlayerbotFactory::InitClassSpells() void PlayerbotFactory::InitSpecialSpells() { - for (std::vector::iterator i = sPlayerbotAIConfig->randomBotSpellIds.begin(); - i != sPlayerbotAIConfig->randomBotSpellIds.end(); ++i) + for (std::vector::iterator i = sPlayerbotAIConfig.randomBotSpellIds.begin(); + i != sPlayerbotAIConfig.randomBotSpellIds.end(); ++i) { uint32 spellId = *i; bot->learnSpell(spellId); @@ -2752,13 +2752,13 @@ void PlayerbotFactory::InitTalents(uint32 specNo) void PlayerbotFactory::InitTalentsByTemplate(uint32 specTab) { - // if (sPlayerbotAIConfig->parsedSpecLinkOrder[bot->getClass()][specNo][80].size() == 0) + // if (sPlayerbotAIConfig.parsedSpecLinkOrder[bot->getClass()][specNo][80].size() == 0) // { // return; // } uint32 cls = bot->getClass(); int startLevel = bot->GetLevel(); - uint32 specIndex = sPlayerbotAIConfig->randomClassSpecIndex[cls][specTab]; + uint32 specIndex = sPlayerbotAIConfig.randomClassSpecIndex[cls][specTab]; uint32 classMask = bot->getClassMask(); std::unordered_map> spells_row; for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) @@ -2777,23 +2777,23 @@ void PlayerbotFactory::InitTalentsByTemplate(uint32 specTab) spells_row[talentInfo->Row].push_back(talentInfo); } while (startLevel > 1 && startLevel < 80 && - sPlayerbotAIConfig->parsedSpecLinkOrder[cls][specIndex][startLevel].size() == 0) + sPlayerbotAIConfig.parsedSpecLinkOrder[cls][specIndex][startLevel].size() == 0) { startLevel--; } for (int level = startLevel; level <= 80; level++) { - if (sPlayerbotAIConfig->parsedSpecLinkOrder[cls][specIndex][level].size() == 0) + if (sPlayerbotAIConfig.parsedSpecLinkOrder[cls][specIndex][level].size() == 0) { continue; } - for (std::vector& p : sPlayerbotAIConfig->parsedSpecLinkOrder[cls][specIndex][level]) + for (std::vector& p : sPlayerbotAIConfig.parsedSpecLinkOrder[cls][specIndex][level]) { uint32 tab = p[0], row = p[1], col = p[2], lvl = p[3]; - if (sPlayerbotAIConfig->limitTalentsExpansion && bot->GetLevel() <= 60 && (row > 6 || (row == 6 && col != 1))) + if (sPlayerbotAIConfig.limitTalentsExpansion && bot->GetLevel() <= 60 && (row > 6 || (row == 6 && col != 1))) continue; - if (sPlayerbotAIConfig->limitTalentsExpansion && bot->GetLevel() <= 70 && (row > 8 || (row == 8 && col != 1))) + if (sPlayerbotAIConfig.limitTalentsExpansion && bot->GetLevel() <= 70 && (row > 8 || (row == 8 && col != 1))) continue; uint32 talentID = 0; @@ -2848,8 +2848,8 @@ void PlayerbotFactory::InitTalentsByTemplate(uint32 specTab) ObjectGuid PlayerbotFactory::GetRandomBot() { GuidVector guids; - for (std::vector::iterator i = sPlayerbotAIConfig->randomBotAccounts.begin(); - i != sPlayerbotAIConfig->randomBotAccounts.end(); i++) + for (std::vector::iterator i = sPlayerbotAIConfig.randomBotAccounts.begin(); + i != sPlayerbotAIConfig.randomBotAccounts.end(); i++) { uint32 accountId = *i; if (!AccountMgr::GetCharactersCount(accountId)) @@ -2981,7 +2981,7 @@ void PlayerbotFactory::InitAmmo() if (!subClass) return; - std::vector ammoEntryList = sRandomItemMgr->GetAmmo(level, subClass); + std::vector ammoEntryList = sRandomItemMgr.GetAmmo(level, subClass); uint32 entry = 0; for (uint32 tEntry : ammoEntryList) { @@ -2990,10 +2990,10 @@ void PlayerbotFactory::InitAmmo() continue; // disable next expansion ammo - if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && tEntry >= 23728) + if (sPlayerbotAIConfig.limitGearExpansion && bot->GetLevel() <= 60 && tEntry >= 23728) continue; - if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 70 && tEntry >= 35570) + if (sPlayerbotAIConfig.limitGearExpansion && bot->GetLevel() <= 70 && tEntry >= 35570) continue; entry = tEntry; @@ -3023,10 +3023,10 @@ uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) void PlayerbotFactory::InitMounts() { - uint32 firstmount = sPlayerbotAIConfig->useGroundMountAtMinLevel; - uint32 secondmount = sPlayerbotAIConfig->useFastGroundMountAtMinLevel; - uint32 thirdmount = sPlayerbotAIConfig->useFlyMountAtMinLevel; - uint32 fourthmount = sPlayerbotAIConfig->useFastFlyMountAtMinLevel; + uint32 firstmount = sPlayerbotAIConfig.useGroundMountAtMinLevel; + uint32 secondmount = sPlayerbotAIConfig.useFastGroundMountAtMinLevel; + uint32 thirdmount = sPlayerbotAIConfig.useFlyMountAtMinLevel; + uint32 fourthmount = sPlayerbotAIConfig.useFastFlyMountAtMinLevel; if (bot->GetLevel() < firstmount) return; @@ -3165,7 +3165,7 @@ void PlayerbotFactory::InitPotions() if (!visitor.GetResult().empty()) continue; - uint32 itemId = sRandomItemMgr->GetRandomPotion(level, effect); + uint32 itemId = sRandomItemMgr.GetRandomPotion(level, effect); if (!itemId) { // LOG_INFO("playerbots", "No potions (type {}) available for bot {} ({} level)", effect, @@ -3499,7 +3499,7 @@ void PlayerbotFactory::InitGlyphs(bool increment) } } - if (sPlayerbotAIConfig->limitTalentsExpansion && bot->GetLevel() <= 70) + if (sPlayerbotAIConfig.limitTalentsExpansion && bot->GetLevel() <= 70) { bot->SendTalentsInfoData(false); return; @@ -3722,10 +3722,10 @@ void PlayerbotFactory::InitGlyphs(bool increment) // GlyphSlotEntry const *gs = sGlyphSlotStore.LookupEntry(slot); // if (!gs) // continue; - if (sPlayerbotAIConfig->parsedSpecGlyph[cls][tab].size() > slotIndex && - sPlayerbotAIConfig->parsedSpecGlyph[cls][tab][slotIndex] != 0) + if (sPlayerbotAIConfig.parsedSpecGlyph[cls][tab].size() > slotIndex && + sPlayerbotAIConfig.parsedSpecGlyph[cls][tab][slotIndex] != 0) { - uint32 itemId = sPlayerbotAIConfig->parsedSpecGlyph[cls][tab][slotIndex]; + uint32 itemId = sPlayerbotAIConfig.parsedSpecGlyph[cls][tab][slotIndex]; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (proto->Class != ITEM_CLASS_GLYPH) continue; @@ -3859,7 +3859,7 @@ Item* PlayerbotFactory::StoreItem(uint32 itemId, uint32 count) void PlayerbotFactory::InitInventoryTrade() { - uint32 itemId = sRandomItemMgr->GetRandomTrade(level); + uint32 itemId = sRandomItemMgr.GetRandomTrade(level); if (!itemId) { LOG_ERROR("playerbots", "No trade items available for bot {} ({} level)", bot->GetName().c_str(), @@ -3895,7 +3895,7 @@ void PlayerbotFactory::InitInventoryEquip() std::vector ids; uint32 desiredQuality = itemQuality; - if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) + if (urand(0, 100) < 100 * sPlayerbotAIConfig.randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { desiredQuality--; } @@ -3949,21 +3949,21 @@ void PlayerbotFactory::InitGuild() return; } - std::string guildName = sPlayerbotGuildMgr->AssignToGuild(bot); + std::string guildName = PlayerbotGuildMgr::instance().AssignToGuild(bot); if (guildName.empty()) return; Guild* guild = sGuildMgr->GetGuildByName(guildName); if (!guild) { - if (!sPlayerbotGuildMgr->CreateGuild(bot, guildName)) + if (!PlayerbotGuildMgr::instance().CreateGuild(bot, guildName)) LOG_ERROR("playerbots","Failed to create guild {} for bot {}", guildName, bot->GetName()); return; } else { if (guild->AddMember(bot->GetGUID(),urand(GR_OFFICER, GR_INITIATE))) - sPlayerbotGuildMgr->OnGuildUpdate(guild); + PlayerbotGuildMgr::instance().OnGuildUpdate(guild); else LOG_ERROR("playerbots","Bot {} failed to join guild {}.", bot->GetName(), guildName); } @@ -3985,7 +3985,7 @@ void PlayerbotFactory::InitImmersive() std::ostringstream name; name << "immersive_stat_" << i; - uint32 value = sRandomPlayerbotMgr->GetValue(owner, name.str()); + uint32 value = sRandomPlayerbotMgr.GetValue(owner, name.str()); if (value) initialized = true; @@ -4058,7 +4058,7 @@ void PlayerbotFactory::InitImmersive() std::ostringstream name; name << "immersive_stat_" << i; - sRandomPlayerbotMgr->SetValue(owner, name.str(), percentMap[type]); + sRandomPlayerbotMgr.SetValue(owner, name.str(), percentMap[type]); } } } @@ -4066,15 +4066,15 @@ void PlayerbotFactory::InitImmersive() void PlayerbotFactory::InitArenaTeam() { - if (!sPlayerbotAIConfig->IsInRandomAccountList(bot->GetSession()->GetAccountId())) + if (!sPlayerbotAIConfig.IsInRandomAccountList(bot->GetSession()->GetAccountId())) return; // Currently the teams are only remade after a server restart and if deleteRandomBotArenaTeams = 1 // This is because randomBotArenaTeams is only empty on server restart. // A manual reinitalization (.playerbots rndbot init) is also required after the teams have been deleted. - if (sPlayerbotAIConfig->randomBotArenaTeams.empty()) + if (sPlayerbotAIConfig.randomBotArenaTeams.empty()) { - if (sPlayerbotAIConfig->deleteRandomBotArenaTeams) + if (sPlayerbotAIConfig.deleteRandomBotArenaTeams) { LOG_INFO("playerbots", "Deleting random bot arena teams..."); @@ -4099,14 +4099,14 @@ void PlayerbotFactory::InitArenaTeam() LOG_INFO("playerbots", "Random bot arena teams deleted"); } - RandomPlayerbotFactory::CreateRandomArenaTeams(ARENA_TYPE_2v2, sPlayerbotAIConfig->randomBotArenaTeam2v2Count); - RandomPlayerbotFactory::CreateRandomArenaTeams(ARENA_TYPE_3v3, sPlayerbotAIConfig->randomBotArenaTeam3v3Count); - RandomPlayerbotFactory::CreateRandomArenaTeams(ARENA_TYPE_5v5, sPlayerbotAIConfig->randomBotArenaTeam5v5Count); + RandomPlayerbotFactory::CreateRandomArenaTeams(ARENA_TYPE_2v2, sPlayerbotAIConfig.randomBotArenaTeam2v2Count); + RandomPlayerbotFactory::CreateRandomArenaTeams(ARENA_TYPE_3v3, sPlayerbotAIConfig.randomBotArenaTeam3v3Count); + RandomPlayerbotFactory::CreateRandomArenaTeams(ARENA_TYPE_5v5, sPlayerbotAIConfig.randomBotArenaTeam5v5Count); } std::vector arenateams; - for (std::vector::iterator i = sPlayerbotAIConfig->randomBotArenaTeams.begin(); - i != sPlayerbotAIConfig->randomBotArenaTeams.end(); ++i) + for (std::vector::iterator i = sPlayerbotAIConfig.randomBotArenaTeams.begin(); + i != sPlayerbotAIConfig.randomBotArenaTeams.end(); ++i) arenateams.push_back(*i); if (arenateams.empty()) @@ -4300,7 +4300,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) if (!gemProperties) continue; - if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 70 && enchantGem >= 39900) + if (sPlayerbotAIConfig.limitEnchantExpansion && bot->GetLevel() <= 70 && enchantGem >= 39900) continue; uint32 requiredLevel = gemTemplate->ItemLevel; @@ -4363,10 +4363,10 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) } // disable next expansion enchantments - if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 60 && enchantSpell >= 27899) + if (sPlayerbotAIConfig.limitEnchantExpansion && bot->GetLevel() <= 60 && enchantSpell >= 27899) continue; - if (sPlayerbotAIConfig->limitEnchantExpansion && bot->GetLevel() <= 70 && enchantSpell >= 44483) + if (sPlayerbotAIConfig.limitEnchantExpansion && bot->GetLevel() <= 70 && enchantSpell >= 44483) continue; for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) diff --git a/src/Bot/Factory/RandomPlayerbotFactory.cpp b/src/Bot/Factory/RandomPlayerbotFactory.cpp index 5a4672f5eb..0fd7e90e21 100644 --- a/src/Bot/Factory/RandomPlayerbotFactory.cpp +++ b/src/Bot/Factory/RandomPlayerbotFactory.cpp @@ -317,14 +317,14 @@ uint32 RandomPlayerbotFactory::CalculateTotalAccountCount() { // Reset account types if features are disabled // Reset is done here to precede needed accounts calculations - if (sPlayerbotAIConfig->maxRandomBots == 0 || sPlayerbotAIConfig->addClassAccountPoolSize == 0) + if (sPlayerbotAIConfig.maxRandomBots == 0 || sPlayerbotAIConfig.addClassAccountPoolSize == 0) { - if (sPlayerbotAIConfig->maxRandomBots == 0) + if (sPlayerbotAIConfig.maxRandomBots == 0) { PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 0 WHERE account_type = 1"); LOG_INFO("playerbots", "MaxRandomBots set to 0, any RNDbot accounts (type 1) will be unassigned (type 0)"); } - if (sPlayerbotAIConfig->addClassAccountPoolSize == 0) + if (sPlayerbotAIConfig.addClassAccountPoolSize == 0) { PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 0 WHERE account_type = 2"); LOG_INFO("playerbots", "AddClassAccountPoolSize set to 0, any AddClass accounts (type 2) will be unassigned (type 0)"); @@ -334,8 +334,8 @@ uint32 RandomPlayerbotFactory::CalculateTotalAccountCount() for (int waited = 0; waited < 1000; waited += 50) { QueryResult res = PlayerbotsDatabase.Query("SELECT COUNT(*) FROM playerbots_account_type WHERE account_type IN ({}, {})", - sPlayerbotAIConfig->maxRandomBots == 0 ? 1 : -1, - sPlayerbotAIConfig->addClassAccountPoolSize == 0 ? 2 : -1); + sPlayerbotAIConfig.maxRandomBots == 0 ? 1 : -1, + sPlayerbotAIConfig.addClassAccountPoolSize == 0 ? 2 : -1); if (!res || res->Fetch()[0].Get() == 0) { @@ -347,8 +347,8 @@ uint32 RandomPlayerbotFactory::CalculateTotalAccountCount() } // Checks if randomBotAccountCount is set, otherwise calculate it dynamically. - if (sPlayerbotAIConfig->randomBotAccountCount > 0) - return sPlayerbotAIConfig->randomBotAccountCount; + if (sPlayerbotAIConfig.randomBotAccountCount > 0) + return sPlayerbotAIConfig.randomBotAccountCount; // Check existing account types uint32 existingRndBotAccounts = 0; @@ -374,17 +374,17 @@ uint32 RandomPlayerbotFactory::CalculateTotalAccountCount() int divisor = CalculateAvailableCharsPerAccount(); // Calculate max bots - int maxBots = sPlayerbotAIConfig->maxRandomBots; + int maxBots = sPlayerbotAIConfig.maxRandomBots; // Take periodic online - offline into account - if (sPlayerbotAIConfig->enablePeriodicOnlineOffline) + if (sPlayerbotAIConfig.enablePeriodicOnlineOffline) { - maxBots *= sPlayerbotAIConfig->periodicOnlineOfflineRatio; + maxBots *= sPlayerbotAIConfig.periodicOnlineOfflineRatio; } // Calculate number of accounts needed for RNDbots // Result is rounded up for maxBots not cleanly divisible by the divisor uint32 neededRndBotAccounts = (maxBots + divisor - 1) / divisor; - uint32 neededAddClassAccounts = sPlayerbotAIConfig->addClassAccountPoolSize; + uint32 neededAddClassAccounts = sPlayerbotAIConfig.addClassAccountPoolSize; // Start with existing total uint32 existingTotal = existingRndBotAccounts + existingAddClassAccounts + existingUnassignedAccounts; @@ -425,12 +425,12 @@ uint32 RandomPlayerbotFactory::CalculateTotalAccountCount() uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount() { - bool noDK = sPlayerbotAIConfig->disableDeathKnightLogin || sWorld->getIntConfig(CONFIG_EXPANSION) != EXPANSION_WRATH_OF_THE_LICH_KING; + bool noDK = sPlayerbotAIConfig.disableDeathKnightLogin || sWorld->getIntConfig(CONFIG_EXPANSION) != EXPANSION_WRATH_OF_THE_LICH_KING; uint32 availableChars = noDK ? 9 : 10; - uint32 hordeRatio = sPlayerbotAIConfig->randomBotHordeRatio; - uint32 allianceRatio = sPlayerbotAIConfig->randomBotAllianceRatio; + uint32 hordeRatio = sPlayerbotAIConfig.randomBotHordeRatio; + uint32 allianceRatio = sPlayerbotAIConfig.randomBotAllianceRatio; // horde : alliance = 50 : 50 -> 0% // horde : alliance = 0 : 50 -> 50% @@ -451,7 +451,7 @@ void RandomPlayerbotFactory::CreateRandomBots() { /* multi-thread here is meaningless? since the async db operations */ - if (sPlayerbotAIConfig->deleteRandomBotAccounts) + if (sPlayerbotAIConfig.deleteRandomBotAccounts) { std::vector botAccounts; std::vector botFriends; @@ -462,7 +462,7 @@ void RandomPlayerbotFactory::CreateRandomBots() for (uint32 accountNumber = 0; accountNumber < totalAccountCount; ++accountNumber) { std::ostringstream out; - out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber; + out << sPlayerbotAIConfig.randomBotAccountPrefix << accountNumber; std::string const accountName = out.str(); if (uint32 accountId = AccountMgr::GetId(accountName)) @@ -482,7 +482,7 @@ void RandomPlayerbotFactory::CreateRandomBots() // Delete all characters from bot accounts CharacterDatabase.Execute("DELETE FROM characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username LIKE '{}%%')", - sPlayerbotAIConfig->randomBotAccountPrefix.c_str()); + sPlayerbotAIConfig.randomBotAccountPrefix.c_str()); // Wait for the characters to be deleted before proceeding to dependent deletes while (CharacterDatabase.QueueSize()) @@ -496,7 +496,7 @@ void RandomPlayerbotFactory::CreateRandomBots() // Clean up orphaned entries in playerbots_db_store PlayerbotsDatabase.Execute("DELETE FROM playerbots_db_store WHERE guid NOT IN (SELECT guid FROM " + characterDBName + ".characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username NOT LIKE '{}%%'))", - sPlayerbotAIConfig->randomBotAccountPrefix.c_str()); + sPlayerbotAIConfig.randomBotAccountPrefix.c_str()); // Clean up orphaned records in character-related tables CharacterDatabase.Execute("DELETE FROM arena_team_member WHERE guid NOT IN (SELECT guid FROM characters)"); @@ -551,7 +551,7 @@ void RandomPlayerbotFactory::CreateRandomBots() // Finally, delete the bot accounts themselves LOG_INFO("playerbots", "Deleting random bot accounts..."); QueryResult results = LoginDatabase.Query("SELECT id FROM account WHERE username LIKE '{}%%'", - sPlayerbotAIConfig->randomBotAccountPrefix.c_str()); + sPlayerbotAIConfig.randomBotAccountPrefix.c_str()); int32 deletion_count = 0; if (results) { @@ -601,7 +601,7 @@ void RandomPlayerbotFactory::CreateRandomBots() for (uint32 accountNumber = 0; accountNumber < totalAccountCount; ++accountNumber) { std::ostringstream out; - out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber; + out << sPlayerbotAIConfig.randomBotAccountPrefix << accountNumber; std::string const accountName = out.str(); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); @@ -613,7 +613,7 @@ void RandomPlayerbotFactory::CreateRandomBots() } account_creation++; std::string password = ""; - if (sPlayerbotAIConfig->randomBotRandomPassword) + if (sPlayerbotAIConfig.randomBotRandomPassword) { for (int i = 0; i < 10; i++) { @@ -649,7 +649,7 @@ void RandomPlayerbotFactory::CreateRandomBots() for (uint32 accountNumber = 0; accountNumber < totalAccountCount; ++accountNumber) { std::ostringstream out; - out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber; + out << sPlayerbotAIConfig.randomBotAccountPrefix << accountNumber; std::string const accountName = out.str(); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); @@ -661,7 +661,7 @@ void RandomPlayerbotFactory::CreateRandomBots() Field* fields = result->Fetch(); uint32 accountId = fields[0].Get(); - sPlayerbotAIConfig->randomBotAccounts.push_back(accountId); + sPlayerbotAIConfig.randomBotAccounts.push_back(accountId); uint32 count = AccountMgr::GetCharactersCount(accountId); if (count >= 10) @@ -746,13 +746,13 @@ void RandomPlayerbotFactory::CreateRandomBots() for (WorldSession* session : sessionBots) delete session; - for (uint32 accountId : sPlayerbotAIConfig->randomBotAccounts) + for (uint32 accountId : sPlayerbotAIConfig.randomBotAccounts) { totalRandomBotChars += AccountMgr::GetCharactersCount(accountId); } LOG_INFO("server.loading", ">> {} random bot accounts with {} characters available", - sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars); + sPlayerbotAIConfig.randomBotAccounts.size(), totalRandomBotChars); } std::string const RandomPlayerbotFactory::CreateRandomGuildName() @@ -811,7 +811,7 @@ void RandomPlayerbotFactory::CreateRandomArenaTeams(ArenaType type, uint32 count if (arenateam) { ++arenaTeamNumber; - sPlayerbotAIConfig->randomBotArenaTeams.push_back(arenateam->GetId()); + sPlayerbotAIConfig.randomBotArenaTeams.push_back(arenateam->GetId()); } else { @@ -872,7 +872,7 @@ void RandomPlayerbotFactory::CreateRandomArenaTeams(ArenaType type, uint32 count // set random rating arenateam->SetRatingForAll( - urand(sPlayerbotAIConfig->randomBotArenaTeamMinRating, sPlayerbotAIConfig->randomBotArenaTeamMaxRating)); + urand(sPlayerbotAIConfig.randomBotArenaTeamMinRating, sPlayerbotAIConfig.randomBotArenaTeamMaxRating)); // set random emblem uint32 backgroundColor = urand(0xFF000000, 0xFFFFFFFF); @@ -891,7 +891,7 @@ void RandomPlayerbotFactory::CreateRandomArenaTeams(ArenaType type, uint32 count arenateam->SaveToDB(); sArenaTeamMgr->AddArenaTeam(arenateam); - sPlayerbotAIConfig->randomBotArenaTeams.push_back(arenateam->GetId()); + sPlayerbotAIConfig.randomBotArenaTeams.push_back(arenateam->GetId()); } LOG_DEBUG("playerbots", "{} random bot {}vs{} arena teams available", arenaTeamNumber, type, type); diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 39db7c5948..34fc133645 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -16,7 +16,6 @@ #include "CharacterPackets.h" #include "ChatHelper.h" #include "Common.h" -#include "CreatureAIImpl.h" #include "CreatureData.h" #include "EmoteAction.h" #include "Engine.h" @@ -33,7 +32,6 @@ #include "LootObjectStack.h" #include "MapMgr.h" #include "MotionMaster.h" -#include "MoveSpline.h" #include "MoveSplineInit.h" #include "NewRpgStrategy.h" #include "ObjectGuid.h" @@ -45,7 +43,6 @@ #include "PlayerbotMgr.h" #include "PlayerbotGuildMgr.h" #include "Playerbots.h" -#include "PointMovementGenerator.h" #include "PositionValue.h" #include "RandomPlayerbotMgr.h" #include "SayAction.h" @@ -153,7 +150,7 @@ PlayerbotAI::PlayerbotAI(Player* bot) engines[BOT_STATE_COMBAT] = AiFactory::createCombatEngine(bot, this, aiObjectContext); engines[BOT_STATE_NON_COMBAT] = AiFactory::createNonCombatEngine(bot, this, aiObjectContext); engines[BOT_STATE_DEAD] = AiFactory::createDeadEngine(bot, this, aiObjectContext); - if (sPlayerbotAIConfig->applyInstanceStrategies) + if (sPlayerbotAIConfig.applyInstanceStrategies) ApplyInstanceStrategies(bot->GetMapId()); currentEngine = engines[BOT_STATE_NON_COMBAT]; currentState = BOT_STATE_NON_COMBAT; @@ -233,7 +230,7 @@ PlayerbotAI::~PlayerbotAI() delete aiObjectContext; if (bot) - sPlayerbotsMgr->RemovePlayerBotData(bot->GetGUID(), true); + PlayerbotsMgr::instance().RemovePlayerBotData(bot->GetGUID(), true); } void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) @@ -251,7 +248,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) // Handle cheat options (set bot health and power if cheats are enabled) if (bot->IsAlive() && - (static_cast(GetCheat()) > 0 || static_cast(sPlayerbotAIConfig->botCheatMask) > 0)) + (static_cast(GetCheat()) > 0 || static_cast(sPlayerbotAIConfig.botCheatMask) > 0)) { if (HasCheat(BotCheatMask::health)) bot->SetFullHealth(); @@ -332,7 +329,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) if (spellTarget && !bot->HasInArc(CAST_ANGLE_IN_FRONT, spellTarget) && (spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT)) { - sServerFacade->SetFacingTo(bot, spellTarget); + ServerFacade::instance().SetFacingTo(bot, spellTarget); } // Wait for spell cast @@ -387,7 +384,7 @@ void PlayerbotAI::UpdateAIGroupMaster() Group* group = bot->GetGroup(); - bool IsRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool IsRandomBot = sRandomPlayerbotMgr.IsRandomBot(bot); // If bot is not in group verify that for is RandomBot before clearing master and resetting. if (!group) @@ -451,7 +448,7 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal std::string const mapString = WorldPosition(bot).isOverworld() ? std::to_string(bot->GetMapId()) : "I"; PerfMonitorOperation* pmo = - sPerfMonitor->start(PERF_MON_TOTAL, "PlayerbotAI::UpdateAIInternal " + mapString); + sPerfMonitor.start(PERF_MON_TOTAL, "PlayerbotAI::UpdateAIInternal " + mapString); ExternalEventHelper helper(aiObjectContext); // chat replies @@ -503,12 +500,12 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal } else { - sRandomPlayerbotMgr->LogoutPlayerBot(bot->GetGUID()); + sRandomPlayerbotMgr.LogoutPlayerBot(bot->GetGUID()); } return; } - SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); return; } @@ -583,10 +580,10 @@ void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fr if (type == CHAT_MSG_SYSTEM) return; - if (filtered.find(sPlayerbotAIConfig->commandSeparator) != std::string::npos) + if (filtered.find(sPlayerbotAIConfig.commandSeparator) != std::string::npos) { std::vector commands; - split(commands, filtered, sPlayerbotAIConfig->commandSeparator.c_str()); + split(commands, filtered, sPlayerbotAIConfig.commandSeparator.c_str()); for (std::vector::iterator i = commands.begin(); i != commands.end(); ++i) { HandleCommand(type, *i, fromPlayer); @@ -594,12 +591,12 @@ void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fr return; } - if (!sPlayerbotAIConfig->commandPrefix.empty()) + if (!sPlayerbotAIConfig.commandPrefix.empty()) { - if (filtered.find(sPlayerbotAIConfig->commandPrefix) != 0) + if (filtered.find(sPlayerbotAIConfig.commandPrefix) != 0) return; - filtered = filtered.substr(sPlayerbotAIConfig->commandPrefix.size()); + filtered = filtered.substr(sPlayerbotAIConfig.commandPrefix.size()); } if (chatMap.empty()) @@ -631,7 +628,7 @@ void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fr WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_ADDON, response.c_str(), LANG_ADDON, CHAT_TAG_NONE, bot->GetGUID(), bot->GetName()); - sServerFacade->SendPacket(&fromPlayer, &data); + ServerFacade::instance().SendPacket(&fromPlayer, &data); return; } @@ -761,10 +758,10 @@ void PlayerbotAI::HandleTeleportAck() } // apply instance-related strategies after map attach - if (sPlayerbotAIConfig->applyInstanceStrategies) + if (sPlayerbotAIConfig.applyInstanceStrategies) ApplyInstanceStrategies(bot->GetMapId(), true); - if (sPlayerbotAIConfig->restrictHealerDPS) + if (sPlayerbotAIConfig.restrictHealerDPS) EvaluateHealerDpsStrategy(); // reset AI state after teleport @@ -855,7 +852,7 @@ void PlayerbotAI::Reset(bool full) aiObjectContext->GetValue("last taxi")->Get().Set(nullptr); aiObjectContext->GetValue("travel target") ->Get() - ->setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true); + ->setTarget(TravelMgr::instance().nullTravelDestination, TravelMgr::instance().nullWorldPosition, true); aiObjectContext->GetValue("travel target")->Get()->setStatus(TRAVEL_STATUS_EXPIRED); aiObjectContext->GetValue("travel target")->Get()->setExpireIn(1000); rpgInfo = NewRpgInfo(); @@ -920,10 +917,10 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro if (type == CHAT_MSG_SYSTEM) return; - if (text.find(sPlayerbotAIConfig->commandSeparator) != std::string::npos) + if (text.find(sPlayerbotAIConfig.commandSeparator) != std::string::npos) { std::vector commands; - split(commands, text, sPlayerbotAIConfig->commandSeparator.c_str()); + split(commands, text, sPlayerbotAIConfig.commandSeparator.c_str()); for (std::vector::iterator i = commands.begin(); i != commands.end(); ++i) { HandleCommand(type, *i, fromPlayer); @@ -933,12 +930,12 @@ void PlayerbotAI::HandleCommand(uint32 type, std::string const text, Player* fro } std::string filtered = text; - if (!sPlayerbotAIConfig->commandPrefix.empty()) + if (!sPlayerbotAIConfig.commandPrefix.empty()) { - if (filtered.find(sPlayerbotAIConfig->commandPrefix) != 0) + if (filtered.find(sPlayerbotAIConfig.commandPrefix) != 0) return; - filtered = filtered.substr(sPlayerbotAIConfig->commandPrefix.size()); + filtered = filtered.substr(sPlayerbotAIConfig.commandPrefix.size()); } if (chatMap.empty()) @@ -1100,7 +1097,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) } case SMSG_MESSAGECHAT: // do not react to self or if not ready to reply { - if (!sPlayerbotAIConfig->randomBotTalk) + if (!sPlayerbotAIConfig.randomBotTalk) return; if (!AllowActivity()) @@ -1156,7 +1153,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) bool isFromFreeBot = false; sCharacterCache->GetCharacterNameByGuid(guid1, name); uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guid1); - isFromFreeBot = sPlayerbotAIConfig->IsInRandomAccountList(accountId); + isFromFreeBot = sPlayerbotAIConfig.IsInRandomAccountList(accountId); bool isMentioned = message.find(bot->GetName()) != std::string::npos; // ChatChannelSource chatChannelSource = GetChatChannelSource(bot, msgtype, chanName); @@ -1174,20 +1171,20 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) if (lang == LANG_ADDON) return; - if (message.starts_with(sPlayerbotAIConfig->toxicLinksPrefix) && + if (message.starts_with(sPlayerbotAIConfig.toxicLinksPrefix) && (GetChatHelper()->ExtractAllItemIds(message).size() > 0 || GetChatHelper()->ExtractAllQuestIds(message).size() > 0) && - sPlayerbotAIConfig->toxicLinksRepliesChance) + sPlayerbotAIConfig.toxicLinksRepliesChance) { - if (urand(0, 50) > 0 || urand(1, 100) > sPlayerbotAIConfig->toxicLinksRepliesChance) + if (urand(0, 50) > 0 || urand(1, 100) > sPlayerbotAIConfig.toxicLinksRepliesChance) { return; } } else if ((GetChatHelper()->ExtractAllItemIds(message).count(19019) && - sPlayerbotAIConfig->thunderfuryRepliesChance)) + sPlayerbotAIConfig.thunderfuryRepliesChance)) { - if (urand(0, 60) > 0 || urand(1, 100) > sPlayerbotAIConfig->thunderfuryRepliesChance) + if (urand(0, 60) > 0 || urand(1, 100) > sPlayerbotAIConfig.thunderfuryRepliesChance) { return; } @@ -1197,8 +1194,8 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) if (isFromFreeBot && urand(0, 20)) return; - // if (msgtype == CHAT_MSG_GUILD && (!sPlayerbotAIConfig->guildRepliesRate || urand(1, 100) >= - // sPlayerbotAIConfig->guildRepliesRate)) return; + // if (msgtype == CHAT_MSG_GUILD && (!sPlayerbotAIConfig.guildRepliesRate || urand(1, 100) >= + // sPlayerbotAIConfig.guildRepliesRate)) return; if (!isFromFreeBot) { @@ -1285,7 +1282,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) // bot->GetMotionMaster()->MoveKnockbackFrom(fx, fy, horizontalSpeed, verticalSpeed); // // set delay based on actual distance - // float newdis = sqrt(sServerFacade->GetDistance2d(bot, fx, fy)); + // float newdis = sqrt(ServerFacade::instance().GetDistance2d(bot, fx, fy)); // SetNextCheckDelay((uint32)((newdis / dis) * moveTimeHalf * 4 * IN_MILLISECONDS)); // // add moveflags @@ -1347,9 +1344,9 @@ int32 PlayerbotAI::CalculateGlobalCooldown(uint32 spellid) return 0; if (bot->HasSpellCooldown(spellid)) - return sPlayerbotAIConfig->globalCoolDown; + return sPlayerbotAIConfig.globalCoolDown; - return sPlayerbotAIConfig->reactDelay; + return sPlayerbotAIConfig.reactDelay; } void PlayerbotAI::HandleMasterIncomingPacket(WorldPacket const& packet) @@ -1410,7 +1407,7 @@ void PlayerbotAI::DoNextAction(bool min) { if (!bot->IsInWorld() || bot->IsBeingTeleported() || (GetMaster() && GetMaster()->IsBeingTeleported())) { - SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); + SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); return; } @@ -1454,7 +1451,7 @@ void PlayerbotAI::DoNextAction(bool min) } } - bool minimal = !AllowActivity(); + bool minimal = !this->AllowActivity(); currentEngine->DoNextAction(nullptr, 0, (minimal || min)); @@ -1463,7 +1460,7 @@ void PlayerbotAI::DoNextAction(bool min) if (!bot->isAFK() && !bot->InBattleground() && !HasRealPlayerMaster()) bot->ToggleAFK(); - SetNextCheckDelay(sPlayerbotAIConfig->passiveDelay); + SetNextCheckDelay(sPlayerbotAIConfig.passiveDelay); return; } else if (bot->isAFK()) @@ -1471,7 +1468,8 @@ void PlayerbotAI::DoNextAction(bool min) if (master && master->IsInWorld()) { - float distance = sServerFacade->GetDistance2d(bot, master); + float distance = ServerFacade::instance().GetDistance2d(bot, master); + if (master->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING) && distance < 20.0f) bot->m_movementInfo.AddMovementFlag(MOVEMENTFLAG_WALKING); else @@ -1704,7 +1702,7 @@ bool PlayerbotAI::PlayEmote(uint32 emote) WorldPacket data(SMSG_TEXT_EMOTE); data << (TextEmotes)emote; data << EmoteAction::GetNumberOfEmoteVariants((TextEmotes)emote, bot->getRace(), bot->getGender()); - data << ((master && (sServerFacade->GetDistance2d(bot, master) < 30.0f) && urand(0, 1)) ? master->GetGUID() + data << ((master && (ServerFacade::instance().GetDistance2d(bot, master) < 30.0f) && urand(0, 1)) ? master->GetGUID() : (bot->GetTarget() && urand(0, 1)) ? bot->GetTarget() : ObjectGuid::Empty); bot->GetSession()->HandleTextEmoteOpcode(data); @@ -1733,14 +1731,14 @@ void PlayerbotAI::ResetStrategies(bool load) AiFactory::AddDefaultCombatStrategies(bot, this, engines[BOT_STATE_COMBAT]); AiFactory::AddDefaultNonCombatStrategies(bot, this, engines[BOT_STATE_NON_COMBAT]); AiFactory::AddDefaultDeadStrategies(bot, this, engines[BOT_STATE_DEAD]); - if (sPlayerbotAIConfig->applyInstanceStrategies) + if (sPlayerbotAIConfig.applyInstanceStrategies) ApplyInstanceStrategies(bot->GetMapId()); for (uint8 i = 0; i < BOT_STATE_MAX; i++) engines[i]->Init(); // if (load) - // sPlayerbotRepository->Load(this); + // PlayerbotRepository::instance().Load(this); } bool PlayerbotAI::IsRanged(Player* player, bool bySpec) @@ -2718,7 +2716,7 @@ bool PlayerbotAI::SayToParty(const std::string& msg) for (auto reciever : GetPlayersInGroup()) { - sServerFacade->SendPacket(reciever, &data); + ServerFacade::instance().SendPacket(reciever, &data); } return true; @@ -2735,7 +2733,7 @@ bool PlayerbotAI::SayToRaid(const std::string& msg) for (auto reciever : GetPlayersInGroup()) { - sServerFacade->SendPacket(reciever, &data); + ServerFacade::instance().SendPacket(reciever, &data); } return true; @@ -2802,7 +2800,7 @@ bool PlayerbotAI::TellMasterNoFacing(std::string const text, PlayerbotSecurityLe masterBotAI = GET_PLAYERBOT_AI(master); if ((!master || (masterBotAI && !masterBotAI->IsRealPlayer())) && - (sPlayerbotAIConfig->randomBotSayWithoutMaster || HasStrategy("debug", BOT_STATE_NON_COMBAT))) + (sPlayerbotAIConfig.randomBotSayWithoutMaster || HasStrategy("debug", BOT_STATE_NON_COMBAT))) { bot->Say(text, (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); return true; @@ -2813,7 +2811,7 @@ bool PlayerbotAI::TellMasterNoFacing(std::string const text, PlayerbotSecurityLe time_t lastSaid = whispers[text]; - if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000) + if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig.repeatDelay / 1000) { whispers[text] = time(nullptr); @@ -2851,10 +2849,10 @@ bool PlayerbotAI::IsTellAllowed(PlayerbotSecurityLevel securityLevel) if (!GetSecurity()->CheckLevelFor(securityLevel, true, master)) return false; - if (sPlayerbotAIConfig->whisperDistance && !bot->GetGroup() && sRandomPlayerbotMgr->IsRandomBot(bot) && + if (sPlayerbotAIConfig.whisperDistance && !bot->GetGroup() && sRandomPlayerbotMgr.IsRandomBot(bot) && master->GetSession()->GetSecurity() < SEC_GAMEMASTER && (bot->GetMapId() != master->GetMapId() || - sServerFacade->GetDistance2d(bot, master) > sPlayerbotAIConfig->whisperDistance)) + ServerFacade::instance().GetDistance2d(bot, master) > sPlayerbotAIConfig.whisperDistance)) return false; return true; @@ -2869,7 +2867,7 @@ bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel secu { if (!master) { - if (sPlayerbotAIConfig->randomBotSayWithoutMaster) + if (sPlayerbotAIConfig.randomBotSayWithoutMaster) return TellMasterNoFacing(text, securityLevel); } if (!TellMasterNoFacing(text, securityLevel)) @@ -2878,7 +2876,7 @@ bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel secu if (!bot->isMoving() && !bot->IsInCombat() && bot->GetMapId() == master->GetMapId() && !bot->HasUnitState(UNIT_STATE_IN_FLIGHT) && !bot->IsFlying()) { - if (!bot->HasInArc(EMOTE_ANGLE_IN_FRONT, master, sPlayerbotAIConfig->sightDistance)) + if (!bot->HasInArc(EMOTE_ANGLE_IN_FRONT, master, sPlayerbotAIConfig.sightDistance)) bot->SetFacingToObject(master); bot->HandleEmoteCommand(EMOTE_ONESHOT_TALK); @@ -3092,7 +3090,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, { if (!spellid) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "Can cast spell failed. No spellid. - spellid: {}, bot name: {}", spellid, bot->GetName()); @@ -3102,7 +3100,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "Can cast spell failed. Unit state lost control. - spellid: {}, bot name: {}", spellid, bot->GetName()); @@ -3122,7 +3120,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, if (checkHasSpell && !bot->HasSpell(spellid)) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "Can cast spell failed. Bot not has spell. - target name: {}, spellid: {}, bot name: {}", @@ -3133,7 +3131,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != nullptr) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG( "playerbots", @@ -3145,7 +3143,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, if (bot->HasSpellCooldown(spellid)) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "Can cast spell failed. Spell not has cooldown. - target name: {}, spellid: {}, bot name: {}", @@ -3157,7 +3155,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "Can cast spell failed. No spellInfo. - target name: {}, spellid: {}, bot name: {}", target->GetName(), spellid, bot->GetName()); @@ -3167,7 +3165,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, if ((bot->GetShapeshiftForm() == FORM_FLIGHT || bot->GetShapeshiftForm() == FORM_FLIGHT_EPIC) && !bot->IsInCombat()) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG( "playerbots", @@ -3181,7 +3179,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, // bool interruptOnMove = spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT; if ((CastingTime || spellInfo->IsAutoRepeatRangedSpell()) && bot->isMoving()) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "Casting time and bot is moving - target name: {}, spellid: {}, bot name: {}", target->GetName(), spellid, bot->GetName()); @@ -3196,7 +3194,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, { if (spellid != 44572) // Deep Freeze { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "target is immuned to spell - target name: {}, spellid: {}, bot name: {}", target->GetName(), spellid, bot->GetName()); @@ -3206,9 +3204,9 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, // Otherwise, allow Deep Freeze even if immune } - if (bot != target && sServerFacade->GetDistance2d(bot, target) > sPlayerbotAIConfig->sightDistance) + if (bot != target && ServerFacade::instance().GetDistance2d(bot, target) > sPlayerbotAIConfig.sightDistance) { - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "target is out of sight distance - target name: {}, spellid: {}, bot name: {}", target->GetName(), spellid, bot->GetName()); @@ -3234,7 +3232,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, SpellCastResult result = spell->CheckCast(true); delete spell; - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) // { // if (result != SPELL_FAILED_NOT_READY && result != SPELL_CAST_OK) // { @@ -3258,7 +3256,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell, case SPELL_FAILED_OUT_OF_RANGE: return true; default: - if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) { LOG_DEBUG("playerbots", "CanCastSpell Check Failed. - target name: {}, spellid: {}, bot name: {}, result: {}", @@ -3294,7 +3292,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, GameObject* goTarget, bool checkH if (CastingTime > 0 && bot->isMoving()) return false; - if (sServerFacade->GetDistance2d(bot, goTarget) > sPlayerbotAIConfig->sightDistance) + if (ServerFacade::instance().GetDistance2d(bot, goTarget) > sPlayerbotAIConfig.sightDistance) return false; // ObjectGuid oldSel = bot->GetTarget(); @@ -3347,7 +3345,7 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool c if (!itemTarget) { - if (bot->GetDistance(x, y, z) > sPlayerbotAIConfig->sightDistance) + if (bot->GetDistance(x, y, z) > sPlayerbotAIConfig.sightDistance) return false; } @@ -3449,7 +3447,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) if (bot->IsFlying() || bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) { - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) // { // LOG_DEBUG("playerbots", "Spell cast is flying - target name: {}, spellid: {}, bot name: {}}", // target->GetName(), spellId, bot->GetName()); @@ -3472,13 +3470,13 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) WorldObject* faceTo = target; if (!bot->HasInArc(CAST_ANGLE_IN_FRONT, faceTo) && (spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT)) { - sServerFacade->SetFacingTo(bot, faceTo); + ServerFacade::instance().SetFacingTo(bot, faceTo); // failWithDelay = true; } if (failWithDelay) { - SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); return false; } @@ -3495,7 +3493,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) { bot->GetTradeData()->SetSpell(spellId); delete spell; - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) // { // LOG_DEBUG("playerbots", "Spell cast no item - target name: {}, spellid: {}, bot name: {}", // target->GetName(), spellId, bot->GetName()); @@ -3557,7 +3555,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) if (bot->isMoving() && spell->GetCastTime()) { // bot->StopMoving(); - SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); spell->cancel(); delete spell; return false; @@ -3565,7 +3563,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) // spell->m_targets.SetUnitTarget(target); // SpellCastResult spellSuccess = spell->CheckCast(true); - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) // { // LOG_DEBUG("playerbots", "Spell cast result - target name: {}, spellid: {}, bot name: {}, result: {}", // target->GetName(), spellId, bot->GetName(), spellSuccess); @@ -3577,7 +3575,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) if (result != SPELL_CAST_OK) { - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) // { // LOG_DEBUG("playerbots", "Spell cast failed. - target name: {}, spellid: {}, bot name: {}, result: {}", // target->GetName(), spellId, bot->GetName(), result); @@ -3645,7 +3643,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) // { // spell->cancel(); // delete spell; - // if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) + // if (!sPlayerbotAIConfig.logInGroupOnly || (bot->GetGroup() && HasRealPlayerMaster())) // { // LOG_DEBUG("playerbots", "Spell cast loot - target name: {}, spellid: {}, bot name: {}", // target->GetName(), spellId, bot->GetName()); @@ -3725,7 +3723,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite if (failWithDelay) { - SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); + SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); return false; } @@ -3768,7 +3766,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite if (bot->isMoving() && spell->GetCastTime()) { // bot->StopMoving(); - SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); spell->cancel(); delete spell; return false; @@ -3846,12 +3844,12 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target) if (CastingTime && vehicleBase->isMoving()) return false; - if (vehicleBase != spellTarget && sServerFacade->GetDistance2d(vehicleBase, spellTarget) > 120.0f) + if (vehicleBase != spellTarget && ServerFacade::instance().GetDistance2d(vehicleBase, spellTarget) > 120.0f) return false; if (!target && siegePos.isSet()) { - if (sServerFacade->GetDistance2d(vehicleBase, siegePos.x, siegePos.y) > 120.0f) + if (ServerFacade::instance().GetDistance2d(vehicleBase, siegePos.x, siegePos.y) > 120.0f) return false; } @@ -3921,7 +3919,7 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target) PositionInfo siegePos = GetAiObjectContext()->GetValue("position")->Get()["bg siege"]; if (!target && siegePos.isSet()) { - if (sServerFacade->GetDistance2d(vehicleBase, siegePos.x, siegePos.y) > 120.0f) + if (ServerFacade::instance().GetDistance2d(vehicleBase, siegePos.x, siegePos.y) > 120.0f) return false; } @@ -3959,7 +3957,7 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target) if (failWithDelay) { - SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); return false; } @@ -4000,7 +3998,7 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target) if (seat->CanControl() && vehicleBase->isMoving() && spell->GetCastTime()) { vehicleBase->StopMoving(); - SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); + SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); spell->cancel(); // delete spell; return false; @@ -4077,7 +4075,7 @@ void PlayerbotAI::WaitForSpellCast(Spell* spell) castTime += duration; } - SetNextCheckDelay(castTime + sPlayerbotAIConfig->reactDelay); + SetNextCheckDelay(castTime + sPlayerbotAIConfig.reactDelay); } void PlayerbotAI::InterruptSpell() @@ -4169,8 +4167,8 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType) if (!aura || aura->IsPassive() || aura->IsRemoved()) continue; - if (sPlayerbotAIConfig->dispelAuraDuration && aura->GetDuration() && - aura->GetDuration() < (int32)sPlayerbotAIConfig->dispelAuraDuration) + if (sPlayerbotAIConfig.dispelAuraDuration && aura->GetDuration() && + aura->GetDuration() < (int32)sPlayerbotAIConfig.dispelAuraDuration) continue; SpellInfo const* spellInfo = aura->GetSpellInfo(); @@ -4290,7 +4288,7 @@ bool PlayerbotAI::HasRealPlayerMaster() bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(master); } -bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); } +bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr.IsRandomBot(bot); } Player* PlayerbotAI::GetGroupLeader() { @@ -4379,7 +4377,7 @@ bool PlayerbotAI::HasPlayerNearby(WorldPosition* pos, float range) { float sqRange = range * range; bool nearPlayer = false; - for (auto& player : sRandomPlayerbotMgr->GetPlayers()) + for (auto& player : sRandomPlayerbotMgr.GetPlayers()) { if (!player->IsGameMaster() || player->isGMVisible()) { @@ -4413,9 +4411,9 @@ bool PlayerbotAI::HasManyPlayersNearby(uint32 trigerrValue, float range) float sqRange = range * range; uint32 found = 0; - for (auto& player : sRandomPlayerbotMgr->GetPlayers()) + for (auto& player : sRandomPlayerbotMgr.GetPlayers()) { - if ((!player->IsGameMaster() || player->isGMVisible()) && sServerFacade->GetDistance2d(player, bot) < sqRange) + if ((!player->IsGameMaster() || player->isGMVisible()) && ServerFacade::instance().GetDistance2d(player, bot) < sqRange) { found++; @@ -4461,7 +4459,7 @@ inline bool ZoneHasRealPlayers(Player* bot) return false; } - for (Player* player : sRandomPlayerbotMgr->GetPlayers()) + for (Player* player : sRandomPlayerbotMgr.GetPlayers()) { if (player->GetMapId() != bot->GetMapId()) continue; @@ -4492,7 +4490,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) return false; // when botActiveAlone is 100% and smartScale disabled - if (sPlayerbotAIConfig->botActiveAlone >= 100 && !sPlayerbotAIConfig->botActiveAloneSmartScale) + if (sPlayerbotAIConfig.botActiveAlone >= 100 && !sPlayerbotAIConfig.botActiveAloneSmartScale) { return true; } @@ -4510,7 +4508,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) // which prevents unneeded expensive GameTime calls. if (_isBotInitializing) { - _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.11; + _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig.maxRandomBots * 0.11; // no activity allowed during bot initialization if (_isBotInitializing) @@ -4532,7 +4530,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) } // bot map has active players. - if (sPlayerbotAIConfig->BotActiveAloneForceWhenInMap) + if (sPlayerbotAIConfig.BotActiveAloneForceWhenInMap) { if (HasRealPlayers(bot->GetMap())) { @@ -4541,7 +4539,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) } // bot zone has active players. - if (sPlayerbotAIConfig->BotActiveAloneForceWhenInZone) + if (sPlayerbotAIConfig.BotActiveAloneForceWhenInZone) { if (ZoneHasRealPlayers(bot)) { @@ -4550,7 +4548,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) } // when in real guild - if (sPlayerbotAIConfig->BotActiveAloneForceWhenInGuild) + if (sPlayerbotAIConfig.BotActiveAloneForceWhenInGuild) { if (IsInRealGuild()) { @@ -4559,7 +4557,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) } // Player is near. Always active. - if (HasPlayerNearby(sPlayerbotAIConfig->BotActiveAloneForceWhenInRadius)) + if (HasPlayerNearby(sPlayerbotAIConfig.BotActiveAloneForceWhenInRadius)) { return true; } @@ -4631,13 +4629,13 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) } // HasFriend - if (sPlayerbotAIConfig->BotActiveAloneForceWhenIsFriend) + if (sPlayerbotAIConfig.BotActiveAloneForceWhenIsFriend) { // shouldnt be needed analyse in future if (!bot->GetGUID()) return false; - for (auto& player : sRandomPlayerbotMgr->GetPlayers()) + for (auto& player : sRandomPlayerbotMgr.GetPlayers()) { if (!player || !player->GetSession() || !player->IsInWorld() || player->IsDuringRemoveFromWorld() || player->GetSession()->isLogingOut()) @@ -4669,7 +4667,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) return false; } - if (sPlayerbotAIConfig->botActiveAlone <= 0) + if (sPlayerbotAIConfig.botActiveAlone <= 0) { return false; } @@ -4681,19 +4679,19 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) // Below is code to have a specified % of bots active at all times. // The default is 100%. With 1% of all bots going active or inactive each minute. - uint32 mod = sPlayerbotAIConfig->botActiveAlone > 100 ? 100 : sPlayerbotAIConfig->botActiveAlone; - if (sPlayerbotAIConfig->botActiveAloneSmartScale && - bot->GetLevel() >= sPlayerbotAIConfig->botActiveAloneSmartScaleWhenMinLevel && - bot->GetLevel() <= sPlayerbotAIConfig->botActiveAloneSmartScaleWhenMaxLevel) + uint32 mod = sPlayerbotAIConfig.botActiveAlone > 100 ? 100 : sPlayerbotAIConfig.botActiveAlone; + if (sPlayerbotAIConfig.botActiveAloneSmartScale && + bot->GetLevel() >= sPlayerbotAIConfig.botActiveAloneSmartScaleWhenMinLevel && + bot->GetLevel() <= sPlayerbotAIConfig.botActiveAloneSmartScaleWhenMaxLevel) { mod = AutoScaleActivity(mod); } uint32 ActivityNumber = - GetFixedBotNumer(100, sPlayerbotAIConfig->botActiveAlone * static_cast(mod) / 100 * 0.01f); + GetFixedBotNumer(100, sPlayerbotAIConfig.botActiveAlone * static_cast(mod) / 100 * 0.01f); return ActivityNumber <= - (sPlayerbotAIConfig->botActiveAlone * mod) / + (sPlayerbotAIConfig.botActiveAlone * mod) / 100; // The given percentage of bots should be active and rotate 1% of those active bots each minute. } @@ -4725,8 +4723,8 @@ uint32 PlayerbotAI::AutoScaleActivity(uint32 mod) { // Current max server update time (ms), and the configured floor/ceiling values for bot scaling uint32 maxDiff = sWorldUpdateTime.GetMaxUpdateTimeOfCurrentTable(); - uint32 diffLimitFloor = sPlayerbotAIConfig->botActiveAloneSmartScaleDiffLimitfloor; - uint32 diffLimitCeiling = sPlayerbotAIConfig->botActiveAloneSmartScaleDiffLimitCeiling; + uint32 diffLimitFloor = sPlayerbotAIConfig.botActiveAloneSmartScaleDiffLimitfloor; + uint32 diffLimitCeiling = sPlayerbotAIConfig.botActiveAloneSmartScaleDiffLimitCeiling; if (diffLimitCeiling <= diffLimitFloor) { @@ -5318,19 +5316,19 @@ float PlayerbotAI::GetRange(std::string const type) return val; if (type == "spell") - return sPlayerbotAIConfig->spellDistance; + return sPlayerbotAIConfig.spellDistance; if (type == "shoot") - return sPlayerbotAIConfig->shootDistance; + return sPlayerbotAIConfig.shootDistance; if (type == "flee") - return sPlayerbotAIConfig->fleeDistance; + return sPlayerbotAIConfig.fleeDistance; if (type == "heal") - return sPlayerbotAIConfig->healDistance; + return sPlayerbotAIConfig.healDistance; if (type == "melee") - return sPlayerbotAIConfig->meleeDistance; + return sPlayerbotAIConfig.meleeDistance; return 0; } @@ -5999,7 +5997,7 @@ bool PlayerbotAI::IsInRealGuild() if (!bot->GetGuildId()) return false; - return sPlayerbotGuildMgr->IsRealGuild(bot->GetGuildId()); + return PlayerbotGuildMgr::instance().IsRealGuild(bot->GetGuildId()); } void PlayerbotAI::QueueChatResponse(const ChatQueuedReply chatReply) { chatReplies.push_back(std::move(chatReply)); } @@ -6553,17 +6551,17 @@ std::set PlayerbotAI::GetCurrentIncompleteQuestIds() uint32 PlayerbotAI::GetReactDelay() { - uint32 base = sPlayerbotAIConfig->reactDelay; // Default 100(ms) + uint32 base = sPlayerbotAIConfig.reactDelay; // Default 100(ms) // If dynamic react delay is disabled, use a static calculation - if (!sPlayerbotAIConfig->dynamicReactDelay) + if (!sPlayerbotAIConfig.dynamicReactDelay) { if (HasRealPlayerMaster()) return base; bool inBG = bot->InBattleground() || bot->InArena(); - if (sPlayerbotAIConfig->fastReactInBG && inBG) + if (sPlayerbotAIConfig.fastReactInBG && inBG) return base; bool inCombat = bot->IsInCombat(); @@ -6588,11 +6586,11 @@ uint32 PlayerbotAI::GetReactDelay() { if (bot->IsInCombat() || currentState == BOT_STATE_COMBAT) { - return static_cast(base * (sPlayerbotAIConfig->fastReactInBG ? 2.5f : 5.0f)); + return static_cast(base * (sPlayerbotAIConfig.fastReactInBG ? 2.5f : 5.0f)); } else { - return static_cast(base * (sPlayerbotAIConfig->fastReactInBG ? 1.0f : 10.0f)); + return static_cast(base * (sPlayerbotAIConfig.fastReactInBG ? 1.0f : 10.0f)); } } @@ -6765,7 +6763,7 @@ void PlayerbotAI::EvaluateHealerDpsStrategy() if (!IsHeal(bot, true)) return; - if (sPlayerbotAIConfig->IsRestrictedHealerDPSMap(bot->GetMapId())) + if (sPlayerbotAIConfig.IsRestrictedHealerDPSMap(bot->GetMapId())) ChangeStrategy("-healer dps", BOT_STATE_COMBAT); else ChangeStrategy("+healer dps", BOT_STATE_COMBAT); diff --git a/src/Bot/PlayerbotAI.h b/src/Bot/PlayerbotAI.h index b2df4352cc..c2d4aeb75b 100644 --- a/src/Bot/PlayerbotAI.h +++ b/src/Bot/PlayerbotAI.h @@ -491,7 +491,7 @@ class PlayerbotAI : public PlayerbotAIBase void ImbueItem(Item* item); void EnchantItemT(uint32 spellid, uint8 slot); uint32 GetBuffedCount(Player* player, std::string const spellname); - int32 GetNearGroupMemberCount(float dis = sPlayerbotAIConfig->sightDistance); + int32 GetNearGroupMemberCount(float dis = sPlayerbotAIConfig.sightDistance); virtual bool CanCastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); virtual bool CastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr); @@ -545,9 +545,9 @@ class PlayerbotAI : public PlayerbotAIBase uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1); GrouperType GetGrouperType(); GuilderType GetGuilderType(); - bool HasPlayerNearby(WorldPosition* pos, float range = sPlayerbotAIConfig->reactDistance); - bool HasPlayerNearby(float range = sPlayerbotAIConfig->reactDistance); - bool HasManyPlayersNearby(uint32 trigerrValue = 20, float range = sPlayerbotAIConfig->sightDistance); + bool HasPlayerNearby(WorldPosition* pos, float range = sPlayerbotAIConfig.reactDistance); + bool HasPlayerNearby(float range = sPlayerbotAIConfig.reactDistance); + bool HasManyPlayersNearby(uint32 trigerrValue = 20, float range = sPlayerbotAIConfig.sightDistance); bool AllowActive(ActivityType activityType); bool AllowActivity(ActivityType activityType = ALL_ACTIVITY, bool checkNow = false); uint32 AutoScaleActivity(uint32 mod); @@ -562,7 +562,7 @@ class PlayerbotAI : public PlayerbotAIBase bool HasCheat(BotCheatMask mask) { return ((uint32)mask & (uint32)cheatMask) != 0 || - ((uint32)mask & (uint32)sPlayerbotAIConfig->botCheatMask) != 0; + ((uint32)mask & (uint32)sPlayerbotAIConfig.botCheatMask) != 0; } BotCheatMask GetCheat() { return cheatMask; } void SetCheat(BotCheatMask mask) { cheatMask = mask; } diff --git a/src/Bot/PlayerbotMgr.cpp b/src/Bot/PlayerbotMgr.cpp index e01afb0f7c..79850ab24e 100644 --- a/src/Bot/PlayerbotMgr.cpp +++ b/src/Bot/PlayerbotMgr.cpp @@ -102,11 +102,11 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr; bool isRndbot = !masterAccountId; - bool sameAccount = sPlayerbotAIConfig->allowAccountBots && accountId == masterAccountId; + bool sameAccount = sPlayerbotAIConfig.allowAccountBots && accountId == masterAccountId; Guild* guild = masterPlayer ? sGuildMgr->GetGuildById(masterPlayer->GetGuildId()) : nullptr; - bool sameGuild = sPlayerbotAIConfig->allowGuildBots && guild && guild->GetMember(playerGuid); - bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(playerGuid.GetCounter()); - bool linkedAccount = sPlayerbotAIConfig->allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId); + bool sameGuild = sPlayerbotAIConfig.allowGuildBots && guild && guild->GetMember(playerGuid); + bool addClassBot = sRandomPlayerbotMgr.IsAddclassBot(playerGuid.GetCounter()); + bool linkedAccount = sPlayerbotAIConfig.allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId); bool allowed = true; std::ostringstream out; @@ -126,10 +126,10 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId return; } uint32 count = mgr->GetPlayerbotsCount() + botLoading.size(); - if (count >= sPlayerbotAIConfig->maxAddedBots) + if (count >= sPlayerbotAIConfig.maxAddedBots) { allowed = false; - out << "Failure: You have added too many bots (more than " << sPlayerbotAIConfig->maxAddedBots << ")"; + out << "Failure: You have added too many bots (more than " << sPlayerbotAIConfig.maxAddedBots << ")"; } } if (!allowed) @@ -156,7 +156,6 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId [](SQLQueryHolderBase const& queryHolder) { PlayerbotLoginQueryHolder const& holder = static_cast(queryHolder); - PlayerbotHolder* mgr = sRandomPlayerbotMgr; // could be null uint32 masterAccountId = holder.GetMasterAccountId(); if (masterAccountId) @@ -164,14 +163,25 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId // verify and find current world session of master WorldSession* masterSession = sWorldSessionMgr->FindSession(masterAccountId); Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr; + if (masterPlayer) - mgr = GET_PLAYERBOT_MGR(masterPlayer); + { + PlayerbotHolder* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(masterPlayer); + + if (mgr != nullptr) + { + mgr->HandlePlayerBotLoginCallback(holder); + + return; + } + + PlayerbotHolder::botLoading.erase(holder.GetGuid()); + + return; + } } - if (mgr) - mgr->HandlePlayerBotLoginCallback(holder); - else - PlayerbotHolder::botLoading.erase(holder.GetGuid()); + RandomPlayerbotMgr ::instance().HandlePlayerBotLoginCallback(holder); }); } @@ -216,9 +226,9 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con masterAccountId); } - sRandomPlayerbotMgr->OnPlayerLogin(bot); + sRandomPlayerbotMgr.OnPlayerLogin(bot); auto op = std::make_unique(bot->GetGUID(), masterAccountId); - sPlayerbotWorldProcessor->QueueOperation(std::move(op)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(op)); PlayerbotHolder::botLoading.erase(holder.GetGuid()); } @@ -312,8 +322,8 @@ void PlayerbotMgr::CancelLogout() } } - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) + for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr.GetPlayerBotsBegin(); + it != sRandomPlayerbotMgr.GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); @@ -341,7 +351,7 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid) // Queue group cleanup operation for world thread auto cleanupOp = std::make_unique(guid); - sPlayerbotWorldProcessor->QueueOperation(std::move(cleanupOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(cleanupOp)); LOG_DEBUG("playerbots", "Bot {} logging out", bot->GetName().c_str()); bot->SaveToDB(false, false); @@ -442,7 +452,7 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid) Group* group = bot->GetGroup(); if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster()) { - sPlayerbotRepository->Save(botAI); + PlayerbotRepository::instance().Save(botAI); } LOG_DEBUG("playerbots", "Bot {} logged out", bot->GetName().c_str()); @@ -488,7 +498,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) return; } - sPlayerbotsMgr->AddPlayerbotData(bot, true); + PlayerbotsMgr::instance().AddPlayerbotData(bot, true); playerBots[bot->GetGUID()] = bot; OnBotLoginInternal(bot); @@ -528,10 +538,10 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) // Don't disband alt groups when master goes away // Controlled by config - if (sPlayerbotAIConfig->KeepAltsInGroup()) + if (sPlayerbotAIConfig.KeepAltsInGroup()) { uint32 account = sCharacterCache->GetCharacterAccountIdByGuid(member); - if (!sPlayerbotAIConfig->IsInRandomAccountList(account)) + if (!sPlayerbotAIConfig.IsInRandomAccountList(account)) { groupValid = true; break; @@ -552,9 +562,9 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) } else { - botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot)); + botAI->ResetStrategies(!sRandomPlayerbotMgr.IsRandomBot(bot)); } - sPlayerbotRepository->Load(botAI); + PlayerbotRepository::instance().Load(botAI); if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT)) { @@ -580,52 +590,52 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) { // Queue ConvertToRaid operation auto convertOp = std::make_unique(master->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(convertOp)); } if (mgroup->isRaidGroup()) { // Queue AddMember operation auto addOp = std::make_unique(master->GetGUID(), bot->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(addOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(addOp)); } } else { // Queue AddMember operation auto addOp = std::make_unique(master->GetGUID(), bot->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(addOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(addOp)); } } else if (master && !group) { // Queue group creation and AddMember operation auto inviteOp = std::make_unique(master->GetGUID(), bot->GetGUID()); - sPlayerbotWorldProcessor->QueueOperation(std::move(inviteOp)); + PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(inviteOp)); } // if (master) // { // // bot->TeleportTo(master); // } uint32 accountId = bot->GetSession()->GetAccountId(); - bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(accountId); + bool isRandomAccount = sPlayerbotAIConfig.IsInRandomAccountList(accountId); - if (isRandomAccount && sPlayerbotAIConfig->randomBotFixedLevel) + if (isRandomAccount && sPlayerbotAIConfig.randomBotFixedLevel) { bot->SetPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN); } - else if (isRandomAccount && !sPlayerbotAIConfig->randomBotFixedLevel) + else if (isRandomAccount && !sPlayerbotAIConfig.randomBotFixedLevel) { bot->RemovePlayerFlag(PLAYER_FLAGS_NO_XP_GAIN); } bot->SaveToDB(false, false); - bool addClassBot = sRandomPlayerbotMgr->IsAccountType(accountId, 2); + bool addClassBot = sRandomPlayerbotMgr.IsAccountType(accountId, 2); if (addClassBot && master && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3) { // PlayerbotFactory factory(bot, master->GetLevel()); // factory.Randomize(false); uint32 mixedGearScore = - PlayerbotAI::GetMixedGearScore(master, true, false, 12) * sPlayerbotAIConfig->autoInitEquipLevelLimitRatio; + PlayerbotAI::GetMixedGearScore(master, true, false, 12) * sPlayerbotAIConfig.autoInitEquipLevelLimitRatio; // work around: distinguish from 0 if no gear if (mixedGearScore == 0) mixedGearScore = 1; @@ -634,7 +644,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) } // bots join World chat if not solo oriented - if (bot->GetLevel() >= 10 && sRandomPlayerbotMgr->IsRandomBot(bot) && GET_PLAYERBOT_AI(bot) && + if (bot->GetLevel() >= 10 && sRandomPlayerbotMgr.IsRandomBot(bot) && GET_PLAYERBOT_AI(bot) && GET_PLAYERBOT_AI(bot)->GetGrouperType() != GrouperType::SOLO) { // TODO make action/config @@ -702,12 +712,12 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, ObjectGuid guid, ObjectGuid masterguid, bool admin, uint32 masterAccountId, uint32 masterGuildId) { - if (!sPlayerbotAIConfig->enabled || guid.IsEmpty()) + if (!sPlayerbotAIConfig.enabled || guid.IsEmpty()) return "bot system is disabled"; uint32 botAccount = sCharacterCache->GetCharacterAccountIdByGuid(guid); - //bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(guid.GetCounter()); //not used, line marked for removal. - //bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(botAccount); //not used, shadowed, line marked for removal. + //bool isRandomBot = sRandomPlayerbotMgr.IsRandomBot(guid.GetCounter()); //not used, line marked for removal. + //bool isRandomAccount = sPlayerbotAIConfig.IsInRandomAccountList(botAccount); //not used, shadowed, line marked for removal. //bool isMasterAccount = (masterAccountId == botAccount); //not used, line marked for removal. if (cmd == "add" || cmd == "addaccount" || cmd == "login") @@ -722,8 +732,8 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje if (!accountId) return "character not found"; - if (!sPlayerbotAIConfig->allowAccountBots && accountId != masterAccountId && - !(sPlayerbotAIConfig->allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId))) + if (!sPlayerbotAIConfig.allowAccountBots && accountId != masterAccountId && + !(sPlayerbotAIConfig.allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId))) { return "you can only add bots from your own account or linked accounts"; } @@ -748,12 +758,12 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje // { Player* bot = GetPlayerBot(guid); if (!bot) - bot = sRandomPlayerbotMgr->GetPlayerBot(guid); + bot = sRandomPlayerbotMgr.GetPlayerBot(guid); if (!bot) return "bot not found"; - bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(guid.GetCounter()); + bool addClassBot = sRandomPlayerbotMgr.IsAddclassBot(guid.GetCounter()); if (!addClassBot) return "ERROR: You can not use this command on non-addclass bot."; @@ -771,7 +781,7 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje { if (Player* master = GET_PLAYERBOT_AI(bot)->GetMaster()) { - if (master->GetSession()->GetSecurity() <= SEC_PLAYER && sPlayerbotAIConfig->autoInitOnly && + if (master->GetSession()->GetSecurity() <= SEC_PLAYER && sPlayerbotAIConfig.autoInitOnly && cmd != "init=auto") { return "The command is not allowed, use init=auto instead."; @@ -818,7 +828,7 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje else if (cmd == "init=auto") { uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(master, true, false, 12) * - sPlayerbotAIConfig->autoInitEquipLevelLimitRatio; + sPlayerbotAIConfig.autoInitEquipLevelLimitRatio; // work around: distinguish from 0 if no gear if (mixedGearScore == 0) mixedGearScore = 1; @@ -858,7 +868,7 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje } else if (cmd == "random") { - sRandomPlayerbotMgr->Randomize(bot); + sRandomPlayerbotMgr.Randomize(bot); return "ok"; } else if (cmd == "quests") @@ -886,7 +896,7 @@ static uint8 GetOfflinePlayerGender(ObjectGuid guid) bool PlayerbotMgr::HandlePlayerbotMgrCommand(ChatHandler* handler, char const* args) { - if (!sPlayerbotAIConfig->enabled) + if (!sPlayerbotAIConfig.enabled) { handler->PSendSysMessage("|cffff0000Playerbot system is currently disabled!"); return false; @@ -1052,7 +1062,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg { if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) { - sPlayerbotAIConfig->Initialize(); + sPlayerbotAIConfig.Initialize(); messages.push_back("Config reloaded."); return messages; } @@ -1065,11 +1075,11 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!strcmp(cmd, "tweak")) { - sPlayerbotAIConfig->tweakValue = sPlayerbotAIConfig->tweakValue++; - if (sPlayerbotAIConfig->tweakValue > 2) - sPlayerbotAIConfig->tweakValue = 0; + sPlayerbotAIConfig.tweakValue = sPlayerbotAIConfig.tweakValue++; + if (sPlayerbotAIConfig.tweakValue > 2) + sPlayerbotAIConfig.tweakValue = 0; - messages.push_back("Set tweakvalue to " + std::to_string(sPlayerbotAIConfig->tweakValue)); + messages.push_back("Set tweakvalue to " + std::to_string(sPlayerbotAIConfig.tweakValue)); return messages; } @@ -1080,14 +1090,14 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg messages.push_back("Disable player botAI"); delete GET_PLAYERBOT_AI(master); } - else if (sPlayerbotAIConfig->selfBotLevel == 0) + else if (sPlayerbotAIConfig.selfBotLevel == 0) messages.push_back("Self-bot is disabled"); - else if (sPlayerbotAIConfig->selfBotLevel == 1 && master->GetSession()->GetSecurity() < SEC_GAMEMASTER) + else if (sPlayerbotAIConfig.selfBotLevel == 1 && master->GetSession()->GetSecurity() < SEC_GAMEMASTER) messages.push_back("You do not have permission to enable player botAI"); else { messages.push_back("Enable player botAI"); - sPlayerbotsMgr->AddPlayerbotData(master, true); + PlayerbotsMgr::instance().AddPlayerbotData(master, true); GET_PLAYERBOT_AI(master)->SetMaster(master); } @@ -1102,7 +1112,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!strcmp(cmd, "addclass")) { - if (sPlayerbotAIConfig->addClassCommand == 0 && master->GetSession()->GetSecurity() < SEC_GAMEMASTER) + if (sPlayerbotAIConfig.addClassCommand == 0 && master->GetSession()->GetSecurity() < SEC_GAMEMASTER) { messages.push_back("You do not have permission to create bot by addclass command"); return messages; @@ -1183,7 +1193,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg return messages; } uint8 teamId = master->GetTeamId(true); - const std::unordered_set &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)]; + const std::unordered_set &guidCache = sRandomPlayerbotMgr.addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)]; for (const ObjectGuid &guid: guidCache) { // If the user requested a specific gender, skip any character that doesn't match. @@ -1194,7 +1204,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (ObjectAccessor::FindConnectedPlayer(guid)) continue; uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid); - if (guildId && sPlayerbotGuildMgr->IsRealGuild(guildId)) + if (guildId && PlayerbotGuildMgr::instance().IsRealGuild(guildId)) continue; AddPlayerBot(guid, master->GetSession()->GetAccountId()); messages.push_back("Add class " + std::string(charname)); @@ -1424,7 +1434,7 @@ std::string const PlayerbotHolder::ListBots(Player* master) for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) { Player* member = ObjectAccessor::FindPlayer(itr->guid); - if (member && sRandomPlayerbotMgr->IsRandomBot(member)) + if (member && sRandomPlayerbotMgr.IsRandomBot(member)) { std::string const name = member->GetName(); @@ -1494,12 +1504,12 @@ PlayerbotMgr::PlayerbotMgr(Player* const master) : PlayerbotHolder(), master(mas PlayerbotMgr::~PlayerbotMgr() { if (master) - sPlayerbotsMgr->RemovePlayerBotData(master->GetGUID(), false); + PlayerbotsMgr::instance().RemovePlayerBotData(master->GetGUID(), false); } void PlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) { - SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); + SetNextCheckDelay(sPlayerbotAIConfig.reactDelay); CheckTellErrors(elapsed); } @@ -1509,10 +1519,10 @@ void PlayerbotMgr::HandleCommand(uint32 type, std::string const text) if (!master) return; - if (text.find(sPlayerbotAIConfig->commandSeparator) != std::string::npos) + if (text.find(sPlayerbotAIConfig.commandSeparator) != std::string::npos) { std::vector commands; - split(commands, text, sPlayerbotAIConfig->commandSeparator.c_str()); + split(commands, text, sPlayerbotAIConfig.commandSeparator.c_str()); for (std::vector::iterator i = commands.begin(); i != commands.end(); ++i) { HandleCommand(type, *i); @@ -1529,8 +1539,8 @@ void PlayerbotMgr::HandleCommand(uint32 type, std::string const text) botAI->HandleCommand(type, text, master); } - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) + for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr.GetPlayerBotsBegin(); + it != sRandomPlayerbotMgr.GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); @@ -1551,8 +1561,8 @@ void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet) botAI->HandleMasterIncomingPacket(packet); } - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) + for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr.GetPlayerBotsBegin(); + it != sRandomPlayerbotMgr.GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); @@ -1587,8 +1597,8 @@ void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet) botAI->HandleMasterOutgoingPacket(packet); } - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) + for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr.GetPlayerBotsBegin(); + it != sRandomPlayerbotMgr.GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); @@ -1605,8 +1615,8 @@ void PlayerbotMgr::SaveToDB() bot->SaveToDB(false, false); } - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) + for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr.GetPlayerBotsBegin(); + it != sRandomPlayerbotMgr.GetPlayerBotsEnd(); ++it) { Player* const bot = it->second; if (GET_PLAYERBOT_AI(bot) && GET_PLAYERBOT_AI(bot)->GetMaster() == GetMaster()) @@ -1648,12 +1658,12 @@ void PlayerbotMgr::OnPlayerLogin(Player* player) usedLocale = LOCALE_enUS; // fallback // set locale priority for bot texts - sPlayerbotTextMgr->AddLocalePriority(usedLocale); + PlayerbotTextMgr::instance().AddLocalePriority(usedLocale); - if (sPlayerbotAIConfig->selfBotLevel > 2) + if (sPlayerbotAIConfig.selfBotLevel > 2) HandlePlayerbotCommand("self", player); - if (!sPlayerbotAIConfig->botAutologin) + if (!sPlayerbotAIConfig.botAutologin) return; uint32 accountId = session->GetAccountId(); @@ -1693,7 +1703,7 @@ void PlayerbotMgr::TellError(std::string const botName, std::string const text) void PlayerbotMgr::CheckTellErrors(uint32 elapsed) { time_t now = time(nullptr); - if ((now - lastErrorTell) < sPlayerbotAIConfig->errorDelay / 1000) + if ((now - lastErrorTell) < sPlayerbotAIConfig.errorDelay / 1000) return; lastErrorTell = now; @@ -1777,7 +1787,7 @@ void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid, bool is_AI) PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player) { - if (!(sPlayerbotAIConfig->enabled) || !player) + if (!(sPlayerbotAIConfig.enabled) || !player) { return nullptr; } @@ -1797,7 +1807,7 @@ PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player) PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player) { - if (!(sPlayerbotAIConfig->enabled) || !player) + if (!(sPlayerbotAIConfig.enabled) || !player) { return nullptr; } diff --git a/src/Bot/PlayerbotMgr.h b/src/Bot/PlayerbotMgr.h index 7ee576006b..b80f6f2363 100644 --- a/src/Bot/PlayerbotMgr.h +++ b/src/Bot/PlayerbotMgr.h @@ -6,12 +6,9 @@ #ifndef _PLAYERBOT_PLAYERBOTMGR_H #define _PLAYERBOT_PLAYERBOTMGR_H -#include "Common.h" #include "ObjectGuid.h" #include "Player.h" #include "PlayerbotAIBase.h" -#include "QueryHolder.h" -#include "QueryResult.h" class ChatHandler; class PlayerbotAI; @@ -101,13 +98,10 @@ class PlayerbotMgr : public PlayerbotHolder class PlayerbotsMgr { public: - PlayerbotsMgr() {} - ~PlayerbotsMgr() {} - - static PlayerbotsMgr* instance() + static PlayerbotsMgr& instance() { static PlayerbotsMgr instance; - return &instance; + return instance; } void AddPlayerbotData(Player* player, bool isBotAI); @@ -117,6 +111,15 @@ class PlayerbotsMgr PlayerbotMgr* GetPlayerbotMgr(Player* player); private: + PlayerbotsMgr() = default; + ~PlayerbotsMgr() = default; + + PlayerbotsMgr(const PlayerbotsMgr&) = delete; + PlayerbotsMgr& operator=(const PlayerbotsMgr&) = delete; + + PlayerbotsMgr(PlayerbotsMgr&&) = delete; + PlayerbotsMgr& operator=(PlayerbotsMgr&&) = delete; + std::unordered_map _playerbotsAIMap; std::unordered_map _playerbotsMgrMap; }; diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 4d589ef1b4..4707d854ff 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -14,12 +14,9 @@ #include #include -#include "AccountMgr.h" #include "AiFactory.h" -#include "ArenaTeamMgr.h" #include "Battleground.h" #include "BattlegroundMgr.h" -#include "CellImpl.h" #include "ChannelMgr.h" #include "DBCStores.h" #include "DBCStructure.h" @@ -28,8 +25,6 @@ #include "FleeManager.h" #include "FlightMasterCache.h" #include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "GuildMgr.h" #include "GuildTaskMgr.h" #include "LFGMgr.h" #include "MapMgr.h" @@ -40,7 +35,6 @@ #include "Player.h" #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" -#include "PlayerbotCommandServer.h" #include "PlayerbotFactory.h" #include "Playerbots.h" #include "Position.h" @@ -50,8 +44,13 @@ #include "SharedDefines.h" #include "TravelMgr.h" #include "Unit.h" -#include "UpdateTime.h" #include "World.h" +#include "Cell.h" +#include "GridNotifiers.h" +// Required for Cell because of poor AC implementation +#include "CellImpl.h" +// Required for GridNotifiers because of poor AC implementation +#include "GridNotifiersImpl.h" struct GuidClassRaceInfo { @@ -102,7 +101,7 @@ static const std::unordered_map> cityToBankers = { // Quick lookup map: banker entry → location static std::unordered_map bankerEntryToLocation; -void PrintStatsThread() { sRandomPlayerbotMgr->PrintStats(); } +void PrintStatsThread() { sRandomPlayerbotMgr.PrintStats(); } void activatePrintStatsThread() { @@ -110,7 +109,7 @@ void activatePrintStatsThread() t.detach(); } -void CheckBgQueueThread() { sRandomPlayerbotMgr->CheckBgQueue(); } +void CheckBgQueueThread() { sRandomPlayerbotMgr.CheckBgQueue(); } void activateCheckBgQueueThread() { @@ -118,7 +117,7 @@ void activateCheckBgQueueThread() t.detach(); } -void CheckLfgQueueThread() { sRandomPlayerbotMgr->CheckLfgQueue(); } +void CheckLfgQueueThread() { sRandomPlayerbotMgr.CheckLfgQueue(); } void activateCheckLfgQueueThread() { @@ -126,7 +125,7 @@ void activateCheckLfgQueueThread() t.detach(); } -void CheckPlayersThread() { sRandomPlayerbotMgr->CheckPlayers(); } +void CheckPlayersThread() { sRandomPlayerbotMgr.CheckPlayers(); } void activateCheckPlayersThread() { @@ -215,36 +214,6 @@ double botPIDImpl::calculate(double setpoint, double pv) botPIDImpl::~botPIDImpl() {} -RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0) -{ - playersLevel = sPlayerbotAIConfig->randombotStartingLevel; - - if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin) - { - sPlayerbotCommandServer->Start(); - } - - BattlegroundData.clear(); // Clear here and here only. - - // Cleanup on server start: orphaned pet data that's often left behind by bot pets that no longer exist in the DB - CharacterDatabase.Execute("DELETE FROM pet_aura WHERE guid NOT IN (SELECT id FROM character_pet)"); - CharacterDatabase.Execute("DELETE FROM pet_spell WHERE guid NOT IN (SELECT id FROM character_pet)"); - CharacterDatabase.Execute("DELETE FROM pet_spell_cooldown WHERE guid NOT IN (SELECT id FROM character_pet)"); - - for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) - { - for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType) - { - BattlegroundData[queueType][bracket] = BattlegroundInfo(); - } - } - BgCheckTimer = 0; - LfgCheckTimer = 0; - PlayersCheckTimer = 0; -} - -RandomPlayerbotMgr::~RandomPlayerbotMgr() {} - uint32 RandomPlayerbotMgr::GetMaxAllowedBotCount() { return GetEventValue(0, "bot_count"); } void RandomPlayerbotMgr::LogPlayerLocation() @@ -253,9 +222,9 @@ void RandomPlayerbotMgr::LogPlayerLocation() try { - sPlayerbotAIConfig->openLog("player_location.csv", "w"); + sPlayerbotAIConfig.openLog("player_location.csv", "w"); - if (sPlayerbotAIConfig->randomBotAutologin) + if (sPlayerbotAIConfig.randomBotAutologin) { for (auto i : GetAllBots()) { @@ -264,7 +233,7 @@ void RandomPlayerbotMgr::LogPlayerLocation() continue; std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr() << "+00,"; + out << sPlayerbotAIConfig.GetTimestampStr() << "+00,"; out << "RND" << ","; out << bot->GetName() << ","; @@ -298,7 +267,7 @@ void RandomPlayerbotMgr::LogPlayerLocation() out << (bot->IsInCombat() ? "combat" : "safe") << ","; out << (bot->isDead() ? (bot->GetCorpse() ? "ghost" : "dead") : "alive"); - sPlayerbotAIConfig->log("player_location.csv", out.str().c_str()); + sPlayerbotAIConfig.log("player_location.csv", out.str().c_str()); } for (auto i : GetPlayers()) @@ -308,7 +277,7 @@ void RandomPlayerbotMgr::LogPlayerLocation() continue; std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr() << "+00,"; + out << sPlayerbotAIConfig.GetTimestampStr() << "+00,"; out << "PLR" << ","; out << bot->GetName() << ","; @@ -342,7 +311,7 @@ void RandomPlayerbotMgr::LogPlayerLocation() out << (bot->IsInCombat() ? "combat" : "safe") << ","; out << (bot->isDead() ? (bot->GetCorpse() ? "ghost" : "dead") : "alive"); - sPlayerbotAIConfig->log("player_location.csv", out.str().c_str()); + sPlayerbotAIConfig.log("player_location.csv", out.str().c_str()); } } } @@ -359,12 +328,12 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) if (totalPmo) totalPmo->finish(); - totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick"); + totalPmo = sPerfMonitor.start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick"); - if (!sPlayerbotAIConfig->randomBotAutologin || !sPlayerbotAIConfig->enabled) + if (!sPlayerbotAIConfig.randomBotAutologin || !sPlayerbotAIConfig.enabled) return; - /*if (sPlayerbotAIConfig->enablePrototypePerformanceDiff) + /*if (sPlayerbotAIConfig.enablePrototypePerformanceDiff) { LOG_INFO("playerbots", "---------------------------------------"); LOG_INFO("playerbots", @@ -374,13 +343,13 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) }*/ uint32 maxAllowedBotCount = GetEventValue(0, "bot_count"); - if (!maxAllowedBotCount || (maxAllowedBotCount < sPlayerbotAIConfig->minRandomBots || - maxAllowedBotCount > sPlayerbotAIConfig->maxRandomBots)) + if (!maxAllowedBotCount || (maxAllowedBotCount < sPlayerbotAIConfig.minRandomBots || + maxAllowedBotCount > sPlayerbotAIConfig.maxRandomBots)) { - maxAllowedBotCount = urand(sPlayerbotAIConfig->minRandomBots, sPlayerbotAIConfig->maxRandomBots); + maxAllowedBotCount = urand(sPlayerbotAIConfig.minRandomBots, sPlayerbotAIConfig.maxRandomBots); SetEventValue(0, "bot_count", maxAllowedBotCount, - urand(sPlayerbotAIConfig->randomBotCountChangeMinInterval, - sPlayerbotAIConfig->randomBotCountChangeMaxInterval)); + urand(sPlayerbotAIConfig.randomBotCountChangeMinInterval, + sPlayerbotAIConfig.randomBotCountChangeMaxInterval)); } GetBots(); @@ -389,25 +358,25 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) uint32 onlineBotCount = playerBots.size(); uint32 onlineBotFocus = 75; - if (onlineBotCount < (uint32)(sPlayerbotAIConfig->minRandomBots * 90 / 100)) + if (onlineBotCount < (uint32)(sPlayerbotAIConfig.minRandomBots * 90 / 100)) onlineBotFocus = 25; // only keep updating till initializing time has completed, // which prevents unneeded expensive GameTime calls. if (_isBotInitializing) { - _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * (0.11 + 0.4); + _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig.maxRandomBots * (0.11 + 0.4); } - uint32 updateIntervalTurboBoost = _isBotInitializing ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval; + uint32 updateIntervalTurboBoost = _isBotInitializing ? 1 : sPlayerbotAIConfig.randomBotUpdateInterval; SetNextCheckDelay(updateIntervalTurboBoost * (onlineBotFocus + 25) * 10); - PerfMonitorOperation* pmo = sPerfMonitor->start( + PerfMonitorOperation* pmo = sPerfMonitor.start( PERF_MON_TOTAL, onlineBotCount < maxAllowedBotCount ? "RandomPlayerbotMgr::Login" : "RandomPlayerbotMgr::UpdateAIInternal"); bool realPlayerIsLogged = false; - if (sPlayerbotAIConfig->disabledWithoutRealPlayer) + if (sPlayerbotAIConfig.disabledWithoutRealPlayer) { if (sWorldSessionMgr->GetActiveAndQueuedSessionCount() > 0) { @@ -416,7 +385,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) if (DelayLoginBotsTimer == 0) { - DelayLoginBotsTimer = time(nullptr) + sPlayerbotAIConfig->disabledWithoutRealPlayerLoginDelay; + DelayLoginBotsTimer = time(nullptr) + sPlayerbotAIConfig.disabledWithoutRealPlayerLoginDelay; } } else @@ -427,7 +396,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) } if (RealPlayerLastTimeSeen != 0 && onlineBotCount > 0 && - time(nullptr) > RealPlayerLastTimeSeen + sPlayerbotAIConfig->disabledWithoutRealPlayerLogoutDelay) + time(nullptr) > RealPlayerLastTimeSeen + sPlayerbotAIConfig.disabledWithoutRealPlayerLogoutDelay) { LogoutAllBots(); LOG_INFO("playerbots", "Logout all bots due no real player session."); @@ -435,7 +404,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) } if (availableBotCount < maxAllowedBotCount && - (sPlayerbotAIConfig->disabledWithoutRealPlayer == false || + (sPlayerbotAIConfig.disabledWithoutRealPlayer == false || (realPlayerIsLogged && DelayLoginBotsTimer != 0 && time(nullptr) >= DelayLoginBotsTimer))) { AddRandomBots(); @@ -446,25 +415,25 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) AddRandomBots(); } - if (sPlayerbotAIConfig->syncLevelWithPlayers && !players.empty()) + if (sPlayerbotAIConfig.syncLevelWithPlayers && !players.empty()) { if (time(nullptr) > (PlayersCheckTimer + 60)) - sRandomPlayerbotMgr->CheckPlayers(); + sRandomPlayerbotMgr.CheckPlayers(); } - if (sPlayerbotAIConfig->randomBotJoinBG /* && !players.empty()*/) + if (sPlayerbotAIConfig.randomBotJoinBG /* && !players.empty()*/) { if (time(nullptr) > (BgCheckTimer + 35)) - sRandomPlayerbotMgr->CheckBgQueue(); + sRandomPlayerbotMgr.CheckBgQueue(); } - if (sPlayerbotAIConfig->randomBotJoinLfg /* && !players.empty()*/) + if (sPlayerbotAIConfig.randomBotJoinLfg /* && !players.empty()*/) { if (time(nullptr) > (LfgCheckTimer + 30)) - sRandomPlayerbotMgr->CheckLfgQueue(); + sRandomPlayerbotMgr.CheckLfgQueue(); } - if (sPlayerbotAIConfig->randomBotAutologin && time(nullptr) > (printStatsTimer + 300)) + if (sPlayerbotAIConfig.randomBotAutologin && time(nullptr) > (printStatsTimer + 300)) { if (!printStatsTimer) { @@ -472,18 +441,18 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) } else { - sRandomPlayerbotMgr->PrintStats(); + sRandomPlayerbotMgr.PrintStats(); // activatePrintStatsThread(); } } - uint32 updateBots = sPlayerbotAIConfig->randomBotsPerInterval * onlineBotFocus / 100; + uint32 updateBots = sPlayerbotAIConfig.randomBotsPerInterval * onlineBotFocus / 100; uint32 maxNewBots = onlineBotCount < maxAllowedBotCount && - (sPlayerbotAIConfig->disabledWithoutRealPlayer == false || + (sPlayerbotAIConfig.disabledWithoutRealPlayer == false || (realPlayerIsLogged && DelayLoginBotsTimer != 0 && time(nullptr) >= DelayLoginBotsTimer)) ? maxAllowedBotCount - onlineBotCount : 0; - uint32 loginBots = std::min(sPlayerbotAIConfig->randomBotsPerInterval - updateBots, maxNewBots); + uint32 loginBots = std::min(sPlayerbotAIConfig.randomBotsPerInterval - updateBots, maxNewBots); if (!availableBots.empty()) { @@ -531,7 +500,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) if (pmo) pmo->finish(); - if (sPlayerbotAIConfig->hasLog("player_location.csv")) + if (sPlayerbotAIConfig.hasLog("player_location.csv")) { LogPlayerLocation(); } @@ -546,8 +515,8 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) // // // % increase/decrease wanted diff , avg diff // float activityPercentageMod = pid.calculate( -// sRandomPlayerbotMgr->GetPlayers().empty() ? sPlayerbotAIConfig->diffEmpty : -// sPlayerbotAIConfig->diffWithPlayer, sWorldUpdateTime.GetAverageUpdateTime()); +// sRandomPlayerbotMgr.GetPlayers().empty() ? sPlayerbotAIConfig.diffEmpty : +// sPlayerbotAIConfig.diffWithPlayer, sWorldUpdateTime.GetAverageUpdateTime()); // // activityPercentage = activityPercentageMod + 50; // @@ -573,7 +542,7 @@ void RandomPlayerbotMgr::AssignAccountTypes() std::vector allRandomBotAccounts; QueryResult allAccounts = LoginDatabase.Query( "SELECT id FROM account WHERE username LIKE '{}%%' ORDER BY id", - sPlayerbotAIConfig->randomBotAccountPrefix.c_str()); + sPlayerbotAIConfig.randomBotAccountPrefix.c_str()); if (allAccounts) { @@ -614,15 +583,15 @@ void RandomPlayerbotMgr::AssignAccountTypes() // Calculate needed RNDbot accounts uint32 neededRndBotAccounts = 0; - if (sPlayerbotAIConfig->maxRandomBots > 0) + if (sPlayerbotAIConfig.maxRandomBots > 0) { int divisor = RandomPlayerbotFactory::CalculateAvailableCharsPerAccount(); - int maxBots = sPlayerbotAIConfig->maxRandomBots; + int maxBots = sPlayerbotAIConfig.maxRandomBots; // Take periodic online-offline into account - if (sPlayerbotAIConfig->enablePeriodicOnlineOffline) + if (sPlayerbotAIConfig.enablePeriodicOnlineOffline) { - maxBots *= sPlayerbotAIConfig->periodicOnlineOfflineRatio; + maxBots *= sPlayerbotAIConfig.periodicOnlineOfflineRatio; } // Calculate base accounts needed for RNDbots, ensuring round up for maxBots not cleanly divisible by the divisor @@ -663,7 +632,7 @@ void RandomPlayerbotMgr::AssignAccountTypes() } // Assign AddClass accounts from highest position if needed - uint32 neededAddClassAccounts = sPlayerbotAIConfig->addClassAccountPoolSize; + uint32 neededAddClassAccounts = sPlayerbotAIConfig.addClassAccountPoolSize; if (existingAddClassAccounts < neededAddClassAccounts) { @@ -719,16 +688,16 @@ uint32 RandomPlayerbotMgr::AddRandomBots() { // Calculate how many bots to add maxAllowedBotCount -= currentBots.size(); - maxAllowedBotCount = std::min(sPlayerbotAIConfig->randomBotsPerInterval, maxAllowedBotCount); + maxAllowedBotCount = std::min(sPlayerbotAIConfig.randomBotsPerInterval, maxAllowedBotCount); // Single RNG instance for all shuffling std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); // Only need to track the Alliance count, as it's in Phase 1 - uint32 totalRatio = sPlayerbotAIConfig->randomBotAllianceRatio + sPlayerbotAIConfig->randomBotHordeRatio; - uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) / totalRatio; + uint32 totalRatio = sPlayerbotAIConfig.randomBotAllianceRatio + sPlayerbotAIConfig.randomBotHordeRatio; + uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig.randomBotAllianceRatio) / totalRatio; - uint32 remainder = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) % totalRatio; + uint32 remainder = maxAllowedBotCount * (sPlayerbotAIConfig.randomBotAllianceRatio) % totalRatio; // Fix #1082: Randomly add one based on reminder if (remainder && urand(1, totalRatio) <= remainder) @@ -738,13 +707,13 @@ uint32 RandomPlayerbotMgr::AddRandomBots() // Determine which accounts to use based on EnablePeriodicOnlineOffline std::vector accountsToUse; - if (sPlayerbotAIConfig->enablePeriodicOnlineOffline) + if (sPlayerbotAIConfig.enablePeriodicOnlineOffline) { // Calculate how many accounts can be used // With enablePeriodicOnlineOffline, don't use all of rndBotTypeAccounts right away. Fraction results are rounded up - uint32 accountsToUseCount = (rndBotTypeAccounts.size() + sPlayerbotAIConfig->periodicOnlineOfflineRatio - 1) - / sPlayerbotAIConfig->periodicOnlineOfflineRatio; + uint32 accountsToUseCount = (rndBotTypeAccounts.size() + sPlayerbotAIConfig.periodicOnlineOfflineRatio - 1) + / sPlayerbotAIConfig.periodicOnlineOfflineRatio; // Randomly select accounts std::vector shuffledAccounts = rndBotTypeAccounts; @@ -814,15 +783,15 @@ uint32 RandomPlayerbotMgr::AddRandomBots() GetEventValue(charInfo.guid, "logout") || GetPlayerBot(charInfo.guid) || std::find(currentBots.begin(), currentBots.end(), charInfo.guid) != currentBots.end() || - (sPlayerbotAIConfig->disableDeathKnightLogin && charInfo.rClass == CLASS_DEATH_KNIGHT)) + (sPlayerbotAIConfig.disableDeathKnightLogin && charInfo.rClass == CLASS_DEATH_KNIGHT)) { return false; } - uint32 add_time = sPlayerbotAIConfig->enablePeriodicOnlineOffline - ? urand(sPlayerbotAIConfig->minRandomBotInWorldTime, - sPlayerbotAIConfig->maxRandomBotInWorldTime) - : sPlayerbotAIConfig->permanentlyInWorldTime; + uint32 add_time = sPlayerbotAIConfig.enablePeriodicOnlineOffline + ? urand(sPlayerbotAIConfig.minRandomBotInWorldTime, + sPlayerbotAIConfig.maxRandomBotInWorldTime) + : sPlayerbotAIConfig.permanentlyInWorldTime; SetEventValue(charInfo.guid, "add", 1, add_time); SetEventValue(charInfo.guid, "logout", 0, 0); @@ -1187,24 +1156,24 @@ void RandomPlayerbotMgr::CheckBgQueue() } // If enabled, wait for all bots to have logged in before queueing for Arena's / BG's - if (sPlayerbotAIConfig->randomBotAutoJoinBG && playerBots.size() >= GetMaxAllowedBotCount()) + if (sPlayerbotAIConfig.randomBotAutoJoinBG && playerBots.size() >= GetMaxAllowedBotCount()) { - uint32 randomBotAutoJoinArenaBracket = sPlayerbotAIConfig->randomBotAutoJoinArenaBracket; - uint32 randomBotAutoJoinBGRatedArena2v2Count = sPlayerbotAIConfig->randomBotAutoJoinBGRatedArena2v2Count; - uint32 randomBotAutoJoinBGRatedArena3v3Count = sPlayerbotAIConfig->randomBotAutoJoinBGRatedArena3v3Count; - uint32 randomBotAutoJoinBGRatedArena5v5Count = sPlayerbotAIConfig->randomBotAutoJoinBGRatedArena5v5Count; + uint32 randomBotAutoJoinArenaBracket = sPlayerbotAIConfig.randomBotAutoJoinArenaBracket; + uint32 randomBotAutoJoinBGRatedArena2v2Count = sPlayerbotAIConfig.randomBotAutoJoinBGRatedArena2v2Count; + uint32 randomBotAutoJoinBGRatedArena3v3Count = sPlayerbotAIConfig.randomBotAutoJoinBGRatedArena3v3Count; + uint32 randomBotAutoJoinBGRatedArena5v5Count = sPlayerbotAIConfig.randomBotAutoJoinBGRatedArena5v5Count; - uint32 randomBotAutoJoinBGICCount = sPlayerbotAIConfig->randomBotAutoJoinBGICCount; - uint32 randomBotAutoJoinBGEYCount = sPlayerbotAIConfig->randomBotAutoJoinBGEYCount; - uint32 randomBotAutoJoinBGAVCount = sPlayerbotAIConfig->randomBotAutoJoinBGAVCount; - uint32 randomBotAutoJoinBGABCount = sPlayerbotAIConfig->randomBotAutoJoinBGABCount; - uint32 randomBotAutoJoinBGWSCount = sPlayerbotAIConfig->randomBotAutoJoinBGWSCount; + uint32 randomBotAutoJoinBGICCount = sPlayerbotAIConfig.randomBotAutoJoinBGICCount; + uint32 randomBotAutoJoinBGEYCount = sPlayerbotAIConfig.randomBotAutoJoinBGEYCount; + uint32 randomBotAutoJoinBGAVCount = sPlayerbotAIConfig.randomBotAutoJoinBGAVCount; + uint32 randomBotAutoJoinBGABCount = sPlayerbotAIConfig.randomBotAutoJoinBGABCount; + uint32 randomBotAutoJoinBGWSCount = sPlayerbotAIConfig.randomBotAutoJoinBGWSCount; - std::vector icBrackets = parseBrackets(sPlayerbotAIConfig->randomBotAutoJoinICBrackets); - std::vector eyBrackets = parseBrackets(sPlayerbotAIConfig->randomBotAutoJoinEYBrackets); - std::vector avBrackets = parseBrackets(sPlayerbotAIConfig->randomBotAutoJoinAVBrackets); - std::vector abBrackets = parseBrackets(sPlayerbotAIConfig->randomBotAutoJoinABBrackets); - std::vector wsBrackets = parseBrackets(sPlayerbotAIConfig->randomBotAutoJoinWSBrackets); + std::vector icBrackets = parseBrackets(sPlayerbotAIConfig.randomBotAutoJoinICBrackets); + std::vector eyBrackets = parseBrackets(sPlayerbotAIConfig.randomBotAutoJoinEYBrackets); + std::vector avBrackets = parseBrackets(sPlayerbotAIConfig.randomBotAutoJoinAVBrackets); + std::vector abBrackets = parseBrackets(sPlayerbotAIConfig.randomBotAutoJoinABBrackets); + std::vector wsBrackets = parseBrackets(sPlayerbotAIConfig.randomBotAutoJoinWSBrackets); // Check both bgInstanceCount / bgInstances.size // to help counter against potentional inconsistencies @@ -1369,7 +1338,7 @@ void RandomPlayerbotMgr::CheckPlayers() LOG_INFO("playerbots", "Checking Players..."); if (!playersLevel) - playersLevel = sPlayerbotAIConfig->randombotStartingLevel; + playersLevel = sPlayerbotAIConfig.randombotStartingLevel; for (std::vector::iterator i = players.begin(); i != players.end(); ++i) { @@ -1393,7 +1362,7 @@ void RandomPlayerbotMgr::ScheduleRandomize(uint32 bot, uint32 time) { SetEventVa void RandomPlayerbotMgr::ScheduleTeleport(uint32 bot, uint32 time) { if (!time) - time = 60 + urand(sPlayerbotAIConfig->randomBotUpdateInterval, sPlayerbotAIConfig->randomBotUpdateInterval * 3); + time = 60 + urand(sPlayerbotAIConfig.randomBotUpdateInterval, sPlayerbotAIConfig.randomBotUpdateInterval * 3); SetEventValue(bot, "teleport", 1, time); } @@ -1401,8 +1370,8 @@ void RandomPlayerbotMgr::ScheduleTeleport(uint32 bot, uint32 time) void RandomPlayerbotMgr::ScheduleChangeStrategy(uint32 bot, uint32 time) { if (!time) - time = urand(sPlayerbotAIConfig->minRandomBotChangeStrategyTime, - sPlayerbotAIConfig->maxRandomBotChangeStrategyTime); + time = urand(sPlayerbotAIConfig.minRandomBotChangeStrategyTime, + sPlayerbotAIConfig.maxRandomBotChangeStrategyTime); SetEventValue(bot, "change_strategy", 1, time); } @@ -1440,7 +1409,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) AddPlayerBot(botGUID, 0); randomTime = urand(1, 2); - uint32 randomBotUpdateInterval = _isBotInitializing ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval; + uint32 randomBotUpdateInterval = _isBotInitializing ? 1 : sPlayerbotAIConfig.randomBotUpdateInterval; randomTime = urand(std::max(5, static_cast(randomBotUpdateInterval * 0.5)), std::max(12, static_cast(randomBotUpdateInterval * 2))); SetEventValue(bot, "update", 1, randomTime); @@ -1477,7 +1446,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) if (botAI) { // botAI->GetAiObjectContext()->GetValue("random bot update")->Set(true); - if (!sRandomPlayerbotMgr->IsRandomBot(player)) + if (!sRandomPlayerbotMgr.IsRandomBot(player)) update = false; if (player->GetGroup() && botAI->GetGroupLeader()) @@ -1489,14 +1458,14 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) } } - // if (botAI->HasPlayerNearby(sPlayerbotAIConfig->grindDistance)) + // if (botAI->HasPlayerNearby(sPlayerbotAIConfig.grindDistance)) // update = false; } if (update) ProcessBot(player); - randomTime = urand(sPlayerbotAIConfig->minRandomBotReviveTime, sPlayerbotAIConfig->maxRandomBotReviveTime); + randomTime = urand(sPlayerbotAIConfig.minRandomBotReviveTime, sPlayerbotAIConfig.maxRandomBotReviveTime); SetEventValue(bot, "update", 1, randomTime); return true; @@ -1510,7 +1479,7 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) LogoutPlayerBot(botGUID); currentBots.remove(bot); SetEventValue(bot, "logout", 1, - urand(sPlayerbotAIConfig->minRandomBotInWorldTime, sPlayerbotAIConfig->maxRandomBotInWorldTime)); + urand(sPlayerbotAIConfig.minRandomBotInWorldTime, sPlayerbotAIConfig.maxRandomBotInWorldTime)); return true; } @@ -1538,10 +1507,10 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot) if (!GetEventValue(botId, "dead")) { uint32 randomTime = - urand(sPlayerbotAIConfig->minRandomBotReviveTime, sPlayerbotAIConfig->maxRandomBotReviveTime); + urand(sPlayerbotAIConfig.minRandomBotReviveTime, sPlayerbotAIConfig.maxRandomBotReviveTime); LOG_DEBUG("playerbots", "Mark bot {} as dead, will be revived in {}s.", bot->GetName().c_str(), randomTime); - SetEventValue(botId, "dead", 1, sPlayerbotAIConfig->maxRandomBotInWorldTime); + SetEventValue(botId, "dead", 1, sPlayerbotAIConfig.maxRandomBotInWorldTime); SetEventValue(botId, "revive", 1, randomTime); return false; } @@ -1591,11 +1560,11 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot) // if (guild->GetLeaderGUID() == player->GetGUID()) // { // for (std::vector::iterator i = players.begin(); i != players.end(); ++i) - // sGuildTaskMgr->Update(*i, player); + // GuildTaskMgr::instance().Update(*i, player); // } // uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guild->GetLeaderGUID()); - // if (!sPlayerbotAIConfig->IsInRandomAccountList(accountId)) + // if (!sPlayerbotAIConfig.IsInRandomAccountList(accountId)) // { // uint8 rank = player->GetRank(); // randomiser = rank < 4 ? false : true; @@ -1608,7 +1577,7 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot) LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: randomized", botId, bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName()); uint32 randomTime = - urand(sPlayerbotAIConfig->minRandomBotRandomizeTime, sPlayerbotAIConfig->maxRandomBotRandomizeTime); + urand(sPlayerbotAIConfig.minRandomBotRandomizeTime, sPlayerbotAIConfig.maxRandomBotRandomizeTime); ScheduleRandomize(botId, randomTime); return true; } @@ -1627,8 +1596,8 @@ bool RandomPlayerbotMgr::ProcessBot(Player* bot) LOG_DEBUG("playerbots", "Bot #{} <{}>: teleport for level and refresh", botId, bot->GetName()); Refresh(bot); RandomTeleportForLevel(bot); - uint32 time = urand(sPlayerbotAIConfig->minRandomBotTeleportInterval, - sPlayerbotAIConfig->maxRandomBotTeleportInterval); + uint32 time = urand(sPlayerbotAIConfig.minRandomBotTeleportInterval, + sPlayerbotAIConfig.maxRandomBotTeleportInterval); ScheduleTeleport(botId, time); return true; } @@ -1680,7 +1649,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& return; } - // if (sPlayerbotAIConfig->randomBotRpgChance < 0) + // if (sPlayerbotAIConfig.randomBotRpgChance < 0) // return; if (locs.empty()) @@ -1697,9 +1666,9 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& [bot](WorldPosition l) { std::vector::iterator i = - find(sPlayerbotAIConfig->randomBotMaps.begin(), - sPlayerbotAIConfig->randomBotMaps.end(), l.getMapId()); - return i == sPlayerbotAIConfig->randomBotMaps.end(); + find(sPlayerbotAIConfig.randomBotMaps.begin(), + sPlayerbotAIConfig.randomBotMaps.end(), l.getMapId()); + return i == sPlayerbotAIConfig.randomBotMaps.end(); }), tlocs.end()); if (tlocs.empty()) @@ -1708,17 +1677,17 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& return; } - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations"); + PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "RandomTeleportByLocations"); std::shuffle(std::begin(tlocs), std::end(tlocs), RandomEngine::Instance()); for (uint32 i = 0; i < tlocs.size(); i++) { WorldLocation loc = tlocs[i]; - float x = loc.GetPositionX(); // + (attemtps > 0 ? urand(0, sPlayerbotAIConfig->grindDistance) - - // sPlayerbotAIConfig->grindDistance / 2 : 0); - float y = loc.GetPositionY(); // + (attemtps > 0 ? urand(0, sPlayerbotAIConfig->grindDistance) - - // sPlayerbotAIConfig->grindDistance / 2 : 0); + float x = loc.GetPositionX(); // + (attemtps > 0 ? urand(0, sPlayerbotAIConfig.grindDistance) - + // sPlayerbotAIConfig.grindDistance / 2 : 0); + float y = loc.GetPositionY(); // + (attemtps > 0 ? urand(0, sPlayerbotAIConfig.grindDistance) - + // sPlayerbotAIConfig.grindDistance / 2 : 0); float z = loc.GetPositionZ(); Map* map = sMapMgr->FindMap(loc.GetMapId(), 0); @@ -1869,7 +1838,7 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket() zone2LevelBracket[4197] = {79, 80}; // Wintergrasp // Override with values from config - for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig->zoneBrackets) + for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig.zoneBrackets) { zone2LevelBracket[zoneId] = {bracketPair.first, bracketPair.second}; } @@ -1920,7 +1889,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache() "INNER JOIN creature_template t on c.id1 = t.entry " "ORDER BY " "t.minlevel;", - sPlayerbotAIConfig->randomBotMapsAsString.c_str()); + sPlayerbotAIConfig.randomBotMapsAsString.c_str()); uint32 collected_locs = 0; if (results) { @@ -1936,8 +1905,8 @@ void RandomPlayerbotMgr::PrepareTeleportCache() uint32 level = (min_level + max_level + 1) / 2; WorldLocation loc(mapId, x, y, z, 0); collected_locs++; - for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleLowerLevel; - l <= (int32)level + (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel; l++) + for (int32 l = (int32)level - (int32)sPlayerbotAIConfig.randomBotTeleLowerLevel; + l <= (int32)level + (int32)sPlayerbotAIConfig.randomBotTeleHigherLevel; l++) { if (l < 1 || l > maxLevel) { @@ -1949,7 +1918,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache() } LOG_INFO("playerbots", ">> {} locations for level collected.", collected_locs); - if (sPlayerbotAIConfig->enableNewRpgStrategy) + if (sPlayerbotAIConfig.enableNewRpgStrategy) { PrepareZone2LevelBracket(); LOG_INFO("playerbots", "Preparing innkeepers / flightmasters locations for level..."); @@ -1972,7 +1941,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache() "AND map IN ({}) " "ORDER BY " "t.minlevel;", - sPlayerbotAIConfig->randomBotMapsAsString.c_str()); + sPlayerbotAIConfig.randomBotMapsAsString.c_str()); collected_locs = 0; if (results) { @@ -2005,10 +1974,10 @@ void RandomPlayerbotMgr::PrepareTeleportCache() { WorldPosition pos(mapId, x, y, z, orient); if (forHorde) - sFlightMasterCache->AddHordeFlightMaster(guid, pos); + FlightMasterCache::Instance().AddHordeFlightMaster(guid, pos); if (forAlliance) - sFlightMasterCache->AddAllianceFlightMaster(guid, pos); + FlightMasterCache::Instance().AddAllianceFlightMaster(guid, pos); } const AreaTableEntry* area = sAreaTableStore.LookupEntry(map->GetAreaId(PHASEMASK_NORMAL, x, y, z)); uint32 zoneId = area->zone ? area->zone : area->ID; @@ -2077,7 +2046,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache() "AND map IN ({}) " "ORDER BY " "t.minlevel;", - sPlayerbotAIConfig->randomBotMapsAsString.c_str()); + sPlayerbotAIConfig.randomBotMapsAsString.c_str()); collected_locs = 0; if (results) { @@ -2157,16 +2126,16 @@ void RandomPlayerbotMgr::PrepareAddclassCache() void RandomPlayerbotMgr::Init() { - if (sPlayerbotAIConfig->addClassCommand) - sRandomPlayerbotMgr->PrepareAddclassCache(); + if (sPlayerbotAIConfig.addClassCommand) + sRandomPlayerbotMgr.PrepareAddclassCache(); - if (sPlayerbotAIConfig->enabled) + if (sPlayerbotAIConfig.enabled) { - sRandomPlayerbotMgr->PrepareTeleportCache(); + sRandomPlayerbotMgr.PrepareTeleportCache(); } - if (sPlayerbotAIConfig->randomBotJoinBG) - sRandomPlayerbotMgr->LoadBattleMastersCache(); + if (sPlayerbotAIConfig.randomBotJoinBG) + sRandomPlayerbotMgr.LoadBattleMastersCache(); PlayerbotsDatabase.Execute("DELETE FROM playerbots_random_bots WHERE event = 'add'"); } @@ -2179,17 +2148,17 @@ void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot) uint32 level = bot->GetLevel(); uint8 race = bot->getRace(); std::vector* locs = nullptr; - if (sPlayerbotAIConfig->enableNewRpgStrategy) + if (sPlayerbotAIConfig.enableNewRpgStrategy) locs = IsAlliance(race) ? &allianceStarterPerLevelCache[level] : &hordeStarterPerLevelCache[level]; else locs = &locsPerLevelCache[level]; - if (level >= 10 && urand(0, 100) < sPlayerbotAIConfig->probTeleToBankers * 100) + if (level >= 10 && urand(0, 100) < sPlayerbotAIConfig.probTeleToBankers * 100) { std::vector fallbackLocs; for (auto& bLoc : bankerLocsPerLevelCache[level]) fallbackLocs.push_back(bLoc.loc); - if (!sPlayerbotAIConfig->enableWeightTeleToCityBankers) + if (!sPlayerbotAIConfig.enableWeightTeleToCityBankers) { RandomTeleport(bot, fallbackLocs, true); return; @@ -2227,16 +2196,16 @@ void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot) int weight = 0; switch (city) { - case CityId::STORMWIND: weight = sPlayerbotAIConfig->weightTeleToStormwind; break; - case CityId::IRONFORGE: weight = sPlayerbotAIConfig->weightTeleToIronforge; break; - case CityId::DARNASSUS: weight = sPlayerbotAIConfig->weightTeleToDarnassus; break; - case CityId::EXODAR: weight = sPlayerbotAIConfig->weightTeleToExodar; break; - case CityId::ORGRIMMAR: weight = sPlayerbotAIConfig->weightTeleToOrgrimmar; break; - case CityId::UNDERCITY: weight = sPlayerbotAIConfig->weightTeleToUndercity; break; - case CityId::THUNDER_BLUFF: weight = sPlayerbotAIConfig->weightTeleToThunderBluff; break; - case CityId::SILVERMOON_CITY: weight = sPlayerbotAIConfig->weightTeleToSilvermoonCity; break; - case CityId::SHATTRATH_CITY: weight = sPlayerbotAIConfig->weightTeleToShattrathCity; break; - case CityId::DALARAN: weight = sPlayerbotAIConfig->weightTeleToDalaran; break; + case CityId::STORMWIND: weight = sPlayerbotAIConfig.weightTeleToStormwind; break; + case CityId::IRONFORGE: weight = sPlayerbotAIConfig.weightTeleToIronforge; break; + case CityId::DARNASSUS: weight = sPlayerbotAIConfig.weightTeleToDarnassus; break; + case CityId::EXODAR: weight = sPlayerbotAIConfig.weightTeleToExodar; break; + case CityId::ORGRIMMAR: weight = sPlayerbotAIConfig.weightTeleToOrgrimmar; break; + case CityId::UNDERCITY: weight = sPlayerbotAIConfig.weightTeleToUndercity; break; + case CityId::THUNDER_BLUFF: weight = sPlayerbotAIConfig.weightTeleToThunderBluff; break; + case CityId::SILVERMOON_CITY: weight = sPlayerbotAIConfig.weightTeleToSilvermoonCity; break; + case CityId::SHATTRATH_CITY: weight = sPlayerbotAIConfig.weightTeleToShattrathCity; break; + case CityId::DALARAN: weight = sPlayerbotAIConfig.weightTeleToDalaran; break; default: weight = 0; break; } if (weight <= 0) continue; @@ -2284,7 +2253,7 @@ void RandomPlayerbotMgr::RandomTeleportGrindForLevel(Player* bot) uint32 level = bot->GetLevel(); uint8 race = bot->getRace(); std::vector* locs = nullptr; - if (sPlayerbotAIConfig->enableNewRpgStrategy) + if (sPlayerbotAIConfig.enableNewRpgStrategy) locs = IsAlliance(race) ? &allianceStarterPerLevelCache[level] : &hordeStarterPerLevelCache[level]; else locs = &locsPerLevelCache[level]; @@ -2299,11 +2268,11 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot) if (bot->InBattleground()) return; - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomTeleport"); + PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "RandomTeleport"); std::vector locs; std::list targets; - float range = sPlayerbotAIConfig->randomBotTeleportDistance; + float range = sPlayerbotAIConfig.randomBotTeleportDistance; Acore::AnyUnitInObjectRangeCheck u_check(bot, range); Acore::UnitListSearcher searcher(bot, targets, u_check); Cell::VisitObjects(bot, searcher, range); @@ -2313,7 +2282,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot) for (Unit* unit : targets) { bot->UpdatePosition(*unit); - FleeManager manager(bot, sPlayerbotAIConfig->sightDistance, 0, true); + FleeManager manager(bot, sPlayerbotAIConfig.sightDistance, 0, true); float rx, ry, rz; if (manager.CalculateDestination(&rx, &ry, &rz)) { @@ -2342,7 +2311,7 @@ void RandomPlayerbotMgr::Randomize(Player* bot) { RandomizeFirst(bot); } - else if (bot->GetLevel() < sPlayerbotAIConfig->randomBotMaxLevel || !sPlayerbotAIConfig->downgradeMaxLevelBot) + else if (bot->GetLevel() < sPlayerbotAIConfig.randomBotMaxLevel || !sPlayerbotAIConfig.downgradeMaxLevelBot) { uint8 level = bot->GetLevel(); PlayerbotFactory factory(bot, level); @@ -2357,11 +2326,11 @@ void RandomPlayerbotMgr::Randomize(Player* bot) void RandomPlayerbotMgr::IncreaseLevel(Player* bot) { - uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; + uint32 maxLevel = sPlayerbotAIConfig.randomBotMaxLevel; if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "IncreaseLevel"); + PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "IncreaseLevel"); uint32 lastLevel = GetValue(bot, "level"); uint8 level = bot->GetLevel() + 1; if (level > maxLevel) @@ -2384,27 +2353,27 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot) if (!botAI) return; - uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; + uint32 maxLevel = sPlayerbotAIConfig.randomBotMaxLevel; if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); // if lvl sync is enabled, max level is limited by online players lvl - if (sPlayerbotAIConfig->syncLevelWithPlayers) - maxLevel = std::max(sPlayerbotAIConfig->randomBotMinLevel, + if (sPlayerbotAIConfig.syncLevelWithPlayers) + maxLevel = std::max(sPlayerbotAIConfig.randomBotMinLevel, std::min(playersLevel, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))); - uint32 minLevel = sPlayerbotAIConfig->randomBotMinLevel; + uint32 minLevel = sPlayerbotAIConfig.randomBotMinLevel; if (bot->getClass() == CLASS_DEATH_KNIGHT) { maxLevel = std::max(maxLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)); minLevel = std::max(minLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)); } - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomizeFirst"); + PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "RandomizeFirst"); uint32 level; - if (sPlayerbotAIConfig->downgradeMaxLevelBot && bot->GetLevel() >= sPlayerbotAIConfig->randomBotMaxLevel) + if (sPlayerbotAIConfig.downgradeMaxLevelBot && bot->GetLevel() >= sPlayerbotAIConfig.randomBotMaxLevel) { if (bot->getClass() == CLASS_DEATH_KNIGHT) { @@ -2412,18 +2381,18 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot) } else { - level = sPlayerbotAIConfig->randomBotMinLevel; + level = sPlayerbotAIConfig.randomBotMinLevel; } } else { uint32 roll = urand(1, 100); - if (roll <= 100 * sPlayerbotAIConfig->randomBotMaxLevelChance) + if (roll <= 100 * sPlayerbotAIConfig.randomBotMaxLevelChance) { level = maxLevel; } else if (roll <= - (100 * (sPlayerbotAIConfig->randomBotMaxLevelChance + sPlayerbotAIConfig->randomBotMinLevelChance))) + (100 * (sPlayerbotAIConfig.randomBotMaxLevelChance + sPlayerbotAIConfig.randomBotMinLevelChance))) { level = minLevel; } @@ -2433,11 +2402,11 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot) } } - if (sPlayerbotAIConfig->disableRandomLevels) + if (sPlayerbotAIConfig.disableRandomLevels) { - level = bot->getClass() == CLASS_DEATH_KNIGHT ? std::max(sPlayerbotAIConfig->randombotStartingLevel, + level = bot->getClass() == CLASS_DEATH_KNIGHT ? std::max(sPlayerbotAIConfig.randombotStartingLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)) - : sPlayerbotAIConfig->randombotStartingLevel; + : sPlayerbotAIConfig.randombotStartingLevel; } SetValue(bot, "level", level); @@ -2445,9 +2414,9 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot) factory.Randomize(false); uint32 randomTime = - urand(sPlayerbotAIConfig->minRandomBotRandomizeTime, sPlayerbotAIConfig->maxRandomBotRandomizeTime); + urand(sPlayerbotAIConfig.minRandomBotRandomizeTime, sPlayerbotAIConfig.maxRandomBotRandomizeTime); uint32 inworldTime = - urand(sPlayerbotAIConfig->minRandomBotInWorldTime, sPlayerbotAIConfig->maxRandomBotInWorldTime); + urand(sPlayerbotAIConfig.minRandomBotInWorldTime, sPlayerbotAIConfig.maxRandomBotInWorldTime); PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_UPD_RANDOM_BOTS); stmt->SetData(0, randomTime); @@ -2479,16 +2448,16 @@ void RandomPlayerbotMgr::RandomizeMin(Player* bot) if (!botAI) return; - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomizeMin"); - uint32 level = sPlayerbotAIConfig->randomBotMinLevel; + PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "RandomizeMin"); + uint32 level = sPlayerbotAIConfig.randomBotMinLevel; SetValue(bot, "level", level); PlayerbotFactory factory(bot, level); factory.Randomize(false); uint32 randomTime = - urand(sPlayerbotAIConfig->minRandomBotRandomizeTime, sPlayerbotAIConfig->maxRandomBotRandomizeTime); + urand(sPlayerbotAIConfig.minRandomBotRandomizeTime, sPlayerbotAIConfig.maxRandomBotRandomizeTime); uint32 inworldTime = - urand(sPlayerbotAIConfig->minRandomBotInWorldTime, sPlayerbotAIConfig->maxRandomBotInWorldTime); + urand(sPlayerbotAIConfig.minRandomBotInWorldTime, sPlayerbotAIConfig.maxRandomBotInWorldTime); PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_UPD_RANDOM_BOTS); stmt->SetData(0, randomTime); @@ -2527,8 +2496,8 @@ uint32 RandomPlayerbotMgr::GetZoneLevel(uint16 mapId, float teleX, float teleY, "SELECT AVG(t.minlevel) minlevel, AVG(t.maxlevel) maxlevel FROM creature c " "INNER JOIN creature_template t ON c.id1 = t.entry WHERE map = {} AND minlevel > 1 AND ABS(position_x - {}) < " "{} AND ABS(position_y - {}) < {}", - mapId, teleX, sPlayerbotAIConfig->randomBotTeleportDistance / 2, teleY, - sPlayerbotAIConfig->randomBotTeleportDistance / 2); + mapId, teleX, sPlayerbotAIConfig.randomBotTeleportDistance / 2, teleY, + sPlayerbotAIConfig.randomBotTeleportDistance / 2); if (results) { @@ -2560,7 +2529,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot) botAI->ResetStrategies(false); } - // if (sPlayerbotAIConfig->disableRandomLevels) + // if (sPlayerbotAIConfig.disableRandomLevels) // return; if (bot->InBattleground()) @@ -2568,7 +2537,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot) LOG_DEBUG("playerbots", "Refreshing bot {} <{}>", bot->GetGUID().ToString().c_str(), bot->GetName().c_str()); - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "Refresh"); + PerfMonitorOperation* pmo = sPerfMonitor.start(PERF_MON_RNDBOT, "Refresh"); botAI->Reset(); @@ -2612,7 +2581,7 @@ bool RandomPlayerbotMgr::IsRandomBot(Player* bot) bool RandomPlayerbotMgr::IsRandomBot(ObjectGuid::LowType bot) { ObjectGuid guid = ObjectGuid::Create(bot); - if (!sPlayerbotAIConfig->IsInRandomAccountList(sCharacterCache->GetCharacterAccountIdByGuid(guid))) + if (!sPlayerbotAIConfig.IsInRandomAccountList(sCharacterCache->GetCharacterAccountIdByGuid(guid))) return false; if (std::find(currentBots.begin(), currentBots.end(), bot) != currentBots.end()) @@ -2841,7 +2810,7 @@ std::string RandomPlayerbotMgr::GetData(uint32 bot, std::string const& type) { r void RandomPlayerbotMgr::SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data) { - SetEventValue(bot, type, value, sPlayerbotAIConfig->maxRandomBotInWorldTime, data); + SetEventValue(bot, type, value, sPlayerbotAIConfig.maxRandomBotInWorldTime, data); } void RandomPlayerbotMgr::SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data) @@ -2851,7 +2820,7 @@ void RandomPlayerbotMgr::SetValue(Player* bot, std::string const& type, uint32 v bool RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args) { - if (!sPlayerbotAIConfig->enabled) + if (!sPlayerbotAIConfig.enabled) { LOG_ERROR("playerbots", "Playerbots system is currently disabled!"); return false; @@ -2868,27 +2837,27 @@ bool RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(ChatHandler* handler, cha if (cmd == "reset") { PlayerbotsDatabase.Execute(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_RANDOM_BOTS)); - sRandomPlayerbotMgr->eventCache.clear(); + sRandomPlayerbotMgr.eventCache.clear(); LOG_INFO("playerbots", "Random bots were reset for all players. Please restart the Server."); return true; } if (cmd == "stats") { - sRandomPlayerbotMgr->PrintStats(); + sRandomPlayerbotMgr.PrintStats(); // activatePrintStatsThread(); return true; } if (cmd == "reload") { - sPlayerbotAIConfig->Initialize(); + sPlayerbotAIConfig.Initialize(); return true; } if (cmd == "update") { - sRandomPlayerbotMgr->UpdateAIInternal(0); + sRandomPlayerbotMgr.UpdateAIInternal(0); return true; } @@ -2913,8 +2882,8 @@ bool RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(ChatHandler* handler, cha std::string const name = cmd.size() > prefix.size() + 1 ? cmd.substr(1 + prefix.size()) : "%"; std::vector botIds; - for (std::vector::iterator i = sPlayerbotAIConfig->randomBotAccounts.begin(); - i != sPlayerbotAIConfig->randomBotAccounts.end(); ++i) + for (std::vector::iterator i = sPlayerbotAIConfig.randomBotAccounts.begin(); + i != sPlayerbotAIConfig.randomBotAccounts.end(); ++i) { uint32 account = *i; if (QueryResult results = CharacterDatabase.Query( @@ -2926,7 +2895,7 @@ bool RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(ChatHandler* handler, cha uint32 botId = fields[0].Get(); ObjectGuid guid = ObjectGuid::Create(botId); - if (!sRandomPlayerbotMgr->IsRandomBot(guid.GetCounter())) + if (!sRandomPlayerbotMgr.IsRandomBot(guid.GetCounter())) { continue; } @@ -2957,13 +2926,13 @@ bool RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(ChatHandler* handler, cha bot->GetName().c_str()); ConsoleCommandHandler handler = j->second; - (sRandomPlayerbotMgr->*handler)(bot); + (sRandomPlayerbotMgr.*handler)(bot); } return true; } - // std::vector messages = sRandomPlayerbotMgr->HandlePlayerbotCommand(args); + // std::vector messages = sRandomPlayerbotMgr.HandlePlayerbotCommand(args); // for (std::vector::iterator i = messages.begin(); i != messages.end(); ++i) // { // LOG_INFO("playerbots", "{}", i->c_str()); @@ -3021,15 +2990,15 @@ void RandomPlayerbotMgr::OnBotLoginInternal(Player* const bot) if (_isBotLogging) { LOG_INFO("playerbots", "{}/{} Bot {} logged in", playerBots.size(), - sRandomPlayerbotMgr->GetMaxAllowedBotCount(), bot->GetName().c_str()); + sRandomPlayerbotMgr.GetMaxAllowedBotCount(), bot->GetName().c_str()); - if (playerBots.size() == sRandomPlayerbotMgr->GetMaxAllowedBotCount()) + if (playerBots.size() == sRandomPlayerbotMgr.GetMaxAllowedBotCount()) { _isBotLogging = false; } } - if (sPlayerbotAIConfig->randomBotFixedLevel) + if (sPlayerbotAIConfig.randomBotFixedLevel) { bot->SetPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN); } @@ -3081,18 +3050,18 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) { WorldPosition botPos(player); - // botPos.GetReachableRandomPointOnGround(player, sPlayerbotAIConfig->reactDistance * 2, true); + // botPos.GetReachableRandomPointOnGround(player, sPlayerbotAIConfig.reactDistance * 2, true); // player->TeleportTo(botPos); // player->Relocate(botPos.coord_x, botPos.coord_y, botPos.coord_z, botPos.orientation); if (!player->GetFactionTemplateEntry()) { - botPos.GetReachableRandomPointOnGround(player, sPlayerbotAIConfig->reactDistance * 2, true); + botPos.GetReachableRandomPointOnGround(player, sPlayerbotAIConfig.reactDistance * 2, true); } else { - std::vector dests = sTravelMgr->getRpgTravelDestinations(player, true, true, 200000.0f); + std::vector dests = TravelMgr::instance().getRpgTravelDestinations(player, true, true, 200000.0f); do { @@ -3281,7 +3250,7 @@ void RandomPlayerbotMgr::PrintStats() zoneCount[bot->GetZoneId()]++; - if (sPlayerbotAIConfig->enableNewRpgStrategy) + if (sPlayerbotAIConfig.enableNewRpgStrategy) { rpgStatusCount[botAI->rpgInfo.status]++; rpgStasticTotal += botAI->rpgStatistic; @@ -3355,7 +3324,7 @@ void RandomPlayerbotMgr::PrintStats() LOG_INFO("playerbots", " In Rest: {}", rest); LOG_INFO("playerbots", " Dead: {}", dead); - if (sPlayerbotAIConfig->enableNewRpgStrategy) + if (sPlayerbotAIConfig.enableNewRpgStrategy) { LOG_INFO("playerbots", "Bots rpg status:"); LOG_INFO("playerbots", @@ -3381,8 +3350,8 @@ double RandomPlayerbotMgr::GetBuyMultiplier(Player* bot) if (!value) { value = urand(50, 120); - uint32 validIn = urand(sPlayerbotAIConfig->minRandomBotsPriceChangeInterval, - sPlayerbotAIConfig->maxRandomBotsPriceChangeInterval); + uint32 validIn = urand(sPlayerbotAIConfig.minRandomBotsPriceChangeInterval, + sPlayerbotAIConfig.maxRandomBotsPriceChangeInterval); SetEventValue(id, "buymultiplier", value, validIn); } @@ -3396,8 +3365,8 @@ double RandomPlayerbotMgr::GetSellMultiplier(Player* bot) if (!value) { value = urand(80, 250); - uint32 validIn = urand(sPlayerbotAIConfig->minRandomBotsPriceChangeInterval, - sPlayerbotAIConfig->maxRandomBotsPriceChangeInterval); + uint32 validIn = urand(sPlayerbotAIConfig.minRandomBotsPriceChangeInterval, + sPlayerbotAIConfig.maxRandomBotsPriceChangeInterval); SetEventValue(id, "sellmultiplier", value, validIn); } @@ -3426,7 +3395,7 @@ void RandomPlayerbotMgr::SetTradeDiscount(Player* bot, Player* master, uint32 va std::ostringstream name; name << "trade_discount_" << masterId; - SetEventValue(botId, name.str(), value, sPlayerbotAIConfig->maxRandomBotInWorldTime); + SetEventValue(botId, name.str(), value, sPlayerbotAIConfig.maxRandomBotInWorldTime); } uint32 RandomPlayerbotMgr::GetTradeDiscount(Player* bot, Player* master) @@ -3469,7 +3438,7 @@ void RandomPlayerbotMgr::ChangeStrategy(Player* player) { uint32 bot = player->GetGUID().GetCounter(); - if (frand(0.f, 100.f) > sPlayerbotAIConfig->randomBotRpgChance) + if (frand(0.f, 100.f) > sPlayerbotAIConfig.randomBotRpgChance) { LOG_INFO("playerbots", "Bot #{} <{}>: sent to grind spot", bot, player->GetName().c_str()); ScheduleTeleport(bot, 30); @@ -3479,7 +3448,7 @@ void RandomPlayerbotMgr::ChangeStrategy(Player* player) LOG_INFO("playerbots", "Changing strategy for bot #{} <{}> to RPG", bot, player->GetName().c_str()); LOG_INFO("playerbots", "Bot #{} <{}>: sent to inn", bot, player->GetName().c_str()); RandomTeleportForLevel(player); - SetEventValue(bot, "teleport", 1, sPlayerbotAIConfig->maxRandomBotInWorldTime); + SetEventValue(bot, "teleport", 1, sPlayerbotAIConfig.maxRandomBotInWorldTime); } ScheduleChangeStrategy(bot); @@ -3489,7 +3458,7 @@ void RandomPlayerbotMgr::ChangeStrategyOnce(Player* player) { uint32 bot = player->GetGUID().GetCounter(); - if (frand(0.f, 100.f) > sPlayerbotAIConfig->randomBotRpgChance) // select grind / pvp + if (frand(0.f, 100.f) > sPlayerbotAIConfig.randomBotRpgChance) // select grind / pvp { LOG_INFO("playerbots", "Bot #{} <{}>: sent to grind spot", bot, player->GetName().c_str()); RandomTeleportForLevel(player); @@ -3565,7 +3534,7 @@ ObjectGuid RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, BattlegroundType for (auto i = begin(Bms); i != end(Bms); ++i) { - CreatureData const* data = sRandomPlayerbotMgr->GetCreatureDataByEntry(*i); + CreatureData const* data = sRandomPlayerbotMgr.GetCreatureDataByEntry(*i); if (!data) continue; @@ -3596,7 +3565,7 @@ ObjectGuid RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, BattlegroundType if (Bm->getDeathState() == DeathState::Dead) continue; - float dist2 = sServerFacade->GetDistance2d(bot, data->posX, data->posY); + float dist2 = ServerFacade::instance().GetDistance2d(bot, data->posX, data->posY); if (dist2 < dist1) { dist1 = dist2; diff --git a/src/Bot/RandomPlayerbotMgr.h b/src/Bot/RandomPlayerbotMgr.h index 26d09d454e..94c0a01513 100644 --- a/src/Bot/RandomPlayerbotMgr.h +++ b/src/Bot/RandomPlayerbotMgr.h @@ -10,6 +10,7 @@ #include "ObjectGuid.h" #include "PlayerbotMgr.h" #include "GameTime.h" +#include "PlayerbotCommandServer.h" struct BattlegroundInfo { @@ -88,12 +89,11 @@ class botPID class RandomPlayerbotMgr : public PlayerbotHolder { public: - RandomPlayerbotMgr(); - virtual ~RandomPlayerbotMgr(); - static RandomPlayerbotMgr* instance() + static RandomPlayerbotMgr& instance() { static RandomPlayerbotMgr instance; - return &instance; + + return instance; } void LogPlayerLocation(); @@ -192,6 +192,43 @@ class RandomPlayerbotMgr : public PlayerbotHolder void OnBotLoginInternal(Player* const bot) override; private: + RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0) + { + this->playersLevel = sPlayerbotAIConfig.randombotStartingLevel; + + if (sPlayerbotAIConfig.enabled || sPlayerbotAIConfig.randomBotAutologin) + { + PlayerbotCommandServer::instance().Start(); + } + + BattlegroundData.clear(); // Clear here and here only. + + // Cleanup on server start: orphaned pet data that's often left behind by bot pets that no longer exist in the DB + CharacterDatabase.Execute("DELETE FROM pet_aura WHERE guid NOT IN (SELECT id FROM character_pet)"); + CharacterDatabase.Execute("DELETE FROM pet_spell WHERE guid NOT IN (SELECT id FROM character_pet)"); + CharacterDatabase.Execute("DELETE FROM pet_spell_cooldown WHERE guid NOT IN (SELECT id FROM character_pet)"); + + for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) + { + for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType) + { + this->BattlegroundData[queueType][bracket] = BattlegroundInfo(); + } + } + + this->BgCheckTimer = 0; + this->LfgCheckTimer = 0; + this->PlayersCheckTimer = 0; + } + + ~RandomPlayerbotMgr() = default; + + RandomPlayerbotMgr(const RandomPlayerbotMgr&) = delete; + RandomPlayerbotMgr& operator=(const RandomPlayerbotMgr&) = delete; + + RandomPlayerbotMgr(RandomPlayerbotMgr&&) = delete; + RandomPlayerbotMgr& operator=(RandomPlayerbotMgr&&) = delete; + // pid values are set in constructor botPID pid = botPID(1, 50, -50, 0, 0, 0); float activityMod = 0.25; diff --git a/src/Db/FlightMasterCache.h b/src/Db/FlightMasterCache.h index 519d6fc7c8..7f8b95310d 100644 --- a/src/Db/FlightMasterCache.h +++ b/src/Db/FlightMasterCache.h @@ -8,10 +8,11 @@ class FlightMasterCache { public: - static FlightMasterCache* Instance() + static FlightMasterCache& Instance() { static FlightMasterCache instance; - return &instance; + + return instance; } Creature* GetNearestFlightMaster(Player* bot); @@ -19,9 +20,17 @@ class FlightMasterCache void AddAllianceFlightMaster(uint32 entry, WorldPosition pos); private: + FlightMasterCache() = default; + ~FlightMasterCache() = default; + + FlightMasterCache(const FlightMasterCache&) = delete; + FlightMasterCache& operator=(const FlightMasterCache&) = delete; + + FlightMasterCache(FlightMasterCache&&) = delete; + FlightMasterCache& operator=(FlightMasterCache&&) = delete; + std::map allianceFlightMasterCache; std::map hordeFlightMasterCache; }; -#define sFlightMasterCache FlightMasterCache::Instance() #endif diff --git a/src/Db/PlayerbotDungeonRepository.cpp b/src/Db/PlayerbotDungeonRepository.cpp index 3ee40e5616..5e1f370b6a 100644 --- a/src/Db/PlayerbotDungeonRepository.cpp +++ b/src/Db/PlayerbotDungeonRepository.cpp @@ -3,9 +3,15 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#include "PlayerbotDungeonRepository.h" +#include "Log.h" +#include "Timer.h" +#include "DatabaseEnv.h" +#include "Field.h" +#include "World.h" +// Required import due to poor implementation by AC +#include "QueryResult.h" -#include "Playerbots.h" +#include "PlayerbotDungeonRepository.h" std::vector const PlayerbotDungeonRepository::GetDungeonSuggestions() { diff --git a/src/Db/PlayerbotDungeonRepository.h b/src/Db/PlayerbotDungeonRepository.h index 8c709152a8..160a7622c3 100644 --- a/src/Db/PlayerbotDungeonRepository.h +++ b/src/Db/PlayerbotDungeonRepository.h @@ -6,10 +6,9 @@ #ifndef _PLAYERBOT_PLAYERBOTDUNGEONREPOSITORY_H #define _PLAYERBOT_PLAYERBOTDUNGEONREPOSITORY_H -#include #include +#include -#include "Common.h" #include "DBCEnums.h" struct DungeonSuggestion @@ -22,24 +21,31 @@ struct DungeonSuggestion std::string strategy; }; +// @TODO: Completely unused at this moment. class PlayerbotDungeonRepository { public: - PlayerbotDungeonRepository(){}; - ~PlayerbotDungeonRepository(){}; - static PlayerbotDungeonRepository* instance() + static PlayerbotDungeonRepository& instance() { static PlayerbotDungeonRepository instance; - return &instance; + + return instance; } void LoadDungeonSuggestions(); std::vector const GetDungeonSuggestions(); private: + PlayerbotDungeonRepository() = default; + ~PlayerbotDungeonRepository() = default; + + PlayerbotDungeonRepository(const PlayerbotDungeonRepository&) = delete; + PlayerbotDungeonRepository& operator=(const PlayerbotDungeonRepository&) = delete; + + PlayerbotDungeonRepository(PlayerbotDungeonRepository&&) = delete; + PlayerbotDungeonRepository& operator=(PlayerbotDungeonRepository&&) = delete; + std::vector m_dungeonSuggestions; }; -#define sPlayerbotDungeonRepository PlayerbotDungeonRepository::instance() - #endif diff --git a/src/Db/PlayerbotRepository.h b/src/Db/PlayerbotRepository.h index 90dbce54af..b7b9b4b3cc 100644 --- a/src/Db/PlayerbotRepository.h +++ b/src/Db/PlayerbotRepository.h @@ -6,21 +6,20 @@ #ifndef _PLAYERBOT_PLAYERBOTREPOSITORY_H #define _PLAYERBOT_PLAYERBOTREPOSITORY_H +#include +#include #include -#include "Common.h" - -class PlayerbotAI; +#include "PlayerbotAI.h" class PlayerbotRepository { public: - PlayerbotRepository() {} - virtual ~PlayerbotRepository() {} - static PlayerbotRepository* instance() + static PlayerbotRepository& instance() { static PlayerbotRepository instance; - return &instance; + + return instance; } void Save(PlayerbotAI* botAI); @@ -28,10 +27,17 @@ class PlayerbotRepository void Reset(PlayerbotAI* botAI); private: - void SaveValue(uint32 guid, std::string const key, std::string const value); + PlayerbotRepository() = default; + ~PlayerbotRepository() = default; + + PlayerbotRepository(const PlayerbotRepository&) = delete; + PlayerbotRepository& operator=(const PlayerbotRepository&) = delete; + + PlayerbotRepository(PlayerbotRepository&&) = delete; + PlayerbotRepository& operator=(PlayerbotRepository&&) = delete; + + void SaveValue(uint32_t guid, std::string const key, std::string const value); std::string const FormatStrategies(std::string const type, std::vector strategies); }; -#define sPlayerbotRepository PlayerbotRepository::instance() - #endif diff --git a/src/Db/PlayerbotSpellRepository.cpp b/src/Db/PlayerbotSpellRepository.cpp index 6642eeeea2..d745ba3074 100644 --- a/src/Db/PlayerbotSpellRepository.cpp +++ b/src/Db/PlayerbotSpellRepository.cpp @@ -1,3 +1,10 @@ +#include "Log.h" +#include "DBCStores.h" +#include "DatabaseEnv.h" +#include "Field.h" +// Required due to poor implementation on AC side +#include "QueryResult.h" + #include "PlayerbotSpellRepository.h" // caches the result set diff --git a/src/Db/PlayerbotSpellRepository.h b/src/Db/PlayerbotSpellRepository.h index 55046e5956..7b026dd296 100644 --- a/src/Db/PlayerbotSpellRepository.h +++ b/src/Db/PlayerbotSpellRepository.h @@ -6,29 +6,37 @@ #ifndef _PLAYERBOT_PLAYERBOTSPELLREPOSITORY_H #define _PLAYERBOT_PLAYERBOTSPELLREPOSITORY_H -#include "Playerbots.h" +#include + +#include "DBCStructure.h" class PlayerbotSpellRepository { public: - static PlayerbotSpellRepository* Instance() + static PlayerbotSpellRepository& Instance() { static PlayerbotSpellRepository instance; - return &instance; + + return instance; } - void Initialize(); // call once on startup + void Initialize(); - SkillLineAbilityEntry const* GetSkillLine(uint32 spellId) const; - bool IsItemBuyable(uint32 itemId) const; + SkillLineAbilityEntry const* GetSkillLine(uint32_t spellId) const; + bool IsItemBuyable(uint32_t itemId) const; private: PlayerbotSpellRepository() = default; + ~PlayerbotSpellRepository() = default; - std::map skillSpells; - std::set vendorItems; -}; + PlayerbotSpellRepository(const PlayerbotSpellRepository&) = delete; + PlayerbotSpellRepository& operator=(const PlayerbotSpellRepository&) = delete; -#define sPlayerbotSpellRepository PlayerbotSpellRepository::Instance() + PlayerbotSpellRepository(PlayerbotSpellRepository&&) = delete; + PlayerbotSpellRepository& operator=(PlayerbotSpellRepository&&) = delete; + + std::map skillSpells; + std::set vendorItems; +}; #endif diff --git a/src/Mgr/Guild/GuildTaskMgr.cpp b/src/Mgr/Guild/GuildTaskMgr.cpp index 8fb4711dd7..96162c044a 100644 --- a/src/Mgr/Guild/GuildTaskMgr.cpp +++ b/src/Mgr/Guild/GuildTaskMgr.cpp @@ -26,14 +26,14 @@ enum GuildTaskType void GuildTaskMgr::Update(Player* player, Player* guildMaster) { - if (!sPlayerbotAIConfig->guildTaskEnabled) + if (!sPlayerbotAIConfig.guildTaskEnabled) return; if (!GetTaskValue(0, 0, "advert_cleanup")) { CleanupAdverts(); RemoveDuplicatedAdverts(); - SetTaskValue(0, 0, "advert_cleanup", 1, sPlayerbotAIConfig->guildTaskAdvertCleanupTime); + SetTaskValue(0, 0, "advert_cleanup", 1, sPlayerbotAIConfig.guildTaskAdvertCleanupTime); } PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(guildMaster); @@ -69,8 +69,8 @@ void GuildTaskMgr::Update(Player* player, Player* guildMaster) SetTaskValue(owner, guildId, "killTask", 0, 0); SetTaskValue(owner, guildId, "killCount", 0, 0); SetTaskValue(owner, guildId, "payment", 0, 0); - SetTaskValue(owner, guildId, "thanks", 1, 2 * sPlayerbotAIConfig->maxGuildTaskChangeTime); - SetTaskValue(owner, guildId, "reward", 1, 2 * sPlayerbotAIConfig->maxGuildTaskChangeTime); + SetTaskValue(owner, guildId, "thanks", 1, 2 * sPlayerbotAIConfig.maxGuildTaskChangeTime); + SetTaskValue(owner, guildId, "reward", 1, 2 * sPlayerbotAIConfig.maxGuildTaskChangeTime); uint32 task = CreateTask(player, guildId); @@ -80,11 +80,11 @@ void GuildTaskMgr::Update(Player* player, Player* guildMaster) player->GetName().c_str()); } - uint32 time = urand(sPlayerbotAIConfig->minGuildTaskChangeTime, sPlayerbotAIConfig->maxGuildTaskChangeTime); + uint32 time = urand(sPlayerbotAIConfig.minGuildTaskChangeTime, sPlayerbotAIConfig.maxGuildTaskChangeTime); SetTaskValue(owner, guildId, "activeTask", task, time); SetTaskValue(owner, guildId, "advertisement", 1, - urand(sPlayerbotAIConfig->minGuildTaskAdvertisementTime, - sPlayerbotAIConfig->maxGuildTaskAdvertisementTime)); + urand(sPlayerbotAIConfig.minGuildTaskAdvertisementTime, + sPlayerbotAIConfig.maxGuildTaskAdvertisementTime)); LOG_DEBUG("playerbots", "{} / {}: guild task {} is set for {} secs", guild->GetName().c_str(), player->GetName().c_str(), task, time); @@ -101,8 +101,8 @@ void GuildTaskMgr::Update(Player* player, Player* guildMaster) if (SendAdvertisement(trans, owner, guildId)) { SetTaskValue(owner, guildId, "advertisement", 1, - urand(sPlayerbotAIConfig->minGuildTaskAdvertisementTime, - sPlayerbotAIConfig->maxGuildTaskAdvertisementTime)); + urand(sPlayerbotAIConfig.minGuildTaskAdvertisementTime, + sPlayerbotAIConfig.maxGuildTaskAdvertisementTime)); } else { @@ -118,7 +118,7 @@ void GuildTaskMgr::Update(Player* player, Player* guildMaster) if (SendThanks(trans, owner, guildId, GetTaskValue(owner, guildId, "payment"))) { - SetTaskValue(owner, guildId, "thanks", 1, 2 * sPlayerbotAIConfig->maxGuildTaskChangeTime); + SetTaskValue(owner, guildId, "thanks", 1, 2 * sPlayerbotAIConfig.maxGuildTaskChangeTime); SetTaskValue(owner, guildId, "payment", 0, 0); } else @@ -135,7 +135,7 @@ void GuildTaskMgr::Update(Player* player, Player* guildMaster) if (Reward(trans, owner, guildId)) { - SetTaskValue(owner, guildId, "reward", 1, 2 * sPlayerbotAIConfig->maxGuildTaskChangeTime); + SetTaskValue(owner, guildId, "reward", 1, 2 * sPlayerbotAIConfig.maxGuildTaskChangeTime); SetTaskValue(owner, guildId, "payment", 0, 0); } else @@ -190,7 +190,7 @@ bool GuildTaskMgr::CreateItemTask(Player* player, uint32 guildId) return false; RandomItemBySkillGuildTaskPredicate predicate(player); - uint32 itemId = sRandomItemMgr->GetRandomItem(player->GetLevel() - 5, RANDOM_ITEM_GUILD_TASK, &predicate); + uint32 itemId = sRandomItemMgr.GetRandomItem(player->GetLevel() - 5, RANDOM_ITEM_GUILD_TASK, &predicate); if (!itemId) { LOG_ERROR("playerbots", "{} / {}: no items avaible for item task", @@ -204,9 +204,9 @@ bool GuildTaskMgr::CreateItemTask(Player* player, uint32 guildId) player->GetName().c_str(), itemId, count); SetTaskValue(player->GetGUID().GetCounter(), guildId, "itemCount", count, - sPlayerbotAIConfig->maxGuildTaskChangeTime); + sPlayerbotAIConfig.maxGuildTaskChangeTime); SetTaskValue(player->GetGUID().GetCounter(), guildId, "itemTask", itemId, - sPlayerbotAIConfig->maxGuildTaskChangeTime); + sPlayerbotAIConfig.maxGuildTaskChangeTime); return true; } @@ -239,8 +239,8 @@ bool GuildTaskMgr::CreateKillTask(Player* player, uint32 guildId) if (strstr(name.c_str(), "UNUSED")) continue; - float dist = sServerFacade->GetDistance2d(player, x, y); - if (dist > sPlayerbotAIConfig->guildTaskKillTaskDistance || player->GetMapId() != map) + float dist = ServerFacade::instance().GetDistance2d(player, x, y); + if (dist > sPlayerbotAIConfig.guildTaskKillTaskDistance || player->GetMapId() != map) continue; if (find(ids.begin(), ids.end(), id) == ids.end()) @@ -262,7 +262,7 @@ bool GuildTaskMgr::CreateKillTask(Player* player, uint32 guildId) player->GetName().c_str(), creatureId); SetTaskValue(player->GetGUID().GetCounter(), guildId, "killTask", creatureId, - sPlayerbotAIConfig->maxGuildTaskChangeTime); + sPlayerbotAIConfig.maxGuildTaskChangeTime); return true; } @@ -525,7 +525,7 @@ uint32 GuildTaskMgr::GetMaxItemTaskCount(uint32 itemId) bool GuildTaskMgr::IsGuildTaskItem(uint32 itemId, uint32 guildId) { - if (!sPlayerbotAIConfig->guildTaskEnabled) + if (!sPlayerbotAIConfig.guildTaskEnabled) { return 0; } @@ -553,7 +553,7 @@ bool GuildTaskMgr::IsGuildTaskItem(uint32 itemId, uint32 guildId) std::map GuildTaskMgr::GetTaskValues(uint32 owner, std::string const type, [[maybe_unused]] uint32* validIn /* = nullptr */) { - if (!sPlayerbotAIConfig->guildTaskEnabled) + if (!sPlayerbotAIConfig.guildTaskEnabled) { return std::map(); } @@ -586,7 +586,7 @@ std::map GuildTaskMgr::GetTaskValues(uint32 owner, std::string c uint32 GuildTaskMgr::GetTaskValue(uint32 owner, uint32 guildId, std::string const type, [[maybe_unused]] uint32* validIn /* = nullptr */) { - if (!sPlayerbotAIConfig->guildTaskEnabled) + if (!sPlayerbotAIConfig.guildTaskEnabled) { return 0; } @@ -639,7 +639,7 @@ uint32 GuildTaskMgr::SetTaskValue(uint32 owner, uint32 guildId, std::string cons bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* args) { - if (!sPlayerbotAIConfig->guildTaskEnabled) + if (!sPlayerbotAIConfig.guildTaskEnabled) { LOG_ERROR("playerbots", "Guild task system is currently disabled!"); return false; @@ -703,8 +703,8 @@ bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* if (value == GUILD_TASK_TYPE_ITEM) { name << "ItemTask"; - uint32 itemId = sGuildTaskMgr->GetTaskValue(owner, guildId, "itemTask"); - uint32 itemCount = sGuildTaskMgr->GetTaskValue(owner, guildId, "itemCount"); + uint32 itemId = GuildTaskMgr::instance().GetTaskValue(owner, guildId, "itemTask"); + uint32 itemCount = GuildTaskMgr::instance().GetTaskValue(owner, guildId, "itemCount"); if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId)) { @@ -735,7 +735,7 @@ bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* else if (value == GUILD_TASK_TYPE_KILL) { name << "KillTask"; - uint32 creatureId = sGuildTaskMgr->GetTaskValue(owner, guildId, "killTask"); + uint32 creatureId = GuildTaskMgr::instance().GetTaskValue(owner, guildId, "killTask"); if (CreatureTemplate const* proto = sObjectMgr->GetCreatureTemplate(creatureId)) { @@ -758,22 +758,22 @@ bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* continue; uint32 advertValidIn = 0; - uint32 advert = sGuildTaskMgr->GetTaskValue(owner, guildId, "advertisement", &advertValidIn); + uint32 advert = GuildTaskMgr::instance().GetTaskValue(owner, guildId, "advertisement", &advertValidIn); if (advert && advertValidIn < validIn) name << " advert in " << formatTime(advertValidIn); uint32 thanksValidIn = 0; - uint32 thanks = sGuildTaskMgr->GetTaskValue(owner, guildId, "thanks", &thanksValidIn); + uint32 thanks = GuildTaskMgr::instance().GetTaskValue(owner, guildId, "thanks", &thanksValidIn); if (thanks && thanksValidIn < validIn) name << " thanks in " << formatTime(thanksValidIn); uint32 rewardValidIn = 0; - uint32 reward = sGuildTaskMgr->GetTaskValue(owner, guildId, "reward", &rewardValidIn); + uint32 reward = GuildTaskMgr::instance().GetTaskValue(owner, guildId, "reward", &rewardValidIn); if (reward && rewardValidIn < validIn) name << " reward in " << formatTime(rewardValidIn); uint32 paymentValidIn = 0; - uint32 payment = sGuildTaskMgr->GetTaskValue(owner, guildId, "payment", &paymentValidIn); + uint32 payment = GuildTaskMgr::instance().GetTaskValue(owner, guildId, "payment", &paymentValidIn); if (payment && paymentValidIn < validIn) name << " payment " << ChatHelper::formatMoney(payment) << " in " << formatTime(paymentValidIn); @@ -788,8 +788,8 @@ bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* if (cmd == "cleanup") { - sGuildTaskMgr->CleanupAdverts(); - sGuildTaskMgr->RemoveDuplicatedAdverts(); + GuildTaskMgr::instance().CleanupAdverts(); + GuildTaskMgr::instance().RemoveDuplicatedAdverts(); return true; } @@ -840,10 +840,10 @@ bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* continue; if (reward) - sGuildTaskMgr->Reward(trans, owner, guildId); + GuildTaskMgr::instance().Reward(trans, owner, guildId); if (advert) - sGuildTaskMgr->SendAdvertisement(trans, owner, guildId); + GuildTaskMgr::instance().SendAdvertisement(trans, owner, guildId); } while (result->NextRow()); CharacterDatabase.CommitTransaction(trans); @@ -868,7 +868,7 @@ bool GuildTaskMgr::CheckItemTask(uint32 itemId, uint32 obtained, Player* ownerPl if (!guild) return false; - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return false; LOG_DEBUG("playerbots", "{} / {}: checking guild task", guild->GetName().c_str(), ownerPlayer->GetName().c_str()); @@ -891,7 +891,7 @@ bool GuildTaskMgr::CheckItemTask(uint32 itemId, uint32 obtained, Player* ownerPl return false; } - uint32 rewardTime = urand(sPlayerbotAIConfig->minGuildTaskRewardTime, sPlayerbotAIConfig->maxGuildTaskRewardTime); + uint32 rewardTime = urand(sPlayerbotAIConfig.minGuildTaskRewardTime, sPlayerbotAIConfig.maxGuildTaskRewardTime); if (byMail) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); @@ -915,7 +915,7 @@ bool GuildTaskMgr::CheckItemTask(uint32 itemId, uint32 obtained, Player* ownerPl { LOG_DEBUG("playerbots", "{} / {}: guild task progress {}/{}", guild->GetName().c_str(), ownerPlayer->GetName().c_str(), obtained, count); - SetTaskValue(owner, guildId, "itemCount", count - obtained, sPlayerbotAIConfig->maxGuildTaskChangeTime); + SetTaskValue(owner, guildId, "itemCount", count - obtained, sPlayerbotAIConfig.maxGuildTaskChangeTime); SetTaskValue(owner, guildId, "thanks", 1, rewardTime - 30); SendCompletionMessage(ownerPlayer, "made a progress with"); } @@ -961,7 +961,7 @@ bool GuildTaskMgr::Reward(CharacterDatabaseTransaction& trans, uint32 owner, uin body << leader->GetName() << "\n"; rewardType = proto->Quality > ITEM_QUALITY_NORMAL ? RANDOM_ITEM_GUILD_TASK_REWARD_EQUIP_BLUE : RANDOM_ITEM_GUILD_TASK_REWARD_EQUIP_GREEN; - itemId = sRandomItemMgr->GetRandomItem(player->GetLevel() - 5, rewardType); + itemId = sRandomItemMgr.GetRandomItem(player->GetLevel() - 5, rewardType); } else if (killTask) { @@ -977,7 +977,7 @@ bool GuildTaskMgr::Reward(CharacterDatabaseTransaction& trans, uint32 owner, uin body << leader->GetName() << "\n"; rewardType = proto->rank == CREATURE_ELITE_RARE ? RANDOM_ITEM_GUILD_TASK_REWARD_TRADE : RANDOM_ITEM_GUILD_TASK_REWARD_TRADE_RARE; - itemId = sRandomItemMgr->GetRandomItem(player->GetLevel(), rewardType); + itemId = sRandomItemMgr.GetRandomItem(player->GetLevel(), rewardType); if (itemId) { ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(itemId); @@ -1086,7 +1086,7 @@ void GuildTaskMgr::CheckKillTaskInternal(Player* player, Unit* victim) LOG_DEBUG("playerbots", "{} / {}: guild task complete", guild->GetName().c_str(), player->GetName().c_str()); SetTaskValue(owner, guildId, "reward", 1, - urand(sPlayerbotAIConfig->minGuildTaskRewardTime, sPlayerbotAIConfig->maxGuildTaskRewardTime)); + urand(sPlayerbotAIConfig.minGuildTaskRewardTime, sPlayerbotAIConfig.maxGuildTaskRewardTime)); SendCompletionMessage(player, "completed"); } @@ -1094,7 +1094,7 @@ void GuildTaskMgr::CheckKillTaskInternal(Player* player, Unit* victim) void GuildTaskMgr::CleanupAdverts() { - uint32 deliverTime = time(nullptr) - sPlayerbotAIConfig->minGuildTaskChangeTime; + uint32 deliverTime = time(nullptr) - sPlayerbotAIConfig.minGuildTaskChangeTime; QueryResult result = CharacterDatabase.Query( "SELECT id, receiver FROM mail WHERE subject LIKE 'Guild Task%%' AND deliver_time <= {}", deliverTime); if (!result) @@ -1200,7 +1200,7 @@ bool GuildTaskMgr::CheckTaskTransfer(std::string const text, Player* ownerPlayer if (!guild) return false; - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return false; if (text.empty()) diff --git a/src/Mgr/Guild/GuildTaskMgr.h b/src/Mgr/Guild/GuildTaskMgr.h index c050281ca6..6251f2c1e2 100644 --- a/src/Mgr/Guild/GuildTaskMgr.h +++ b/src/Mgr/Guild/GuildTaskMgr.h @@ -7,56 +7,61 @@ #define _PLAYERBOT_GUILDTASKMGR_H #include +#include +#include -#include "Common.h" -#include "Transaction.h" - -class ChatHandler; -class Player; -class Unit; +#include "DatabaseEnvFwd.h" +#include "Unit.h" +#include "Player.h" +#include "Chat.h" class GuildTaskMgr { public: - GuildTaskMgr(){}; - virtual ~GuildTaskMgr(){}; - - static GuildTaskMgr* instance() + static GuildTaskMgr& instance() { static GuildTaskMgr instance; - return &instance; + + return instance; } void Update(Player* owner, Player* guildMaster); static bool HandleConsoleCommand(ChatHandler* handler, char const* args); - bool IsGuildTaskItem(uint32 itemId, uint32 guildId); - bool CheckItemTask(uint32 itemId, uint32 obtained, Player* owner, Player* bot, bool byMail = false); + bool IsGuildTaskItem(uint32_t itemId, uint32_t guildId); + bool CheckItemTask(uint32_t itemId, uint32_t obtained, Player* owner, Player* bot, bool byMail = false); void CheckKillTask(Player* owner, Unit* victim); void CheckKillTaskInternal(Player* owner, Unit* victim); bool CheckTaskTransfer(std::string const text, Player* owner, Player* bot); private: - std::map GetTaskValues(uint32 owner, std::string const type, uint32* validIn = nullptr); - uint32 GetTaskValue(uint32 owner, uint32 guildId, std::string const type, uint32* validIn = nullptr); - uint32 SetTaskValue(uint32 owner, uint32 guildId, std::string const type, uint32 value, uint32 validIn); - uint32 CreateTask(Player* owner, uint32 guildId); - bool SendAdvertisement(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId); - bool SendItemAdvertisement(CharacterDatabaseTransaction& trans, uint32 itemId, uint32 owner, uint32 guildId, - uint32 validIn); - bool SendKillAdvertisement(CharacterDatabaseTransaction& trans, uint32 creatureId, uint32 owner, uint32 guildId, - uint32 validIn); - bool SendThanks(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId, uint32 payment); - bool Reward(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId); - bool CreateItemTask(Player* owner, uint32 guildId); - bool CreateKillTask(Player* owner, uint32 guildId); - uint32 GetMaxItemTaskCount(uint32 itemId); + GuildTaskMgr() = default; + ~GuildTaskMgr() = default; + + GuildTaskMgr(const GuildTaskMgr&) = delete; + GuildTaskMgr& operator=(const GuildTaskMgr&) = delete; + + GuildTaskMgr(GuildTaskMgr&&) = delete; + GuildTaskMgr& operator=(GuildTaskMgr&&) = delete; + + std::map GetTaskValues(uint32_t owner, std::string const type, uint32_t* validIn = nullptr); + uint32_t GetTaskValue(uint32_t owner, uint32_t guildId, std::string const type, uint32_t* validIn = nullptr); + uint32_t SetTaskValue(uint32_t owner, uint32_t guildId, std::string const type, uint32_t value, uint32_t validIn); + uint32_t CreateTask(Player* owner, uint32_t guildId); + bool SendAdvertisement(CharacterDatabaseTransaction& trans, uint32_t owner, uint32_t guildId); + bool SendItemAdvertisement(CharacterDatabaseTransaction& trans, uint32_t itemId, uint32_t owner, uint32_t guildId, + uint32_t validIn); + bool SendKillAdvertisement(CharacterDatabaseTransaction& trans, uint32_t creatureId, uint32_t owner, uint32_t guildId, + uint32_t validIn); + bool SendThanks(CharacterDatabaseTransaction& trans, uint32_t owner, uint32_t guildId, uint32_t payment); + bool Reward(CharacterDatabaseTransaction& trans, uint32_t owner, uint32_t guildId); + bool CreateItemTask(Player* owner, uint32_t guildId); + bool CreateKillTask(Player* owner, uint32_t guildId); + uint32_t GetMaxItemTaskCount(uint32_t itemId); void CleanupAdverts(); void RemoveDuplicatedAdverts(); - void DeleteMail(std::vector buffer); + void DeleteMail(std::vector buffer); void SendCompletionMessage(Player* player, std::string const verb); }; -#define sGuildTaskMgr GuildTaskMgr::instance() - #endif diff --git a/src/Mgr/Guild/PlayerbotGuildMgr.cpp b/src/Mgr/Guild/PlayerbotGuildMgr.cpp index c1f7aa5b2d..001a438cbb 100644 --- a/src/Mgr/Guild/PlayerbotGuildMgr.cpp +++ b/src/Mgr/Guild/PlayerbotGuildMgr.cpp @@ -4,15 +4,12 @@ #include "DatabaseEnv.h" #include "Guild.h" #include "GuildMgr.h" -#include "RandomPlayerbotMgr.h" #include "ScriptMgr.h" -PlayerbotGuildMgr::PlayerbotGuildMgr(){} - void PlayerbotGuildMgr::Init() { _guildCache.clear(); - if (sPlayerbotAIConfig->deleteRandomBotGuilds) + if (sPlayerbotAIConfig.deleteRandomBotGuilds) DeleteBotGuilds(); LoadGuildNames(); @@ -38,7 +35,7 @@ bool PlayerbotGuildMgr::CreateGuild(Player* player, std::string guildName) entry.name = guildName; entry.memberCount = 1; entry.status = 1; - entry.maxMembers = sPlayerbotAIConfig->randomBotGuildSizeMax; + entry.maxMembers = sPlayerbotAIConfig.randomBotGuildSizeMax; entry.faction = player->GetTeamId(); _guildCache[guild->GetId()] = entry; @@ -113,7 +110,7 @@ std::string PlayerbotGuildMgr::AssignToGuild(Player* player) } ); - if (count < sPlayerbotAIConfig->randomBotGuildCount) + if (count < sPlayerbotAIConfig.randomBotGuildCount) { for (auto& key : _shuffled_guild_keys) { @@ -214,7 +211,7 @@ void PlayerbotGuildMgr::ValidateGuildCache() uint32 guildId = it->first; GuildCache cache; cache.name = it->second; - cache.maxMembers = sPlayerbotAIConfig->randomBotGuildSizeMax; + cache.maxMembers = sPlayerbotAIConfig.randomBotGuildSizeMax; Guild* guild = sGuildMgr ->GetGuildById(guildId); if (!guild) @@ -224,7 +221,7 @@ void PlayerbotGuildMgr::ValidateGuildCache() ObjectGuid leaderGuid = guild->GetLeaderGUID(); CharacterCacheEntry const* leaderEntry = sCharacterCache->GetCharacterCacheByGuid(leaderGuid); uint32 leaderAccount = leaderEntry->AccountId; - cache.hasRealPlayer = !(sPlayerbotAIConfig->IsInRandomAccountList(leaderAccount)); + cache.hasRealPlayer = !(sPlayerbotAIConfig.IsInRandomAccountList(leaderAccount)); cache.faction = Player::TeamIdForRace(leaderEntry->Race); if (cache.memberCount == 0) cache.status = 0; // empty @@ -306,7 +303,7 @@ class BotGuildCacheWorldScript : public WorldScript if (_validateTimer >= _validateInterval) // Validate every hour { _validateTimer = 0; - sPlayerbotGuildMgr->ValidateGuildCache(); + PlayerbotGuildMgr::instance().ValidateGuildCache(); LOG_INFO("playerbots", "Scheduled guild cache validation"); } } @@ -319,4 +316,4 @@ class BotGuildCacheWorldScript : public WorldScript void PlayerBotsGuildValidationScript() { new BotGuildCacheWorldScript(); -} \ No newline at end of file +} diff --git a/src/Mgr/Guild/PlayerbotGuildMgr.h b/src/Mgr/Guild/PlayerbotGuildMgr.h index 0df0df7371..5d85ce9e79 100644 --- a/src/Mgr/Guild/PlayerbotGuildMgr.h +++ b/src/Mgr/Guild/PlayerbotGuildMgr.h @@ -5,15 +5,14 @@ #include "Player.h" #include "PlayerbotAI.h" -class PlayerbotAI; - class PlayerbotGuildMgr { public: - static PlayerbotGuildMgr* instance() + static PlayerbotGuildMgr& instance() { static PlayerbotGuildMgr instance; - return &instance; + + return instance; } void Init(); @@ -29,7 +28,15 @@ class PlayerbotGuildMgr bool IsRealGuild(Player* bot); private: - PlayerbotGuildMgr(); + PlayerbotGuildMgr() = default; + ~PlayerbotGuildMgr() = default; + + PlayerbotGuildMgr(const PlayerbotGuildMgr&) = delete; + PlayerbotGuildMgr& operator=(const PlayerbotGuildMgr&) = delete; + + PlayerbotGuildMgr(PlayerbotGuildMgr&&) = delete; + PlayerbotGuildMgr& operator=(PlayerbotGuildMgr&&) = delete; + std::unordered_map _guildNames; struct GuildCache @@ -47,6 +54,4 @@ class PlayerbotGuildMgr void PlayerBotsGuildValidationScript(); -#define sPlayerbotGuildMgr PlayerbotGuildMgr::instance() - -#endif \ No newline at end of file +#endif diff --git a/src/Mgr/Item/ItemVisitors.cpp b/src/Mgr/Item/ItemVisitors.cpp index 5615146f63..5ed8d1b04a 100644 --- a/src/Mgr/Item/ItemVisitors.cpp +++ b/src/Mgr/Item/ItemVisitors.cpp @@ -92,4 +92,4 @@ bool FindItemUsageVisitor::Accept(ItemTemplate const* proto) bool FindUsableNamedItemVisitor::Accept(ItemTemplate const* proto) { return proto && !proto->Name1.empty() && strstri(proto->Name1.c_str(), name.c_str()); -} \ No newline at end of file +} diff --git a/src/Mgr/Item/RandomItemMgr.cpp b/src/Mgr/Item/RandomItemMgr.cpp index 87b8379355..5c0e8c94ab 100644 --- a/src/Mgr/Item/RandomItemMgr.cpp +++ b/src/Mgr/Item/RandomItemMgr.cpp @@ -272,7 +272,7 @@ void RandomItemMgr::BuildRandomItemCache() } } - uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; + uint32 maxLevel = sPlayerbotAIConfig.randomBotMaxLevel; if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); @@ -2092,7 +2092,7 @@ uint32 RandomItemMgr::GetLiveStatWeight(Player* player, uint32 itemId) void RandomItemMgr::BuildEquipCache() { - uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; + uint32 maxLevel = sPlayerbotAIConfig.randomBotMaxLevel; if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); @@ -2415,7 +2415,7 @@ void RandomItemMgr::BuildPotionCache() void RandomItemMgr::BuildFoodCache() { - uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; + uint32 maxLevel = sPlayerbotAIConfig.randomBotMaxLevel; if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); @@ -2548,7 +2548,7 @@ uint32 RandomItemMgr::GetRandomFood(uint32 level, uint32 category) void RandomItemMgr::BuildTradeCache() { - uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; + uint32 maxLevel = sPlayerbotAIConfig.randomBotMaxLevel; if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); diff --git a/src/Mgr/Item/RandomItemMgr.h b/src/Mgr/Item/RandomItemMgr.h index 216ddf9bfc..54e2e63516 100644 --- a/src/Mgr/Item/RandomItemMgr.h +++ b/src/Mgr/Item/RandomItemMgr.h @@ -12,7 +12,6 @@ #include #include "AiFactory.h" -#include "Common.h" #include "ItemTemplate.h" class ChatHandler; @@ -135,12 +134,11 @@ typedef std::map BotEquipCache; class RandomItemMgr { public: - RandomItemMgr(); - virtual ~RandomItemMgr(); - static RandomItemMgr* instance() + static RandomItemMgr& instance() { static RandomItemMgr instance; - return &instance; + + return instance; } public: @@ -191,6 +189,17 @@ class RandomItemMgr bool CheckItemStats(uint8 clazz, uint8 sp, uint8 ap, uint8 tank); private: + // Implemented in RandomItemMgr.cpp + RandomItemMgr(); + // Implemented in RandomItemMgr.cpp + ~RandomItemMgr(); + + RandomItemMgr(const RandomItemMgr&) = delete; + RandomItemMgr& operator=(const RandomItemMgr&) = delete; + + RandomItemMgr(RandomItemMgr&&) = delete; + RandomItemMgr& operator=(RandomItemMgr&&) = delete; + std::map randomItemCache; std::map predicates; BotEquipCache equipCache; diff --git a/src/Mgr/Item/StatsCollector.h b/src/Mgr/Item/StatsCollector.h index 27ef871ec5..1e54546f5d 100644 --- a/src/Mgr/Item/StatsCollector.h +++ b/src/Mgr/Item/StatsCollector.h @@ -87,4 +87,4 @@ class StatsCollector uint32 cls_; }; -#endif \ No newline at end of file +#endif diff --git a/src/Mgr/Move/FleeManager.cpp b/src/Mgr/Move/FleeManager.cpp index 34e17fe391..d3d1a8bb02 100644 --- a/src/Mgr/Move/FleeManager.cpp +++ b/src/Mgr/Move/FleeManager.cpp @@ -34,7 +34,7 @@ void FleeManager::calculateDistanceToCreatures(FleePoint* point) if (!unit) continue; - float d = sServerFacade->GetDistance2d(unit, point->x, point->y); + float d = ServerFacade::instance().GetDistance2d(unit, point->x, point->y); point->sumDistance += d; if (point->minDistance < 0 || point->minDistance > d) point->minDistance = d; @@ -81,11 +81,11 @@ void FleeManager::calculatePossibleDestinations(std::vector& points) enemyOri.push_back(ori); } - float distIncrement = std::max(sPlayerbotAIConfig->followDistance, - (maxAllowedDistance - sPlayerbotAIConfig->tooCloseDistance) / 10.0f); - for (float dist = maxAllowedDistance; dist >= sPlayerbotAIConfig->tooCloseDistance; dist -= distIncrement) + float distIncrement = std::max(sPlayerbotAIConfig.followDistance, + (maxAllowedDistance - sPlayerbotAIConfig.tooCloseDistance) / 10.0f); + for (float dist = maxAllowedDistance; dist >= sPlayerbotAIConfig.tooCloseDistance; dist -= distIncrement) { - float angleIncrement = std::max(M_PI / 20, M_PI / 4 / (1.0 + dist - sPlayerbotAIConfig->tooCloseDistance)); + float angleIncrement = std::max(M_PI / 20, M_PI / 4 / (1.0 + dist - sPlayerbotAIConfig.tooCloseDistance)); for (float add = 0.0f; add < M_PI / 4 + angleIncrement; add += angleIncrement) { for (float angle = add; angle < add + 2 * static_cast(M_PI) + angleIncrement; @@ -97,8 +97,8 @@ void FleeManager::calculatePossibleDestinations(std::vector& points) float x = botPosX + cos(angle) * maxAllowedDistance, y = botPosY + sin(angle) * maxAllowedDistance, z = botPosZ + CONTACT_DISTANCE; if (forceMaxDistance && - sServerFacade->IsDistanceLessThan(sServerFacade->GetDistance2d(bot, x, y), - maxAllowedDistance - sPlayerbotAIConfig->tooCloseDistance)) + ServerFacade::instance().IsDistanceLessThan(ServerFacade::instance().GetDistance2d(bot, x, y), + maxAllowedDistance - sPlayerbotAIConfig.tooCloseDistance)) continue; bot->UpdateAllowedPositionZ(x, y, z); @@ -113,8 +113,8 @@ void FleeManager::calculatePossibleDestinations(std::vector& points) FleePoint* point = new FleePoint(botAI, x, y, z); calculateDistanceToCreatures(point); - if (sServerFacade->IsDistanceGreaterOrEqualThan(point->minDistance - start.minDistance, - sPlayerbotAIConfig->followDistance)) + if (ServerFacade::instance().IsDistanceGreaterOrEqualThan(point->minDistance - start.minDistance, + sPlayerbotAIConfig.followDistance)) points.push_back(point); else delete point; @@ -189,8 +189,8 @@ bool FleeManager::isUseful() creature->GetAttackDistance(bot) * creature->GetAttackDistance(bot)) return true; - // float d = sServerFacade->GetDistance2d(unit, bot); - // if (sServerFacade->IsDistanceLessThan(d, sPlayerbotAIConfig->aggroDistance)) return true; + // float d = ServerFacade::instance().GetDistance2d(unit, bot); + // if (ServerFacade::instance().IsDistanceLessThan(d, sPlayerbotAIConfig.aggroDistance)) return true; } return false; diff --git a/src/Mgr/Security/PlayerbotSecurity.cpp b/src/Mgr/Security/PlayerbotSecurity.cpp index 68bb2db29a..4a3bd36553 100644 --- a/src/Mgr/Security/PlayerbotSecurity.cpp +++ b/src/Mgr/Security/PlayerbotSecurity.cpp @@ -47,7 +47,7 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea return PLAYERBOT_SECURITY_DENY_ALL; } - if (sPlayerbotAIConfig->IsInRandomAccountList(account)) + if (sPlayerbotAIConfig.IsInRandomAccountList(account)) { // (duplicate check in case of faction change) if (botAI->IsOpposing(from)) @@ -72,7 +72,7 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea return PLAYERBOT_SECURITY_TALK; } - if (sPlayerbotAIConfig->groupInvitationPermission <= 0) + if (sPlayerbotAIConfig.groupInvitationPermission <= 0) { if (reason) *reason = PLAYERBOT_DENY_NONE; @@ -80,7 +80,7 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea return PLAYERBOT_SECURITY_TALK; } - if (sPlayerbotAIConfig->groupInvitationPermission <= 1) + if (sPlayerbotAIConfig.groupInvitationPermission <= 1) { int32 levelDiff = int32(bot->GetLevel()) - int32(from->GetLevel()); if (levelDiff > 5) @@ -98,7 +98,7 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea int32 botGS = static_cast(botAI->GetEquipGearScore(bot)); int32 fromGS = static_cast(botAI->GetEquipGearScore(from)); - if (sPlayerbotAIConfig->gearscorecheck && botGS && bot->GetLevel() > 15 && botGS > fromGS) + if (sPlayerbotAIConfig.gearscorecheck && botGS && bot->GetLevel() > 15 && botGS > fromGS) { uint32 diffPct = uint32(100 * (botGS - fromGS) / botGS); uint32 reqPct = uint32(12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->GetLevel()); @@ -277,7 +277,7 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent, ObjectGuid guid = from->GetGUID(); time_t lastSaid = whispers[guid][text]; - if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000) + if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig.repeatDelay / 1000) { whispers[guid][text] = time(nullptr); diff --git a/src/Mgr/Text/PlayerbotTextMgr.cpp b/src/Mgr/Text/PlayerbotTextMgr.cpp index 1dce9a29a5..0998a5392f 100644 --- a/src/Mgr/Text/PlayerbotTextMgr.cpp +++ b/src/Mgr/Text/PlayerbotTextMgr.cpp @@ -2,12 +2,14 @@ * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "DatabaseEnv.h" +#include "WorldSessionMgr.h" +#include "Random.h" +// Required due to a poor implementation by AC +#include "QueryResult.h" #include "PlayerbotTextMgr.h" -#include "Playerbots.h" -#include "WorldSessionMgr.h" - void PlayerbotTextMgr::replaceAll(std::string& str, const std::string& from, const std::string& to) { if (from.empty()) diff --git a/src/Mgr/Text/PlayerbotTextMgr.h b/src/Mgr/Text/PlayerbotTextMgr.h index f4ec2c403c..398d7640ab 100644 --- a/src/Mgr/Text/PlayerbotTextMgr.h +++ b/src/Mgr/Text/PlayerbotTextMgr.h @@ -11,9 +11,6 @@ #include "Common.h" -#define BOT_TEXT1(name) sPlayerbotTextMgr->GetBotText(name) -#define BOT_TEXT2(name, replace) sPlayerbotTextMgr->GetBotText(name, replace) - struct BotTextEntry { BotTextEntry(std::string name, std::map text, uint32 say_type, uint32 reply_type) @@ -63,18 +60,11 @@ enum ChatReplyType class PlayerbotTextMgr { public: - PlayerbotTextMgr() - { - for (uint8 i = 0; i < MAX_LOCALES; ++i) - { - botTextLocalePriority[i] = 0; - } - }; - virtual ~PlayerbotTextMgr(){}; - static PlayerbotTextMgr* instance() + static PlayerbotTextMgr& instance() { static PlayerbotTextMgr instance; - return &instance; + + return instance; } std::string GetBotText(std::string name, std::map placeholders); @@ -95,11 +85,24 @@ class PlayerbotTextMgr void ResetLocalePriority(); private: + PlayerbotTextMgr() + { + for (uint8 i = 0; i < MAX_LOCALES; ++i) + { + botTextLocalePriority[i] = 0; + } + }; + ~PlayerbotTextMgr() = default; + + PlayerbotTextMgr(const PlayerbotTextMgr&) = delete; + PlayerbotTextMgr& operator=(const PlayerbotTextMgr&) = delete; + + PlayerbotTextMgr(PlayerbotTextMgr&&) = delete; + PlayerbotTextMgr& operator=(PlayerbotTextMgr&&) = delete; + std::map> botTexts; std::map botTextChance; uint32 botTextLocalePriority[MAX_LOCALES]; }; -#define sPlayerbotTextMgr PlayerbotTextMgr::instance() - #endif diff --git a/src/Mgr/Travel/TravelMgr.cpp b/src/Mgr/Travel/TravelMgr.cpp index ab70576502..84eef9b99e 100644 --- a/src/Mgr/Travel/TravelMgr.cpp +++ b/src/Mgr/Travel/TravelMgr.cpp @@ -8,17 +8,18 @@ #include #include -#include "CellImpl.h" +#include "Talentspec.h" #include "ChatHelper.h" #include "MMapFactory.h" #include "MapMgr.h" #include "PathGenerator.h" #include "Playerbots.h" -#include "StrategyContext.h" #include "TransportMgr.h" #include "VMapFactory.h" #include "VMapMgr2.h" +#include "Map.h" #include "Corpse.h" +#include "CellImpl.h" WorldPosition::WorldPosition(std::string const str) { @@ -246,7 +247,7 @@ float WorldPosition::distance(WorldPosition* center) return relPoint(center).size(); // this -> mapTransfer | mapTransfer -> center - return sTravelMgr->mapTransDistance(*this, *center); + return TravelMgr::instance().mapTransDistance(*this, *center); }; float WorldPosition::fDist(WorldPosition* center) @@ -255,7 +256,7 @@ float WorldPosition::fDist(WorldPosition* center) return sqrt(sqDistance2d(center)); // this -> mapTransfer | mapTransfer -> center - return sTravelMgr->fastMapTransDistance(*this, *center); + return TravelMgr::instance().fastMapTransDistance(*this, *center); }; float mapTransfer::fDist(WorldPosition start, WorldPosition end) @@ -428,7 +429,7 @@ void WorldPosition::printWKT(std::vector points, std::ostringstre WorldPosition WorldPosition::getDisplayLocation() { - WorldPosition pos = sTravelNodeMap->getMapOffset(getMapId()); + WorldPosition pos = TravelNodeMap::instance().getMapOffset(getMapId()); return offset(const_cast(&pos)); } @@ -630,14 +631,14 @@ void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y) if (isOverworld() && false || false) { if (!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(mapId, x, y)) - if (sPlayerbotAIConfig->hasLog(fileName)) + if (sPlayerbotAIConfig.hasLog(fileName)) { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); - out << "+00,\"mmap\", " << x << "," << y << "," << (sTravelMgr->isBadMmap(mapId, x, y) ? "0" : "1") + out << sPlayerbotAIConfig.GetTimestampStr(); + out << "+00,\"mmap\", " << x << "," << y << "," << (TravelMgr::instance().isBadMmap(mapId, x, y) ? "0" : "1") << ","; printWKT(fromGridCoord(GridCoord(x, y)), out, 1, true); - sPlayerbotAIConfig->log(fileName, out.str().c_str()); + sPlayerbotAIConfig.log(fileName, out.str().c_str()); } } else @@ -645,7 +646,7 @@ void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y) // This needs to be disabled or maps will not load. // Needs more testing to check for impact on movement. if (false) - if (!sTravelMgr->isBadVmap(mapId, x, y)) + if (!TravelMgr::instance().isBadVmap(mapId, x, y)) { // load VMAPs for current map/grid... const MapEntry* i_mapEntry = sMapStore.LookupEntry(mapId); @@ -662,40 +663,40 @@ void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y) case VMAP::VMAP_LOAD_RESULT_ERROR: // LOG_ERROR("playerbots", "Could not load VMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, // y:{})", mapName, mapId, x, y, x, y); - sTravelMgr->addBadVmap(mapId, x, y); + TravelMgr::instance().addBadVmap(mapId, x, y); break; case VMAP::VMAP_LOAD_RESULT_IGNORED: - sTravelMgr->addBadVmap(mapId, x, y); + TravelMgr::instance().addBadVmap(mapId, x, y); // LOG_INFO("playerbots", "Ignored VMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})", // mapName, mapId, x, y, x, y); break; } - if (sPlayerbotAIConfig->hasLog(fileName)) + if (sPlayerbotAIConfig.hasLog(fileName)) { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); - out << "+00,\"vmap\", " << x << "," << y << ", " << (sTravelMgr->isBadVmap(mapId, x, y) ? "0" : "1") + out << sPlayerbotAIConfig.GetTimestampStr(); + out << "+00,\"vmap\", " << x << "," << y << ", " << (TravelMgr::instance().isBadVmap(mapId, x, y) ? "0" : "1") << ","; printWKT(frommGridCoord(mGridCoord(x, y)), out, 1, true); - sPlayerbotAIConfig->log(fileName, out.str().c_str()); + sPlayerbotAIConfig.log(fileName, out.str().c_str()); } } - if (!sTravelMgr->isBadMmap(mapId, x, y)) + if (!TravelMgr::instance().isBadMmap(mapId, x, y)) { // load navmesh if (!MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(mapId, x, y)) - sTravelMgr->addBadMmap(mapId, x, y); + TravelMgr::instance().addBadMmap(mapId, x, y); - if (sPlayerbotAIConfig->hasLog(fileName)) + if (sPlayerbotAIConfig.hasLog(fileName)) { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); - out << "+00,\"mmap\", " << x << "," << y << "," << (sTravelMgr->isBadMmap(mapId, x, y) ? "0" : "1") + out << sPlayerbotAIConfig.GetTimestampStr(); + out << "+00,\"mmap\", " << x << "," << y << "," << (TravelMgr::instance().isBadMmap(mapId, x, y) ? "0" : "1") << ","; printWKT(fromGridCoord(GridCoord(x, y)), out, 1, true); - sPlayerbotAIConfig->log(fileName, out.str().c_str()); + sPlayerbotAIConfig.log(fileName, out.str().c_str()); } } } @@ -733,21 +734,21 @@ std::vector WorldPosition::getPathStepFrom(WorldPosition startPos Movement::PointsArray points = path.GetPath(); PathType type = path.GetPathType(); - if (sPlayerbotAIConfig->hasLog("pathfind_attempt_point.csv")) + if (sPlayerbotAIConfig.hasLog("pathfind_attempt_point.csv")) { std::ostringstream out; out << std::fixed << std::setprecision(1); printWKT({startPos, *this}, out); - sPlayerbotAIConfig->log("pathfind_attempt_point.csv", out.str().c_str()); + sPlayerbotAIConfig.log("pathfind_attempt_point.csv", out.str().c_str()); } - if (sPlayerbotAIConfig->hasLog("pathfind_attempt.csv") && (type == PATHFIND_INCOMPLETE || type == PATHFIND_NORMAL)) + if (sPlayerbotAIConfig.hasLog("pathfind_attempt.csv") && (type == PATHFIND_INCOMPLETE || type == PATHFIND_NORMAL)) { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr() << "+00,"; + out << sPlayerbotAIConfig.GetTimestampStr() << "+00,"; out << std::fixed << std::setprecision(1) << type << ","; printWKT(fromPointsArray(points), out, 1); - sPlayerbotAIConfig->log("pathfind_attempt.csv", out.str().c_str()); + sPlayerbotAIConfig.log("pathfind_attempt.csv", out.str().c_str()); } if (type == PATHFIND_INCOMPLETE || type == PATHFIND_NORMAL) @@ -796,7 +797,7 @@ std::vector WorldPosition::getPathFromPath(std::vectortargetPosRecalcDistance) + if (subPath.empty() || currentPos.distance(&subPath.back()) < sPlayerbotAIConfig.targetPosRecalcDistance) break; // Append the path excluding the start (this should be the same as the end of the startPath) @@ -1071,7 +1072,7 @@ std::vector TravelDestination::sortedPoints(WorldPosition* pos) std::vector TravelDestination::nextPoint(WorldPosition* pos, bool ignoreFull) { - return sTravelMgr->getNextPoint(pos, ignoreFull ? points : getPoints()); + return TravelMgr::instance().getNextPoint(pos, ignoreFull ? points : getPoints()); } bool TravelDestination::isFull(bool ignoreFull) @@ -1108,7 +1109,7 @@ bool QuestRelationTravelDestination::isActive(Player* bot) if (!bot->GetMap()->GetEntry()->IsWorldMap() || !bot->CanTakeQuest(questTemplate, false)) return false; - //uint32 dialogStatus = sTravelMgr->getDialogStatus(bot, entry, questTemplate); //not used, shadowed by the next declaration, line marked for removal. + //uint32 dialogStatus = TravelMgr::instance().getDialogStatus(bot, entry, questTemplate); //not used, shadowed by the next declaration, line marked for removal. if (AI_VALUE(bool, "can fight equal")) { @@ -1197,7 +1198,7 @@ bool QuestObjectiveTravelDestination::isActive(Player* bot) if (questTemplate->GetType() == QUEST_TYPE_ELITE && !AI_VALUE(bool, "can fight elite")) return false; - if (!sTravelMgr->getObjectiveStatus(bot, questTemplate, objective)) + if (!TravelMgr::instance().getObjectiveStatus(bot, questTemplate, objective)) return false; WorldPosition botPos(bot); @@ -1435,8 +1436,8 @@ TravelTarget::~TravelTarget() return; releaseVisitors(); - // sTravelMgr->botTargets.erase(std::remove(sTravelMgr->botTargets.begin(), sTravelMgr->botTargets.end(), this), - // sTravelMgr->botTargets.end()); + // TravelMgr::instance().botTargets.erase(std::remove(TravelMgr::instance().botTargets.begin(), TravelMgr::instance().botTargets.end(), this), + // TravelMgr::instance().botTargets.end()); } void TravelTarget::setTarget(TravelDestination* tDestination1, WorldPosition* wPosition1, bool groupCopy1) @@ -1509,7 +1510,7 @@ void TravelTarget::setStatus(TravelStatus status) statusTime = 1; break; case TRAVEL_STATUS_TRAVEL: - statusTime = getMaxTravelTime() * 2 + sPlayerbotAIConfig->maxWaitForMove; + statusTime = getMaxTravelTime() * 2 + sPlayerbotAIConfig.maxWaitForMove; break; case TRAVEL_STATUS_WORK: statusTime = tDestination->getExpireDelay(); @@ -1578,7 +1579,7 @@ bool TravelTarget::isTraveling() if (!botAI->HasStrategy("travel", BOT_STATE_NON_COMBAT)) { - setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true); + setTarget(TravelMgr::instance().nullTravelDestination, TravelMgr::instance().nullWorldPosition, true); return false; } @@ -1610,7 +1611,7 @@ bool TravelTarget::isWorking() if (!botAI->HasStrategy("travel", BOT_STATE_NON_COMBAT)) { - setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true); + setTarget(TravelMgr::instance().nullTravelDestination, TravelMgr::instance().nullWorldPosition, true); return false; } @@ -1767,7 +1768,7 @@ void TravelMgr::logQuestError(uint32 errorNr, Quest* quest, uint32 objective, ui void TravelMgr::LoadQuestTravelTable() { - if (!sTravelMgr->quests.empty()) + if (!TravelMgr::instance().quests.empty()) return; // Clearing store (for reloading case) @@ -1960,7 +1961,7 @@ void TravelMgr::LoadQuestTravelTable() bool loadQuestData = true; if (loadQuestData) { - questGuidpMap questMap = GAI_VALUE(questGuidpMap, "quest guidp map"); + questGuidpMap questMap = SharedValueContext::instance().getGlobalValue("quest guidp map")->Get(); for (auto& q : questMap) { @@ -1982,7 +1983,7 @@ void TravelMgr::LoadQuestTravelTable() if (flag & (uint32)QuestRelationFlag::questGiver) { loc = new QuestRelationTravelDestination( - questId, entry, 0, sPlayerbotAIConfig->tooCloseDistance, sPlayerbotAIConfig->sightDistance); + questId, entry, 0, sPlayerbotAIConfig.tooCloseDistance, sPlayerbotAIConfig.sightDistance); loc->setExpireDelay(5 * 60 * 1000); loc->setMaxVisitors(15, 0); container->questGivers.push_back(loc); @@ -1991,7 +1992,7 @@ void TravelMgr::LoadQuestTravelTable() if (flag & (uint32)QuestRelationFlag::questTaker) { loc = new QuestRelationTravelDestination( - questId, entry, 1, sPlayerbotAIConfig->tooCloseDistance, sPlayerbotAIConfig->sightDistance); + questId, entry, 1, sPlayerbotAIConfig.tooCloseDistance, sPlayerbotAIConfig.sightDistance); loc->setExpireDelay(5 * 60 * 1000); loc->setMaxVisitors(15, 0); container->questTakers.push_back(loc); @@ -2010,8 +2011,8 @@ void TravelMgr::LoadQuestTravelTable() objective = 3; loc = new QuestObjectiveTravelDestination(questId, entry, objective, - sPlayerbotAIConfig->tooCloseDistance, - sPlayerbotAIConfig->sightDistance); + sPlayerbotAIConfig.tooCloseDistance, + sPlayerbotAIConfig.sightDistance); loc->setExpireDelay(1 * 60 * 1000); loc->setMaxVisitors(100, 1); container->questObjectives.push_back(loc); @@ -2060,8 +2061,8 @@ void TravelMgr::LoadQuestTravelTable() int32 entry = r.type == 0 ? r.entry : r.entry * -1; - loc = new QuestRelationTravelDestination(r.questId, entry, r.role, sPlayerbotAIConfig->tooCloseDistance, - sPlayerbotAIConfig->sightDistance); loc->setExpireDelay(5 * 60 * 1000); loc->setMaxVisitors(15, 0); + loc = new QuestRelationTravelDestination(r.questId, entry, r.role, sPlayerbotAIConfig.tooCloseDistance, + sPlayerbotAIConfig.sightDistance); loc->setExpireDelay(5 * 60 * 1000); loc->setMaxVisitors(15, 0); for (auto& u : units) { @@ -2098,8 +2099,8 @@ void TravelMgr::LoadQuestTravelTable() uint32 reqEntry = quest->RequiredNpcOrGo[i]; - loc = new QuestObjectiveTravelDestination(questId, reqEntry, i, sPlayerbotAIConfig->tooCloseDistance, - sPlayerbotAIConfig->sightDistance); loc->setExpireDelay(1 * 60 * 1000); loc->setMaxVisitors(100, 1); + loc = new QuestObjectiveTravelDestination(questId, reqEntry, i, sPlayerbotAIConfig.tooCloseDistance, + sPlayerbotAIConfig.sightDistance); loc->setExpireDelay(1 * 60 * 1000); loc->setMaxVisitors(100, 1); for (auto& u : units) { @@ -2149,8 +2150,8 @@ void TravelMgr::LoadQuestTravelTable() int32 entry = l.type == 0 ? l.entry : l.entry * -1; - loc = new QuestObjectiveTravelDestination(questId, entry, i, sPlayerbotAIConfig->tooCloseDistance, - sPlayerbotAIConfig->sightDistance, l.item); loc->setExpireDelay(1 * 60 * 1000); loc->setMaxVisitors(100, 1); + loc = new QuestObjectiveTravelDestination(questId, entry, i, sPlayerbotAIConfig.tooCloseDistance, + sPlayerbotAIConfig.sightDistance, l.item); loc->setExpireDelay(1 * 60 * 1000); loc->setMaxVisitors(100, 1); for (auto& u : units) { @@ -2238,8 +2239,8 @@ void TravelMgr::LoadQuestTravelTable() { if ((cInfo->npcflag & *i) != 0) { - rLoc = new RpgTravelDestination(u.entry, sPlayerbotAIConfig->tooCloseDistance, - sPlayerbotAIConfig->sightDistance); + rLoc = new RpgTravelDestination(u.entry, sPlayerbotAIConfig.tooCloseDistance, + sPlayerbotAIConfig.sightDistance); rLoc->setExpireDelay(5 * 60 * 1000); rLoc->setMaxVisitors(15, 0); @@ -2251,8 +2252,8 @@ void TravelMgr::LoadQuestTravelTable() if (cInfo->mingold > 0) { - gLoc = new GrindTravelDestination(u.entry, sPlayerbotAIConfig->tooCloseDistance, - sPlayerbotAIConfig->sightDistance); + gLoc = new GrindTravelDestination(u.entry, sPlayerbotAIConfig.tooCloseDistance, + sPlayerbotAIConfig.sightDistance); gLoc->setExpireDelay(5 * 60 * 1000); gLoc->setMaxVisitors(100, 0); @@ -2265,8 +2266,8 @@ void TravelMgr::LoadQuestTravelTable() { std::string const nodeName = cInfo->Name; - bLoc = new BossTravelDestination(u.entry, sPlayerbotAIConfig->tooCloseDistance, - sPlayerbotAIConfig->sightDistance); + bLoc = new BossTravelDestination(u.entry, sPlayerbotAIConfig.tooCloseDistance, + sPlayerbotAIConfig.sightDistance); bLoc->setExpireDelay(5 * 60 * 1000); bLoc->setMaxVisitors(0, 0); @@ -2298,8 +2299,8 @@ void TravelMgr::LoadQuestTravelTable() if (iloc == exploreLocs.end()) { - loc = new ExploreTravelDestination(area->ID, sPlayerbotAIConfig->tooCloseDistance, - sPlayerbotAIConfig->sightDistance); + loc = new ExploreTravelDestination(area->ID, sPlayerbotAIConfig.tooCloseDistance, + sPlayerbotAIConfig.sightDistance); loc->setMaxVisitors(1000, 0); loc->setCooldownDelay(1000); loc->setExpireDelay(1000); @@ -2315,23 +2316,23 @@ void TravelMgr::LoadQuestTravelTable() } // Clear these logs files - sPlayerbotAIConfig->openLog("zones.csv", "w"); - sPlayerbotAIConfig->openLog("creatures.csv", "w"); - sPlayerbotAIConfig->openLog("gos.csv", "w"); - sPlayerbotAIConfig->openLog("bot_movement.csv", "w"); - sPlayerbotAIConfig->openLog("bot_pathfinding.csv", "w"); - sPlayerbotAIConfig->openLog("pathfind_attempt.csv", "w"); - sPlayerbotAIConfig->openLog("pathfind_attempt_point.csv", "w"); - sPlayerbotAIConfig->openLog("pathfind_result.csv", "w"); - sPlayerbotAIConfig->openLog("load_map_grid.csv", "w"); - sPlayerbotAIConfig->openLog("strategy.csv", "w"); + sPlayerbotAIConfig.openLog("zones.csv", "w"); + sPlayerbotAIConfig.openLog("creatures.csv", "w"); + sPlayerbotAIConfig.openLog("gos.csv", "w"); + sPlayerbotAIConfig.openLog("bot_movement.csv", "w"); + sPlayerbotAIConfig.openLog("bot_pathfinding.csv", "w"); + sPlayerbotAIConfig.openLog("pathfind_attempt.csv", "w"); + sPlayerbotAIConfig.openLog("pathfind_attempt_point.csv", "w"); + sPlayerbotAIConfig.openLog("pathfind_result.csv", "w"); + sPlayerbotAIConfig.openLog("load_map_grid.csv", "w"); + sPlayerbotAIConfig.openLog("strategy.csv", "w"); - sPlayerbotAIConfig->openLog("unload_grid.csv", "w"); - sPlayerbotAIConfig->openLog("unload_obj.csv", "w"); + sPlayerbotAIConfig.openLog("unload_grid.csv", "w"); + sPlayerbotAIConfig.openLog("unload_obj.csv", "w"); - sTravelNodeMap->loadNodeStore(); + TravelNodeMap::instance().loadNodeStore(); - sTravelNodeMap->generateAll(); + TravelNodeMap::instance().generateAll(); /* bool fullNavPointReload = false; @@ -2340,9 +2341,9 @@ void TravelMgr::LoadQuestTravelTable() if (!fullNavPointReload && true) TravelNodeStore::loadNodes(); - //sTravelNodeMap->loadNodeStore(); + //TravelNodeMap::instance().loadNodeStore(); - for (auto node : sTravelNodeMap->getNodes()) + for (auto node : TravelNodeMap::instance().getNodes()) { node->setLinked(true); } @@ -2384,7 +2385,7 @@ void TravelMgr::LoadQuestTravelTable() else nodeName += " flightMaster"; - sTravelNodeMap->addNode(&pos, nodeName, true, true); + TravelNodeMap::instance().addNode(&pos, nodeName, true, true); break; } @@ -2414,8 +2415,8 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition startPos(startTaxiNode->map_id, startTaxiNode->x, startTaxiNode->y, startTaxiNode->z); WorldPosition endPos(endTaxiNode->map_id, endTaxiNode->x, endTaxiNode->y, endTaxiNode->z); - TravelNode* startNode = sTravelNodeMap->getNode(&startPos, nullptr, 15.0f); - TravelNode* endNode = sTravelNodeMap->getNode(&endPos, nullptr, 15.0f); + TravelNode* startNode = TravelNodeMap::instance().getNode(&startPos, nullptr, 15.0f); + TravelNode* endNode = TravelNodeMap::instance().getNode(&endPos, nullptr, 15.0f); if (!startNode || !endNode) continue; @@ -2448,7 +2449,7 @@ void TravelMgr::LoadQuestTravelTable() if (cInfo->rank == 3 || (cInfo->rank == 1 && !pos.isOverworld() && u.c == 1)) { std::string const nodeName = cInfo->Name; - sTravelNodeMap->addNode(&pos, nodeName, true, true); + TravelNodeMap::instance().addNode(&pos, nodeName, true, true); } } @@ -2475,7 +2476,7 @@ void TravelMgr::LoadQuestTravelTable() pos = WorldPosition(info->mapId, info->positionX, info->positionY, info->positionZ, info->orientation); std::string const nodeName = startNames[i] + " start"; - sTravelNodeMap->addNode(&pos, nodeName, true, true); + TravelNodeMap::instance().addNode(&pos, nodeName, true, true); } } @@ -2530,7 +2531,7 @@ void TravelMgr::LoadQuestTravelTable() if (pos.distance(&lPos) == 0) { - TravelNode* node = sTravelNodeMap->addNode(&pos, data->name, true, true, true, + TravelNode* node = TravelNodeMap::instance().addNode(&pos, data->name, true, true, true, iter.first); if (!prevNode) @@ -2566,7 +2567,7 @@ void TravelMgr::LoadQuestTravelTable() if (pos.distance(&lPos) == 0) { - TravelNode* node = sTravelNodeMap->addNode(&pos, data->name, true, true, true, + TravelNode* node = TravelNodeMap::instance().addNode(&pos, data->name, true, true, true, iter.first); if (node != prevNode) { float totalTime = (p.second->TimeSeg - timeStart) / 1000.0f; @@ -2605,7 +2606,7 @@ void TravelMgr::LoadQuestTravelTable() if (p->delay > 0) { - TravelNode* node = sTravelNodeMap->addNode(&pos, data->name, true, true, true, iter.first); + TravelNode* node = TravelNodeMap::instance().addNode(&pos, data->name, true, true, true, iter.first); if (!prevNode) { @@ -2638,7 +2639,7 @@ void TravelMgr::LoadQuestTravelTable() if (p->delay > 0) { - TravelNode* node = sTravelNodeMap->getNode(&pos, nullptr, 5.0f); + TravelNode* node = TravelNodeMap::instance().getNode(&pos, nullptr, 5.0f); if (node != prevNode) { TravelNodePath travelPath(0.1f, 0.0, (uint8) TravelNodePathType::transport, entry, @@ -2667,13 +2668,13 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition pos = WorldPosition(points, WP_MEAN_CENTROID); - TravelNode* node = sTravelNodeMap->addNode(&pos, pos.getAreaName(), true, true, false); + TravelNode* node = TravelNodeMap::instance().addNode(&pos, pos.getAreaName(), true, true, false); } - LOG_INFO("playerbots", ">> Loaded {} navigation points.", sTravelNodeMap->getNodes().size()); + LOG_INFO("playerbots", ">> Loaded {} navigation points.", TravelNodeMap::instance().getNodes().size()); } - sTravelNodeMap->calcMapOffset(); + TravelNodeMap::instance().calcMapOffset(); loadMapTransfers(); */ @@ -2692,14 +2693,14 @@ void TravelMgr::LoadQuestTravelTable() //PathGenerator std::vector ppath; - uint32 cur = 0, max = sTravelNodeMap->getNodes().size(); + uint32 cur = 0, max = TravelNodeMap::instance().getNodes().size(); - for (auto& startNode : sTravelNodeMap->getNodes()) + for (auto& startNode : TravelNodeMap::instance().getNodes()) { if (!preloadReLinkFullyLinked && startNode->isLinked()) continue; - for (auto& endNode : sTravelNodeMap->getNodes()) + for (auto& endNode : TravelNodeMap::instance().getNodes()) { if (startNode == endNode) continue; @@ -2734,18 +2735,18 @@ void TravelMgr::LoadQuestTravelTable() if (preloadSubPrint && (cur * 50) / max > ((cur - 1) * 50) / max) { - sTravelNodeMap->printMap(); - sTravelNodeMap->printNodeStore(); + TravelNodeMap::instance().printMap(); + TravelNodeMap::instance().printNodeStore(); } } if (!preloadSubPrint) { - sTravelNodeMap->printNodeStore(); - sTravelNodeMap->printMap(); + TravelNodeMap::instance().printNodeStore(); + TravelNodeMap::instance().printMap(); } - LOG_INFO("playerbots", ">> Loaded paths for {} nodes.", sTravelNodeMap->getNodes().size()); + LOG_INFO("playerbots", ">> Loaded paths for {} nodes.", TravelNodeMap::instance().getNodes().size()); } bool removeLowLinkNodes = false || fullNavPointReload || storeNavPointReload; @@ -2754,7 +2755,7 @@ void TravelMgr::LoadQuestTravelTable() { std::vector goodNodes; std::vector remNodes; - for (auto& node : sTravelNodeMap->getNodes()) + for (auto& node : TravelNodeMap::instance().getNodes()) { if (!node->getPosition()->isOverworld()) continue; @@ -2774,9 +2775,9 @@ void TravelMgr::LoadQuestTravelTable() } for (auto& node : remNodes) - sTravelNodeMap->removeNode(node); + TravelNodeMap::instance().removeNode(node); - LOG_INFO("playerbots", ">> Checked {} nodes.", sTravelNodeMap->getNodes().size()); + LOG_INFO("playerbots", ">> Checked {} nodes.", TravelNodeMap::instance().getNodes().size()); } bool cleanUpNodeLinks = false || fullNavPointReload || storeNavPointReload; @@ -2786,22 +2787,22 @@ void TravelMgr::LoadQuestTravelTable() { //Routes uint32 cur = 0; - uint32 max = sTravelNodeMap->getNodes().size(); + uint32 max = TravelNodeMap::instance().getNodes().size(); //Clean up node links - for (auto& startNode : sTravelNodeMap->getNodes()) + for (auto& startNode : TravelNodeMap::instance().getNodes()) { startNode->cropUselessLinks(); cur++; if (cleanUpSubPrint && (cur * 10) / max > ((cur - 1) * 10) / max) { - sTravelNodeMap->printMap(); - sTravelNodeMap->printNodeStore(); + TravelNodeMap::instance().printMap(); + TravelNodeMap::instance().printNodeStore(); } } - LOG_INFO("playerbots", ">> Cleaned paths for {} nodes.", sTravelNodeMap->getNodes().size()); + LOG_INFO("playerbots", ">> Cleaned paths for {} nodes.", TravelNodeMap::instance().getNodes().size()); } bool reCalculateCost = false || fullNavPointReload || storeNavPointReload; @@ -2809,7 +2810,7 @@ void TravelMgr::LoadQuestTravelTable() if (reCalculateCost) { - for (auto& startNode : sTravelNodeMap->getNodes()) + for (auto& startNode : TravelNodeMap::instance().getNodes()) { for (auto& path : *startNode->getLinks()) { @@ -2825,14 +2826,14 @@ void TravelMgr::LoadQuestTravelTable() } } - LOG_INFO("playerbots", ">> Calculated pathcost for {} nodes.", sTravelNodeMap->getNodes().size()); + LOG_INFO("playerbots", ">> Calculated pathcost for {} nodes.", TravelNodeMap::instance().getNodes().size()); } bool mirrorMissingPaths = true || fullNavPointReload || storeNavPointReload; if (mirrorMissingPaths) { - for (auto& startNode : sTravelNodeMap->getNodes()) + for (auto& startNode : TravelNodeMap::instance().getNodes()) { for (auto& path : *startNode->getLinks()) { @@ -2855,16 +2856,16 @@ void TravelMgr::LoadQuestTravelTable() } } - LOG_INFO("playerbots", ">> Reversed missing paths for {} nodes.", sTravelNodeMap->getNodes().size()); + LOG_INFO("playerbots", ">> Reversed missing paths for {} nodes.", TravelNodeMap::instance().getNodes().size()); } */ - sTravelNodeMap->printMap(); - sTravelNodeMap->printNodeStore(); - sTravelNodeMap->saveNodeStore(); + TravelNodeMap::instance().printMap(); + TravelNodeMap::instance().printNodeStore(); + TravelNodeMap::instance().saveNodeStore(); // Creature/gos/zone export. - if (sPlayerbotAIConfig->hasLog("creatures.csv")) + if (sPlayerbotAIConfig.hasLog("creatures.csv")) { for (CreatureData const* cData : WorldPosition().getCreaturesNear()) { @@ -2889,11 +2890,11 @@ void TravelMgr::LoadQuestTravelTable() out << point.getAreaName() << ","; out << std::fixed; - sPlayerbotAIConfig->log("creatures.csv", out.str().c_str()); + sPlayerbotAIConfig.log("creatures.csv", out.str().c_str()); } } - if (sPlayerbotAIConfig->hasLog("vmangoslines.csv")) + if (sPlayerbotAIConfig.hasLog("vmangoslines.csv")) { uint32 mapId = 0; std::vector pos; @@ -2924,7 +2925,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); static float const ironforgeAreaSouthLimit[] = { -7491.33f, 3093.740f, -7472.04f, -391.880f, -6366.68f, -730.100f, -6063.96f, -1411.76f, @@ -2951,7 +2952,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); static float const stormwindAreaNorthLimit[] = { -8004.250f, 3714.110f, -8075.000f, -179.000f, -8638.000f, 169.0000f, -9044.000f, 35.00000f, @@ -2979,7 +2980,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); static float const stormwindAreaSouthLimit[] = { -8725.3378910f, 3535.62402300f, -9525.6992190f, 910.13256800f, -9796.9531250f, 839.06958000f, @@ -3010,7 +3011,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); mapId = 1; @@ -3044,7 +3045,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); static float const durotarSouthLimit[] = { 2755.0f, -3766.f, 2225.0f, -3596.f, 1762.0f, -3746.f, 1564.0f, -3943.f, 1184.0f, -3915.f, 737.00f, @@ -3072,7 +3073,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); static float const valleyoftrialsSouthLimit[] = {-324.f, -3869.f, -774.f, -3992.f, -965.f, -4290.f, -932.f, -4349.f, -828.f, -4414.f, -661.f, -4541.f, -521.f, -4582.f}; @@ -3097,7 +3098,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); static float const middleToSouthLimit[] = { -2402.010000f, 4255.7000000f, -2475.933105f, 3199.5683590f, // Desolace @@ -3129,7 +3130,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); static float const orgrimmarSouthLimit[] = { 2132.5076f, -3912.2478f, 1944.4298f, -3855.2583f, 1735.6906f, -3834.2417f, 1654.3671f, -3380.9902f, @@ -3157,7 +3158,7 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); static float const feralasThousandNeedlesSouthLimit[] = { -6495.4995f, -4711.9810f, -6674.9995f, -4515.0019f, -6769.5717f, -4122.4272f, -6838.2651f, -3874.2792f, @@ -3187,10 +3188,10 @@ void TravelMgr::LoadQuestTravelTable() WorldPosition().printWKT(pos, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("vmangoslines.csv", out.str().c_str()); + sPlayerbotAIConfig.log("vmangoslines.csv", out.str().c_str()); } - if (sPlayerbotAIConfig->hasLog("gos.csv")) + if (sPlayerbotAIConfig.hasLog("gos.csv")) { for (GameObjectData const* gData : WorldPosition().getGameObjectsNear()) { @@ -3212,11 +3213,11 @@ void TravelMgr::LoadQuestTravelTable() out << point.getAreaName() << ","; out << std::fixed; - sPlayerbotAIConfig->log("gos.csv", out.str().c_str()); + sPlayerbotAIConfig.log("gos.csv", out.str().c_str()); } } - if (sPlayerbotAIConfig->hasLog("zones.csv")) + if (sPlayerbotAIConfig.hasLog("zones.csv")) { std::unordered_map> zoneLocs; @@ -3238,7 +3239,7 @@ void TravelMgr::LoadQuestTravelTable() if (loc.second.empty()) continue; - if (!sTravelNodeMap->getMapOffset(loc.second.front().getMapId()) && loc.second.front().getMapId() != 0) + if (!TravelNodeMap::instance().getMapOffset(loc.second.front().getMapId()) && loc.second.front().getMapId() != 0) continue; std::vector points = loc.second; @@ -3266,13 +3267,13 @@ void TravelMgr::LoadQuestTravelTable() point.printWKT(points, out, 0); - sPlayerbotAIConfig->log("zones.csv", out.str().c_str()); + sPlayerbotAIConfig.log("zones.csv", out.str().c_str()); } } bool printStrategyMap = false; - if (printStrategyMap && sPlayerbotAIConfig->hasLog("strategy.csv")) + if (printStrategyMap && sPlayerbotAIConfig.hasLog("strategy.csv")) { static std::map classes; static std::map> specs; @@ -3328,7 +3329,7 @@ void TravelMgr::LoadQuestTravelTable() // Use randombot 0. std::ostringstream cout; - cout << sPlayerbotAIConfig->randomBotAccountPrefix << 0; + cout << sPlayerbotAIConfig.randomBotAccountPrefix << 0; std::string const accountName = cout.str(); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); @@ -3502,7 +3503,7 @@ void TravelMgr::LoadQuestTravelTable() return false; }); - sPlayerbotAIConfig->log("strategy.csv", "relevance, action, trigger, strategy, classes"); + sPlayerbotAIConfig.log("strategy.csv", "relevance, action, trigger, strategy, classes"); for (auto& actionkey : actionKeys) { @@ -3607,17 +3608,17 @@ void TravelMgr::LoadQuestTravelTable() out << actionkey << "\n"; } - sPlayerbotAIConfig->log("strategy.csv", out.str().c_str()); + sPlayerbotAIConfig.log("strategy.csv", out.str().c_str()); } } /* - sPlayerbotAIConfig->openLog(7, "w"); + sPlayerbotAIConfig.openLog(7, "w"); //Zone area map REMOVE! uint32 k = 0; - for (auto& node : sTravelNodeMap->getNodes()) + for (auto& node : TravelNodeMap::instance().getNodes()) { WorldPosition* pos = node->getPosition(); //map area @@ -3642,7 +3643,7 @@ void TravelMgr::LoadQuestTravelTable() std::ostringstream out; out << std::fixed << area << "," << npos.getDisplayX() << "," << npos.getDisplayY(); - sPlayerbotAIConfig->log(7, out.str().c_str()); + sPlayerbotAIConfig.log(7, out.str().c_str()); } } k++; @@ -3653,7 +3654,7 @@ void TravelMgr::LoadQuestTravelTable() //Explore map output (REMOVE!) - sPlayerbotAIConfig->openLog(5, "w"); + sPlayerbotAIConfig.openLog(5, "w"); for (auto i : exploreLocs) { for (auto j : i.second->getPoints()) @@ -3662,7 +3663,7 @@ void TravelMgr::LoadQuestTravelTable() std::string const name = i.second->getTitle(); name.erase(remove(name.begin(), name.end(), '\"'), name.end()); out << std::fixed << std::setprecision(2) << name.c_str() << "," << i.first << "," << j->getDisplayX() << - "," << j->getDisplayY() << "," << j->getX() << "," << j->getY() << "," << j->getZ(); sPlayerbotAIConfig->log(5, + "," << j->getDisplayY() << "," << j->getX() << "," << j->getY() << "," << j->getZ(); sPlayerbotAIConfig.log(5, out.str().c_str()); } } @@ -3897,7 +3898,7 @@ bool TravelMgr::getObjectiveStatus(Player* bot, Quest const* pQuest, uint32 obje if (bot->GetQuestStatus(questId) != QUEST_STATUS_INCOMPLETE) return false; - QuestStatusData* questStatus = sTravelMgr->getQuestStatus(bot, questId); + QuestStatusData* questStatus = TravelMgr::instance().getQuestStatus(bot, questId); uint32 reqCount = pQuest->RequiredItemCount[objective]; uint32 hasCount = questStatus->ItemCount[objective]; @@ -4097,7 +4098,7 @@ void TravelMgr::setNullTravelTarget(Player* player) TravelTarget* target = playerBotAI->GetAiObjectContext()->GetValue("travel target")->Get(); if (target) - target->setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition, true); + target->setTarget(TravelMgr::instance().nullTravelDestination, TravelMgr::instance().nullWorldPosition, true); } void TravelMgr::addMapTransfer(WorldPosition start, WorldPosition end, float portalDistance, bool makeShortcuts) @@ -4146,7 +4147,7 @@ void TravelMgr::addMapTransfer(WorldPosition start, WorldPosition end, float por void TravelMgr::loadMapTransfers() { - for (auto& node : sTravelNodeMap->getNodes()) + for (auto& node : TravelNodeMap::instance().getNodes()) { for (auto& link : *node->getLinks()) { @@ -4233,15 +4234,15 @@ void TravelMgr::printGrid(uint32 mapId, int x, int y, std::string const type) { std::string const fileName = "unload_grid.csv"; - if (sPlayerbotAIConfig->hasLog(fileName)) + if (sPlayerbotAIConfig.hasLog(fileName)) { WorldPosition p = WorldPosition(mapId, 0, 0, 0, 0); std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); + out << sPlayerbotAIConfig.GetTimestampStr(); out << "+00, " << 0 << 0 << x << "," << y << ", " << type << ","; p.printWKT(p.fromGridCoord(GridCoord(x, y)), out, 1, true); - sPlayerbotAIConfig->log(fileName, out.str().c_str()); + sPlayerbotAIConfig.log(fileName, out.str().c_str()); } } @@ -4249,7 +4250,7 @@ void TravelMgr::printObj(WorldObject* obj, std::string const type) { std::string fileName = "unload_grid.csv"; - if (sPlayerbotAIConfig->hasLog(fileName)) + if (sPlayerbotAIConfig.hasLog(fileName)) { WorldPosition p = WorldPosition(obj); @@ -4261,40 +4262,40 @@ void TravelMgr::printObj(WorldObject* obj, std::string const type) { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); + out << sPlayerbotAIConfig.GetTimestampStr(); out << "+00, " << obj->GetGUID().GetEntry() << "," << obj->GetGUID().GetCounter() << "," << cell.GridX() << "," << cell.GridY() << ", " << type << ","; p.printWKT(vcell, out, 1, true); - sPlayerbotAIConfig->log(fileName, out.str().c_str()); + sPlayerbotAIConfig.log(fileName, out.str().c_str()); } { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); + out << sPlayerbotAIConfig.GetTimestampStr(); out << "+00, " << obj->GetGUID().GetEntry() << "," << obj->GetGUID().GetCounter() << "," << cell.GridX() << "," << cell.GridY() << ", " << type << ","; p.printWKT(vgrid, out, 1, true); - sPlayerbotAIConfig->log(fileName, out.str().c_str()); + sPlayerbotAIConfig.log(fileName, out.str().c_str()); } } fileName = "unload_obj.csv"; - if (sPlayerbotAIConfig->hasLog(fileName)) + if (sPlayerbotAIConfig.hasLog(fileName)) { WorldPosition p = WorldPosition(obj); Cell cell(obj->GetPositionX(), obj->GetPositionY()); { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); + out << sPlayerbotAIConfig.GetTimestampStr(); out << "+00, " << obj->GetGUID().GetEntry() << "," << obj->GetGUID().GetCounter() << "," << cell.GridX() << "," << cell.GridY() << ", " << type << ","; p.printWKT({p}, out, 0); - sPlayerbotAIConfig->log(fileName, out.str().c_str()); + sPlayerbotAIConfig.log(fileName, out.str().c_str()); } } } diff --git a/src/Mgr/Travel/TravelMgr.h b/src/Mgr/Travel/TravelMgr.h index 39a79a4078..3223444bfe 100644 --- a/src/Mgr/Travel/TravelMgr.h +++ b/src/Mgr/Travel/TravelMgr.h @@ -10,7 +10,6 @@ #include #include "AiObject.h" -#include "Corpse.h" #include "CreatureData.h" #include "GameObject.h" #include "GridDefines.h" @@ -298,11 +297,11 @@ class WorldPosition : public WorldLocation std::vector getPathTo(WorldPosition endPos, Unit* bot) { return endPos.getPathFrom(*this, bot); } - bool isPathTo(std::vector path, float maxDistance = sPlayerbotAIConfig->targetPosRecalcDistance) + bool isPathTo(std::vector path, float maxDistance = sPlayerbotAIConfig.targetPosRecalcDistance) { return !path.empty() && distance(path.back()) < maxDistance; }; - bool cropPathTo(std::vector& path, float maxDistance = sPlayerbotAIConfig->targetPosRecalcDistance); + bool cropPathTo(std::vector& path, float maxDistance = sPlayerbotAIConfig.targetPosRecalcDistance); bool canPathTo(WorldPosition endPos, Unit* bot) { return endPos.isPathTo(getPathTo(endPos, bot)); } float getPathLength(std::vector points) @@ -848,12 +847,11 @@ class TravelTarget : AiObject class TravelMgr { public: - TravelMgr(){}; - - static TravelMgr* instance() + static TravelMgr& instance() { static TravelMgr instance; - return &instance; + + return instance; } void Clear(); @@ -922,7 +920,6 @@ class TravelMgr void printGrid(uint32 mapId, int x, int y, std::string const type); void printObj(WorldObject* obj, std::string const type); - // protected: void logQuestError(uint32 errorNr, Quest* quest, uint32 objective = 0, uint32 unitId = 0, uint32 itemId = 0); std::vector avoidLoaded; @@ -939,8 +936,16 @@ class TravelMgr std::unordered_map, std::vector, boost::hash>> mapTransfersMap; -}; -#define sTravelMgr TravelMgr::instance() +private: + TravelMgr() = default; + ~TravelMgr() = default; + + TravelMgr(const TravelMgr&) = delete; + TravelMgr& operator=(const TravelMgr&) = delete; + + TravelMgr(TravelMgr&&) = delete; + TravelMgr& operator=(TravelMgr&&) = delete; +}; #endif diff --git a/src/Mgr/Travel/TravelNode.cpp b/src/Mgr/Travel/TravelNode.cpp index d3c03ed97e..5d740075da 100644 --- a/src/Mgr/Travel/TravelNode.cpp +++ b/src/Mgr/Travel/TravelNode.cpp @@ -323,7 +323,7 @@ void TravelNode::removeLinkTo(TravelNode* node, bool removePaths) else { // Remove all references to this node. - for (auto& node : sTravelNodeMap->getNodes()) + for (auto& node : TravelNodeMap::instance().getNodes()) { if (node->hasPathTo(this)) node->removeLinkTo(this, removePaths); @@ -397,7 +397,7 @@ bool TravelNode::isUselessLink(TravelNode* farNode) } else { - TravelNodeRoute route = sTravelNodeMap->getRoute(nearNode, farNode, nullptr); + TravelNodeRoute route = TravelNodeMap::instance().getRoute(nearNode, farNode, nullptr); if (route.isEmpty()) continue; @@ -432,7 +432,7 @@ bool TravelNode::cropUselessLinks() this->removeLinkTo(farNode); hasRemoved = true; - if (sPlayerbotAIConfig->hasLog("crop.csv")) + if (sPlayerbotAIConfig.hasLog("crop.csv")) { std::ostringstream out; out << getName() << ","; @@ -440,7 +440,7 @@ bool TravelNode::cropUselessLinks() WorldPosition().printWKT({*getPosition(), *farNode->getPosition()}, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("crop.csv", out.str().c_str()); + sPlayerbotAIConfig.log("crop.csv", out.str().c_str()); } } @@ -449,7 +449,7 @@ bool TravelNode::cropUselessLinks() farNode->removeLinkTo(this); hasRemoved = true; - if (sPlayerbotAIConfig->hasLog("crop.csv")) + if (sPlayerbotAIConfig.hasLog("crop.csv")) { std::ostringstream out; out << getName() << ","; @@ -457,7 +457,7 @@ bool TravelNode::cropUselessLinks() WorldPosition().printWKT({*getPosition(), *farNode->getPosition()}, out, 1); out << std::fixed; - sPlayerbotAIConfig->log("crop.csv", out.str().c_str()); + sPlayerbotAIConfig.log("crop.csv", out.str().c_str()); } } } @@ -496,7 +496,7 @@ bool TravelNode::cropUselessLinks() } else { - TravelNodeRoute route = sTravelNodeMap->getRoute(firstNode, secondNode, false); + TravelNodeRoute route = TravelNodeMap::instance().getRoute(firstNode, secondNode, false); if (route.isEmpty()) continue; @@ -544,7 +544,7 @@ bool TravelNode::cropUselessLinks() } else { - TravelNodeRoute route = sTravelNodeMap->getRoute(firstNode, secondNode, false); + TravelNodeRoute route = TravelNodeMap::instance().getRoute(firstNode, secondNode, false); if (route.isEmpty()) continue; @@ -579,7 +579,7 @@ bool TravelNode::isEqual(TravelNode* compareNode) if (!compareNode->hasLinkTo(this)) return false; - for (auto& node : sTravelNodeMap->getNodes()) + for (auto& node : TravelNodeMap::instance().getNodes()) { if (node == this || node == compareNode) continue; @@ -611,11 +611,11 @@ void TravelNode::print([[maybe_unused]] bool printFailed) out << (isImportant() ? 1 : 0) << ","; out << mapSize; - sPlayerbotAIConfig->log("travelNodes.csv", out.str().c_str()); + sPlayerbotAIConfig.log("travelNodes.csv", out.str().c_str()); std::vector ppath; - for (auto& endNode : sTravelNodeMap->getNodes()) + for (auto& endNode : TravelNodeMap::instance().getNodes()) { if (endNode == this) continue; @@ -665,7 +665,7 @@ void TravelNode::print([[maybe_unused]] bool printFailed) out << std::to_string(path->getMaxLevelCreature()[1]) << ","; out << std::to_string(path->getMaxLevelCreature()[2]); - sPlayerbotAIConfig->log("travelPaths.csv", out.str().c_str()); + sPlayerbotAIConfig.log("travelPaths.csv", out.str().c_str()); } } } @@ -695,8 +695,8 @@ bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist) totalDist += p.point.sqDistance(std::prev(&p)->point); if (curDist < - sPlayerbotAIConfig->tooCloseDistance * - sPlayerbotAIConfig->tooCloseDistance) // We are on the path. This is a good starting point + sPlayerbotAIConfig.tooCloseDistance * + sPlayerbotAIConfig.tooCloseDistance) // We are on the path. This is a good starting point { minDist = curDist; totalDist = curDist; @@ -731,11 +731,11 @@ bool TravelPath::makeShortCut(WorldPosition startPos, float maxDist) WorldPosition beginPos = newPath.begin()->point; // The old path seems to be the best. - if (beginPos.distance(firstNode) < sPlayerbotAIConfig->tooCloseDistance) + if (beginPos.distance(firstNode) < sPlayerbotAIConfig.tooCloseDistance) return false; // We are (nearly) on the new path. Just follow the rest. - if (beginPos.distance(startPos) < sPlayerbotAIConfig->tooCloseDistance) + if (beginPos.distance(startPos) < sPlayerbotAIConfig.tooCloseDistance) { fullPath = newPath; return true; @@ -891,7 +891,7 @@ WorldPosition TravelPath::getNextPoint(WorldPosition startPos, float maxDist, Tr } // We have to move far for next point. Try to make a cropped path. - if (moveDist < sPlayerbotAIConfig->targetPosRecalcDistance && std::next(startP) != ed) + if (moveDist < sPlayerbotAIConfig.targetPosRecalcDistance && std::next(startP) != ed) { // std::vector path = startPos.getPathTo(std::next(startP)->point, nullptr); // startP->point = startPos.lastInRange(path, -1, maxDist); @@ -905,7 +905,7 @@ std::ostringstream const TravelPath::print() { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); + out << sPlayerbotAIConfig.GetTimestampStr(); out << "+00," << "1,"; out << std::fixed; @@ -1031,7 +1031,7 @@ std::ostringstream const TravelNodeRoute::print() { std::ostringstream out; - out << sPlayerbotAIConfig->GetTimestampStr(); + out << sPlayerbotAIConfig.GetTimestampStr(); out << "+00" << ",0," << "\"LINESTRING("; @@ -1046,34 +1046,6 @@ std::ostringstream const TravelNodeRoute::print() return out; } -TravelNodeMap::TravelNodeMap(TravelNodeMap* baseMap) -{ - TravelNode* newNode; - - baseMap->m_nMapMtx.lock_shared(); - - for (auto& node : baseMap->getNodes()) - { - newNode = new TravelNode(node); - - m_nodes.push_back(newNode); - } - - for (auto& node : baseMap->getNodes()) - { - newNode = getNode(node); - - for (auto& path : *node->getPaths()) - { - TravelNode* endNode = getNode(path.first); - - newNode->setPathTo(endNode, path.second); - } - } - - baseMap->m_nMapMtx.unlock_shared(); -} - TravelNode* TravelNodeMap::addNode(WorldPosition pos, std::string const preferedName, bool isImportant, bool checkDuplicate, [[maybe_unused]] bool transport, [[maybe_unused]] uint32 transportId) @@ -1178,7 +1150,7 @@ TravelNode* TravelNodeMap::getNode(WorldPosition pos, [[maybe_unused]] std::vect uint32 c = 0; - std::vector nodes = sTravelNodeMap->getNodes(pos, range); + std::vector nodes = TravelNodeMap::instance().getNodes(pos, range); for (auto& node : nodes) { if (!bot || pos.canPathTo(*node->getPosition(), bot)) @@ -1233,14 +1205,14 @@ TravelNodeRoute TravelNodeMap::getRoute(TravelNode* start, TravelNode* goal, Pla { AiObjectContext* context = botAI->GetAiObjectContext(); - TravelNode* homeNode = sTravelNodeMap->getNode(AI_VALUE(WorldPosition, "home bind"), nullptr, 10.0f); + TravelNode* homeNode = TravelNodeMap::instance().getNode(AI_VALUE(WorldPosition, "home bind"), nullptr, 10.0f); if (homeNode) { - PortalNode* portNode = (PortalNode*)sTravelNodeMap->teleportNodes[bot->GetGUID()][8690]; + PortalNode* portNode = (PortalNode*)TravelNodeMap::instance().teleportNodes[bot->GetGUID()][8690]; { portNode = new PortalNode(start); - sTravelNodeMap->teleportNodes[bot->GetGUID()][8690] = portNode; + TravelNodeMap::instance().teleportNodes[bot->GetGUID()][8690] = portNode; } portNode->SetPortal(start, homeNode, 8690); @@ -1370,7 +1342,7 @@ TravelNodeRoute TravelNodeMap::getRoute(WorldPosition startPos, WorldPosition en WorldPosition startNodePosition = *startNode->getPosition(); WorldPosition endNodePosition = *endNode->getPosition(); - float maxStartDistance = startNode->isTransport() ? 20.0f : sPlayerbotAIConfig->targetPosRecalcDistance; + float maxStartDistance = startNode->isTransport() ? 20.0f : sPlayerbotAIConfig.targetPosRecalcDistance; TravelNodeRoute route = getRoute(startNode, endNode, bot); @@ -1403,10 +1375,10 @@ TravelNodeRoute TravelNodeMap::getRoute(WorldPosition startPos, WorldPosition en if (bot && !bot->HasSpellCooldown(8690)) { startPath.clear(); - TravelNode* botNode = sTravelNodeMap->teleportNodes[bot->GetGUID()][0]; + TravelNode* botNode = TravelNodeMap::instance().teleportNodes[bot->GetGUID()][0]; { botNode = new TravelNode(startPos, "Bot Pos", false); - sTravelNodeMap->teleportNodes[bot->GetGUID()][0] = botNode; + TravelNodeMap::instance().teleportNodes[bot->GetGUID()][0] = botNode; } botNode->setPoint(startPos); @@ -1440,37 +1412,37 @@ TravelPath TravelNodeMap::getFullPath(WorldPosition startPos, WorldPosition endP //[[Node pathfinding system]] // We try to find nodes near the bot and near the end position that have a route between them. // Then bot has to move towards/along the route. - sTravelNodeMap->m_nMapMtx.lock_shared(); + TravelNodeMap::instance().m_nMapMtx.lock_shared(); // Find the route of nodes starting at a node closest to the start position and ending at a node closest to the // endposition. Also returns longPath: The path from the start position to the first node in the route. - TravelNodeRoute route = sTravelNodeMap->getRoute(startPos, endPos, beginPath, bot); + TravelNodeRoute route = TravelNodeMap::instance().getRoute(startPos, endPos, beginPath, bot); if (route.isEmpty()) return movePath; - if (sPlayerbotAIConfig->hasLog("bot_pathfinding.csv")) + if (sPlayerbotAIConfig.hasLog("bot_pathfinding.csv")) { if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) { - sPlayerbotAIConfig->openLog("bot_pathfinding.csv", "w"); - sPlayerbotAIConfig->log("bot_pathfinding.csv", route.print().str().c_str()); + sPlayerbotAIConfig.openLog("bot_pathfinding.csv", "w"); + sPlayerbotAIConfig.log("bot_pathfinding.csv", route.print().str().c_str()); } } endPath = route.getNodes().back()->getPosition()->getPathTo(endPos, nullptr); movePath = route.buildPath(beginPath, endPath); - if (sPlayerbotAIConfig->hasLog("bot_pathfinding.csv")) + if (sPlayerbotAIConfig.hasLog("bot_pathfinding.csv")) { if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) { - sPlayerbotAIConfig->openLog("bot_pathfinding.csv", "w"); - sPlayerbotAIConfig->log("bot_pathfinding.csv", movePath.print().str().c_str()); + sPlayerbotAIConfig.openLog("bot_pathfinding.csv", "w"); + sPlayerbotAIConfig.log("bot_pathfinding.csv", movePath.print().str().c_str()); } } - sTravelNodeMap->m_nMapMtx.unlock_shared(); + TravelNodeMap::instance().m_nMapMtx.unlock_shared(); return movePath; } @@ -1511,7 +1483,7 @@ TravelNode* TravelNodeMap::addZoneLinkNode(TravelNode* startNode) if (!getNode(pos, nullptr, 100.0f)) { std::string const nodeName = zoneName + " to " + newZoneName; - return sTravelNodeMap->addNode(pos, nodeName, false, true); + return TravelNodeMap::instance().addNode(pos, nodeName, false, true); } zoneName = newZoneName; @@ -1550,7 +1522,7 @@ TravelNode* TravelNodeMap::addRandomExtNode(TravelNode* startNode) WorldPosition point = path[urand(0, path.size() - 1)]; if (!getNode(point, nullptr, 100.0f)) - return sTravelNodeMap->addNode(point, startNode->getName(), false, true); + return TravelNodeMap::instance().addNode(point, startNode->getName(), false, true); } return nullptr; @@ -1606,7 +1578,7 @@ void TravelNodeMap::manageNodes(Unit* bot, bool mapFull) m_nMapMtx.unlock(); } - sTravelNodeMap->m_nMapMtx.lock_shared(); + TravelNodeMap::instance().m_nMapMtx.lock_shared(); if (!rePrint && mapFull) printMap(); @@ -1643,13 +1615,13 @@ void TravelNodeMap::generateNpcNodes() else if (cInfo->npcflag & UNIT_NPC_FLAG_SPIRITGUIDE) nodeName += " spiritguide"; - /*TravelNode* node = */ sTravelNodeMap->addNode(guidP, nodeName, true, true); //node not used, fragment marked for removal. + /*TravelNode* node = */ TravelNodeMap::instance().addNode(guidP, nodeName, true, true); //node not used, fragment marked for removal. } else if (cInfo->rank == 3) { std::string const nodeName = cInfo->Name; - sTravelNodeMap->addNode(guidP, nodeName, true, true); + TravelNodeMap::instance().addNode(guidP, nodeName, true, true); } else if (cInfo->rank == 1 && !guidP.isOverworld()) { @@ -1672,7 +1644,7 @@ void TravelNodeMap::generateNpcNodes() std::string const nodeName = cInfo->Name; - sTravelNodeMap->addNode(guidP, nodeName, true, true); + TravelNodeMap::instance().addNode(guidP, nodeName, true, true); } } @@ -1701,7 +1673,7 @@ void TravelNodeMap::generateStartNodes() std::string const nodeName = startNames[i] + " start"; - sTravelNodeMap->addNode(pos, nodeName, true, true); + TravelNodeMap::instance().addNode(pos, nodeName, true, true); break; } @@ -1733,7 +1705,7 @@ void TravelNodeMap::generateAreaTriggerNodes() else nodeName = inPos.getAreaName(false) + " portal"; - sTravelNodeMap->addNode(inPos, nodeName, true, true); + TravelNodeMap::instance().addNode(inPos, nodeName, true, true); } // Exit nodes @@ -1759,11 +1731,11 @@ void TravelNodeMap::generateAreaTriggerNodes() else nodeName = inPos.getAreaName(false) + " portal"; - //TravelNode* entryNode = sTravelNodeMap->getNode(outPos, nullptr, 20.0f); // Entry side, portal exit. //not used, line marked for removal. + //TravelNode* entryNode = TravelNodeMap::instance().getNode(outPos, nullptr, 20.0f); // Entry side, portal exit. //not used, line marked for removal. - TravelNode* outNode = sTravelNodeMap->addNode(outPos, nodeName, true, true); // Exit size, portal exit. + TravelNode* outNode = TravelNodeMap::instance().addNode(outPos, nodeName, true, true); // Exit size, portal exit. - TravelNode* inNode = sTravelNodeMap->getNode(inPos, nullptr, 5.0f); // Entry side, portal center. + TravelNode* inNode = TravelNodeMap::instance().getNode(inPos, nullptr, 5.0f); // Entry side, portal center. // Portal link from area trigger to area trigger destination. if (outNode && inNode) @@ -1826,7 +1798,7 @@ void TravelNodeMap::generateTransportNodes() if (pos.distance(&lPos) == 0) { TravelNode* node = - sTravelNodeMap->addNode(pos, data->name, true, true, true, itr.first); + TravelNodeMap::instance().addNode(pos, data->name, true, true, true, itr.first); if (!prevNode) { @@ -1866,7 +1838,7 @@ void TravelNodeMap::generateTransportNodes() if (pos.distance(&lPos) == 0) { TravelNode* node = - sTravelNodeMap->addNode(pos, data->name, true, true, true, itr.first); + TravelNodeMap::instance().addNode(pos, data->name, true, true, true, itr.first); if (node != prevNode) { if (p.second->TimeSeg < timeStart) @@ -1911,7 +1883,7 @@ void TravelNodeMap::generateTransportNodes() if (p->delay > 0) { - TravelNode* node = sTravelNodeMap->addNode(pos, data->name, true, true, true, itr.first); + TravelNode* node = TravelNodeMap::instance().addNode(pos, data->name, true, true, true, itr.first); if (!prevNode) { @@ -1946,7 +1918,7 @@ void TravelNodeMap::generateTransportNodes() if (p->delay > 0) { - TravelNode* node = sTravelNodeMap->getNode(pos, nullptr, 5.0f); + TravelNode* node = TravelNodeMap::instance().getNode(pos, nullptr, 5.0f); if (node != prevNode) { @@ -1968,7 +1940,7 @@ void TravelNodeMap::generateTransportNodes() void TravelNodeMap::generateZoneMeanNodes() { // Zone means - for (auto& loc : sTravelMgr->exploreLocs) + for (auto& loc : TravelMgr::instance().exploreLocs) { std::vector points; @@ -1981,7 +1953,7 @@ void TravelNodeMap::generateZoneMeanNodes() WorldPosition pos = WorldPosition(points, WP_MEAN_CENTROID); - /*TravelNode* node = */sTravelNodeMap->addNode(pos, pos.getAreaName(), true, true, false); //node not used, but addNode as side effect, fragment marked for removal. + /*TravelNode* node = */TravelNodeMap::instance().addNode(pos, pos.getAreaName(), true, true, false); //node not used, but addNode as side effect, fragment marked for removal. } } @@ -2006,19 +1978,19 @@ void TravelNodeMap::generateWalkPaths() std::map nodeMaps; - for (auto& startNode : sTravelNodeMap->getNodes()) + for (auto& startNode : TravelNodeMap::instance().getNodes()) { nodeMaps[startNode->getMapId()] = true; } for (auto& map : nodeMaps) { - for (auto& startNode : sTravelNodeMap->getNodes(WorldPosition(map.first, 1, 1))) + for (auto& startNode : TravelNodeMap::instance().getNodes(WorldPosition(map.first, 1, 1))) { if (startNode->isLinked()) continue; - for (auto& endNode : sTravelNodeMap->getNodes(*startNode->getPosition(), 2000.0f)) + for (auto& endNode : TravelNodeMap::instance().getNodes(*startNode->getPosition(), 2000.0f)) { if (startNode == endNode) continue; @@ -2036,7 +2008,7 @@ void TravelNodeMap::generateWalkPaths() } } - LOG_INFO("playerbots", ">> Generated paths for {} nodes.", sTravelNodeMap->getNodes().size()); + LOG_INFO("playerbots", ">> Generated paths for {} nodes.", TravelNodeMap::instance().getNodes().size()); } void TravelNodeMap::generateTaxiPaths() @@ -2066,8 +2038,8 @@ void TravelNodeMap::generateTaxiPaths() WorldPosition startPos(startTaxiNode->map_id, startTaxiNode->x, startTaxiNode->y, startTaxiNode->z); WorldPosition endPos(endTaxiNode->map_id, endTaxiNode->x, endTaxiNode->y, endTaxiNode->z); - TravelNode* startNode = sTravelNodeMap->getNode(startPos, nullptr, 15.0f); - TravelNode* endNode = sTravelNodeMap->getNode(endPos, nullptr, 15.0f); + TravelNode* startNode = TravelNodeMap::instance().getNode(startPos, nullptr, 15.0f); + TravelNode* endNode = TravelNodeMap::instance().getNode(endPos, nullptr, 15.0f); if (!startNode || !endNode) continue; @@ -2090,7 +2062,7 @@ void TravelNodeMap::removeLowNodes() { std::vector goodNodes; std::vector remNodes; - for (auto& node : sTravelNodeMap->getNodes()) + for (auto& node : TravelNodeMap::instance().getNodes()) { if (!node->getPosition()->isOverworld()) continue; @@ -2110,13 +2082,13 @@ void TravelNodeMap::removeLowNodes() } for (auto& node : remNodes) - sTravelNodeMap->removeNode(node); + TravelNodeMap::instance().removeNode(node); } void TravelNodeMap::removeUselessPaths() { // Clean up node links - for (auto& startNode : sTravelNodeMap->getNodes()) + for (auto& startNode : TravelNodeMap::instance().getNodes()) { for (auto& path : *startNode->getPaths()) if (path.second.getComplete() && startNode->hasLinkTo(path.first)) @@ -2127,7 +2099,7 @@ void TravelNodeMap::removeUselessPaths() { uint32 rem = 0; // Clean up node links - for (auto& startNode : sTravelNodeMap->getNodes()) + for (auto& startNode : TravelNodeMap::instance().getNodes()) { if (startNode->cropUselessLinks()) rem++; @@ -2146,7 +2118,7 @@ void TravelNodeMap::removeUselessPaths() void TravelNodeMap::calculatePathCosts() { - for (auto& startNode : sTravelNodeMap->getNodes()) + for (auto& startNode : TravelNodeMap::instance().getNodes()) { for (auto& path : *startNode->getLinks()) { @@ -2162,7 +2134,7 @@ void TravelNodeMap::calculatePathCosts() } } - LOG_INFO("playerbots", ">> Calculated pathcost for {} nodes.", sTravelNodeMap->getNodes().size()); + LOG_INFO("playerbots", ">> Calculated pathcost for {} nodes.", TravelNodeMap::instance().getNodes().size()); } void TravelNodeMap::generatePaths() @@ -2188,7 +2160,7 @@ void TravelNodeMap::generateAll() calcMapOffset(); LOG_INFO("playerbots", "-Generating maptransfers"); - sTravelMgr->loadMapTransfers(); + TravelMgr::instance().loadMapTransfers(); if (hasToGen || hasToFullGen) { @@ -2201,14 +2173,14 @@ void TravelNodeMap::generateAll() void TravelNodeMap::printMap() { - if (!sPlayerbotAIConfig->hasLog("travelNodes.csv") && !sPlayerbotAIConfig->hasLog("travelPaths.csv")) + if (!sPlayerbotAIConfig.hasLog("travelNodes.csv") && !sPlayerbotAIConfig.hasLog("travelPaths.csv")) return; printf("\r [Qgis] \r\x3D"); fflush(stdout); - sPlayerbotAIConfig->openLog("travelNodes.csv", "w"); - sPlayerbotAIConfig->openLog("travelPaths.csv", "w"); + sPlayerbotAIConfig.openLog("travelNodes.csv", "w"); + sPlayerbotAIConfig.openLog("travelPaths.csv", "w"); std::vector anodes = getNodes(); @@ -2224,26 +2196,26 @@ void TravelNodeMap::printNodeStore() { std::string const nodeStore = "TravelNodeStore.h"; - if (!sPlayerbotAIConfig->hasLog(nodeStore)) + if (!sPlayerbotAIConfig.hasLog(nodeStore)) return; printf("\r [Map] \r\x3D"); fflush(stdout); - sPlayerbotAIConfig->openLog(nodeStore, "w"); + sPlayerbotAIConfig.openLog(nodeStore, "w"); std::unordered_map saveNodes; std::vector anodes = getNodes(); - sPlayerbotAIConfig->log(nodeStore, "#pragma once"); - sPlayerbotAIConfig->log(nodeStore, "#include \"TravelMgr.h\""); - sPlayerbotAIConfig->log(nodeStore, "class TravelNodeStore"); - sPlayerbotAIConfig->log(nodeStore, " {"); - sPlayerbotAIConfig->log(nodeStore, " public:"); - sPlayerbotAIConfig->log(nodeStore, " static void loadNodes()"); - sPlayerbotAIConfig->log(nodeStore, " {"); - sPlayerbotAIConfig->log(nodeStore, " TravelNode** nodes = new TravelNode*[%zu];", anodes.size()); + sPlayerbotAIConfig.log(nodeStore, "#pragma once"); + sPlayerbotAIConfig.log(nodeStore, "#include \"TravelMgr.h\""); + sPlayerbotAIConfig.log(nodeStore, "class TravelNodeStore"); + sPlayerbotAIConfig.log(nodeStore, " {"); + sPlayerbotAIConfig.log(nodeStore, " public:"); + sPlayerbotAIConfig.log(nodeStore, " static void loadNodes()"); + sPlayerbotAIConfig.log(nodeStore, " {"); + sPlayerbotAIConfig.log(nodeStore, " TravelNode** nodes = new TravelNode*[%zu];", anodes.size()); for (uint32 i = 0; i < anodes.size(); i++) { @@ -2266,7 +2238,7 @@ void TravelNodeMap::printNodeStore() /* out << std::fixed << std::setprecision(2) << " nodes[" << i << "] = - sTravelNodeMap->addNode(&WorldPosition(" << node->getMapId() << "," << node->getX() << "f," << node->getY() + TravelNodeMap::instance().addNode(&WorldPosition(" << node->getMapId() << "," << node->getX() << "f," << node->getY() << "f," << node->getZ() << "f,"<< node->getO() <<"f), \"" << name << "\", " << (node->isImportant() ? "true" : "false") << ", true"; if (node->isTransport()) @@ -2274,7 +2246,7 @@ void TravelNodeMap::printNodeStore() out << ");"; */ - sPlayerbotAIConfig->log(nodeStore, out.str().c_str()); + sPlayerbotAIConfig.log(nodeStore, out.str().c_str()); saveNodes.insert(std::make_pair(node, i)); } @@ -2297,12 +2269,12 @@ void TravelNodeMap::printNodeStore() // out << std::fixed << std::setprecision(1) << " nodes[" << i << "]->setPathTo(nodes[" << // saveNodes.find(Link.first)->second << "],TravelNodePath("; out << Link.second->print() << "), true);"; - sPlayerbotAIConfig->log(nodeStore, out.str().c_str()); + sPlayerbotAIConfig.log(nodeStore, out.str().c_str()); } } - sPlayerbotAIConfig->log(nodeStore, " }"); - sPlayerbotAIConfig->log(nodeStore, "};"); + sPlayerbotAIConfig.log(nodeStore, " }"); + sPlayerbotAIConfig.log(nodeStore, "};"); printf("\r [Done] \r\x3D"); fflush(stdout); @@ -2322,7 +2294,7 @@ void TravelNodeMap::saveNodeStore() trans->Append(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_TRAVELNODE_PATH)); std::unordered_map saveNodes; - std::vector anodes = sTravelNodeMap->getNodes(); + std::vector anodes = TravelNodeMap::instance().getNodes(); for (uint32 i = 0; i < anodes.size(); i++) { diff --git a/src/Mgr/Travel/TravelNode.h b/src/Mgr/Travel/TravelNode.h index 3a86cbb169..41419f8a97 100644 --- a/src/Mgr/Travel/TravelNode.h +++ b/src/Mgr/Travel/TravelNode.h @@ -477,13 +477,11 @@ class TravelNodeStub class TravelNodeMap { public: - TravelNodeMap(){}; - TravelNodeMap(TravelNodeMap* baseMap); - - static TravelNodeMap* instance() + static TravelNodeMap& instance() { static TravelNodeMap instance; - return &instance; + + return instance; } TravelNode* addNode(WorldPosition pos, std::string const preferedName = "Travel Node", bool isImportant = false, @@ -586,6 +584,15 @@ class TravelNodeMap std::unordered_map> teleportNodes; private: + TravelNodeMap() = default; + ~TravelNodeMap() = default; + + TravelNodeMap(const TravelNodeMap&) = delete; + TravelNodeMap& operator=(const TravelNodeMap&) = delete; + + TravelNodeMap(TravelNodeMap&&) = delete; + TravelNodeMap& operator=(TravelNodeMap&&) = delete; + std::vector m_nodes; std::vector> mapOffsets; diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index b9ef257d95..b511369484 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -660,25 +660,25 @@ bool PlayerbotAIConfig::Initialize() } // Assign account types after accounts are created - sRandomPlayerbotMgr->AssignAccountTypes(); + sRandomPlayerbotMgr.AssignAccountTypes(); - if (sPlayerbotAIConfig->enabled) + if (sPlayerbotAIConfig.enabled) { - sRandomPlayerbotMgr->Init(); + sRandomPlayerbotMgr.Init(); } - sPlayerbotGuildMgr->Init(); - sRandomItemMgr->Init(); - sRandomItemMgr->InitAfterAhBot(); - sPlayerbotTextMgr->LoadBotTexts(); - sPlayerbotTextMgr->LoadBotTextChance(); + PlayerbotGuildMgr::instance().Init(); + sRandomItemMgr.Init(); + sRandomItemMgr.InitAfterAhBot(); + PlayerbotTextMgr::instance().LoadBotTexts(); + PlayerbotTextMgr::instance().LoadBotTextChance(); PlayerbotFactory::Init(); AiObjectContext::BuildAllSharedContexts(); - if (sPlayerbotAIConfig->randomBotSuggestDungeons) + if (sPlayerbotAIConfig.randomBotSuggestDungeons) { - sPlayerbotDungeonRepository->LoadDungeonSuggestions(); + PlayerbotDungeonRepository::instance().LoadDungeonSuggestions(); } excludedHunterPetFamilies.clear(); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index fb112fc907..729fc5be16 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -8,11 +8,14 @@ #include #include +#include +#include +#include +#include +#include -#include "Common.h" #include "DBCEnums.h" #include "SharedDefines.h" -#include "Talentspec.h" enum class BotCheatMask : uint32 { @@ -62,11 +65,11 @@ enum NewRpgStatus : int class PlayerbotAIConfig { public: - PlayerbotAIConfig(){}; - static PlayerbotAIConfig* instance() + static PlayerbotAIConfig& instance() { static PlayerbotAIConfig instance; - return &instance; + + return instance; } bool Initialize(); @@ -449,6 +452,16 @@ class PlayerbotAIConfig bool IsRestrictedHealerDPSMap(uint32 mapId) const; std::vector excludedHunterPetFamilies; + +private: + PlayerbotAIConfig() = default; + ~PlayerbotAIConfig() = default; + + PlayerbotAIConfig(const PlayerbotAIConfig&) = delete; + PlayerbotAIConfig& operator=(const PlayerbotAIConfig&) = delete; + + PlayerbotAIConfig(PlayerbotAIConfig&&) = delete; + PlayerbotAIConfig& operator=(PlayerbotAIConfig&&) = delete; }; #define sPlayerbotAIConfig PlayerbotAIConfig::instance() diff --git a/src/Script/PlayerbotCommandScript.cpp b/src/Script/PlayerbotCommandScript.cpp index 4e3c5611f2..a7a0739528 100644 --- a/src/Script/PlayerbotCommandScript.cpp +++ b/src/Script/PlayerbotCommandScript.cpp @@ -76,33 +76,33 @@ class playerbots_commandscript : public CommandScript { if (!strcmp(args, "reset")) { - sPerfMonitor->Reset(); + sPerfMonitor.Reset(); return true; } if (!strcmp(args, "tick")) { - sPerfMonitor->PrintStats(true, false); + sPerfMonitor.PrintStats(true, false); return true; } if (!strcmp(args, "stack")) { - sPerfMonitor->PrintStats(false, true); + sPerfMonitor.PrintStats(false, true); return true; } if (!strcmp(args, "toggle")) { - sPlayerbotAIConfig->perfMonEnabled = !sPlayerbotAIConfig->perfMonEnabled; - if (sPlayerbotAIConfig->perfMonEnabled) + sPlayerbotAIConfig.perfMonEnabled = !sPlayerbotAIConfig.perfMonEnabled; + if (sPlayerbotAIConfig.perfMonEnabled) LOG_INFO("playerbots", "Performance monitor enabled"); else LOG_INFO("playerbots", "Performance monitor disabled"); return true; } - sPerfMonitor->PrintStats(); + sPerfMonitor.PrintStats(); return true; } @@ -122,7 +122,7 @@ class playerbots_commandscript : public CommandScript Player* player = handler->GetSession()->GetPlayer(); std::string key = args; - PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player); + PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player); if (mgr) { mgr->HandleSetSecurityKeyCommand(player, key); @@ -151,7 +151,7 @@ class playerbots_commandscript : public CommandScript Player* player = handler->GetSession()->GetPlayer(); - PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player); + PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player); if (mgr) { mgr->HandleLinkAccountCommand(player, accountName, key); @@ -168,7 +168,7 @@ class playerbots_commandscript : public CommandScript { Player* player = handler->GetSession()->GetPlayer(); - PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player); + PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player); if (mgr) { mgr->HandleViewLinkedAccountsCommand(player); @@ -195,7 +195,7 @@ class playerbots_commandscript : public CommandScript Player* player = handler->GetSession()->GetPlayer(); - PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player); + PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player); if (mgr) { mgr->HandleUnlinkAccountCommand(player, accountName); diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index 8a8e1e0b73..065ccb8719 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -22,7 +22,6 @@ #include "DatabaseEnv.h" #include "DatabaseLoader.h" #include "GuildTaskMgr.h" -#include "Metric.h" #include "PlayerScript.h" #include "PlayerbotAIConfig.h" #include "PlayerbotGuildMgr.h" @@ -98,24 +97,24 @@ class PlayerbotsPlayerScript : public PlayerScript { if (!player->GetSession()->IsBot()) { - sPlayerbotsMgr->AddPlayerbotData(player, false); - sRandomPlayerbotMgr->OnPlayerLogin(player); + PlayerbotsMgr::instance().AddPlayerbotData(player, false); + sRandomPlayerbotMgr.OnPlayerLogin(player); // Before modifying the following messages, please make sure it does not violate the AGPLv3.0 license // especially if you are distributing a repack or hosting a public server // e.g. you can replace the URL with your own repository, // but it should be publicly accessible and include all modifications you've made - if (sPlayerbotAIConfig->enabled) + if (sPlayerbotAIConfig.enabled) { ChatHandler(player->GetSession()).SendSysMessage( "|cff00ff00This server runs with |cff00ccffmod-playerbots|r " "|cffcccccchttps://github.com/mod-playerbots/mod-playerbots|r"); } - if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin) + if (sPlayerbotAIConfig.enabled || sPlayerbotAIConfig.randomBotAutologin) { std::string roundedTime = - std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0); + std::to_string(std::ceil((sPlayerbotAIConfig.maxRandomBots * 0.11 / 60) * 10) / 10.0); roundedTime = roundedTime.substr(0, roundedTime.find('.') + 2); ChatHandler(player->GetSession()).SendSysMessage( @@ -125,54 +124,11 @@ class PlayerbotsPlayerScript : public PlayerScript } } - bool OnPlayerBeforeTeleport(Player* /*player*/, uint32 /*mapid*/, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override - { - /* for now commmented out until proven its actually required - * havent seen any proof CleanVisibilityReferences() is needed - - // If the player is not safe to touch, do nothing - if (!player) - return true; - - // If same map or not in world do nothing - if (!player->IsInWorld() || player->GetMapId() == mapid) - return true; - - // If real player do nothing - PlayerbotAI* ai = GET_PLAYERBOT_AI(player); - if (!ai || ai->IsRealPlayer()) - return true; - - // Cross-map bot teleport: defer visibility reference cleanup. - // CleanVisibilityReferences() erases this bot's GUID from other objects' visibility containers. - // This is intentionally done via the event queue (instead of directly here) because erasing - // from other players' visibility maps inside the teleport call stack can hit unsafe re-entrancy - // or iterator invalidation while visibility updates are in progress - ObjectGuid guid = player->GetGUID(); - player->m_Events.AddEventAtOffset( - [guid, mapid]() - { - // do nothing, if the player is not safe to touch - Player* p = ObjectAccessor::FindPlayer(guid); - if (!p || !p->IsInWorld() || p->IsDuringRemoveFromWorld()) - return; - - // do nothing if we are already on the target map - if (p->GetMapId() == mapid) - return; - - p->GetObjectVisibilityContainer().CleanVisibilityReferences(); - }, - Milliseconds(0)); - - */ - - return true; - } - void OnPlayerAfterUpdate(Player* player, uint32 diff) override { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) + PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); + + if (botAI != nullptr) { botAI->UpdateAI(diff); } @@ -185,19 +141,26 @@ class PlayerbotsPlayerScript : public PlayerScript bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override { - if (type == CHAT_MSG_WHISPER) + if (type != CHAT_MSG_WHISPER) { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver)) - { - botAI->HandleCommand(type, msg, player); + return true; + } - // hotfix; otherwise the server will crash when whispering logout - // https://github.com/mod-playerbots/mod-playerbots/pull/1838 - // TODO: find the root cause and solve it. (does not happen in party chat) - if (msg == "logout") - return false; - } + PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(receiver); + + if (botAI == nullptr) + { + return true; } + + botAI->HandleCommand(type, msg, player); + + // hotfix; otherwise the server will crash when whispering logout + // https://github.com/mod-playerbots/mod-playerbots/pull/1838 + // TODO: find the root cause and solve it. (does not happen in party chat) + if (msg == "logout") + return false; + return true; } @@ -205,56 +168,77 @@ class PlayerbotsPlayerScript : public PlayerScript { for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { - if (Player* member = itr->GetSource()) + Player* const member = itr->GetSource(); + + if (member == nullptr) { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(member)) - { - botAI->HandleCommand(type, msg, player); - } + continue; + } + + PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(member); + + if (botAI == nullptr) + { + continue; } + + botAI->HandleCommand(type, msg, player); } + return true; } bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override { - if (type == CHAT_MSG_GUILD) + if (type != CHAT_MSG_GUILD) { - if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) + return true; + } + + PlayerbotMgr* playerbotMgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player); + + if (playerbotMgr == nullptr) + { + return true; + } + + for (PlayerBotMap::const_iterator it = playerbotMgr->GetPlayerBotsBegin(); it != playerbotMgr->GetPlayerBotsEnd(); ++it) + { + Player* const bot = it->second; + + if (bot == nullptr) { - for (PlayerBotMap::const_iterator it = playerbotMgr->GetPlayerBotsBegin(); - it != playerbotMgr->GetPlayerBotsEnd(); ++it) - { - if (Player* const bot = it->second) - { - if (bot->GetGuildId() == player->GetGuildId()) - { - GET_PLAYERBOT_AI(bot)->HandleCommand(type, msg, player); - } - } - } + continue; } + + if (bot->GetGuildId() != player->GetGuildId()) + { + continue; + } + + PlayerbotsMgr::instance().GetPlayerbotAI(bot)->HandleCommand(type, msg, player); } + return true; } bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override { - if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) + PlayerbotMgr* const playerbotMgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player); + + if (playerbotMgr != nullptr && channel->GetFlags() & 0x18) { - if (channel->GetFlags() & 0x18) - { - playerbotMgr->HandleCommand(type, msg); - } + playerbotMgr->HandleCommand(type, msg); } - sRandomPlayerbotMgr->HandleCommand(type, msg, player); + sRandomPlayerbotMgr.HandleCommand(type, msg, player); + return true; } bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override { - if ((sRandomPlayerbotMgr->IsRandomBot(player) || sRandomPlayerbotMgr->IsAddclassBot(player)) && + if ((sRandomPlayerbotMgr.IsRandomBot(player) || sRandomPlayerbotMgr.IsAddclassBot(player)) && (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))) { return false; @@ -266,11 +250,11 @@ class PlayerbotsPlayerScript : public PlayerScript void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override { // early return - if (sPlayerbotAIConfig->randomBotXPRate == 1.0 || !player) + if (sPlayerbotAIConfig.randomBotXPRate == 1.0 || !player) return; // no XP multiplier, when player is no bot. - if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr->IsRandomBot(player)) + if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr.IsRandomBot(player)) return; // no XP multiplier, when bot is in a group with a real player. @@ -292,7 +276,7 @@ class PlayerbotsPlayerScript : public PlayerScript } // otherwise apply bot XP multiplier. - amount = static_cast(std::round(static_cast(amount) * sPlayerbotAIConfig->randomBotXPRate)); + amount = static_cast(std::round(static_cast(amount) * sPlayerbotAIConfig.randomBotXPRate)); } }; @@ -303,7 +287,9 @@ class PlayerbotsMiscScript : public MiscScript void OnDestructPlayer(Player* player) override { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) + PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); + + if (botAI != nullptr) { delete botAI; } @@ -360,20 +346,20 @@ class PlayerbotsWorldScript : public WorldScript LOG_INFO("server.loading", " "); LOG_INFO("server.loading", "Load Playerbots Config..."); - sPlayerbotAIConfig->Initialize(); + sPlayerbotAIConfig.Initialize(); LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); - sPlayerbotSpellRepository->Initialize(); + PlayerbotSpellRepository::Instance().Initialize(); LOG_INFO("server.loading", "Playerbots World Thread Processor initialized"); } void OnUpdate(uint32 diff) override { - sPlayerbotWorldProcessor->Update(diff); - sRandomPlayerbotMgr->UpdateAI(diff); // World thread only + PlayerbotWorldThreadProcessor::instance().Update(diff); + sRandomPlayerbotMgr.UpdateAI(diff); // World thread only } }; @@ -385,10 +371,12 @@ class PlayerbotsScript : public PlayerbotScript bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override { bool nonBotFound = false; + for (ObjectGuid const& guid : guidsList.guids) { Player* player = ObjectAccessor::FindPlayer(guid); - if (guid.IsGroup() || (player && !GET_PLAYERBOT_AI(player))) + + if (guid.IsGroup() || (player && !PlayerbotsMgr::instance().GetPlayerbotAI(player))) { nonBotFound = true; break; @@ -401,32 +389,48 @@ class PlayerbotsScript : public PlayerbotScript void OnPlayerbotCheckKillTask(Player* player, Unit* victim) override { if (player) - sGuildTaskMgr->CheckKillTask(player, victim); + GuildTaskMgr::instance().CheckKillTask(player, victim); } void OnPlayerbotCheckPetitionAccount(Player* player, bool& found) override { - if (found && GET_PLAYERBOT_AI(player)) + if (!found) + { + return; + } + + if (PlayerbotsMgr::instance().GetPlayerbotAI(player) != nullptr) + { found = false; + } } bool OnPlayerbotCheckUpdatesToSend(Player* player) override { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) - return botAI->IsRealPlayer(); + PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); - return true; + if (botAI == nullptr) + { + return true; + } + + return botAI->IsRealPlayer(); } void OnPlayerbotPacketSent(Player* player, WorldPacket const* packet) override { - if (!player) + if (player == nullptr) + { return; + } + + PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) + if (botAI != nullptr) { botAI->HandleBotOutgoingPacket(*packet); } + if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) { playerbotMgr->HandleMasterOutgoingPacket(*packet); @@ -435,7 +439,7 @@ class PlayerbotsScript : public PlayerbotScript void OnPlayerbotUpdate(uint32 diff) override { - sRandomPlayerbotMgr->UpdateSessions(); // Per-bot updates only + sRandomPlayerbotMgr.UpdateSessions(); // Per-bot updates only } void OnPlayerbotUpdateSessions(Player* player) override @@ -449,20 +453,21 @@ class PlayerbotsScript : public PlayerbotScript { if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) { - PlayerbotAI* botAI = GET_PLAYERBOT_AI(player); - if (!botAI || botAI->IsRealPlayer()) + PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); + + if (botAI == nullptr || botAI->IsRealPlayer()) { playerbotMgr->LogoutAllBots(); } } - sRandomPlayerbotMgr->OnPlayerLogout(player); + sRandomPlayerbotMgr.OnPlayerLogout(player); } void OnPlayerbotLogoutBots() override { LOG_INFO("playerbots", "Logging out all bots..."); - sRandomPlayerbotMgr->LogoutAllBots(); + sRandomPlayerbotMgr.LogoutAllBots(); } }; diff --git a/src/Script/Playerbots.h b/src/Script/Playerbots.h index 72ea9e4f79..006f058f9d 100644 --- a/src/Script/Playerbots.h +++ b/src/Script/Playerbots.h @@ -27,8 +27,8 @@ int strcmpi(char const* s1, char const* s2); #define CAST_ANGLE_IN_FRONT (2.f * static_cast(M_PI) / 3.f) #define EMOTE_ANGLE_IN_FRONT (2.f * static_cast(M_PI) / 6.f) -#define GET_PLAYERBOT_AI(object) sPlayerbotsMgr->GetPlayerbotAI(object) -#define GET_PLAYERBOT_MGR(object) sPlayerbotsMgr->GetPlayerbotMgr(object) +#define GET_PLAYERBOT_AI(object) sPlayerbotsMgr.GetPlayerbotAI(object) +#define GET_PLAYERBOT_MGR(object) sPlayerbotsMgr.GetPlayerbotMgr(object) #define AI_VALUE(type, name) context->GetValue(name)->Get() #define AI_VALUE2(type, name, param) context->GetValue(name, param)->Get() @@ -43,10 +43,10 @@ int strcmpi(char const* s1, char const* s2); #define RESET_AI_VALUE(type, name) context->GetValue(name)->Reset() #define RESET_AI_VALUE2(type, name, param) context->GetValue(name, param)->Reset() -#define PAI_VALUE(type, name) sPlayerbotsMgr->GetPlayerbotAI(player)->GetAiObjectContext()->GetValue(name)->Get() +#define PAI_VALUE(type, name) sPlayerbotsMgr.GetPlayerbotAI(player)->GetAiObjectContext()->GetValue(name)->Get() #define PAI_VALUE2(type, name, param) \ - sPlayerbotsMgr->GetPlayerbotAI(player)->GetAiObjectContext()->GetValue(name, param)->Get() -#define GAI_VALUE(type, name) sSharedValueContext->getGlobalValue(name)->Get() -#define GAI_VALUE2(type, name, param) sSharedValueContext->getGlobalValue(name, param)->Get() + sPlayerbotsMgr.GetPlayerbotAI(player)->GetAiObjectContext()->GetValue(name, param)->Get() +#define GAI_VALUE(type, name) sSharedValueContext.getGlobalValue(name)->Get() +#define GAI_VALUE2(type, name, param) sSharedValueContext.getGlobalValue(name, param)->Get() #endif diff --git a/src/Script/PlayerbotsSecureLogin.cpp b/src/Script/PlayerbotsSecureLogin.cpp index aa5d103d58..fc54a82b7d 100644 --- a/src/Script/PlayerbotsSecureLogin.cpp +++ b/src/Script/PlayerbotsSecureLogin.cpp @@ -29,6 +29,7 @@ namespace return; PlayerbotAI* ai = GET_PLAYERBOT_AI(target); + if (!ai) return; @@ -41,11 +42,7 @@ namespace } } - if (sRandomPlayerbotMgr) - { - sRandomPlayerbotMgr->LogoutPlayerBot(target->GetGUID()); - return; - } + sRandomPlayerbotMgr.LogoutPlayerBot(target->GetGUID()); } } diff --git a/src/Script/WorldThr/PlayerbotOperations.h b/src/Script/WorldThr/PlayerbotOperations.h index cd8851902c..fff44d2c29 100644 --- a/src/Script/WorldThr/PlayerbotOperations.h +++ b/src/Script/WorldThr/PlayerbotOperations.h @@ -355,7 +355,7 @@ class ArenaGroupFormationOperation : public PlayerbotOperation if (!member || !newGroup->IsMember(memberGuid)) continue; - PlayerbotAI* memberBotAI = sPlayerbotsMgr->GetPlayerbotAI(member); + PlayerbotAI* memberBotAI = PlayerbotsMgr::instance().GetPlayerbotAI(member); if (memberBotAI) memberBotAI->Reset(); @@ -412,13 +412,13 @@ class BotLogoutGroupCleanupOperation : public PlayerbotOperation if (!bot) return false; - PlayerbotAI* botAI = sPlayerbotsMgr->GetPlayerbotAI(bot); + PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(bot); if (!botAI) return false; Group* group = bot->GetGroup(); if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster()) - sPlayerbotRepository->Save(botAI); + PlayerbotRepository::instance().Save(botAI); return true; } @@ -448,7 +448,7 @@ class AddPlayerBotOperation : public PlayerbotOperation bool Execute() override { - sRandomPlayerbotMgr->AddPlayerBot(m_botGuid, m_masterAccountId); + sRandomPlayerbotMgr.AddPlayerBot(m_botGuid, m_masterAccountId); return true; } @@ -479,23 +479,33 @@ class OnBotLoginOperation : public PlayerbotOperation bool Execute() override { // find and verify bot still exists - Player* bot = ObjectAccessor::FindConnectedPlayer(m_botGuid); + Player* bot = ObjectAccessor::FindConnectedPlayer(this->m_botGuid); + if (!bot) + { return false; + } - PlayerbotHolder* holder = sRandomPlayerbotMgr; - if (m_masterAccountId) + if (this->m_masterAccountId) { - WorldSession* masterSession = sWorldSessionMgr->FindSession(m_masterAccountId); + WorldSession* masterSession = sWorldSessionMgr->FindSession(this->m_masterAccountId); Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr; - if (masterPlayer) - holder = GET_PLAYERBOT_MGR(masterPlayer); + + if (masterPlayer != nullptr) + { + PlayerbotMgr* manager = PlayerbotsMgr::instance().GetPlayerbotMgr(masterPlayer); + + if (manager == nullptr) + { + return false; + } + + manager->OnBotLogin(bot); + } } - if (!holder) - return false; + sRandomPlayerbotMgr.OnBotLogin(bot); - holder->OnBotLogin(bot); return true; } diff --git a/src/Script/WorldThr/PlayerbotWorldThreadProcessor.cpp b/src/Script/WorldThr/PlayerbotWorldThreadProcessor.cpp index c776eb1207..3679d3169a 100644 --- a/src/Script/WorldThr/PlayerbotWorldThreadProcessor.cpp +++ b/src/Script/WorldThr/PlayerbotWorldThreadProcessor.cpp @@ -3,27 +3,12 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#include "PlayerbotWorldThreadProcessor.h" - -#include "Log.h" -#include "PlayerbotAIConfig.h" - #include -PlayerbotWorldThreadProcessor::PlayerbotWorldThreadProcessor() - : m_enabled(true), m_maxQueueSize(10000), m_batchSize(100), m_queueWarningThreshold(80), - m_timeSinceLastUpdate(0), m_updateInterval(50) // Process at least every 50ms -{ - LOG_INFO("playerbots", "PlayerbotWorldThreadProcessor initialized"); -} - -PlayerbotWorldThreadProcessor::~PlayerbotWorldThreadProcessor() { ClearQueue(); } +#include "PlayerbotWorldThreadProcessor.h" -PlayerbotWorldThreadProcessor* PlayerbotWorldThreadProcessor::instance() -{ - static PlayerbotWorldThreadProcessor instance; - return &instance; -} +#include "Timer.h" +#include "Log.h" void PlayerbotWorldThreadProcessor::Update(uint32 diff) { diff --git a/src/Script/WorldThr/PlayerbotWorldThreadProcessor.h b/src/Script/WorldThr/PlayerbotWorldThreadProcessor.h index e37d2b5ba5..e975b8fbdb 100644 --- a/src/Script/WorldThr/PlayerbotWorldThreadProcessor.h +++ b/src/Script/WorldThr/PlayerbotWorldThreadProcessor.h @@ -6,13 +6,14 @@ #ifndef _PLAYERBOT_WORLD_THREAD_PROCESSOR_H #define _PLAYERBOT_WORLD_THREAD_PROCESSOR_H -#include "Common.h" -#include "PlayerbotOperation.h" - #include #include #include +#include "Log.h" + +#include "PlayerbotOperation.h" + /** * @brief Processes thread-unsafe bot operations in the world thread * @@ -28,15 +29,17 @@ * * Usage: * auto op = std::make_unique(botGuid, params); - * sPlayerbotWorldProcessor->QueueOperation(std::move(op)); + * PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(op)); */ class PlayerbotWorldThreadProcessor { public: - PlayerbotWorldThreadProcessor(); - ~PlayerbotWorldThreadProcessor(); + static PlayerbotWorldThreadProcessor& instance() + { + static PlayerbotWorldThreadProcessor instance; - static PlayerbotWorldThreadProcessor* instance(); + return instance; + } /** * @brief Update and process queued operations (called from world thread) @@ -103,6 +106,21 @@ class PlayerbotWorldThreadProcessor bool IsEnabled() const { return m_enabled; } private: + PlayerbotWorldThreadProcessor() + : m_enabled(true), + m_maxQueueSize(10000), + m_batchSize(100), + m_queueWarningThreshold(80), + m_timeSinceLastUpdate(0), + m_updateInterval(50) // Process at least every 50ms + { + LOG_INFO("playerbots", "PlayerbotWorldThreadProcessor initialized"); + } + ~PlayerbotWorldThreadProcessor() + { + this->ClearQueue(); + } + /** * @brief Process a single batch of operations * @@ -137,6 +155,4 @@ class PlayerbotWorldThreadProcessor uint32 m_updateInterval; // Minimum ms between updates }; -#define sPlayerbotWorldProcessor PlayerbotWorldThreadProcessor::instance() - #endif diff --git a/src/Script/WorldThr/Queue.cpp b/src/Script/WorldThr/Queue.cpp index 136e1b4381..43b1597f88 100644 --- a/src/Script/WorldThr/Queue.cpp +++ b/src/Script/WorldThr/Queue.cpp @@ -50,7 +50,7 @@ uint32 Queue::Size() void Queue::RemoveExpired() { - if (!sPlayerbotAIConfig->expireActionTime) + if (!sPlayerbotAIConfig.expireActionTime) { return; } @@ -113,7 +113,7 @@ ActionNode* Queue::extractAndDeleteBasket(ActionBasket* basket) void Queue::collectExpiredBaskets(std::list& expiredBaskets) { - uint32 expiryTime = sPlayerbotAIConfig->expireActionTime; + uint32 expiryTime = sPlayerbotAIConfig.expireActionTime; for (ActionBasket* basket : actions) { if (basket->isExpired(expiryTime)) diff --git a/src/Script/WorldThr/Queue.h b/src/Script/WorldThr/Queue.h index b8a486dc85..439b61aa0e 100644 --- a/src/Script/WorldThr/Queue.h +++ b/src/Script/WorldThr/Queue.h @@ -56,7 +56,7 @@ class Queue /** * @brief Removes and deletes expired actions from the queue * - * Uses sPlayerbotAIConfig->expireActionTime to determine if actions have expired. + * Uses sPlayerbotAIConfig.expireActionTime to determine if actions have expired. * Both the ActionNode and ActionBasket are deleted for expired actions. */ void RemoveExpired(); diff --git a/src/Util/BroadcastHelper.cpp b/src/Util/BroadcastHelper.cpp index 19979d3d9f..246344bdaf 100644 --- a/src/Util/BroadcastHelper.cpp +++ b/src/Util/BroadcastHelper.cpp @@ -28,29 +28,29 @@ bool BroadcastHelper::BroadcastTest(PlayerbotAI* ai, Player* /* bot */) int32 rand = urand(0, 1); - if (rand == 1 && ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE)) + if (rand == 1 && ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE)) return true; - else if (ai->SayToChannel(BOT_TEXT2("Posted to GuildRecruitment, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GUILD_RECRUITMENT)) + else if (ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to GuildRecruitment, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GUILD_RECRUITMENT)) return true; - return ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE); + return ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE); //int32 rand = urand(1, 8); - if (rand == 1 && ai->SayToGuild(BOT_TEXT2("Posted to guild, %rand1, %rand2, %rand3", placeholders))) + if (rand == 1 && ai->SayToGuild(PlayerbotTextMgr::instance().GetBotText("Posted to guild, %rand1, %rand2, %rand3", placeholders))) return true; - else if (rand == 2 && ai->SayToWorld(BOT_TEXT2("Posted to world, %rand1, %rand2, %rand3", placeholders))) + else if (rand == 2 && ai->SayToWorld(PlayerbotTextMgr::instance().GetBotText("Posted to world, %rand1, %rand2, %rand3", placeholders))) return true; - else if (rand == 3 && ai->SayToChannel(BOT_TEXT2("Posted to general, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GENERAL)) + else if (rand == 3 && ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to general, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GENERAL)) return true; - else if (rand == 4 && ai->SayToChannel(BOT_TEXT2("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE)) + else if (rand == 4 && ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to trade, %rand1, %rand2, %rand3", placeholders), ChatChannelId::TRADE)) return true; - else if (rand == 5 && ai->SayToChannel(BOT_TEXT2("Posted to LFG, %rand1, %rand2, %rand3", placeholders), ChatChannelId::LOOKING_FOR_GROUP)) + else if (rand == 5 && ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to LFG, %rand1, %rand2, %rand3", placeholders), ChatChannelId::LOOKING_FOR_GROUP)) return true; - else if (rand == 6 && ai->SayToChannel(BOT_TEXT2("Posted to LocalDefense, %rand1, %rand2, %rand3", placeholders), ChatChannelId::LOCAL_DEFENSE)) + else if (rand == 6 && ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to LocalDefense, %rand1, %rand2, %rand3", placeholders), ChatChannelId::LOCAL_DEFENSE)) return true; - else if (rand == 7 && ai->SayToChannel(BOT_TEXT2("Posted to WorldDefense, %rand1, %rand2, %rand3", placeholders), ChatChannelId::WORLD_DEFENSE)) + else if (rand == 7 && ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to WorldDefense, %rand1, %rand2, %rand3", placeholders), ChatChannelId::WORLD_DEFENSE)) return true; - else if (rand == 8 && ai->SayToChannel(BOT_TEXT2("Posted to GuildRecruitment, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GUILD_RECRUITMENT)) + else if (rand == 8 && ai->SayToChannel(PlayerbotTextMgr::instance().GetBotText("Posted to GuildRecruitment, %rand1, %rand2, %rand3", placeholders), ChatChannelId::GUILD_RECRUITMENT)) return true; return false; @@ -63,7 +63,7 @@ bool BroadcastHelper::BroadcastTest(PlayerbotAI* ai, Player* /* bot */) */ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::string message, std::list> toChannels) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; if (message.empty()) { @@ -74,14 +74,14 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s { uint32 roll = urand(1, 100); uint32 chance = pair.second; - uint32 broadcastRoll = urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue); + uint32 broadcastRoll = urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue); switch (pair.first) { case TO_GUILD: { if (roll <= chance - && broadcastRoll <= sPlayerbotAIConfig->broadcastToGuildGlobalChance + && broadcastRoll <= sPlayerbotAIConfig.broadcastToGuildGlobalChance && ai->SayToGuild(message)) { return true; @@ -91,7 +91,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s case TO_WORLD: { if (roll <= chance - && broadcastRoll <= sPlayerbotAIConfig->broadcastToWorldGlobalChance + && broadcastRoll <= sPlayerbotAIConfig.broadcastToWorldGlobalChance && ai->SayToWorld(message)) { return true; @@ -101,7 +101,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s case TO_GENERAL: { if (roll <= chance - && broadcastRoll <= sPlayerbotAIConfig->broadcastToGeneralGlobalChance + && broadcastRoll <= sPlayerbotAIConfig.broadcastToGeneralGlobalChance && ai->SayToChannel(message, ChatChannelId::GENERAL)) { return true; @@ -111,7 +111,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s case TO_TRADE: { if (roll <= chance - && broadcastRoll <= sPlayerbotAIConfig->broadcastToTradeGlobalChance + && broadcastRoll <= sPlayerbotAIConfig.broadcastToTradeGlobalChance && ai->SayToChannel(message, ChatChannelId::TRADE)) { return true; @@ -121,7 +121,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s case TO_LOOKING_FOR_GROUP: { if (roll <= chance - && broadcastRoll <= sPlayerbotAIConfig->broadcastToLFGGlobalChance + && broadcastRoll <= sPlayerbotAIConfig.broadcastToLFGGlobalChance && ai->SayToChannel(message, ChatChannelId::LOOKING_FOR_GROUP)) { return true; @@ -131,7 +131,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s case TO_LOCAL_DEFENSE: { if (roll <= chance - && broadcastRoll <= sPlayerbotAIConfig->broadcastToLocalDefenseGlobalChance + && broadcastRoll <= sPlayerbotAIConfig.broadcastToLocalDefenseGlobalChance && ai->SayToChannel(message, ChatChannelId::LOCAL_DEFENSE)) { return true; @@ -141,7 +141,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s case TO_WORLD_DEFENSE: { if (roll <= chance - && broadcastRoll <= sPlayerbotAIConfig->broadcastToWorldDefenseGlobalChance + && broadcastRoll <= sPlayerbotAIConfig.broadcastToWorldDefenseGlobalChance && ai->SayToChannel(message, ChatChannelId::WORLD_DEFENSE)) { return true; @@ -151,7 +151,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s case TO_GUILD_RECRUITMENT: { if (roll <= chance - && broadcastRoll <= sPlayerbotAIConfig->broadcastToGuildRecruitmentGlobalChance + && broadcastRoll <= sPlayerbotAIConfig.broadcastToGuildRecruitmentGlobalChance && ai->SayToChannel(message, ChatChannelId::GUILD_RECRUITMENT)) { return true; @@ -168,14 +168,14 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, ItemTemplate const* proto) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; std::map placeholders; placeholders["%item_link"] = ai->GetChatHelper()->FormatItem(proto); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); @@ -183,71 +183,71 @@ bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, ItemTem switch (proto->Quality) { case ITEM_QUALITY_POOR: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemPoor) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLootingItemPoor) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_looting_item_poor", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_looting_item_poor", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case ITEM_QUALITY_NORMAL: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemNormal) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLootingItemNormal) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_looting_item_normal", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_looting_item_normal", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case ITEM_QUALITY_UNCOMMON: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemUncommon) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLootingItemUncommon) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_looting_item_uncommon", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_looting_item_uncommon", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case ITEM_QUALITY_RARE: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemRare) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLootingItemRare) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_looting_item_rare", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_looting_item_rare", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case ITEM_QUALITY_EPIC: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemEpic) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLootingItemEpic) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_looting_item_epic", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_looting_item_epic", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case ITEM_QUALITY_LEGENDARY: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemLegendary) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLootingItemLegendary) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_looting_item_legendary", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_looting_item_legendary", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case ITEM_QUALITY_ARTIFACT: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLootingItemArtifact) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLootingItemArtifact) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_looting_item_artifact", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_looting_item_artifact", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -261,23 +261,23 @@ bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, ItemTem bool BroadcastHelper::BroadcastQuestAccepted(PlayerbotAI* ai, Player* bot, const Quest* quest) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestAccepted) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceQuestAccepted) { std::map placeholders; placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_quest_accepted_generic", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_quest_accepted_generic", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -287,13 +287,13 @@ bool BroadcastHelper::BroadcastQuestAccepted(PlayerbotAI* ai, Player* bot, const bool BroadcastHelper::BroadcastQuestUpdateAddKill(PlayerbotAI* ai, Player* bot, Quest const* quest, uint32 availableCount, uint32 requiredCount, std::string obectiveName) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; std::map placeholders; AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest); placeholders["%quest_obj_name"] = obectiveName; placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); @@ -305,20 +305,20 @@ bool BroadcastHelper::BroadcastQuestUpdateAddKill(PlayerbotAI* ai, Player* bot, placeholders["%quest_obj_full_formatted"] = ai->GetChatHelper()->FormatQuestObjective(obectiveName, availableCount, requiredCount); if (availableCount < requiredCount - && urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateObjectiveProgress) + && urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceQuestUpdateObjectiveProgress) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_quest_update_add_kill_objective_progress", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_quest_update_add_kill_objective_progress", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } else if (availableCount == requiredCount - && urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateObjectiveCompleted) + && urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceQuestUpdateObjectiveCompleted) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_quest_update_add_kill_objective_completed", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_quest_update_add_kill_objective_completed", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -328,13 +328,13 @@ bool BroadcastHelper::BroadcastQuestUpdateAddKill(PlayerbotAI* ai, Player* bot, bool BroadcastHelper::BroadcastQuestUpdateAddItem(PlayerbotAI* ai, Player* bot, Quest const* quest, uint32 availableCount, uint32 requiredCount, const ItemTemplate* proto) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; std::map placeholders; AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest); std::string itemLinkFormatted = ai->GetChatHelper()->FormatItem(proto); placeholders["%item_link"] = itemLinkFormatted; @@ -347,20 +347,20 @@ bool BroadcastHelper::BroadcastQuestUpdateAddItem(PlayerbotAI* ai, Player* bot, placeholders["%quest_obj_full_formatted"] = ai->GetChatHelper()->FormatQuestObjective(itemLinkFormatted, availableCount, requiredCount); if (availableCount < requiredCount - && urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateObjectiveProgress) + && urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceQuestUpdateObjectiveProgress) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_quest_update_add_item_objective_progress", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_quest_update_add_item_objective_progress", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } else if (availableCount == requiredCount - && urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateObjectiveCompleted) + && urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceQuestUpdateObjectiveCompleted) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_quest_update_add_item_objective_completed", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_quest_update_add_item_objective_completed", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -370,23 +370,23 @@ bool BroadcastHelper::BroadcastQuestUpdateAddItem(PlayerbotAI* ai, Player* bot, bool BroadcastHelper::BroadcastQuestUpdateFailedTimer(PlayerbotAI* ai, Player* bot, Quest const* quest) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateFailedTimer) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceQuestUpdateFailedTimer) { std::map placeholders; placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_quest_update_failed_timer", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_quest_update_failed_timer", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -396,23 +396,23 @@ bool BroadcastHelper::BroadcastQuestUpdateFailedTimer(PlayerbotAI* ai, Player* b bool BroadcastHelper::BroadcastQuestUpdateComplete(PlayerbotAI* ai, Player* bot, Quest const* quest) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestUpdateComplete) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceQuestUpdateComplete) { std::map placeholders; placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_quest_update_complete", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_quest_update_complete", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -422,23 +422,23 @@ bool BroadcastHelper::BroadcastQuestUpdateComplete(PlayerbotAI* ai, Player* bot, bool BroadcastHelper::BroadcastQuestTurnedIn(PlayerbotAI* ai, Player* bot, Quest const* quest) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceQuestTurnedIn) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceQuestTurnedIn) { std::map placeholders; placeholders["%quest_link"] = ai->GetChatHelper()->FormatQuest(quest); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_quest_turned_in", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_quest_turned_in", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -448,14 +448,14 @@ bool BroadcastHelper::BroadcastQuestTurnedIn(PlayerbotAI* ai, Player* bot, Quest bool BroadcastHelper::BroadcastKill(PlayerbotAI* ai, Player* bot, Creature *creature) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; std::map placeholders; placeholders["%victim_name"] = creature->GetName(); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%victim_level"] = creature->GetLevel(); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); @@ -468,24 +468,24 @@ bool BroadcastHelper::BroadcastKill(PlayerbotAI* ai, Player* bot, Creature *crea if (creature->IsPet()) { - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillPet) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceKillPet) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_killed_pet", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_killed_pet", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } } else if (creature->IsPlayer()) { - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillPlayer) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceKillPlayer) { placeholders["%victim_class"] = ai->GetChatHelper()->FormatClass(creature->getClass()); return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_killed_player", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_killed_player", placeholders), { {TO_WORLD_DEFENSE, 50}, {TO_LOCAL_DEFENSE, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -495,61 +495,61 @@ bool BroadcastHelper::BroadcastKill(PlayerbotAI* ai, Player* bot, Creature *crea switch (creature->GetCreatureTemplate()->rank) { case CREATURE_ELITE_NORMAL: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillNormal) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceKillNormal) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_killed_normal", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_killed_normal", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case CREATURE_ELITE_ELITE: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillElite) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceKillElite) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_killed_elite", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_killed_elite", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case CREATURE_ELITE_RAREELITE: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillRareelite) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceKillRareelite) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_killed_rareelite", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_killed_rareelite", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case CREATURE_ELITE_WORLDBOSS: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillWorldboss) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceKillWorldboss) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_killed_worldboss", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_killed_worldboss", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case CREATURE_ELITE_RARE: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillRare) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceKillRare) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_killed_rare", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_killed_rare", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } break; case CREATURE_UNKNOWN: - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceKillUnknown) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceKillUnknown) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_killed_unknown", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_killed_unknown", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -564,43 +564,43 @@ bool BroadcastHelper::BroadcastKill(PlayerbotAI* ai, Player* bot, Creature *crea bool BroadcastHelper::BroadcastLevelup(PlayerbotAI* ai, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; uint32 level = bot->GetLevel(); std::map placeholders; AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(level); - if (level == sPlayerbotAIConfig->randomBotMaxLevel - && urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLevelupMaxLevel) + if (level == sPlayerbotAIConfig.randomBotMaxLevel + && urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLevelupMaxLevel) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_levelup_max_level", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_levelup_max_level", placeholders), { {TO_GUILD, 30}, {TO_WORLD, 90}, {TO_GENERAL, 100} } ); } // It's divisible by 10 else if (level % 10 == 0 - && urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLevelupTenX) + && urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLevelupTenX) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_levelup_10x", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_levelup_10x", placeholders), { {TO_GUILD, 50}, {TO_WORLD, 90}, {TO_GENERAL, 100} } ); } - else if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceLevelupGeneric) + else if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceLevelupGeneric) { return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("broadcast_levelup_generic", placeholders), + PlayerbotTextMgr::instance().GetBotText("broadcast_levelup_generic", placeholders), { {TO_GUILD, 90}, {TO_WORLD, 90}, {TO_GENERAL, 100} } ); } @@ -610,9 +610,9 @@ bool BroadcastHelper::BroadcastLevelup(PlayerbotAI* ai, Player* bot) bool BroadcastHelper::BroadcastGuildMemberPromotion(PlayerbotAI* ai, Player* /* bot */, Player* player) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceGuildManagement) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceGuildManagement) { std::map placeholders; placeholders["%other_name"] = player->GetName(); @@ -620,7 +620,7 @@ bool BroadcastHelper::BroadcastGuildMemberPromotion(PlayerbotAI* ai, Player* /* placeholders["%other_race"] = ai->GetChatHelper()->FormatRace(player->getRace()); placeholders["%other_level"] = std::to_string(player->GetLevel()); - return ai->SayToGuild(BOT_TEXT2("broadcast_guild_promotion", placeholders)); + return ai->SayToGuild(PlayerbotTextMgr::instance().GetBotText("broadcast_guild_promotion", placeholders)); } return false; @@ -628,7 +628,7 @@ bool BroadcastHelper::BroadcastGuildMemberPromotion(PlayerbotAI* ai, Player* /* bool BroadcastHelper::BroadcastGuildMemberDemotion(PlayerbotAI* ai, Player* /* bot */, Player* player) { - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceGuildManagement) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceGuildManagement) { std::map placeholders; placeholders["%other_name"] = player->GetName(); @@ -636,7 +636,7 @@ bool BroadcastHelper::BroadcastGuildMemberDemotion(PlayerbotAI* ai, Player* /* b placeholders["%other_race"] = ai->GetChatHelper()->FormatRace(player->getRace()); placeholders["%other_level"] = std::to_string(player->GetLevel()); - return ai->SayToGuild(BOT_TEXT2("broadcast_guild_demotion", placeholders)); + return ai->SayToGuild(PlayerbotTextMgr::instance().GetBotText("broadcast_guild_demotion", placeholders)); } return false; @@ -644,25 +644,25 @@ bool BroadcastHelper::BroadcastGuildMemberDemotion(PlayerbotAI* ai, Player* /* b bool BroadcastHelper::BroadcastGuildGroupOrRaidInvite(PlayerbotAI* ai, Player* /* bot */, Player* player, Group* group) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; std::map placeholders; placeholders["%name"] = player->GetName(); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); //TODO move texts to sql! if (group && group->isRaidGroup()) { if (urand(0, 3)) { - return ai->SayToGuild(BOT_TEXT2("Hey anyone want to raid in %zone_name", placeholders)); + return ai->SayToGuild(PlayerbotTextMgr::instance().GetBotText("Hey anyone want to raid in %zone_name", placeholders)); } else { - return ai->SayToGuild(BOT_TEXT2("Hey %name I'm raiding in %zone_name do you wan to join me?", placeholders)); + return ai->SayToGuild(PlayerbotTextMgr::instance().GetBotText("Hey %name I'm raiding in %zone_name do you wan to join me?", placeholders)); } } else @@ -670,11 +670,11 @@ bool BroadcastHelper::BroadcastGuildGroupOrRaidInvite(PlayerbotAI* ai, Player* / //(bot->GetTeam() == ALLIANCE ? LANG_COMMON : LANG_ORCISH) if (urand(0, 3)) { - return ai->SayToGuild(BOT_TEXT2("Hey anyone wanna group up in %zone_name?", placeholders)); + return ai->SayToGuild(PlayerbotTextMgr::instance().GetBotText("Hey anyone wanna group up in %zone_name?", placeholders)); } else { - return ai->SayToGuild(BOT_TEXT2("Hey %name do you want join my group? I'm heading for %zone_name", placeholders)); + return ai->SayToGuild(PlayerbotTextMgr::instance().GetBotText("Hey %name do you want join my group? I'm heading for %zone_name", placeholders)); } } @@ -683,9 +683,9 @@ bool BroadcastHelper::BroadcastGuildGroupOrRaidInvite(PlayerbotAI* ai, Player* / bool BroadcastHelper::BroadcastSuggestInstance(PlayerbotAI* ai, std::vector& allowedInstances, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestInstance) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestInstance) { std::map placeholders; placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); @@ -701,7 +701,7 @@ bool BroadcastHelper::BroadcastSuggestInstance(PlayerbotAI* ai, std::vector& quests, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestQuest) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestQuest) { int index = rand() % quests.size(); @@ -730,7 +730,7 @@ bool BroadcastHelper::BroadcastSuggestQuest(PlayerbotAI* ai, std::vector return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("suggest_quest", placeholders), + PlayerbotTextMgr::instance().GetBotText("suggest_quest", placeholders), { {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -740,9 +740,9 @@ bool BroadcastHelper::BroadcastSuggestQuest(PlayerbotAI* ai, std::vector bool BroadcastHelper::BroadcastSuggestGrindMaterials(PlayerbotAI* ai, std::string item, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestGrindMaterials) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestGrindMaterials) { std::map placeholders; @@ -755,7 +755,7 @@ bool BroadcastHelper::BroadcastSuggestGrindMaterials(PlayerbotAI* ai, std::strin return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("suggest_trade", placeholders), + PlayerbotTextMgr::instance().GetBotText("suggest_trade", placeholders), { {TO_TRADE, 50}, {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -765,9 +765,9 @@ bool BroadcastHelper::BroadcastSuggestGrindMaterials(PlayerbotAI* ai, std::strin bool BroadcastHelper::BroadcastSuggestGrindReputation(PlayerbotAI* ai, std::vector levels, std::vector allowedFactions, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestGrindReputation) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestGrindReputation) { std::map placeholders; @@ -787,7 +787,7 @@ bool BroadcastHelper::BroadcastSuggestGrindReputation(PlayerbotAI* ai, std::vect return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("suggest_faction", placeholders), + PlayerbotTextMgr::instance().GetBotText("suggest_faction", placeholders), { {TO_LOOKING_FOR_GROUP, 50}, {TO_GUILD, 50}, {TO_WORLD, 50}, {TO_GENERAL, 100} } ); } @@ -797,9 +797,9 @@ bool BroadcastHelper::BroadcastSuggestGrindReputation(PlayerbotAI* ai, std::vect bool BroadcastHelper::BroadcastSuggestSell(PlayerbotAI* ai, const ItemTemplate* proto, uint32 count, uint32 price, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestSell) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestSell) { std::map placeholders; @@ -814,7 +814,7 @@ bool BroadcastHelper::BroadcastSuggestSell(PlayerbotAI* ai, const ItemTemplate* return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("suggest_sell", placeholders), + PlayerbotTextMgr::instance().GetBotText("suggest_sell", placeholders), { {TO_TRADE, 90}, {TO_GENERAL, 100} } ); } @@ -824,24 +824,24 @@ bool BroadcastHelper::BroadcastSuggestSell(PlayerbotAI* ai, const ItemTemplate* bool BroadcastHelper::BroadcastSuggestSomething(PlayerbotAI* ai, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestSomething) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestSomething) { std::map placeholders; placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("suggest_something", placeholders), + PlayerbotTextMgr::instance().GetBotText("suggest_something", placeholders), { {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} } ); } @@ -851,29 +851,29 @@ bool BroadcastHelper::BroadcastSuggestSomething(PlayerbotAI* ai, Player* bot) bool BroadcastHelper::BroadcastSuggestSomethingToxic(PlayerbotAI* ai, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestSomethingToxic) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestSomethingToxic) { //items std::vector botItems = ai->GetInventoryAndEquippedItems(); std::map placeholders; - placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? ai->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : BOT_TEXT1("string_empty_link"); + placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? ai->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : PlayerbotTextMgr::instance().GetBotText("string_empty_link"); placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("suggest_something_toxic", placeholders), + PlayerbotTextMgr::instance().GetBotText("suggest_something_toxic", placeholders), { {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} } ); } @@ -883,9 +883,9 @@ bool BroadcastHelper::BroadcastSuggestSomethingToxic(PlayerbotAI* ai, Player* bo bool BroadcastHelper::BroadcastSuggestToxicLinks(PlayerbotAI* ai, Player* bot) { - if (!sPlayerbotAIConfig->enableBroadcasts) + if (!sPlayerbotAIConfig.enableBroadcasts) return false; - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestToxicLinks) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestToxicLinks) { //quests std::vector incompleteQuests; @@ -908,8 +908,8 @@ bool BroadcastHelper::BroadcastSuggestToxicLinks(PlayerbotAI* ai, Player* bot) std::map placeholders; - placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? ai->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : BOT_TEXT1("string_empty_link"); - placeholders["%prefix"] = sPlayerbotAIConfig->toxicLinksPrefix; + placeholders["%random_inventory_item_link"] = botItems.size() > 0 ? ai->GetChatHelper()->FormatItem(botItems[rand() % botItems.size()]->GetTemplate()) : PlayerbotTextMgr::instance().GetBotText("string_empty_link"); + placeholders["%prefix"] = sPlayerbotAIConfig.toxicLinksPrefix; if (incompleteQuests.size() > 0) { @@ -924,15 +924,15 @@ bool BroadcastHelper::BroadcastSuggestToxicLinks(PlayerbotAI* ai, Player* bot) placeholders["%my_role"] = ChatHelper::FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); AreaTableEntry const* current_area = ai->GetCurrentArea(); AreaTableEntry const* current_zone = ai->GetCurrentZone(); - placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : BOT_TEXT1("string_unknown_area"); - placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : BOT_TEXT1("string_unknown_area"); + placeholders["%area_name"] = current_area ? ai->GetLocalizedAreaName(current_area) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); + placeholders["%zone_name"] = current_zone ? ai->GetLocalizedAreaName(current_zone) : PlayerbotTextMgr::instance().GetBotText("string_unknown_area"); placeholders["%my_class"] = ai->GetChatHelper()->FormatClass(bot->getClass()); placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace()); placeholders["%my_level"] = std::to_string(bot->GetLevel()); return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("suggest_toxic_links", placeholders), + PlayerbotTextMgr::instance().GetBotText("suggest_toxic_links", placeholders), { {TO_GUILD, 10}, {TO_WORLD, 70}, {TO_GENERAL, 100} } ); } @@ -942,7 +942,7 @@ bool BroadcastHelper::BroadcastSuggestToxicLinks(PlayerbotAI* ai, Player* bot) bool BroadcastHelper::BroadcastSuggestThunderfury(PlayerbotAI* ai, Player* bot) { - if (urand(1, sPlayerbotAIConfig->broadcastChanceMaxValue) <= sPlayerbotAIConfig->broadcastChanceSuggestThunderfury) + if (urand(1, sPlayerbotAIConfig.broadcastChanceMaxValue) <= sPlayerbotAIConfig.broadcastChanceSuggestThunderfury) { std::map placeholders; ItemTemplate const* thunderfuryProto = sObjectMgr->GetItemTemplate(19019); @@ -950,7 +950,7 @@ bool BroadcastHelper::BroadcastSuggestThunderfury(PlayerbotAI* ai, Player* bot) return BroadcastToChannelWithGlobalChance( ai, - BOT_TEXT2("thunderfury_spam", placeholders), + PlayerbotTextMgr::instance().GetBotText("thunderfury_spam", placeholders), { {TO_WORLD, 70}, {TO_GENERAL, 100} } ); } diff --git a/src/Util/BroadcastHelper.h b/src/Util/BroadcastHelper.h index 099d6bf192..c79d837aad 100644 --- a/src/Util/BroadcastHelper.h +++ b/src/Util/BroadcastHelper.h @@ -145,4 +145,4 @@ class BroadcastHelper PlayerbotAI* ai, Player* bot ); -}; \ No newline at end of file +}; diff --git a/src/Util/ServerFacade.cpp b/src/Util/ServerFacade.cpp index d69944c04f..12a77de635 100644 --- a/src/Util/ServerFacade.cpp +++ b/src/Util/ServerFacade.cpp @@ -4,8 +4,8 @@ */ #include "ServerFacade.h" +#include "Player.h" -#include "Playerbots.h" #include "TargetedMovementGenerator.h" float ServerFacade::GetDistance2d(Unit* unit, WorldObject* wo) @@ -25,13 +25,13 @@ float ServerFacade::GetDistance2d(Unit* unit, float x, float y) bool ServerFacade::IsDistanceLessThan(float dist1, float dist2) { - // return dist1 - dist2 < sPlayerbotAIConfig->targetPosRecalcDistance; + // return dist1 - dist2 < sPlayerbotAIConfig.targetPosRecalcDistance; return dist1 < dist2; } bool ServerFacade::IsDistanceGreaterThan(float dist1, float dist2) { - // return dist1 - dist2 > sPlayerbotAIConfig->targetPosRecalcDistance; + // return dist1 - dist2 > sPlayerbotAIConfig.targetPosRecalcDistance; return dist1 > dist2; } diff --git a/src/Util/ServerFacade.h b/src/Util/ServerFacade.h index 6668fa9562..978b8e7681 100644 --- a/src/Util/ServerFacade.h +++ b/src/Util/ServerFacade.h @@ -6,8 +6,6 @@ #ifndef _PLAYERBOT_SERVERFACADE_H #define _PLAYERBOT_SERVERFACADE_H -#include "Common.h" - class Player; class Unit; class WorldObject; @@ -29,12 +27,13 @@ class ServerFacade /** * @brief Get singleton instance. * - * @return ServerFacade* Pointer to the singleton instance. + * @return ServerFacade& Reference to the singleton instance. */ - static ServerFacade* instance() + static ServerFacade& instance() { static ServerFacade instance; - return &instance; + + return instance; } public: @@ -123,7 +122,4 @@ class ServerFacade void SendPacket(Player* player, WorldPacket* packet); }; -/** Global singleton accessor. */ -#define sServerFacade ServerFacade::instance() - #endif From caae524a0a570cdcf47a825d0ff7acafab53bc84 Mon Sep 17 00:00:00 2001 From: Keleborn <22352763+Celandriel@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:50:09 -0800 Subject: [PATCH 29/42] Minor flightMasterCache fix. (#2085) # Pull Request Incorrect comparison fix. --- ## How to Test the Changes - Alliance Bots should now be able to find the correct flightmaster and use it ## 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**) --- ## 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 --- src/Db/FlightMasterCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Db/FlightMasterCache.cpp b/src/Db/FlightMasterCache.cpp index c708e09cb8..effe249936 100644 --- a/src/Db/FlightMasterCache.cpp +++ b/src/Db/FlightMasterCache.cpp @@ -13,7 +13,7 @@ void FlightMasterCache::AddAllianceFlightMaster(uint32 entry, WorldPosition pos) Creature* FlightMasterCache::GetNearestFlightMaster(Player* bot) { std::map& flightMasterCache = - (bot->GetTeamId() == ALLIANCE) ? allianceFlightMasterCache : hordeFlightMasterCache; + (bot->GetTeamId() == TEAM_ALLIANCE) ? allianceFlightMasterCache : hordeFlightMasterCache; Creature* nearestFlightMaster = nullptr; float nearestDistance = std::numeric_limits::max(); From 00d19dbf9cbe1e0d5a75630ff7edf67424657bf6 Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 30 Jan 2026 14:52:35 -0600 Subject: [PATCH 30/42] Fix Destro Warlock Glyphs (#2084) Updates are only to the config. This PR should be simple. Tl;dr is destro pve spec is using the wrong glyphs. Longer explanation--right now, PreMadeSpecGlyph in the config provides for destro pve spec to use the following Major Glyphs at levels 15, 30, and 80, respectively: Life Tap, Quick Decay, Conflagrate. Quick Decay is useless for destro because destro does not cast Corruption except as a filler instant cast when on the move. Meanwhile, the spec is almost unplayable without Glyph of Conflagrate, so that should not be withheld until level 80. After Conflagrate, there are several viable glyphs, including Life Tap, Incinerate, Immolate, and Imp. I understand Glyph of Life Tap gets worse over time to the point that you don't want to use that glyph in ICC, but that's quite late, and it is useful for the vast majority of the game as a glyph that would actually be available at level 15. I also understand that Glyph of Immolate does not excel until high gear levels. Therefore, I decided to use Incinerate as the default level 80 glyph. The new order for default glyphs for destro pve for levels 15, 30, and 80 is Life Tap, Conflagrate, and Incinerate, respectively. I also made a couple of other very minor fixes in the config. No impact on performance or AI, obviously. Sidenote: Glyph of Conflagrate is not available at level 30--it requires level 40, so from 30 to 40, InitGlyphs() will plug in a random glyph for the second Major slot. This issue applies to many specs, and it's not avoidable unless InitGlyphs() is broken up into level brackets, which I think is not worthwhile. I think the better approach for glyphs is to ensure the right ones are applied at high levels, but with an attempt to make them usable at lower levels too where possible. --- conf/playerbots.conf.dist | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 81668a9559..22615b4071 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -544,8 +544,8 @@ AiPlayerbot.AutoGearQualityLimit = 3 # Max iLVL Phase 1(MC, Ony, ZG) = 78 | Phase 2(BWL) = 83 | Phase 2.5(AQ40) = 88 | Phase 3(Naxx40) = 92 # TBC # Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164 -# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164 -# Wotlk +# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 2(SSC, TK, ZA) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164 +# WotLK # Max iLVL Tier 7(10/25) = 200/213 | Tier 8(10/25) = 225/232 | Tier 9(10/25) = 232/245 | Tier 10(10/25/HC) = 251/264/290 # Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290 # Default: 0 (no limit) @@ -736,7 +736,7 @@ AiPlayerbot.RandomGearQualityLimit = 3 # TBC # Max iLVL Tier 4 = 120 | Tier 5 = 133 | Tier 6 = 164 # Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 2(SSC, TK, ZA) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164 -# Wotlk +# WotLK # Max iLVL Tier 7(10/25) = 200/213 | Tier 8(10/25) = 225/232 | Tier 9(10/25) = 232/245 | Tier 10(10/25/HC) = 251/264/290 # Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290 # Default: 0 (no limit) @@ -1624,7 +1624,7 @@ AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530135201051 AiPlayerbot.PremadeSpecLink.9.1.70 = -003203301135112530135201051-55 AiPlayerbot.PremadeSpecLink.9.1.80 = -003203301135112530135221351-55000005 AiPlayerbot.PremadeSpecName.9.2 = destro pve -AiPlayerbot.PremadeSpecGlyph.9.2 = 45785,43390,50077,43394,43393,42454 +AiPlayerbot.PremadeSpecGlyph.9.2 = 45785,43390,42454,43394,43393,45785 AiPlayerbot.PremadeSpecLink.9.2.60 = --05203215200231051305031151 AiPlayerbot.PremadeSpecLink.9.2.80 = 23-0302-05203215220331051335231351 AiPlayerbot.PremadeSpecName.9.3 = affli pvp From 36327235691aa891e7bd14a50a829c45650b613d Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Sat, 31 Jan 2026 01:13:50 +0100 Subject: [PATCH 31/42] Fix Fuc**** compil error --- src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp | 2 +- src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp | 2 +- src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp | 4 ++-- src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp index 7192685013..7cf5f41308 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Gluth.cpp @@ -55,7 +55,7 @@ bool GluthChooseTargetAction::Execute(Event event) for (Unit* t : target_zombies) { if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() == target_boss && - t->GetDistance2d(bot) <= sPlayerbotAIConfig->spellDistance) + t->GetDistance2d(bot) <= sPlayerbotAIConfig.spellDistance) { if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot)) { diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp index 336b5fe51c..1b991e96ef 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp @@ -47,7 +47,7 @@ bool KelthuzadChooseTargetAction::Execute(Event event) { continue; } - if (bot->GetDistance2d(unit) > sPlayerbotAIConfig->spellDistance) + if (bot->GetDistance2d(unit) > sPlayerbotAIConfig.spellDistance) { continue; } diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp index a16469bf15..276545bce0 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Razuvious.cpp @@ -127,9 +127,9 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event event) } if (target) { - if (bot->GetDistance2d(target) > sPlayerbotAIConfig->spellDistance) + if (bot->GetDistance2d(target) > sPlayerbotAIConfig.spellDistance) { - return MoveNear(target, sPlayerbotAIConfig->spellDistance, MovementPriority::MOVEMENT_COMBAT); + return MoveNear(target, sPlayerbotAIConfig.spellDistance, MovementPriority::MOVEMENT_COMBAT); } else { diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp index 65f7b0edd0..515ee680e0 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Thaddius.cpp @@ -72,7 +72,7 @@ bool ThaddiusMoveToPlatformAction::Execute(Event event) if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) { float distance = bot->GetExactDist2d(position[0].first, position[0].second); - if (distance < sPlayerbotAIConfig->contactDistance) + if (distance < sPlayerbotAIConfig.contactDistance) JumpTo(bot->GetMapId(), position[2].first, position[2].second, low_z, MovementPriority::MOVEMENT_COMBAT); // bot->TeleportTo(bot->GetMapId(), position[2].first, position[2].second, low_z, bot->GetOrientation()); } @@ -82,7 +82,7 @@ bool ThaddiusMoveToPlatformAction::Execute(Event event) if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT)) { float distance = bot->GetExactDist2d(position[1].first, position[1].second); - if (distance < sPlayerbotAIConfig->contactDistance) + if (distance < sPlayerbotAIConfig.contactDistance) JumpTo(bot->GetMapId(), position[3].first, position[3].second, low_z, MovementPriority::MOVEMENT_COMBAT); // bot->TeleportTo(bot->GetMapId(), position[3].first, position[3].second, low_z, bot->GetOrientation()); } From 9546363d41cb3cc81eecfc4b37f31eac081247af Mon Sep 17 00:00:00 2001 From: Crow Date: Sat, 31 Jan 2026 03:54:19 -0600 Subject: [PATCH 32/42] Hotfix for OnBotLoginOperation() Crash (#2089) Hotfix for an issue arising from https://github.com/mod-playerbots/mod-playerbots/pull/2082 OnBotLoginOperation() is calling OnBotLogin() twice for altbots. I don't know the full implication, but RandomPlayerbotMgr::OnBotLoginInternal() is being called on altbots, and the server will crash if you attempt to then log out the altbot. This fix works for me right now. Discussed with @Celandriel , going to push this hotfix for now until the rest of the maintainers can take a look. --- src/Script/WorldThr/PlayerbotOperations.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Script/WorldThr/PlayerbotOperations.h b/src/Script/WorldThr/PlayerbotOperations.h index fff44d2c29..4902070288 100644 --- a/src/Script/WorldThr/PlayerbotOperations.h +++ b/src/Script/WorldThr/PlayerbotOperations.h @@ -503,8 +503,10 @@ class OnBotLoginOperation : public PlayerbotOperation manager->OnBotLogin(bot); } } - - sRandomPlayerbotMgr.OnBotLogin(bot); + else + { + sRandomPlayerbotMgr.OnBotLogin(bot); + } return true; } From 6ee1684e9b75150aba84862d7f3f35af0d9b70f5 Mon Sep 17 00:00:00 2001 From: Crow Date: Sat, 31 Jan 2026 16:11:40 -0600 Subject: [PATCH 33/42] Fix WSG graveyard camping by flag carrier (#2086) Quick fix for a very annoying error identified by SmashingQuasar. In WSG, bots will camp the opposing graveyard if up 2-0. This is supposed to exclude the flag carrier, but a logical error has resulted in the flag carrier being excluded for Alliance camping only, meaning the Horde flag carrier will camp the GY with the rest of the team if up 2-0 and thus refuse to end the game. --- src/Ai/Base/Actions/BattleGroundTactics.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ai/Base/Actions/BattleGroundTactics.cpp b/src/Ai/Base/Actions/BattleGroundTactics.cpp index 1704150a30..2b198022e0 100644 --- a/src/Ai/Base/Actions/BattleGroundTactics.cpp +++ b/src/Ai/Base/Actions/BattleGroundTactics.cpp @@ -2227,8 +2227,8 @@ bool BGTactics::selectObjective(bool reset) } // Graveyard Camping if in lead else if (!hasFlag && role < 8 && - (team == TEAM_ALLIANCE && allianceScore == 2 && hordeScore == 0) || - (team == TEAM_HORDE && hordeScore == 2 && allianceScore == 0)) + ((team == TEAM_ALLIANCE && allianceScore == 2 && hordeScore == 0) || + (team == TEAM_HORDE && hordeScore == 2 && allianceScore == 0))) { if (team == TEAM_ALLIANCE) SetSafePos(WS_GY_CAMPING_HORDE, 10.0f); From 8529654f8fc4be92344bf079d65a215fea3b608c Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 1 Feb 2026 10:18:59 +0100 Subject: [PATCH 34/42] Correction after singleton PR (#2095) # Pull Request Some logic was changed but differs from the original code, certain edge cases so not result in same behavior. This returns the original code with only the singleton chances. @Wishmaster117 Reviewed the hotfix and noticed the different code paths. ps: reverted an removed placeholder since its ongoing issue/research. --- src/Script/Playerbots.cpp | 46 +++++++++++++++++++++++ src/Script/WorldThr/PlayerbotOperations.h | 32 +++++----------- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index 065ccb8719..c4487f8986 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -124,6 +124,52 @@ class PlayerbotsPlayerScript : public PlayerScript } } + bool OnPlayerBeforeTeleport(Player* /*player*/, uint32 /*mapid*/, float /*x*/, float /*y*/, float /*z*/, + float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override + { + /* for now commmented out until proven its actually required + * havent seen any proof CleanVisibilityReferences() is needed + + // If the player is not safe to touch, do nothing + if (!player) + return true; + + // If same map or not in world do nothing + if (!player->IsInWorld() || player->GetMapId() == mapid) + return true; + + // If real player do nothing + PlayerbotAI* ai = GET_PLAYERBOT_AI(player); + if (!ai || ai->IsRealPlayer()) + return true; + + // Cross-map bot teleport: defer visibility reference cleanup. + // CleanVisibilityReferences() erases this bot's GUID from other objects' visibility containers. + // This is intentionally done via the event queue (instead of directly here) because erasing + // from other players' visibility maps inside the teleport call stack can hit unsafe re-entrancy + // or iterator invalidation while visibility updates are in progress + ObjectGuid guid = player->GetGUID(); + player->m_Events.AddEventAtOffset( + [guid, mapid]() + { + // do nothing, if the player is not safe to touch + Player* p = ObjectAccessor::FindPlayer(guid); + if (!p || !p->IsInWorld() || p->IsDuringRemoveFromWorld()) + return; + + // do nothing if we are already on the target map + if (p->GetMapId() == mapid) + return; + + p->GetObjectVisibilityContainer().CleanVisibilityReferences(); + }, + Milliseconds(0)); + + */ + + return true; + } + void OnPlayerAfterUpdate(Player* player, uint32 diff) override { PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); diff --git a/src/Script/WorldThr/PlayerbotOperations.h b/src/Script/WorldThr/PlayerbotOperations.h index 4902070288..a80321d535 100644 --- a/src/Script/WorldThr/PlayerbotOperations.h +++ b/src/Script/WorldThr/PlayerbotOperations.h @@ -479,35 +479,23 @@ class OnBotLoginOperation : public PlayerbotOperation bool Execute() override { // find and verify bot still exists - Player* bot = ObjectAccessor::FindConnectedPlayer(this->m_botGuid); - + Player* bot = ObjectAccessor::FindConnectedPlayer(m_botGuid); if (!bot) - { return false; - } - if (this->m_masterAccountId) + PlayerbotHolder* holder = &RandomPlayerbotMgr::instance(); + if (m_masterAccountId) { - WorldSession* masterSession = sWorldSessionMgr->FindSession(this->m_masterAccountId); + WorldSession* masterSession = sWorldSessionMgr->FindSession(m_masterAccountId); Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr; - - if (masterPlayer != nullptr) - { - PlayerbotMgr* manager = PlayerbotsMgr::instance().GetPlayerbotMgr(masterPlayer); - - if (manager == nullptr) - { - return false; - } - - manager->OnBotLogin(bot); - } - } - else - { - sRandomPlayerbotMgr.OnBotLogin(bot); + if (masterPlayer) + holder = PlayerbotsMgr::instance().GetPlayerbotMgr(masterPlayer); } + if (!holder) + return false; + + holder->OnBotLogin(bot); return true; } From 2c915b3d277eef8c06cb320807c2000a29aeb801 Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Sun, 1 Feb 2026 16:39:18 +0100 Subject: [PATCH 35/42] Adjustments for KT and Saphiron 25 --- .../Action/RaidNaxxActions_Kelthuzad.cpp | 173 +++++--- .../Action/RaidNaxxActions_Sapphiron.cpp | 46 +- .../Multiplier/RaidNaxxMultipliers.cpp | 57 ++- src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h | 396 +++++++++++++++++- src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h | 6 +- 5 files changed, 590 insertions(+), 88 deletions(-) diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp index 1b991e96ef..a59798032c 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Kelthuzad.cpp @@ -1,6 +1,7 @@ #include "RaidNaxxActions.h" #include +#include #include "PlayerbotAIConfig.h" #include "Playerbots.h" @@ -11,38 +12,20 @@ bool KelthuzadChooseTargetAction::Execute(Event event) { return false; } - GuidVector attackers = context->GetValue("attackers")->Get(); + GuidVector attackers = context->GetValue("possible targets")->Get(); Unit* target = nullptr; Unit *target_soldier = nullptr, *target_weaver = nullptr, *target_abomination = nullptr, *target_kelthuzad = nullptr, *target_guardian = nullptr; + + bool isOffTankForKT = botAI->IsTank(bot) && !botAI->IsMainTank(bot) && + (botAI->IsAssistTank(bot) || botAI->HasStrategy("tank assist", BOT_STATE_COMBAT)); + for (auto i = attackers.begin(); i != attackers.end(); ++i) { Unit* unit = botAI->GetUnit(*i); if (!unit) continue; - if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown")) - { - if (!target_guardian) - { - target_guardian = unit; - } - else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() && - target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) && - botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer())) - { - target_guardian = unit; - } - else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() && - target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) && - !botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer()) && - target_guardian->GetDistance2d(helper.center.first, helper.center.second) > - bot->GetDistance2d(unit)) - { - target_guardian = unit; - } - } - if (unit->GetDistance2d(helper.center.first, helper.center.second) > 30.0f) { continue; @@ -82,21 +65,31 @@ bool KelthuzadChooseTargetAction::Execute(Event event) target_kelthuzad = unit; } } + + std::vector guardians = helper.GetGuardians(); + bool guardiansPresent = !guardians.empty(); + if (isOffTankForKT && guardiansPresent) + { + target_guardian = helper.GetGuardianToPickup(bot); + } std::vector targets; if (botAI->IsRanged(bot)) { - if (helper.IsPhaseTwo()) - { + bool hasRemainingP1Adds = (target_weaver || target_soldier || target_abomination); + + if (helper.IsPhaseTwo() && hasRemainingP1Adds && !botAI->IsHeal(bot)) + targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; + else if (helper.IsPhaseTwo()) targets = {target_kelthuzad, target_weaver, target_soldier, target_abomination}; - } else - { targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad}; - } } - else if (botAI->IsAssistTank(bot)) + else if (isOffTankForKT) { - targets = {target_abomination, target_guardian, target_kelthuzad}; + if (guardiansPresent) + targets = {target_guardian}; + else + targets = {target_abomination, target_kelthuzad}; } else { @@ -106,7 +99,12 @@ bool KelthuzadChooseTargetAction::Execute(Event event) { if (!botAI->IsRanged(bot)) { - if (t && t->GetDistance2d(helper.center.first, helper.center.second) > 20.0f) + float maxCenterDist = 20.0f; + + if (isOffTankForKT && guardiansPresent) + maxCenterDist = KelthuzadBossHelper::ROOM_MAX_RADIUS + 2.0f; + + if (t && t->GetDistance2d(helper.center.first, helper.center.second) > maxCenterDist) { continue; } @@ -134,8 +132,36 @@ bool KelthuzadPositionAction::Execute(Event event) { return false; } + + helper.RecallControlledPetsToBot(); + if (helper.IsPhaseOne()) { + if (botAI->IsTank(bot)) + { + float dx = helper.center.first; + float dy = helper.center.second; + + helper.ClampToRoom(dx, dy, + KelthuzadBossHelper::PHASE1_TANK_HOLD_RADIUS, + KelthuzadBossHelper::PHASE1_TANK_HOLD_RADIUS); + + if (bot->GetDistance2d(helper.center.first, helper.center.second) > KelthuzadBossHelper::PHASE1_TANK_MAX_RADIUS) + { + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (currentTarget && + currentTarget->GetDistance2d(helper.center.first, helper.center.second) <= KelthuzadBossHelper::PHASE1_TANK_MAX_RADIUS) + { + if (bot->GetDistance2d(currentTarget) > 3.0f) + return MoveNear(currentTarget, 3.0f, MovementPriority::MOVEMENT_COMBAT); + } + + return false; + } if (bot->GetDistance2d(helper.center.first, helper.center.second) > 20.0f) { return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f, @@ -164,10 +190,15 @@ bool KelthuzadPositionAction::Execute(Event event) float angle = helper.center.first == bot->GetPositionX() && helper.center.second == bot->GetPositionY() ? 0.0f : bot->GetAngle(helper.center.first, helper.center.second) + M_PI; - float spreadDistance = std::max(35.0f, bot->GetDistance2d(helper.center.first, helper.center.second) + 10.0f); - float dx = helper.center.first + cos(angle) * spreadDistance; - float dy = helper.center.second + sin(angle) * spreadDistance; - return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + float currentDist = bot->GetDistance2d(helper.center.first, helper.center.second); + float spreadDistance = std::clamp(currentDist + 12.0f, + KelthuzadBossHelper::DETONATE_MIN_RADIUS, + KelthuzadBossHelper::DETONATE_MAX_RADIUS); + float dx = helper.center.first + std::cos(angle) * spreadDistance; + float dy = helper.center.second + std::sin(angle) * spreadDistance; + helper.ClampToRoom(dx, dy, KelthuzadBossHelper::DETONATE_MIN_RADIUS, KelthuzadBossHelper::DETONATE_MAX_RADIUS); + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); } if (helper.HasChains(bot)) { @@ -180,18 +211,22 @@ bool KelthuzadPositionAction::Execute(Event event) float angle = frostBlastTarget->GetAngle(bot); float dx = frostBlastTarget->GetPositionX() + cos(angle) * 8.0f; float dy = frostBlastTarget->GetPositionY() + sin(angle) * 8.0f; + helper.ClampToRoom(dx, dy); return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } Unit* shadow_fissure = helper.GetAnyShadowFissure(); if (!shadow_fissure || !bot->IsWithinDistInMap(shadow_fissure, 10.0f)) { - float distance, angle; + bool isOffTankForKT = botAI->IsTank(bot) && !botAI->IsMainTank(bot) && + (botAI->IsAssistTank(bot) || botAI->HasStrategy("tank assist", BOT_STATE_COMBAT)); + if (botAI->IsMainTank(bot)) { if (AI_VALUE2(bool, "has aggro", "current target")) { - return MoveTo(NAXX_MAP_ID, helper.tank_pos.first, helper.tank_pos.second, bot->GetPositionZ(), false, false, false, - false, MovementPriority::MOVEMENT_COMBAT); + auto hold = helper.GetMainTankHoldPosition(); + return MoveTo(NAXX_MAP_ID, hold.first, hold.second, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); } else { @@ -200,46 +235,47 @@ bool KelthuzadPositionAction::Execute(Event event) } else if (botAI->IsRanged(bot)) { - uint32 index = botAI->GetRangedIndex(bot); - if (index < 8) - { - distance = 20.0f; - angle = index * M_PI / 4; - } - else - { - distance = 32.0f; - angle = (index - 8) * M_PI / 4; - } float dx, dy; - dx = helper.center.first + cos(angle) * distance; - dy = helper.center.second + sin(angle) * distance; + uint32 index = botAI->GetRangedIndex(bot); + uint32 total = std::max(1, helper.GetRangedCount()); + helper.ComputeRangedSpreadPosition(index, total, dx, dy); if (bot->GetDistance2d(dx, dy) <= 2.0f) { return false; } - return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); } else if (botAI->IsTank(bot)) { - Unit* guardian = helper.GetGuardian(); - if (guardian && botAI->IsAssistTank(bot)) + if (isOffTankForKT) { - return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, bot->GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - Unit* cur_tar = AI_VALUE(Unit*, "current target"); - if (cur_tar && cur_tar->GetVictim() && cur_tar->GetVictim()->ToPlayer() && - botAI->EqualLowercaseName(cur_tar->GetName(), "guardian of icecrown") && - botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer())) - { - return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, bot->GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - else - { - return false; + std::vector guardians = helper.GetGuardians(); + if (!guardians.empty()) + { + Unit* pickup = helper.GetGuardianToPickup(bot); + if (pickup && pickup->GetVictim() != bot) + { + if (bot->GetDistance2d(pickup) > 6.0f) + { + return MoveNear(pickup, 4.0f, MovementPriority::MOVEMENT_COMBAT); + } + return false; + } + if (helper.AllGuardiansOnAssistTank(bot)) + { + auto hold = helper.GetAssistTankHoldPosition(); + if (bot->GetDistance2d(hold.first, hold.second) > 3.0f) + { + return MoveTo(NAXX_MAP_ID, hold.first, hold.second, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + return false; + } } + + return false; } } else @@ -256,6 +292,7 @@ bool KelthuzadPositionAction::Execute(Event event) } dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f; dy = shadow_fissure->GetPositionY() + sin(angle) * 10.0f; + helper.ClampToRoom(dx, dy); return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } } diff --git a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp index eba20f956c..082c6206ea 100644 --- a/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp +++ b/src/Ai/Raid/Naxxramas/Action/RaidNaxxActions_Sapphiron.cpp @@ -142,8 +142,12 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt() { return false; } - Player* playerWithIcebolt = nullptr; - float minDistance = std::numeric_limits::max(); + /*Player* playerWithIcebolt = nullptr; + float minDistance = std::numeric_limits::max();*/ + std::vector icebolts; + icebolts.reserve(5); + Player* nearest = nullptr; + float nearestDist = std::numeric_limits::max(); for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); @@ -154,15 +158,43 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt() if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) || botAI->HasAura("icebolt", member, false, false, -1, true)) { - if (!playerWithIcebolt || minDistance > bot->GetDistance(member)) + //if (!playerWithIcebolt || minDistance > bot->GetDistance(member)) + icebolts.push_back(member); + float d = bot->GetDistance(member); + if (!nearest || d < nearestDist) { - playerWithIcebolt = member; - minDistance = bot->GetDistance(member); + /*playerWithIcebolt = member; + minDistance = bot->GetDistance(member);*/ + nearest = member; + nearestDist = d; } } } - if (playerWithIcebolt) + //if (playerWithIcebolt) + if (!icebolts.empty()) { + // In 25-man the core may spawn only 3 iceblocks, so "nearest" can over-stack one block. + // Use a stable round-robin assignment by group slot to spread bots across available blocks, + // but keep a safety fallback to nearest if the assigned block is too far away. + std::sort(icebolts.begin(), icebolts.end(), [](Player* a, Player* b) + { + return a->GetGUID().GetRawValue() < b->GetGUID().GetRawValue(); + }); + + int32 slotIndex = botAI->GetGroupSlotIndex(bot); + Player* assigned = icebolts[std::max(0, slotIndex) % icebolts.size()]; + Player* playerWithIcebolt = assigned ? assigned : nearest; + + if (playerWithIcebolt && nearest && playerWithIcebolt != nearest) + { + float assignedDist = bot->GetDistance(playerWithIcebolt); + // If the assignment sends the bot across the room, prefer nearest to reduce LOS failures. + if (assignedDist > nearestDist + 20.0f) + { + playerWithIcebolt = nearest; + } + } + constexpr float shelterDistance = 9.0f; constexpr float shelterEpsilon = 0.35f; constexpr float lateralOffset = 1.5f; @@ -170,7 +202,7 @@ bool SapphironFlightPositionAction::MoveToNearestIcebolt() float posX = playerWithIcebolt->GetPositionX() + cos(angle) * shelterDistance; float posY = playerWithIcebolt->GetPositionY() + sin(angle) * shelterDistance; float posZ = playerWithIcebolt->GetPositionZ(); - int32 slotIndex = botAI->GetGroupSlotIndex(bot); + //int32 slotIndex = botAI->GetGroupSlotIndex(bot); float offsetSign = (slotIndex % 2 == 0) ? 1.0f : -1.0f; float offsetAngle = angle + (M_PI / 2.0f); float offsetX = cos(offsetAngle) * lateralOffset * offsetSign; diff --git a/src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.cpp b/src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.cpp index e4a4b35165..021b3aeb0a 100644 --- a/src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.cpp +++ b/src/Ai/Raid/Naxxramas/Multiplier/RaidNaxxMultipliers.cpp @@ -258,6 +258,43 @@ float KelthuzadGenericMultiplier::GetValue(Action* action) { return 1.0f; } + + bool guardiansPresent = !helper.GetGuardians().empty(); + bool isOffTankForKT = botAI->IsTank(bot) && !botAI->IsMainTank(bot) && + (botAI->IsAssistTank(bot) || botAI->HasStrategy("tank assist", BOT_STATE_COMBAT)); + + if (dynamic_cast(action)) + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return 0.0f; + + if (!helper.IsWithinRoom(target, KelthuzadBossHelper::ROOM_MAX_RADIUS)) + return 0.0f; + + if (Unit* pet = bot->GetPet()) + if (!helper.IsWithinRoom(pet, KelthuzadBossHelper::ROOM_MAX_RADIUS + 2.0f)) + return 0.0f; + if (Unit* guardianPet = bot->GetGuardianPet()) + if (!helper.IsWithinRoom(guardianPet, KelthuzadBossHelper::ROOM_MAX_RADIUS + 2.0f)) + return 0.0f; + + return 1.0f; + } + + if (helper.IsPhaseTwo() && helper.IsBossCasting(NaxxSpellIds::FrostBoltSingle)) + { + std::string const name = action->getName(); + if (name == "kick" || name == "pummel" || name == "shield bash" || + name == "mind freeze" || name == "strangulate" || + name == "counterspell" || name == "wind shear" || + name == "spell lock" || name == "silencing shot" || + name == "bash" || name == "hammer of justice") + { + return 5.0f; + } + } + if (helper.HasChains(bot)) { if (dynamic_cast(action)) @@ -301,8 +338,24 @@ float KelthuzadGenericMultiplier::GetValue(Action* action) } return 0.0f; } - if ((dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action))) + + if (dynamic_cast(action)) + { + if (isOffTankForKT && guardiansPresent) + { + return 2.0f; + } + return 1.0f; + } + + if (isOffTankForKT && guardiansPresent) + { + if (dynamic_cast(action)) + return 1.0f; + } + + if ((dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) { return 0.0f; } diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h index 50e8388547..1addad1623 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxBossHelper.h @@ -1,14 +1,21 @@ #ifndef _PLAYERBOT_RAIDNAXXBOSSHELPER_H #define _PLAYERBOT_RAIDNAXXBOSSHELPER_H +#include +#include +#include #include +#include #include "AiObject.h" #include "AiObjectContext.h" #include "EventMap.h" #include "Log.h" +#include "MotionMaster.h" #include "NamedObjectContext.h" #include "ObjectGuid.h" +#include "ObjectAccessor.h" +#include "Pet.h" #include "Player.h" #include "PlayerbotAI.h" #include "Playerbots.h" @@ -87,9 +94,31 @@ class KelthuzadBossHelper : public AiObject { public: KelthuzadBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {} + + static constexpr uint32 NPC_GUARDIAN_OF_ICECROWN = 16441; + + bool IsGuardian(Unit* unit) const + { + if (!unit) + return false; + if (Creature* c = unit->ToCreature()) + if (c->GetEntry() == NPC_GUARDIAN_OF_ICECROWN) + return true; + return botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown"); + } + const std::pair center = {3716.19f, -5106.58f}; const std::pair tank_pos = {3709.19f, -5104.86f}; const std::pair assist_tank_pos = {3746.05f, -5112.74f}; + + static constexpr float ROOM_MIN_RADIUS = 6.0f; + static constexpr float ROOM_MAX_RADIUS = 24.0f; + static constexpr float DETONATE_MIN_RADIUS = 20.0f; + static constexpr float DETONATE_MAX_RADIUS = 24.0f; + static constexpr float TANK_HOLD_MAX_RADIUS = 20.0f; + static constexpr float PHASE1_TANK_MAX_RADIUS = 16.0f; + static constexpr float PHASE1_TANK_HOLD_RADIUS = 12.0f; + bool UpdateBossAI() { if (!bot->IsInCombat()) @@ -108,6 +137,188 @@ class KelthuzadBossHelper : public AiObject } bool IsPhaseOne() { return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } bool IsPhaseTwo() { return _unit && !_unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } + + Unit* GetBoss() const { return _unit; } + + bool IsBossCasting(uint32 spellId) const + { + if (!_unit) + { + return false; + } + + if (Spell* spell = _unit->GetCurrentSpell(CURRENT_GENERIC_SPELL)) + { + if (SpellInfo const* info = spell->GetSpellInfo()) + { + return info->Id == spellId; + } + } + return false; + } + + uint32 GetRangedCount() const + { + Group* group = bot->GetGroup(); + if (!group) + { + return 0; + } + + uint32 count = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + { + continue; + } + + if (botAI->IsRanged(member)) + { + ++count; + } + } + return count; + } + + void ClampToRoom(float& x, float& y, float minRadius = ROOM_MIN_RADIUS, float maxRadius = ROOM_MAX_RADIUS) const + { + float dx = x - center.first; + float dy = y - center.second; + float r2 = dx * dx + dy * dy; + if (r2 < 0.0001f) + { + x = center.first + minRadius; + y = center.second; + return; + } + + float r = std::sqrt(r2); + float clamped = std::clamp(r, minRadius, maxRadius); + x = center.first + dx / r * clamped; + y = center.second + dy / r * clamped; + } + + bool IsWithinRoom(WorldObject const* obj, float maxRadius = ROOM_MAX_RADIUS) const + { + return obj && obj->GetDistance2d(center.first, center.second) <= maxRadius; + } + + bool RecallControlledPetsToBot(float leashRadius = (ROOM_MAX_RADIUS + 2.0f), float followDist = 1.5f) + { + bool recalled = false; + + auto RecallUnit = [&](Unit* u) + { + if (!u) + return; + + Creature* creature = u->ToCreature(); + if (!creature) + return; + + if (creature->IsTotem()) + return; + + if (creature->GetDistance2d(center.first, center.second) <= leashRadius) + return; + + creature->AttackStop(); + + if (CharmInfo* charm = creature->GetCharmInfo()) + { + charm->SetIsCommandAttack(false); + charm->SetIsAtStay(false); + charm->SetIsFollowing(true); + charm->SetIsCommandFollow(true); + charm->SetIsReturning(false); + } + + creature->GetMotionMaster()->MoveFollow(bot, followDist, M_PI); + recalled = true; + }; + + RecallUnit(bot->GetPet()); + + for (Unit::ControlSet::const_iterator itr = bot->m_Controlled.begin(); itr != bot->m_Controlled.end(); ++itr) + { + RecallUnit(*itr); + } + + return recalled; + } + + std::pair GetAssistTankHoldPosition() const + { + float x = assist_tank_pos.first; + float y = assist_tank_pos.second; + ClampToRoom(x, y, ROOM_MIN_RADIUS, TANK_HOLD_MAX_RADIUS); + return {x, y}; + } + + std::pair GetMainTankHoldPosition() const + { + float x = tank_pos.first; + float y = tank_pos.second; + ClampToRoom(x, y, ROOM_MIN_RADIUS, TANK_HOLD_MAX_RADIUS); + return {x, y}; + } + + void ComputeRangedSpreadPosition(uint32 index, uint32 total, float& outX, float& outY) const + { + if (total == 0) + { + outX = center.first; + outY = center.second; + return; + } + + float radii[3] = {18.0f, 21.0f, 24.0f}; + uint32 ringSizes[3] = {0, 0, 0}; + uint32 rings = 1; + + if (total <= 10) + { + rings = 1; + ringSizes[0] = total; + } + else if (total <= 18) + { + rings = 2; + ringSizes[0] = (total + 1) / 2; + ringSizes[1] = total - ringSizes[0]; + } + else + { + rings = 3; + ringSizes[0] = (total + 2) / 3; + ringSizes[1] = (total + 1) / 3; + ringSizes[2] = total - ringSizes[0] - ringSizes[1]; + } + + uint32 ring = 0; + uint32 localIndex = index; + for (uint32 r = 0; r < rings; ++r) + { + if (localIndex < ringSizes[r]) + { + ring = r; + break; + } + localIndex -= ringSizes[r]; + } + + uint32 slots = std::max(1, ringSizes[ring]); + float angle = 2.0f * float(M_PI) * (float(localIndex) / float(slots)); + + angle += float(ring) * (float(M_PI) / 8.0f); + + outX = center.first + std::cos(angle) * radii[ring]; + outY = center.second + std::sin(angle) * radii[ring]; + ClampToRoom(outX, outY); + } + Player* GetPlayerWithAura(uint32 spellId) { Group* group = bot->GetGroup(); @@ -146,23 +357,178 @@ class KelthuzadBossHelper : public AiObject } return botAI->HasAura(NaxxSpellIds::ChainsOfKelthuzad, player); } + + std::vector GetGuardians() const + { + std::vector guardians; + GuidVector targets = context->GetValue("possible targets")->Get(); + for (ObjectGuid const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit) + { + continue; + } + + if (!IsGuardian(unit)) + { + continue; + } + + if (unit->GetDistance2d(center.first, center.second) > (ROOM_MAX_RADIUS + 4.0f)) + { + continue; + } + + guardians.push_back(unit); + } + + return guardians; + } + + bool AllGuardiansOnAssistTank(Player* assistTank) const + { + if (!assistTank) + { + return true; + } + + std::vector guardians = GetGuardians(); + if (guardians.empty()) + { + return true; + } + + for (Unit* g : guardians) + { + if (!g) + { + continue; + } + + if (g->GetVictim() != assistTank) + { + return false; + } + } + return true; + } + + Unit* GetGuardianToPickup(Player* assistTank) const + { + if (!assistTank) + { + return nullptr; + } + + std::vector guardians = GetGuardians(); + if (guardians.empty()) + { + return nullptr; + } + + Unit* best = nullptr; + float bestDist = std::numeric_limits::max(); + for (Unit* g : guardians) + { + if (!g) + { + continue; + } + + if (g->GetVictim() == assistTank) + { + continue; + } + + float d = assistTank->GetDistance2d(g); + if (!best || d < bestDist) + { + best = g; + bestDist = d; + } + } + if (!best) + { + for (Unit* g : guardians) + { + float d = assistTank->GetDistance2d(g); + if (!best || d < bestDist) + { + best = g; + bestDist = d; + } + } + } + + return best; + } + Unit* GetGuardian() { - GuidVector attackers = context->GetValue("attackers")->Get(); - for (auto i = attackers.begin(); i != attackers.end(); ++i) + GuidVector targets = context->GetValue("possible targets")->Get(); + for (auto i = targets.begin(); i != targets.end(); ++i) { Unit* unit = botAI->GetUnit(*i); if (!unit) { continue; } - if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown")) + if (IsGuardian(unit)) { return unit; } } return nullptr; } + + Unit* GetGuardianForAssistTank(Player* assistTank) + { + if (!assistTank) + { + return nullptr; + } + + GuidVector targets = context->GetValue("possible targets")->Get(); + Unit* best = nullptr; + float bestScore = std::numeric_limits::max(); + + for (ObjectGuid const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit) + { + continue; + } + + if (!IsGuardian(unit)) + { + continue; + } + if (unit->GetDistance2d(center.first, center.second) > (ROOM_MAX_RADIUS + 4.0f)) + { + continue; + } + + Player* victimPlayer = unit->GetVictim() ? unit->GetVictim()->ToPlayer() : nullptr; + bool victimIsAssistTank = victimPlayer && botAI->IsAssistTank(victimPlayer); + + float score = unit->GetDistance2d(assistTank); + if (victimIsAssistTank) + { + score += 1000.0f; + } + + if (!best || score < bestScore) + { + best = unit; + bestScore = score; + } + } + + return best; + } + Unit* GetAnyShadowFissure() { Unit* shadow_fissure = nullptr; @@ -308,15 +674,26 @@ class SapphironBossHelper : public AiObject Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25}); if (!aura) { - // Fallback to name for custom spell data. aura = botAI->GetAura("chill", bot); } if (!aura) { return false; } - DynamicObject* dyn_obj = aura->GetDynobjOwner(); - if (!dyn_obj) + /*DynamicObject* dyn_obj = aura->GetDynobjOwner(); + if (!dyn_obj)*/ + // Prefer the dynobject (classic ground effect), but keep a fallback for cases where + // the aura is applied by a moving caster (e.g. Blizzard NPC) without a dynobject. + WorldObject* source = aura->GetDynobjOwner(); + if (!source) + { + //return false; + if (Unit* caster = ObjectAccessor::GetUnit(*bot, aura->GetCasterGUID())) + { + source = caster; + } + } + if (!source) { return false; } @@ -329,7 +706,7 @@ class SapphironBossHelper : public AiObject { if (bot->GetExactDist2d(currentTarget) <= 45.0f) { - angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2; + angle = bot->GetAngle(source) - M_PI + (rand_norm() - 0.5) * M_PI / 2; } else { @@ -361,7 +738,7 @@ class SapphironBossHelper : public AiObject } else { - angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2; + angle = bot->GetAngle(source) - M_PI + (rand_norm() - 0.5) * M_PI / 2; } dest = {bot->GetPositionX() + cos(angle) * 5.0f, bot->GetPositionY() + sin(angle) * 5.0f, bot->GetPositionZ()}; return true; @@ -510,7 +887,7 @@ class GluthBossHelper : public AiObject { return true; } - // Fallback to name for custom spell data. + return info->SpellName[LOCALE_enUS] && botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "decimate"); } bool JustStartCombat() const { return _combat_start_ms != 0 && getMSTime() - _combat_start_ms < 10000; } @@ -593,7 +970,6 @@ class NothBossHelper : public AiObject bool isBlink = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Blink}); if (!isBlink && info && info->SpellName[LOCALE_enUS]) { - // Fallback to name for custom spell data. isBlink = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "blink"); } if (isBlink) diff --git a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h index eccf7159fe..aee430e2c7 100644 --- a/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h +++ b/src/Ai/Raid/Naxxramas/RaidNaxxSpellIds.h @@ -82,7 +82,8 @@ namespace NaxxSpellIds // Sapphiron static constexpr uint32 Icebolt10 = 28522; static constexpr uint32 Icebolt25 = 28526; - static constexpr uint32 Chill25 = 55699; + //static constexpr uint32 Chill25 = 55699; + static constexpr uint32 Chill25 = 28547; static constexpr uint32 LifeDrain = 28542; static constexpr uint32 FrostMissile = 30101; static constexpr uint32 FrostExplosion = 28524; @@ -91,6 +92,9 @@ namespace NaxxSpellIds static constexpr uint32 FrostBlast = 27808; static constexpr uint32 DetonateMana = 27819; static constexpr uint32 ChainsOfKelthuzad = 28410; + static constexpr uint32 ShadowFissure = 27810; + static constexpr uint32 FrostBoltSingle = 28478; + static constexpr uint32 FrostBoltMulti = 28479; // Gluth static constexpr uint32 Decimate10 = 28374; From cafb95e7bd2b5c1297082a23d012d1caab671449 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:21:57 +0100 Subject: [PATCH 36/42] Create check_pr_source.yml (#2098) --- check_pr_source.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 check_pr_source.yml diff --git a/check_pr_source.yml b/check_pr_source.yml new file mode 100644 index 0000000000..b0ead9c8fe --- /dev/null +++ b/check_pr_source.yml @@ -0,0 +1,19 @@ +name: Enforce test-staging → main + +on: + pull_request: + branches: + - main + +jobs: + require-test-staging: + runs-on: ubuntu-22.04 + steps: + - name: Ensure PR source is test-staging + run: | + echo "Base: ${{ github.event.pull_request.base.ref }}" + echo "Head: ${{ github.event.pull_request.head.ref }}" + if [ "${{ github.event.pull_request.head.ref }}" != "test-staging" ]; then + echo "✖ Pull request must come from test-staging" + exit 1 + fi From 8e316cd32165aaecf17ec8f00c7d2ad9977f59f6 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:26:07 +0100 Subject: [PATCH 37/42] fix (#2099) --- check_pr_source.yml => .github/workflows/check_pr_source.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename check_pr_source.yml => .github/workflows/check_pr_source.yml (100%) diff --git a/check_pr_source.yml b/.github/workflows/check_pr_source.yml similarity index 100% rename from check_pr_source.yml rename to .github/workflows/check_pr_source.yml From 8c2a27b9fe5f7cd63fae49dd02b1c4d7a7105829 Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Sun, 1 Feb 2026 22:41:49 +0100 Subject: [PATCH 38/42] Update check_pr_source.yml (#2101) --- .github/workflows/check_pr_source.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_pr_source.yml b/.github/workflows/check_pr_source.yml index b0ead9c8fe..877ab85c73 100644 --- a/.github/workflows/check_pr_source.yml +++ b/.github/workflows/check_pr_source.yml @@ -3,7 +3,7 @@ name: Enforce test-staging → main on: pull_request: branches: - - main + - master jobs: require-test-staging: From ba835250c8dc8b99993ccfd107ce1c0deff7f40d Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:40:12 +0100 Subject: [PATCH 39/42] New whisper command "pvp stats" that allows players to ask a bot to report its current Arena Points, Honor Points, and Arena Teams (#2071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Pull Request This PR adds a new whisper command "pvp stats" that allows players to ask a bot to report its current Arena Points, Honor Points, and Arena Teams (name and team rating). Reason: Due to a client limitation in WoW 3.3.5a, the inspection window does not display another player's Arena or Honor points , only team data. This command provides an easy in-game way to check a bot’s PvP currencies without modifying the client or core packets. --- ## Design Philosophy Uses existing core getters (GetArenaPoints, GetHonorPoints, GetArenaTeamId, etc.). Fully integrated into the chat command system (ChatTriggerContext, ChatActionContext). Safe, no gameplay changes, purely informational. No harcoded texts, use database local instead --- ## How to Test the Changes /w BotName pvp stats Bot reply: [PVP] Arena Points: 302 | Honor Points: 11855 [PVP] 2v2: (rating 2000) [PVP] 3v3: (rating 573) ## 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**) --- ## 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 --- Multibot already ready Here is a sample of multibot when merged: image --- ## Notes for Reviewers Anything that significantly improves realism at the cost of stability or performance should be carefully discussed before merging. --------- Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com> --- ..._ai_playerbot_multi_pvp_text_variables.sql | 101 ++++++++++++++++++ src/Ai/Base/Actions/TellPvpStatsAction.cpp | 100 +++++++++++++++++ src/Ai/Base/Actions/TellPvpStatsAction.h | 20 ++++ src/Ai/Base/ChatActionContext.h | 3 + src/Ai/Base/ChatTriggerContext.h | 2 + .../Strategy/ChatCommandHandlerStrategy.cpp | 2 + src/Bot/PlayerbotAI.cpp | 1 + 7 files changed, 229 insertions(+) create mode 100644 data/sql/playerbots/updates/2026_01_31_00_ai_playerbot_multi_pvp_text_variables.sql create mode 100644 src/Ai/Base/Actions/TellPvpStatsAction.cpp create mode 100644 src/Ai/Base/Actions/TellPvpStatsAction.h diff --git a/data/sql/playerbots/updates/2026_01_31_00_ai_playerbot_multi_pvp_text_variables.sql b/data/sql/playerbots/updates/2026_01_31_00_ai_playerbot_multi_pvp_text_variables.sql new file mode 100644 index 0000000000..9889147f60 --- /dev/null +++ b/data/sql/playerbots/updates/2026_01_31_00_ai_playerbot_multi_pvp_text_variables.sql @@ -0,0 +1,101 @@ +-- ######################################################### +-- Playerbots - Add PVP / Arena texts for TellPvpAction +-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN, +-- zhTW, esES, esMX, ruRU) +-- ######################################################### + +-- --------------------------------------------------------- +-- pvp_currency +-- [PVP] Arena points: %arena_points | Honor Points: %honor_points +-- --------------------------------------------------------- +INSERT INTO `ai_playerbot_texts` + (`name`, `text`, `say_type`, `reply_type`, + `text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`, + `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`) +SELECT + 'pvp_currency', + '[PVP] Arena points: %arena_points | Honor Points: %honor_points', + 0, 0, + -- koKR + '[PVP] 투기장 점수: %arena_points | 명예 점수: %honor_points', + -- frFR + '[PVP] Points d''arène : %arena_points | Points d''honneur : %honor_points', + -- deDE + '[PVP] Arenapunkte: %arena_points | Ehrenpunkte: %honor_points', + -- zhCN + '[PVP] 竞技场点数:%arena_points | 荣誉点数:%honor_points', + -- zhTW + '[PVP] 競技場點數:%arena_points | 榮譽點數:%honor_points', + -- esES + '[PVP] Puntos de arena: %arena_points | Puntos de honor: %honor_points', + -- esMX + '[PVP] Puntos de arena: %arena_points | Puntos de honor: %honor_points', + -- ruRU + '[PVP] Очки арены: %arena_points | Очки чести: %honor_points' +WHERE NOT EXISTS ( + SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_currency' +); + +-- --------------------------------------------------------- +-- pvp_arena_team +-- [PVP] %bracket: <%team_name> (rating %team_rating) +-- --------------------------------------------------------- +INSERT INTO `ai_playerbot_texts` + (`name`, `text`, `say_type`, `reply_type`, + `text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`, + `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`) +SELECT + 'pvp_arena_team', + '[PVP] %bracket: <%team_name> (rating %team_rating)', + 0, 0, + -- koKR + '[PVP] %bracket: <%team_name> (평점 %team_rating)', + -- frFR + '[PVP] %bracket : <%team_name> (cote %team_rating)', + -- deDE + '[PVP] %bracket: <%team_name> (Wertung %team_rating)', + -- zhCN + '[PVP] %bracket: <%team_name> (评分 %team_rating)', + -- zhTW + '[PVP] %bracket: <%team_name> (評分 %team_rating)', + -- esES + '[PVP] %bracket: <%team_name> (índice %team_rating)', + -- esMX + '[PVP] %bracket: <%team_name> (índice %team_rating)', + -- ruRU + '[PVP] %bracket: <%team_name> (рейтинг %team_rating)' +WHERE NOT EXISTS ( + SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_arena_team' +); + +-- --------------------------------------------------------- +-- pvp_no_arena_team +-- [PVP] I have no Arena Team. +-- --------------------------------------------------------- +INSERT INTO `ai_playerbot_texts` + (`name`, `text`, `say_type`, `reply_type`, + `text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`, + `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`) +SELECT + 'pvp_no_arena_team', + '[PVP] I have no Arena Team.', + 0, 0, + -- koKR + '[PVP] 투기장 팀이 없습니다.', + -- frFR + '[PVP] Je n''ai aucune équipe d''arène.', + -- deDE + '[PVP] Ich habe kein Arenateam.', + -- zhCN + '[PVP] 我没有竞技场战队。', + -- zhTW + '[PVP] 我沒有競技場隊伍。', + -- esES + '[PVP] No tengo equipo de arena.', + -- esMX + '[PVP] No tengo equipo de arena.', + -- ruRU + '[PVP] У меня нет команды арены.' +WHERE NOT EXISTS ( + SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_no_arena_team' +); diff --git a/src/Ai/Base/Actions/TellPvpStatsAction.cpp b/src/Ai/Base/Actions/TellPvpStatsAction.cpp new file mode 100644 index 0000000000..943cf415c0 --- /dev/null +++ b/src/Ai/Base/Actions/TellPvpStatsAction.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "TellPvpStatsAction.h" + +#include + +#include "ArenaTeam.h" +#include "ArenaTeamMgr.h" +#include "Event.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "PlayerbotTextMgr.h" +#include "Playerbots.h" +#include "SharedDefines.h" +#include "Language.h" + +namespace +{ + inline char const* BracketName(uint8 slot) + { + switch (slot) + { + case ARENA_SLOT_2v2: return "2v2"; + case ARENA_SLOT_3v3: return "3v3"; + default: return "5v5"; // ARENA_SLOT_5v5 + } + } +} + +bool TellPvpStatsAction::Execute(Event event) +{ + if (!bot) + return false; + + // Prefer the actual chat sender (whisper / say / etc.) if available. + Player* requester = nullptr; + + if (Unit* owner = event.getOwner()) + requester = owner->ToPlayer(); + + // Fallback to master if event owner is not available. + if (!requester) + requester = GetMaster(); + + // If we still do not have a valid player to answer to, bail out. + if (!requester) + return false; + + // PVP currencies + std::map currencyPlaceholders; + currencyPlaceholders["%arena_points"] = std::to_string(bot->GetArenaPoints()); + currencyPlaceholders["%honor_points"] = std::to_string(bot->GetHonorPoints()); + + std::string const currencyText = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pvp_currency", + "[PVP] Arena points: %arena_points | Honor Points: %honor_points", + currencyPlaceholders); + + bot->Whisper(currencyText, LANG_UNIVERSAL, requester); + + // Arena Teams by slot + bool anyTeam = false; + for (uint8 slot = 0; slot < MAX_ARENA_SLOT; ++slot) + { + uint32 const teamId = bot->GetArenaTeamId(slot); + if (!teamId) + continue; + + if (ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(teamId)) + { + anyTeam = true; + std::map placeholders; + placeholders["%bracket"] = BracketName(slot); + placeholders["%team_name"] = team->GetName(); + placeholders["%team_rating"] = std::to_string(team->GetRating()); + + std::string const teamText = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pvp_arena_team", + "[PVP] %bracket: <%team_name> (rating %team_rating)", + placeholders); + + bot->Whisper(teamText, LANG_UNIVERSAL, requester); + } + } + + if (!anyTeam) + { + std::string const noTeamText = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pvp_no_arena_team", + "[PVP] I have no Arena Team.", + std::map()); + + bot->Whisper(noTeamText, LANG_UNIVERSAL, requester); + } + + return true; +} diff --git a/src/Ai/Base/Actions/TellPvpStatsAction.h b/src/Ai/Base/Actions/TellPvpStatsAction.h new file mode 100644 index 0000000000..025cbd0d85 --- /dev/null +++ b/src/Ai/Base/Actions/TellPvpStatsAction.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_TELLPVPSTATSACTION_H +#define _PLAYERBOT_TELLPVPSTATSACTION_H + +#include "Action.h" + +class PlayerbotAI; + +class TellPvpStatsAction : public Action +{ +public: + TellPvpStatsAction(PlayerbotAI* botAI) : Action(botAI, "tell pvp stats") {} + + bool Execute(Event event) override; +}; +#endif \ No newline at end of file diff --git a/src/Ai/Base/ChatActionContext.h b/src/Ai/Base/ChatActionContext.h index 4873f52005..5e215e1237 100644 --- a/src/Ai/Base/ChatActionContext.h +++ b/src/Ai/Base/ChatActionContext.h @@ -67,6 +67,7 @@ #include "TellItemCountAction.h" #include "TellLosAction.h" #include "TellReputationAction.h" +#include "TellPvpStatsAction.h" #include "TellTargetAction.h" #include "TradeAction.h" #include "TrainerAction.h" @@ -97,6 +98,7 @@ class ChatActionContext : public NamedObjectContext creators["quests"] = &ChatActionContext::quests; creators["leave"] = &ChatActionContext::leave; creators["reputation"] = &ChatActionContext::reputation; + creators["tell pvp stats"] = &ChatActionContext::tell_pvp_stats; creators["log"] = &ChatActionContext::log; creators["los"] = &ChatActionContext::los; creators["rpg status"] = &ChatActionContext::rpg_status; @@ -279,6 +281,7 @@ class ChatActionContext : public NamedObjectContext static Action* quests(PlayerbotAI* botAI) { return new ListQuestsAction(botAI); } static Action* leave(PlayerbotAI* botAI) { return new LeaveGroupAction(botAI); } static Action* reputation(PlayerbotAI* botAI) { return new TellReputationAction(botAI); } + static Action* tell_pvp_stats(PlayerbotAI* botAI) { return new TellPvpStatsAction(botAI); } static Action* log(PlayerbotAI* botAI) { return new LogLevelAction(botAI); } static Action* los(PlayerbotAI* botAI) { return new TellLosAction(botAI); } static Action* rpg_status(PlayerbotAI* botAI) { return new TellRpgStatusAction(botAI); } diff --git a/src/Ai/Base/ChatTriggerContext.h b/src/Ai/Base/ChatTriggerContext.h index b3498ce301..3e71839bdc 100644 --- a/src/Ai/Base/ChatTriggerContext.h +++ b/src/Ai/Base/ChatTriggerContext.h @@ -24,6 +24,7 @@ class ChatTriggerContext : public NamedObjectContext creators["leave"] = &ChatTriggerContext::leave; creators["rep"] = &ChatTriggerContext::reputation; creators["reputation"] = &ChatTriggerContext::reputation; + creators["pvp stats"] = &ChatTriggerContext::pvp_stats; creators["log"] = &ChatTriggerContext::log; creators["los"] = &ChatTriggerContext::los; creators["rpg status"] = &ChatTriggerContext::rpg_status; @@ -224,6 +225,7 @@ class ChatTriggerContext : public NamedObjectContext static Trigger* stats(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "stats"); } static Trigger* leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "leave"); } static Trigger* reputation(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "reputation"); } + static Trigger* pvp_stats(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pvp stats"); } static Trigger* log(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "log"); } static Trigger* los(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "los"); } static Trigger* rpg_status(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rpg status"); } diff --git a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp index 0a81686a9d..04f7979012 100644 --- a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp @@ -27,6 +27,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger PassTroughStrategy::InitTriggers(triggers); triggers.push_back(new TriggerNode("rep", { NextAction("reputation", relevance) })); + triggers.push_back(new TriggerNode("pvp stats", { NextAction("tell pvp stats", relevance) })); triggers.push_back(new TriggerNode("q", { NextAction("query quest", relevance), NextAction("query item usage", relevance) })); triggers.push_back(new TriggerNode("add all loot", { NextAction("add all loot", relevance), @@ -116,6 +117,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas supported.push_back("stats"); supported.push_back("leave"); supported.push_back("reputation"); + supported.push_back("tell pvp stats"); supported.push_back("log"); supported.push_back("los"); supported.push_back("rpg status"); diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 34fc133645..437e62fc69 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -892,6 +892,7 @@ bool PlayerbotAI::IsAllowedCommand(std::string const text) unsecuredCommands.insert("invite"); unsecuredCommands.insert("leave"); unsecuredCommands.insert("lfg"); + unsecuredCommands.insert("pvp stats"); unsecuredCommands.insert("rpg status"); } From c86032f43b7bccb861c33769385db7a522e75513 Mon Sep 17 00:00:00 2001 From: Thomas Frey <49121001+elderbit@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:42:02 +0100 Subject: [PATCH 40/42] Convert PlayerBots tables to InnoDB (#2083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert PlayerBots tables to InnoDB (disable strict mode during conversion) # Pull Request ### This change converts the PlayerBots-related tables from MyISAM to InnoDB. **Why this is beneficial (even without fixing a specific bug):** - Crash safety & data integrity: InnoDB is transactional and uses redo logs; it provides automatic crash recovery, unlike MyISAM which can require manual repairs after unclean shutdowns. - Row-level locking: InnoDB reduces write contention and improves concurrency under bot-heavy workloads compared to MyISAM’s table-level locks. - Consistent reads: InnoDB supports MVCC, enabling stable reads while writes are happening—useful for mixed read/write access patterns. - Operational robustness: Better behavior under backup/restore and replication scenarios; fewer “table marked as crashed” style issues. Strict mode handling: The migration toggles innodb_strict_mode off only for the session to prevent the conversion from failing on edge-case legacy definitions, then re-enables it immediately after. --- ## How to Test the Changes - Step-by-step instructions to test the change Run the SQL script in the Playerbot database. - Any required setup (e.g. multiple players, bots, specific configuration) No - Expected behavior and how to verify it All tables should now have been converted from InnoDB to MyISAM. This script should return nothing: ``` SELECT t.TABLE_SCHEMA AS db_name, t.TABLE_NAME AS table_name, t.ENGINE AS storage_engine FROM information_schema.TABLES t WHERE t.TABLE_SCHEMA = DATABASE() -- With phpMyAdmin, use the following and insert your database name, e.g., “acore_playerbots.” -- WHERE t.TABLE_SCHEMA = 'YOUR_PLAYERBOT_DB' AND t.TABLE_TYPE = 'BASE TABLE' AND t.ENGINE = 'MyISAM' ORDER BY t.TABLE_NAME; ``` ## 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**) --- ## AI Assistance - Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - [x] No - [ ] Yes (**explain below**) --- ## Final Checklist - [x] Stability is not compromised - [x] Performance impact is understood, tested, and acceptable - [ ] Documentation updated if needed - [x] I tested this script on a server with 2000 bots for 6 days (running 24/h) and had no issues with it. --- ## Notes for Reviewers Anything that significantly improves realism at the cost of stability or performance should be carefully discussed before merging. --- .../updates/2026_01_30_00_change_to_InnoDB.sql | 9 +++++++++ .../updates/2026_01_30_00_change_to_InnoDB.sql | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 data/sql/characters/updates/2026_01_30_00_change_to_InnoDB.sql create mode 100644 data/sql/playerbots/updates/2026_01_30_00_change_to_InnoDB.sql diff --git a/data/sql/characters/updates/2026_01_30_00_change_to_InnoDB.sql b/data/sql/characters/updates/2026_01_30_00_change_to_InnoDB.sql new file mode 100644 index 0000000000..8c2c226b51 --- /dev/null +++ b/data/sql/characters/updates/2026_01_30_00_change_to_InnoDB.sql @@ -0,0 +1,9 @@ +-- Temporarily disables innodb_strict_mode for the session to allow the script to complete even if legacy table definitions contain InnoDB-incompatible attributes +SET SESSION innodb_strict_mode = 0; + +-- Change the tables to InnoDB +ALTER TABLE playerbots_guild_names ENGINE=InnoDB; +ALTER TABLE playerbots_names ENGINE=InnoDB; + +-- Re-enables innodb_strict_mode +SET SESSION innodb_strict_mode = 1; diff --git a/data/sql/playerbots/updates/2026_01_30_00_change_to_InnoDB.sql b/data/sql/playerbots/updates/2026_01_30_00_change_to_InnoDB.sql new file mode 100644 index 0000000000..c7f9c51f0a --- /dev/null +++ b/data/sql/playerbots/updates/2026_01_30_00_change_to_InnoDB.sql @@ -0,0 +1,18 @@ +-- Temporarily disables innodb_strict_mode for the session to allow the script to complete even if legacy table definitions contain InnoDB-incompatible attributes +SET SESSION innodb_strict_mode = 0; + +-- Change the tables to InnoDB +ALTER TABLE playerbots_dungeon_suggestion_abbrevation ENGINE=InnoDB; +ALTER TABLE playerbots_dungeon_suggestion_definition ENGINE=InnoDB; +ALTER TABLE playerbots_dungeon_suggestion_strategy ENGINE=InnoDB; +ALTER TABLE playerbots_equip_cache ENGINE=InnoDB; +ALTER TABLE playerbots_item_info_cache ENGINE=InnoDB; +ALTER TABLE playerbots_rarity_cache ENGINE=InnoDB; +ALTER TABLE playerbots_rnditem_cache ENGINE=InnoDB; +ALTER TABLE playerbots_tele_cache ENGINE=InnoDB; +ALTER TABLE playerbots_travelnode ENGINE=InnoDB; +ALTER TABLE playerbots_travelnode_link ENGINE=InnoDB; +ALTER TABLE playerbots_travelnode_path ENGINE=InnoDB; + +-- Re-enables innodb_strict_mode +SET SESSION innodb_strict_mode = 1; From 31765c77fa56d293019d0c8d1331c4c20530ee8b Mon Sep 17 00:00:00 2001 From: bashermens <31279994+hermensbas@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:48:58 +0100 Subject: [PATCH 41/42] Update PULL_REQUEST_TEMPLATE.md (#2114) --- PULL_REQUEST_TEMPLATE.md | 53 +++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 63d8f2397f..a23a7e67fa 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -66,38 +66,47 @@ Please answer the following: ## Complexity & Impact -- Does this change add new decision branches? - - [ ] No - - [ ] Yes (**explain below**) - -- Does this change increase per-bot or per-tick processing? - - [ ] No - - [ ] Yes (**describe and justify impact**) - -- Could this logic scale poorly under load? - - [ ] No - - [ ] Yes (**explain why**) - +Does this change add new decision branches? +``` +[ ] No +[ ] Yes (**explain below**) +``` + +Does this change increase per-bot or per-tick processing? +``` +[ ] No +[ ] Yes (**describe and justify impact**) +``` + +Could this logic scale poorly under load? +``` +[ ] No +[ ] Yes (**explain why**) +``` --- ## Defaults & Configuration -- Does this change modify default bot behavior? - - [ ] No - - [ ] Yes (**explain why**) +Does this change modify default bot behavior? +``` +[ ] No +[ ] Yes (**explain why**) +``` If this introduces more advanced or AI-heavy logic: - -- [ ] Lightweight mode remains the default -- [ ] More complex behavior is optional and thereby configurable - +``` +[ ] 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? - - [ ] No - - [ ] Yes (**explain below**) +Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? +``` +[ ] No +[ ] Yes (**explain below**) +``` If yes, please specify: From add33f595bba6bed53fa4e64e5b88fb712e41ebd Mon Sep 17 00:00:00 2001 From: Alex Dcnh <140754794+Wishmaster117@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:28:05 +0100 Subject: [PATCH 42/42] Remove commented code --- src/Ai/Base/Actions/ChatShortcutActions.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ai/Base/Actions/ChatShortcutActions.cpp b/src/Ai/Base/Actions/ChatShortcutActions.cpp index 19e075108a..037787bab5 100644 --- a/src/Ai/Base/Actions/ChatShortcutActions.cpp +++ b/src/Ai/Base/Actions/ChatShortcutActions.cpp @@ -251,7 +251,6 @@ bool NaxxChatShortcutAction::Execute(Event event) botAI->ChangeStrategy("+naxx", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+naxx", BOT_STATE_COMBAT); botAI->TellMasterNoFacing("Add Naxx Strategies!"); - // bot->Say("Add Naxx Strategies!", LANG_UNIVERSAL); return true; }